/* Copyright (C) 2007-2010 The Android Open Source Project | |
** | |
** This software is licensed under the terms of the GNU General Public | |
** License version 2, as published by the Free Software Foundation, and | |
** may be copied, distributed, and modified under those terms. | |
** | |
** This program is distributed in the hope that it will be useful, | |
** but WITHOUT ANY WARRANTY; without even the implied warranty of | |
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
** GNU General Public License for more details. | |
*/ | |
/* | |
* Contains declarations of types, constants and structures | |
* describing DWARF format. | |
*/ | |
#ifndef ELFF_DWARF_DEFS_H_ | |
#define ELFF_DWARF_DEFS_H_ | |
#include "dwarf.h" | |
#include "elf_defs.h" | |
/* DWARF structures are packed to 1 byte. */ | |
#define ELFF_PACKED __attribute__ ((packed)) | |
/* | |
* Helper types for misc. DWARF variables. | |
*/ | |
/* Type for DWARF abbreviation number. */ | |
typedef uint32_t Dwarf_AbbrNum; | |
/* Type for DWARF tag ID. */ | |
typedef uint16_t Dwarf_Tag; | |
/* Type for DWARF attribute ID. */ | |
typedef uint16_t Dwarf_At; | |
/* Type for DWARF form ID. */ | |
typedef uint16_t Dwarf_Form; | |
/* Type for offset in 32-bit DWARF. */ | |
typedef uint32_t Dwarf32_Off; | |
/* Type for offset in 64-bit DWARF. */ | |
typedef uint64_t Dwarf64_Off; | |
/* Enumerates types of values, obtained during DWARF attribute decoding. */ | |
typedef enum DwarfValueType { | |
/* Undefined */ | |
DWARF_VALUE_UNKNOWN = 1, | |
/* uint8_t */ | |
DWARF_VALUE_U8, | |
/* int8_t */ | |
DWARF_VALUE_S8, | |
/* uint16_t */ | |
DWARF_VALUE_U16, | |
/* int16_t */ | |
DWARF_VALUE_S16, | |
/* uint32_t */ | |
DWARF_VALUE_U32, | |
/* int32_t */ | |
DWARF_VALUE_S32, | |
/* uint64_t */ | |
DWARF_VALUE_U64, | |
/* int64_t */ | |
DWARF_VALUE_S64, | |
/* const char* */ | |
DWARF_VALUE_STR, | |
/* 32-bit address */ | |
DWARF_VALUE_PTR32, | |
/* 64-bit address */ | |
DWARF_VALUE_PTR64, | |
/* Dwarf_Block */ | |
DWARF_VALUE_BLOCK, | |
} DwarfValueType; | |
/* Describes block of data, stored directly in the mapped .debug_info | |
* section. This type is used to represent an attribute encoded with | |
* DW_FORM_block# form. | |
*/ | |
typedef struct Dwarf_Block { | |
/* Pointer to the block data inside mapped .debug_info section. */ | |
const void* block_ptr; | |
/* Byte size of the block data. */ | |
Elf_Word block_size; | |
} Dwarf_Block; | |
/* Describes a value, obtained from the mapped .debug_info section | |
* during DWARF attribute decoding. | |
*/ | |
typedef struct Dwarf_Value { | |
/* Unites all possible data types for the value. | |
* See DwarfValueType for the list of types. | |
*/ | |
union { | |
Elf_Byte u8; | |
Elf_Sbyte s8; | |
Elf_Half u16; | |
Elf_Shalf s16; | |
Elf_Word u32; | |
Elf_Sword s32; | |
Elf_Xword u64; | |
Elf_Sxword s64; | |
Elf_Word ptr32; | |
Elf_Xword ptr64; | |
const char* str; | |
Dwarf_Block block; | |
}; | |
/* Value type (defines which variable in the union abowe | |
* contains the value). | |
*/ | |
DwarfValueType type; | |
/* Number of bytes that encode this value in .debug_info section | |
* of ELF file. | |
*/ | |
Elf_Word encoded_size; | |
} Dwarf_Value; | |
/* DWARF's LEB128 data type. LEB128 is defined as: | |
* Variable Length Data. "Little Endian Base 128" (LEB128) numbers. LEB128 is | |
* a scheme for encoding integers densely that exploits the assumption that | |
* most integers are small in magnitude. (This encoding is equally suitable | |
* whether the target machine architecture represents data in big-endian or | |
* littleendian order. It is "little endian" only in the sense that it avoids | |
* using space to represent the "big" end of an unsigned integer, when the big | |
* end is all zeroes or sign extension bits). | |
* | |
* Unsigned LEB128 numbers are encoded as follows: start at the low order end | |
* of an unsigned integer and chop it into 7-bit chunks. Place each chunk into | |
* the low order 7 bits of a byte. Typically, several of the high order bytes | |
* will be zero; discard them. Emit the remaining bytes in a stream, starting | |
* with the low order byte; set the high order bit on each byte except the last | |
* emitted byte. The high bit of zero on the last byte indicates to the decoder | |
* that it has encountered the last byte. The integer zero is a special case, | |
* consisting of a single zero byte. | |
* | |
* The encoding for signed LEB128 numbers is similar, except that the criterion | |
* for discarding high order bytes is not whether they are zero, but whether | |
* they consist entirely of sign extension bits. Consider the 32-bit integer | |
* -2. The three high level bytes of the number are sign extension, thus LEB128 | |
* would represent it as a single byte containing the low order 7 bits, with | |
* the high order bit cleared to indicate the end of the byte stream. Note that | |
* there is nothing within the LEB128 representation that indicates whether an | |
* encoded number is signed or unsigned. The decoder must know what type of | |
* number to expect. | |
* | |
* NOTE: It's assumed that LEB128 will not contain encodings for integers, | |
* larger than 64 bit. | |
*/ | |
typedef struct ELFF_PACKED Dwarf_Leb128 { | |
/* Beginning of the LEB128 block. */ | |
Elf_Byte val; | |
/* Pulls actual value, encoded with this LEB128 block. | |
* Param: | |
* value - Upon return will contain value, encoded with this LEB128 block. | |
* sign - If true, the caller expects the LEB128 to contain a signed | |
* integer, otherwise, caller expects an unsigned integer value to be | |
* encoded with this LEB128 block. | |
*/ | |
void get_common(Dwarf_Value* value, bool sign) const { | |
value->u64 = 0; | |
/* Integer zero is a special case. */ | |
if (val == 0) { | |
value->type = sign ? DWARF_VALUE_S32 : DWARF_VALUE_U32; | |
value->encoded_size = 1; | |
return; | |
} | |
/* We've got to reconstruct the integer. */ | |
value->type = DWARF_VALUE_UNKNOWN; | |
value->encoded_size = 0; | |
/* Byte by byte loop though the LEB128, reconstructing the integer from | |
* 7-bits chunks. Byte with 8-th bit set to zero indicates the end | |
* of the LEB128 block. For signed integers, 7-th bit of the last LEB128 | |
* byte controls the sign. If 7-th bit of the last LEB128 byte is set, | |
* the integer is negative. If 7-th bit of the last LEB128 byte is not | |
* set, the integer is positive. | |
*/ | |
const Elf_Byte* cur = &val; | |
Elf_Word shift = 0; | |
while ((*cur & 0x80) != 0) { | |
value->u64 |= (static_cast<Elf_Xword>(*cur) & 0x7F) << shift; | |
shift += 7; | |
value->encoded_size++; | |
cur++; | |
} | |
value->u64 |= (static_cast<Elf_Xword>(*cur) & 0x7F) << shift; | |
value->encoded_size++; | |
/* LEB128 format doesn't carry any info of the sizeof of the integer it | |
* represents. We well guess it, judging by the highest bit set in the | |
* reconstucted integer. | |
*/ | |
if ((value->u64 & 0xFFFFFFFF00000000LL) == 0) { | |
/* 32-bit integer. */ | |
if (sign) { | |
value->type = DWARF_VALUE_S32; | |
if (((*cur) & 0x40) != 0) { | |
// Value is negative. | |
value->u64 |= - (1 << (shift + 7)); | |
} else if ((value->u32 & 0x80000000) != 0) { | |
// Make sure we don't report negative value in this case. | |
value->type = DWARF_VALUE_S64; | |
} | |
} else { | |
value->type = DWARF_VALUE_U32; | |
} | |
} else { | |
/* 64-bit integer. */ | |
if (sign) { | |
value->type = DWARF_VALUE_S64; | |
if (((*cur) & 0x40) != 0) { | |
// Value is negative. | |
value->u64 |= - (1 << (shift + 7)); | |
} | |
} else { | |
value->type = DWARF_VALUE_U64; | |
} | |
} | |
} | |
/* Pulls actual unsigned value, encoded with this LEB128 block. | |
* See get_common() for more info. | |
* Param: | |
* value - Upon return will contain unsigned value, encoded with | |
* this LEB128 block. | |
*/ | |
void get_unsigned(Dwarf_Value* value) const { | |
get_common(value, false); | |
} | |
/* Pulls actual signed value, encoded with this LEB128 block. | |
* See get_common() for more info. | |
* Param: | |
* value - Upon return will contain signed value, encoded with | |
* this LEB128 block. | |
*/ | |
void get_signed(Dwarf_Value* value) const { | |
get_common(value, true); | |
} | |
/* Pulls LEB128 value, advancing past this LEB128 block. | |
* See get_common() for more info. | |
* Return: | |
* Pointer to the byte past this LEB128 block. | |
*/ | |
const void* process(Dwarf_Value* value, bool sign) const { | |
get_common(value, sign); | |
return INC_CPTR(&val, value->encoded_size); | |
} | |
/* Pulls LEB128 unsigned value, advancing past this LEB128 block. | |
* See process() for more info. | |
*/ | |
const void* process_unsigned(Dwarf_Value* value) const { | |
return process(value, false); | |
} | |
/* Pulls LEB128 signed value, advancing past this LEB128 block. | |
* See process() for more info. | |
*/ | |
const void* process_signed(Dwarf_Value* value) const { | |
return process(value, true); | |
} | |
} Dwarf_Leb128; | |
/* DIE attribute descriptor in the .debug_abbrev section. | |
* Attribute descriptor contains two LEB128 values. First one provides | |
* attribute ID (one of DW_AT_XXX values), and the second one provides | |
* format (one of DW_FORMAT_XXX values), in which attribute value is | |
* encoded in the .debug_info section of the ELF file. | |
*/ | |
typedef struct ELFF_PACKED Dwarf_Abbr_AT { | |
/* Attribute ID (DW_AT_XXX). | |
* Attribute format (DW_FORMAT_XXX) follows immediately. | |
*/ | |
Dwarf_Leb128 at; | |
/* Checks if this is a separator descriptor. | |
* Zero is an invalid attribute ID, indicating the end of attribute | |
* list for the current DIE. | |
*/ | |
bool is_separator() const { | |
return at.val == 0; | |
} | |
/* Pulls attribute data, advancing past this descriptor. | |
* Param: | |
* at_value - Upon return contains attribute value of this descriptor. | |
* form - Upon return contains form value of this descriptor. | |
* Return: | |
* Pointer to the byte past this descriptor block (usually, next | |
* attribute decriptor). | |
*/ | |
const Dwarf_Abbr_AT* process(Dwarf_At* at_value, Dwarf_Form* form) const { | |
if (is_separator()) { | |
/* Size of separator descriptor is always 2 bytes. */ | |
*at_value = 0; | |
*form = 0; | |
return INC_CPTR_T(Dwarf_Abbr_AT, &at.val, 2); | |
} | |
Dwarf_Value val; | |
/* Process attribute ID. */ | |
const Dwarf_Leb128* next = | |
reinterpret_cast<const Dwarf_Leb128*>(at.process_unsigned(&val)); | |
*at_value = val.u16; | |
/* Follow with processing the form. */ | |
next = reinterpret_cast<const Dwarf_Leb128*>(next->process_unsigned(&val)); | |
*form = val.u16; | |
return reinterpret_cast<const Dwarf_Abbr_AT*>(next); | |
} | |
} Dwarf_Abbr_AT; | |
/* DIE abbreviation descriptor in the .debug_abbrev section. | |
* DIE abbreviation descriptor contains three parameters. The first one is a | |
* LEB128 value, that encodes 1 - based abbreviation descriptor number. | |
* Abbreviation descriptor numbers seems to be always in sequential order, and | |
* are counted on per-compilation unit basis. I.e. abbreviation number for the | |
* first DIE abbreviation descriptor of each compilation unit is always 1. | |
* | |
* Besides abbreviation number, DIE abbreviation descriptor contains two more | |
* values. The first one (after abbr_num) is a LEB128 value containing DIE's | |
* tag value, and the second one is one byte flag specifying whether or not | |
* the DIE contains any cildren. | |
* | |
* This descriptor is immediately followed by a list of attribute descriptors | |
* (see Dwarf_Abbr_AT) for the DIE represented by this abbreviation descriptor. | |
*/ | |
typedef struct ELFF_PACKED Dwarf_Abbr_DIE { | |
/* 1 - based abbreviation number for the DIE. */ | |
Dwarf_Leb128 abbr_num; | |
/* Gets abbreviation number for this descriptor. */ | |
Dwarf_AbbrNum get_abbr_num() const { | |
Dwarf_Value val; | |
abbr_num.get_unsigned(&val); | |
return val.u16; | |
} | |
/* Gets DIE tag for this descriptor. */ | |
Dwarf_Tag get_tag() const { | |
Dwarf_Tag tag; | |
process(NULL, &tag); | |
return tag; | |
} | |
/* Pulls DIE abbreviation descriptor data, advancing past this descriptor. | |
* Param: | |
* abbr_index - Upon return contains abbreviation number for this | |
* descriptor. This parameter can be NULL, if the caller is not interested | |
* in this value. | |
* tag - Upon return contains tag of the DIE for this descriptor. This | |
* parameter can be NULL, if the caller is not interested in this value. | |
* form - Upon return contains form of the DIE for this descriptor. | |
* Return: | |
* Pointer to the list of attribute descriptors for the DIE. | |
*/ | |
const Dwarf_Abbr_AT* process(Dwarf_AbbrNum* abbr_index, | |
Dwarf_Tag* tag) const { | |
Dwarf_Value val; | |
const Dwarf_Leb128* next = | |
reinterpret_cast<const Dwarf_Leb128*>(abbr_num.process_unsigned(&val)); | |
if (abbr_index != NULL) { | |
*abbr_index = val.u32; | |
} | |
/* Next one is a "tag". */ | |
next = reinterpret_cast<const Dwarf_Leb128*>(next->process_unsigned(&val)); | |
if (tag != NULL) { | |
*tag = val.u16; | |
} | |
/* Next one is a "has children" one byte flag. We're not interested in it, | |
* so jump to the list of attribute descriptors that immediately follows | |
* this DIE descriptor. */ | |
return INC_CPTR_T(Dwarf_Abbr_AT, next, 1); | |
} | |
} Dwarf_Abbr_DIE; | |
/* DIE descriptor in the .debug_info section. | |
* DIE descriptor contains one LEB128-encoded value, containing DIE's | |
* abbreviation descriptor number in the .debug_abbrev section. | |
* | |
* DIE descriptor is immediately followed by the list of DIE attribute values, | |
* format of wich is defined by the list of attribute descriptors in the | |
* .debug_abbrev section, that immediately follow the DIE attribute descriptor, | |
* addressed by this descriptor's abbr_num LEB128. | |
*/ | |
typedef struct ELFF_PACKED Dwarf_DIE { | |
/* 1 - based index of DIE abbreviation descriptor (Dwarf_Abbr_DIE) for this | |
* DIE in the .debug_abbrev section. | |
* | |
* NOTE: DIE abbreviation descriptor indexes are tied to the compilation | |
* unit. In other words, each compilation unit restarts counting DIE | |
* abbreviation descriptors from 1. | |
* | |
* NOTE: Zero is invalid value for this field, indicating that this DIE is a | |
* separator (usually it ends a list of "child" DIEs) | |
*/ | |
Dwarf_Leb128 abbr_num; | |
/* Checks if this is a separator DIE. */ | |
bool is_separator() const { | |
return abbr_num.val == 0; | |
} | |
/* Gets (1 - based) abbreviation number for this DIE. */ | |
Dwarf_AbbrNum get_abbr_num() const { | |
Dwarf_Value val; | |
abbr_num.get_unsigned(&val); | |
return val.u16; | |
} | |
/* Pulls DIE information, advancing past this descriptor to DIE attributes. | |
* Param: | |
* abbr_num - Upon return contains abbreviation number for this DIE. This | |
* parameter can be NULL, if the caller is not interested in this value. | |
* Return: | |
* Pointer to the byte past this descriptor (the list of DIE attributes). | |
*/ | |
const Elf_Byte* process(Dwarf_AbbrNum* abbr_number) const { | |
if (is_separator()) { | |
if (abbr_number != NULL) { | |
*abbr_number = 0; | |
} | |
// Size of a separator DIE is 1 byte. | |
return INC_CPTR_T(Elf_Byte, &abbr_num.val, 1); | |
} | |
Dwarf_Value val; | |
const void* ret = abbr_num.process_unsigned(&val); | |
if (abbr_number != NULL) { | |
*abbr_number = val.u32; | |
} | |
return reinterpret_cast<const Elf_Byte*>(ret); | |
} | |
} Dwarf_DIE; | |
/* | |
* Variable size headers. | |
* When encoding size value in DWARF, the first 32 bits of a "size" header | |
* define header type. If first 32 bits of the header contain 0xFFFFFFFF | |
* value, this is 64-bit size header with the following 64 bits encoding | |
* the size. Otherwise, if first 32 bits are not 0xFFFFFFFF, they contain | |
* 32-bit size value. | |
*/ | |
/* Size header for 32-bit DWARF. */ | |
typedef struct ELFF_PACKED Dwarf32_SizeHdr { | |
/* Size value. */ | |
Elf_Word size; | |
} Dwarf32_SizeHdr; | |
/* Size header for 64-bit DWARF. */ | |
typedef struct ELFF_PACKED Dwarf64_SizeHdr { | |
/* Size selector. For 64-bit DWARF this field is set to 0xFFFFFFFF */ | |
Elf_Word size_selector; | |
/* Actual size value. */ | |
Elf_Xword size; | |
} Dwarf64_SizeHdr; | |
/* Compilation unit header in the .debug_info section. | |
* Template param: | |
* Dwarf_SizeHdr - Type for the header's size field. Must be Dwarf32_SizeHdr | |
* for 32-bit DWARF, or Dwarf64_SizeHdr for 64-bit DWARF. | |
* Elf_Off - Type for abbrev_offset field. Must be Elf_Word for for 32-bit | |
* DWARF, or Elf_Xword for 64-bit DWARF. | |
*/ | |
template <typename Dwarf_SizeHdr, typename Elf_Off> | |
struct ELFF_PACKED Dwarf_CUHdr { | |
/* Size of the compilation unit data in .debug_info section. */ | |
Dwarf_SizeHdr size_hdr; | |
/* Compilation unit's DWARF version stamp. */ | |
Elf_Half version; | |
/* Relative (to the beginning of .debug_abbrev section data) offset of the | |
* beginning of abbreviation sequence for this compilation unit. | |
*/ | |
Elf_Off abbrev_offset; | |
/* Pointer size for this compilation unit (should be 4, or 8). */ | |
Elf_Byte address_size; | |
}; | |
/* Compilation unit header in the .debug_info section for 32-bit DWARF. */ | |
typedef Dwarf_CUHdr<Dwarf32_SizeHdr, Elf_Word> Dwarf32_CUHdr; | |
/* Compilation unit header in the .debug_info section for 64-bit DWARF. */ | |
typedef Dwarf_CUHdr<Dwarf64_SizeHdr, Elf_Xword> Dwarf64_CUHdr; | |
/* CU STMTL header in the .debug_line section. | |
* Template param: | |
* Dwarf_SizeHdr - Type for the header's size field. Must be Dwarf32_SizeHdr | |
* for 32-bit DWARF, or Dwarf64_SizeHdr for 64-bit DWARF. | |
* Elf_Size - Type for header_length field. Must be Elf_Word for for 32-bit | |
* DWARF, or Elf_Xword for 64-bit DWARF. | |
*/ | |
template <typename Dwarf_SizeHdr, typename Elf_Size> | |
struct ELFF_PACKED Dwarf_STMTLHdr { | |
/* The size in bytes of the line number information for this compilation | |
* unit, not including the unit_length field itself. */ | |
Dwarf_SizeHdr unit_length; | |
/* A version number. This number is specific to the line number information | |
* and is independent of the DWARF version number. */ | |
Elf_Half version; | |
/* The number of bytes following the header_length field to the beginning of | |
* the first byte of the line number program itself. In the 32-bit DWARF | |
* format, this is a 4-byte unsigned length; in the 64-bit DWARF format, | |
* this field is an 8-byte unsigned length. */ | |
Elf_Size header_length; | |
/* The size in bytes of the smallest target machine instruction. Line number | |
* program opcodes that alter the address register first multiply their | |
* operands by this value. */ | |
Elf_Byte min_instruction_len; | |
/* The initial value of the is_stmt register. */ | |
Elf_Byte default_is_stmt; | |
/* This parameter affects the meaning of the special opcodes. */ | |
Elf_Sbyte line_base; | |
/* This parameter affects the meaning of the special opcodes. */ | |
Elf_Byte line_range; | |
/* The number assigned to the first special opcode. */ | |
Elf_Byte opcode_base; | |
/* This is first opcode in an array specifying the number of LEB128 operands | |
* for each of the standard opcodes. The first element of the array | |
* corresponds to the opcode whose value is 1, and the last element | |
* corresponds to the opcode whose value is opcode_base - 1. By increasing | |
* opcode_base, and adding elements to this array, new standard opcodes can | |
* be added, while allowing consumers who do not know about these new opcodes | |
* to be able to skip them. NOTE: this array points to the mapped | |
* .debug_line section. */ | |
Elf_Byte standard_opcode_lengths; | |
}; | |
/* CU STMTL header in the .debug_line section for 32-bit DWARF. */ | |
typedef Dwarf_STMTLHdr<Dwarf32_SizeHdr, Elf_Word> Dwarf32_STMTLHdr; | |
/* CU STMTL header in the .debug_line section for 64-bit DWARF. */ | |
typedef Dwarf_STMTLHdr<Dwarf64_SizeHdr, Elf_Xword> Dwarf64_STMTLHdr; | |
/* Source file descriptor in the .debug_line section. | |
* Descriptor begins with zero-terminated file name, followed by an ULEB128, | |
* encoding directory index in the list of included directories, followed by | |
* an ULEB12, encoding file modification time, followed by an ULEB12, encoding | |
* file size. | |
*/ | |
typedef struct ELFF_PACKED Dwarf_STMTL_FileDesc { | |
/* Zero-terminated file name. */ | |
char file_name[1]; | |
/* Checks of this descriptor ends the list. */ | |
bool is_last_entry() const { | |
return file_name[0] == '\0'; | |
} | |
/* Gets file name. */ | |
const char* get_file_name() const { | |
return file_name; | |
} | |
/* Processes this descriptor, advancing to the next one. | |
* Param: | |
* dir_index - Upon return contains index of the parent directory in the | |
* list of included directories. Can be NULL if caller is not interested | |
* in this value. | |
* Return: | |
* Pointer to the next source file descriptor in the list. | |
*/ | |
const Dwarf_STMTL_FileDesc* process(Elf_Word* dir_index) const { | |
if (is_last_entry()) { | |
return this; | |
} | |
/* First parameter: include directory index. */ | |
Dwarf_Value tmp; | |
const Dwarf_Leb128* leb = | |
INC_CPTR_T(Dwarf_Leb128, file_name, strlen(file_name) + 1); | |
leb = reinterpret_cast<const Dwarf_Leb128*>(leb->process_unsigned(&tmp)); | |
if (dir_index != NULL) { | |
*dir_index = tmp.u32; | |
} | |
/* Process file time. */ | |
leb = reinterpret_cast<const Dwarf_Leb128*>(leb->process_unsigned(&tmp)); | |
/* Process file size. */ | |
return reinterpret_cast<const Dwarf_STMTL_FileDesc*>(leb->process_unsigned(&tmp)); | |
} | |
/* Gets directory index for this descriptor. */ | |
Elf_Word get_dir_index() const { | |
assert(!is_last_entry()); | |
if (is_last_entry()) { | |
return 0; | |
} | |
/* Get directory index. */ | |
Dwarf_Value ret; | |
const Dwarf_Leb128* leb = | |
INC_CPTR_T(Dwarf_Leb128, file_name, strlen(file_name) + 1); | |
leb->process_unsigned(&ret); | |
return ret.u32; | |
} | |
} Dwarf_STMTL_FileDesc; | |
/* Encapsulates a DIE attribute, collected during ELF file parsing. | |
*/ | |
class DIEAttrib { | |
public: | |
/* Constructs DIEAttrib intance. */ | |
DIEAttrib() | |
: at_(0), | |
form_(0) { | |
value_.type = DWARF_VALUE_UNKNOWN; | |
} | |
/* Destructs DIEAttrib intance. */ | |
~DIEAttrib() { | |
} | |
/* Gets DWARF attribute ID (DW_AT_Xxx) for this property. */ | |
Dwarf_At at() const { | |
return at_; | |
} | |
/* Gets DWARF form ID (DW_FORM_Xxx) for this property. */ | |
Dwarf_Form form() const { | |
return form_; | |
} | |
/* Gets value of this property. */ | |
const Dwarf_Value* value() const { | |
return &value_; | |
} | |
/* Value of this property. */ | |
Dwarf_Value value_; | |
/* DWARF attribute ID (DW_AT_Xxx) for this property. */ | |
Dwarf_At at_; | |
/* DWARF form ID (DW_FORM_Xxx) for this property. */ | |
Dwarf_Form form_; | |
}; | |
/* Parse tag context. | |
* This structure is used as an ELF file parsing parameter, limiting collected | |
* DIEs by the list of tags. | |
*/ | |
typedef struct DwarfParseContext { | |
/* Zero-terminated list of tags to collect DIEs for. If this field is NULL, | |
* DIEs for all tags will be collected during the parsing. */ | |
const Dwarf_Tag* tags; | |
} DwarfParseContext; | |
/* Checks if a DIE with the given tag should be collected during the parsing. | |
* Param: | |
* parse_context - Parse context to check the tag against. This parameter can | |
* be NULL, indicating that all tags should be collected. | |
* tag - Tag to check. | |
* Return: | |
* true if a DIE with the given tag should be collected during the parsing, | |
* or false, if the DIE should not be collected. | |
*/ | |
static inline bool | |
collect_die(const DwarfParseContext* parse_context, Dwarf_Tag tag) { | |
if (parse_context == NULL || parse_context->tags == NULL) { | |
return true; | |
} | |
for (const Dwarf_Tag* tags = parse_context->tags; *tags != 0; tags++) { | |
if (*tags == tag) { | |
return true; | |
} | |
} | |
return false; | |
} | |
/* Encapsulates an array of Dwarf_Abbr_DIE pointers, cached for a compilation | |
* unit. Although Dwarf_Abbr_DIE descriptors in the .debug_abbrev section of | |
* the ELF file seems to be always in sequential order, DIE descriptors may | |
* reference them randomly. So, to provide better performance, we will cache | |
* all Dwarf_Abbr_DIE pointers, that were found for each DIE. Since all of the | |
* Dwarf_Abbr_DIE are sequential, an array is the best way to cache them. | |
* | |
* NOTE: Objects of this class are instantiated one per each CU, as all DIE | |
* abbreviation numberation is restarted from 1 for each new CU. | |
*/ | |
class DwarfAbbrDieArray { | |
public: | |
/* Constructs DwarfAbbrDieArray instance. | |
* Most of the CUs don't have too many unique Dwarf_Abbr_DIEs, so, in order | |
* to decrease the amount of memory allocation calls, we will preallocate | |
* a relatively small array for them along with the instance of this class, | |
* hopping, that all Dwarf_Abbr_DIEs for the CU will fit into it. | |
*/ | |
DwarfAbbrDieArray() | |
: array_(&small_array_[0]), | |
array_size_(ELFF_ARRAY_SIZE(small_array_)), | |
count_(0) { | |
} | |
/* Destructs DwarfAbbrDieArray instance. */ | |
~DwarfAbbrDieArray() { | |
if (array_ != &small_array_[0]) { | |
delete[] array_; | |
} | |
} | |
/* Adds new entry to the array | |
* Param: | |
* abbr - New entry to add. | |
* num - Abbreviation number for the adding entry. | |
* NOTE: before adding, this method will verify that descriptor for the | |
* given abbreviation number has not been cached yet. | |
* NOTE: due to the nature of this array, entries MUST be added strictly | |
* in sequential order. | |
* Return: | |
* true on success, false on failure. | |
*/ | |
bool add(const Dwarf_Abbr_DIE* abbr, Dwarf_AbbrNum num) { | |
assert(num != 0); | |
if (num == 0) { | |
// Zero is illegal DIE abbreviation number. | |
_set_errno(EINVAL); | |
return false; | |
} | |
if (num <= count_) { | |
// Already cached. | |
return true; | |
} | |
// Enforce strict sequential order. | |
assert(num == (count_ + 1)); | |
if (num != (count_ + 1)) { | |
_set_errno(EINVAL); | |
return false; | |
} | |
if (num >= array_size_) { | |
/* Expand the array. Make it 64 entries bigger than adding entry number. | |
* NOTE: that we don't check for an overflow here, since we secured | |
* ourselves from that by enforcing strict sequential order. So, an | |
* overflow may happen iff number of entries cached in this array is | |
* close to 4G, which is a) totally unreasonable, and b) we would die | |
* long before this amount of entries is cached. | |
*/ | |
Dwarf_AbbrNum new_size = num + 64; | |
// Reallocate. | |
const Dwarf_Abbr_DIE** new_array = new const Dwarf_Abbr_DIE*[new_size]; | |
assert(new_array != NULL); | |
if (new_array == NULL) { | |
_set_errno(ENOMEM); | |
return false; | |
} | |
memcpy(new_array, array_, count_ * sizeof(const Dwarf_Abbr_DIE*)); | |
if (array_ != &small_array_[0]) { | |
delete[] array_; | |
} | |
array_ = new_array; | |
array_size_ = new_size; | |
} | |
// Abbreviation numbers are 1-based. | |
array_[num - 1] = abbr; | |
count_++; | |
return true; | |
} | |
/* Adds new entry to the array | |
* Param: | |
* abbr - New entry to add. | |
* Return: | |
* true on success, false on failure. | |
*/ | |
bool add(const Dwarf_Abbr_DIE* abbr) { | |
return add(abbr, abbr->get_abbr_num()); | |
} | |
/* Gets an entry from the array | |
* Param: | |
* num - 1-based index of an entry to get. | |
* Return: | |
* Entry on success, or NULL if num exceeds the number of entries | |
* contained in the array. | |
*/ | |
const Dwarf_Abbr_DIE* get(Dwarf_AbbrNum num) const { | |
assert(num != 0 && num <= count_); | |
if (num != 0 && num <= count_) { | |
return array_[num - 1]; | |
} else { | |
_set_errno(EINVAL); | |
return NULL; | |
} | |
} | |
/* Caches Dwarf_Abbr_DIEs into this array up to the requested number. | |
* NOTE: This method cannot be called on an empty array. Usually, first | |
* entry is inserted into this array when CU object is initialized. | |
* Param: | |
* num - Entry number to cache entries up to. | |
* Return: | |
* Last cached entry (actually, an entry for the 'num' index). | |
*/ | |
const Dwarf_Abbr_DIE* cache_to(Dwarf_AbbrNum num) { | |
/* Last cached DIE abbreviation. We always should have cached at least one | |
* abbreviation for the CU DIE itself, added via "add" method when CU | |
* object was initialized. */ | |
const Dwarf_Abbr_DIE* cur_abbr = get(count_); | |
assert(cur_abbr != NULL); | |
if (cur_abbr == NULL) { | |
return NULL; | |
} | |
/* Starting with the last cached DIE abbreviation, loop through the | |
* remaining DIE abbreviations in the .debug_abbrev section of the | |
* mapped ELF file, caching them until we reach the requested | |
* abbreviation descriptor number. Normally, the very next DIE | |
* abbreviation will stop the loop. */ | |
while (num > count_) { | |
Dwarf_AbbrNum abbr_num; | |
Dwarf_Tag tmp2; | |
Dwarf_Form tmp3; | |
Dwarf_At tmp4; | |
/* Process all AT abbreviations for the current DIE entry, reaching next | |
* DIE abbreviation. */ | |
const Dwarf_Abbr_AT* abbr_at = cur_abbr->process(&abbr_num, &tmp2); | |
while (!abbr_at->is_separator()) { | |
abbr_at = abbr_at->process(&tmp4, &tmp3); | |
} | |
// Next DIE abbreviation is right after the separator AT abbreviation. | |
cur_abbr = reinterpret_cast<const Dwarf_Abbr_DIE*> | |
(abbr_at->process(&tmp4, &tmp3)); | |
if (!add(cur_abbr)) { | |
return NULL; | |
} | |
} | |
return array_[num - 1]; | |
} | |
/* Empties array and frees allocations. */ | |
void empty() { | |
if (array_ != &small_array_[0]) { | |
delete[] array_; | |
array_ = &small_array_[0]; | |
array_size_ = sizeof(small_array_) / sizeof(small_array_[0]); | |
} | |
count_ = 0; | |
} | |
protected: | |
/* Array, preallocated in anticipation of relatively small number of | |
* DIE abbreviations in compilation unit. */ | |
const Dwarf_Abbr_DIE* small_array_[64]; | |
/* Array of Dwarf_Abbr_DIE pointers, cached for a compilation unit. */ | |
const Dwarf_Abbr_DIE** array_; | |
/* Current size of the array. */ | |
Dwarf_AbbrNum array_size_; | |
/* Number of entries, cached in the array. */ | |
Dwarf_AbbrNum count_; | |
}; | |
/* Encapsulates a state machine for the "Line Number Program", that is run | |
* on data conained in the mapped .debug_line section. | |
*/ | |
class DwarfStateMachine { | |
public: | |
/* Constructs DwarfStateMachine instance. | |
* Param: | |
* set_is_stmt - Matches value of default_is_stmt field in the STMTL header. | |
* see Dwarf_STMTL_HdrXX. | |
*/ | |
explicit DwarfStateMachine(bool set_is_stmt) | |
: address_(0), | |
file_(1), | |
line_(1), | |
column_(0), | |
discriminator_(0), | |
is_stmt_(set_is_stmt), | |
basic_block_(false), | |
end_sequence_(false), | |
prologue_end_(false), | |
epilogue_begin_(false), | |
isa_(0), | |
set_file_info_(NULL) { | |
} | |
/* Destructs DwarfStateMachine instance. */ | |
~DwarfStateMachine() { | |
} | |
/* Resets the state to default. | |
* Param: | |
* set_is_stmt - Matches value of default_is_stmt field in the STMTL header. | |
* see Dwarf_STMTL_HdrXX. | |
*/ | |
void reset(bool set_is_stmt) { | |
address_ = 0; | |
file_ = 1; | |
line_ = 1; | |
column_ = 0; | |
discriminator_ = 0; | |
is_stmt_ = set_is_stmt; | |
basic_block_ = false; | |
end_sequence_ = false; | |
prologue_end_ = false; | |
epilogue_begin_ = false; | |
isa_ = 0; | |
set_file_info_ = NULL; | |
} | |
/* | |
* Machine state. | |
*/ | |
/* Current address (current PC value). */ | |
Elf_Xword address_; | |
/* Current index of source file descriptor. */ | |
Elf_Word file_; | |
/* Current line in the current source file. */ | |
Elf_Word line_; | |
/* Current column. */ | |
Elf_Word column_; | |
/* Current discriminator value. */ | |
Elf_Word discriminator_; | |
/* Current STMT flag. */ | |
bool is_stmt_; | |
/* Current basic block flag. */ | |
bool basic_block_; | |
/* Current end of sequence flag. */ | |
bool end_sequence_; | |
/* Current end of prologue flag. */ | |
bool prologue_end_; | |
/* Current epilogue begin flag. */ | |
bool epilogue_begin_; | |
/* Current ISA value. */ | |
Elf_Word isa_; | |
/* Current value for explicitly set current source file descriptor. | |
* If not NULL, this descriptor has priority over the descriptor, addressed | |
* by the file_ member of this class. */ | |
const Dwarf_STMTL_FileDesc* set_file_info_; | |
}; | |
/* Checks if given tag belongs to a routine. */ | |
static inline bool | |
dwarf_tag_is_routine(Dwarf_Tag tag) { | |
return tag == DW_TAG_inlined_subroutine || | |
tag == DW_TAG_subprogram || | |
tag == DW_AT_main_subprogram; | |
} | |
/* Checks if given tag belongs to a compilation unit. */ | |
static inline bool | |
dwarf_tag_is_cu(Dwarf_Tag tag) { | |
return tag == DW_TAG_compile_unit || | |
tag == DW_TAG_partial_unit; | |
} | |
#endif // ELFF_DWARF_DEFS_H_ |