246 lines
7.8 KiB
C++
246 lines
7.8 KiB
C++
// Copyright (c) 2010 Google Inc. All Rights Reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are
|
|
// met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright
|
|
// notice, this list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above
|
|
// copyright notice, this list of conditions and the following disclaimer
|
|
// in the documentation and/or other materials provided with the
|
|
// distribution.
|
|
// * Neither the name of Google Inc. nor the names of its
|
|
// contributors may be used to endorse or promote products derived from
|
|
// this software without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include <assert.h>
|
|
#include <stdlib.h>
|
|
|
|
#include "common/dwarf/bytereader-inl.h"
|
|
#include "common/dwarf/bytereader.h"
|
|
|
|
namespace dwarf2reader {
|
|
|
|
ByteReader::ByteReader(enum Endianness endian)
|
|
:offset_reader_(NULL), address_reader_(NULL), endian_(endian),
|
|
address_size_(0), offset_size_(0),
|
|
have_section_base_(), have_text_base_(), have_data_base_(),
|
|
have_function_base_() { }
|
|
|
|
ByteReader::~ByteReader() { }
|
|
|
|
void ByteReader::SetOffsetSize(uint8 size) {
|
|
offset_size_ = size;
|
|
assert(size == 4 || size == 8);
|
|
if (size == 4) {
|
|
this->offset_reader_ = &ByteReader::ReadFourBytes;
|
|
} else {
|
|
this->offset_reader_ = &ByteReader::ReadEightBytes;
|
|
}
|
|
}
|
|
|
|
void ByteReader::SetAddressSize(uint8 size) {
|
|
address_size_ = size;
|
|
assert(size == 4 || size == 8);
|
|
if (size == 4) {
|
|
this->address_reader_ = &ByteReader::ReadFourBytes;
|
|
} else {
|
|
this->address_reader_ = &ByteReader::ReadEightBytes;
|
|
}
|
|
}
|
|
|
|
uint64 ByteReader::ReadInitialLength(const char* start, size_t* len) {
|
|
const uint64 initial_length = ReadFourBytes(start);
|
|
start += 4;
|
|
|
|
// In DWARF2/3, if the initial length is all 1 bits, then the offset
|
|
// size is 8 and we need to read the next 8 bytes for the real length.
|
|
if (initial_length == 0xffffffff) {
|
|
SetOffsetSize(8);
|
|
*len = 12;
|
|
return ReadOffset(start);
|
|
} else {
|
|
SetOffsetSize(4);
|
|
*len = 4;
|
|
}
|
|
return initial_length;
|
|
}
|
|
|
|
bool ByteReader::ValidEncoding(DwarfPointerEncoding encoding) const {
|
|
if (encoding == DW_EH_PE_omit) return true;
|
|
if (encoding == DW_EH_PE_aligned) return true;
|
|
if ((encoding & 0x7) > DW_EH_PE_udata8)
|
|
return false;
|
|
if ((encoding & 0x70) > DW_EH_PE_funcrel)
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool ByteReader::UsableEncoding(DwarfPointerEncoding encoding) const {
|
|
switch (encoding & 0x70) {
|
|
case DW_EH_PE_absptr: return true;
|
|
case DW_EH_PE_pcrel: return have_section_base_;
|
|
case DW_EH_PE_textrel: return have_text_base_;
|
|
case DW_EH_PE_datarel: return have_data_base_;
|
|
case DW_EH_PE_funcrel: return have_function_base_;
|
|
default: return false;
|
|
}
|
|
}
|
|
|
|
uint64 ByteReader::ReadEncodedPointer(const char *buffer,
|
|
DwarfPointerEncoding encoding,
|
|
size_t *len) const {
|
|
// UsableEncoding doesn't approve of DW_EH_PE_omit, so we shouldn't
|
|
// see it here.
|
|
assert(encoding != DW_EH_PE_omit);
|
|
|
|
// The Linux Standards Base 4.0 does not make this clear, but the
|
|
// GNU tools (gcc/unwind-pe.h; readelf/dwarf.c; gdb/dwarf2-frame.c)
|
|
// agree that aligned pointers are always absolute, machine-sized,
|
|
// machine-signed pointers.
|
|
if (encoding == DW_EH_PE_aligned) {
|
|
assert(have_section_base_);
|
|
|
|
// We don't need to align BUFFER in *our* address space. Rather, we
|
|
// need to find the next position in our buffer that would be aligned
|
|
// when the .eh_frame section the buffer contains is loaded into the
|
|
// program's memory. So align assuming that buffer_base_ gets loaded at
|
|
// address section_base_, where section_base_ itself may or may not be
|
|
// aligned.
|
|
|
|
// First, find the offset to START from the closest prior aligned
|
|
// address.
|
|
uint64 skew = section_base_ & (AddressSize() - 1);
|
|
// Now find the offset from that aligned address to buffer.
|
|
uint64 offset = skew + (buffer - buffer_base_);
|
|
// Round up to the next boundary.
|
|
uint64 aligned = (offset + AddressSize() - 1) & -AddressSize();
|
|
// Convert back to a pointer.
|
|
const char *aligned_buffer = buffer_base_ + (aligned - skew);
|
|
// Finally, store the length and actually fetch the pointer.
|
|
*len = aligned_buffer - buffer + AddressSize();
|
|
return ReadAddress(aligned_buffer);
|
|
}
|
|
|
|
// Extract the value first, ignoring whether it's a pointer or an
|
|
// offset relative to some base.
|
|
uint64 offset;
|
|
switch (encoding & 0x0f) {
|
|
case DW_EH_PE_absptr:
|
|
// DW_EH_PE_absptr is weird, as it is used as a meaningful value for
|
|
// both the high and low nybble of encoding bytes. When it appears in
|
|
// the high nybble, it means that the pointer is absolute, not an
|
|
// offset from some base address. When it appears in the low nybble,
|
|
// as here, it means that the pointer is stored as a normal
|
|
// machine-sized and machine-signed address. A low nybble of
|
|
// DW_EH_PE_absptr does not imply that the pointer is absolute; it is
|
|
// correct for us to treat the value as an offset from a base address
|
|
// if the upper nybble is not DW_EH_PE_absptr.
|
|
offset = ReadAddress(buffer);
|
|
*len = AddressSize();
|
|
break;
|
|
|
|
case DW_EH_PE_uleb128:
|
|
offset = ReadUnsignedLEB128(buffer, len);
|
|
break;
|
|
|
|
case DW_EH_PE_udata2:
|
|
offset = ReadTwoBytes(buffer);
|
|
*len = 2;
|
|
break;
|
|
|
|
case DW_EH_PE_udata4:
|
|
offset = ReadFourBytes(buffer);
|
|
*len = 4;
|
|
break;
|
|
|
|
case DW_EH_PE_udata8:
|
|
offset = ReadEightBytes(buffer);
|
|
*len = 8;
|
|
break;
|
|
|
|
case DW_EH_PE_sleb128:
|
|
offset = ReadSignedLEB128(buffer, len);
|
|
break;
|
|
|
|
case DW_EH_PE_sdata2:
|
|
offset = ReadTwoBytes(buffer);
|
|
// Sign-extend from 16 bits.
|
|
offset = (offset ^ 0x8000) - 0x8000;
|
|
*len = 2;
|
|
break;
|
|
|
|
case DW_EH_PE_sdata4:
|
|
offset = ReadFourBytes(buffer);
|
|
// Sign-extend from 32 bits.
|
|
offset = (offset ^ 0x80000000ULL) - 0x80000000ULL;
|
|
*len = 4;
|
|
break;
|
|
|
|
case DW_EH_PE_sdata8:
|
|
// No need to sign-extend; this is the full width of our type.
|
|
offset = ReadEightBytes(buffer);
|
|
*len = 8;
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
// Find the appropriate base address.
|
|
uint64 base;
|
|
switch (encoding & 0x70) {
|
|
case DW_EH_PE_absptr:
|
|
base = 0;
|
|
break;
|
|
|
|
case DW_EH_PE_pcrel:
|
|
assert(have_section_base_);
|
|
base = section_base_ + (buffer - buffer_base_);
|
|
break;
|
|
|
|
case DW_EH_PE_textrel:
|
|
assert(have_text_base_);
|
|
base = text_base_;
|
|
break;
|
|
|
|
case DW_EH_PE_datarel:
|
|
assert(have_data_base_);
|
|
base = data_base_;
|
|
break;
|
|
|
|
case DW_EH_PE_funcrel:
|
|
assert(have_function_base_);
|
|
base = function_base_;
|
|
break;
|
|
|
|
default:
|
|
abort();
|
|
}
|
|
|
|
uint64 pointer = base + offset;
|
|
|
|
// Remove inappropriate upper bits.
|
|
if (AddressSize() == 4)
|
|
pointer = pointer & 0xffffffff;
|
|
else
|
|
assert(AddressSize() == sizeof(uint64));
|
|
|
|
return pointer;
|
|
}
|
|
|
|
} // namespace dwarf2reader
|