/* 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 implementation of a class DwarfCU, that encapsulates a compilation | |
* unit in the .debug_info section of the mapped ELF file. | |
*/ | |
#include "string.h" | |
#include "stdio.h" | |
#include "elf_file.h" | |
#include "dwarf_cu.h" | |
#include "dwarf_utils.h" | |
DwarfCU::DwarfCU(ElfFile* elf) | |
: elf_file_(elf), | |
cu_die_(NULL), | |
prev_cu_(NULL) { | |
} | |
DwarfCU::~DwarfCU() { | |
if (cu_die_ != NULL) { | |
delete cu_die_; | |
} | |
abbrs_.empty(); | |
} | |
DwarfCU* DwarfCU::create_instance(ElfFile* elf, const void* hdr) { | |
DwarfCU* ret; | |
/* 64-bit DWARF CU has first 4 bytes in its header set to 0xFFFFFFFF. */ | |
if (*reinterpret_cast<const Elf_Word*>(hdr) == 0xFFFFFFFF) { | |
ret = new(elf) DwarfCUImpl<Dwarf64_CUHdr, Dwarf64_Off> | |
(elf, reinterpret_cast<const Dwarf64_CUHdr*>(hdr)); | |
} else { | |
ret = new(elf) DwarfCUImpl<Dwarf32_CUHdr, Dwarf32_Off> | |
(elf, reinterpret_cast<const Dwarf32_CUHdr*>(hdr)); | |
} | |
assert(ret != NULL); | |
if (ret == NULL) { | |
_set_errno(ENOMEM); | |
} | |
return ret; | |
} | |
const Elf_Byte* DwarfCU::process_attrib(const Elf_Byte* prop, | |
Dwarf_Form form, | |
Dwarf_Value* attr_value) const { | |
assert(form != 0); | |
Dwarf_Value tmp_val; | |
Dwarf_Value leb128; | |
attr_value->type = DWARF_VALUE_UNKNOWN; | |
attr_value->encoded_size = 0; | |
attr_value->u64 = 0; | |
switch (form) { | |
/* Property is a block of data, contained in .debug_info section. Block | |
* size is encoded with 1 byte value, and block data immediately follows | |
* block size. */ | |
case DW_FORM_block1: | |
attr_value->type = DWARF_VALUE_BLOCK; | |
attr_value->block.block_size = *prop; | |
attr_value->block.block_ptr = prop + 1; | |
attr_value->encoded_size = | |
static_cast<Elf_Word>(attr_value->block.block_size + 1); | |
break; | |
/* Property is a block of data, contained in .debug_info section. Block | |
* size is encoded with 2 bytes value, and block data immediately follows | |
* block size. */ | |
case DW_FORM_block2: | |
attr_value->type = DWARF_VALUE_BLOCK; | |
attr_value->block.block_size = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Half*>(prop)); | |
attr_value->block.block_ptr = prop + 2; | |
attr_value->encoded_size = | |
static_cast<Elf_Word>(attr_value->block.block_size + 2); | |
break; | |
/* Property is a block of data, contained in .debug_info section. Block | |
* size is encoded with 4 bytes value, and block data immediately follows | |
* block size. */ | |
case DW_FORM_block4: | |
attr_value->type = DWARF_VALUE_BLOCK; | |
attr_value->block.block_size = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop)); | |
attr_value->block.block_ptr = prop + 4; | |
attr_value->encoded_size = | |
static_cast<Elf_Word>(attr_value->block.block_size + 4); | |
break; | |
/* Property is a block of data, contained in .debug_info section. Block | |
* size is encoded with unsigned LEB128 value, and block data immediately | |
* follows block size. */ | |
case DW_FORM_block: | |
reinterpret_cast<const Dwarf_Leb128*>(prop)->process_unsigned(&leb128); | |
attr_value->type = DWARF_VALUE_BLOCK; | |
attr_value->block.block_size = leb128.u32; | |
attr_value->block.block_ptr = prop + leb128.encoded_size; | |
attr_value->encoded_size = | |
static_cast<Elf_Word>(attr_value->block.block_size + | |
leb128.encoded_size); | |
break; | |
/* Property is unsigned 1 byte value. */ | |
case DW_FORM_flag: | |
case DW_FORM_data1: | |
case DW_FORM_ref1: | |
attr_value->type = DWARF_VALUE_U8; | |
attr_value->u8 = *prop; | |
attr_value->encoded_size = 1; | |
break; | |
/* Property is unsigned 2 bytes value. */ | |
case DW_FORM_data2: | |
case DW_FORM_ref2: | |
attr_value->type = DWARF_VALUE_U16; | |
attr_value->u16 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Half*>(prop)); | |
attr_value->encoded_size = 2; | |
break; | |
/* Property is unsigned 4 bytes value. */ | |
case DW_FORM_data4: | |
case DW_FORM_ref4: | |
attr_value->type = DWARF_VALUE_U32; | |
attr_value->u32 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop)); | |
attr_value->encoded_size = 4; | |
break; | |
/* Property is unsigned 8 bytes value. */ | |
case DW_FORM_data8: | |
case DW_FORM_ref8: | |
case DW_FORM_ref_sig8: | |
attr_value->type = DWARF_VALUE_U64; | |
attr_value->u64 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop)); | |
attr_value->encoded_size = 8; | |
break; | |
/* Property is signed LEB128 value. */ | |
case DW_FORM_sdata: | |
reinterpret_cast<const Dwarf_Leb128*>(prop)->process_signed(attr_value); | |
break; | |
/* Property is unsigned LEB128 value. */ | |
case DW_FORM_ref_udata: | |
case DW_FORM_udata: | |
reinterpret_cast<const Dwarf_Leb128*>(prop)->process_unsigned(attr_value); | |
break; | |
/* Property is a string contained directly in .debug_info section. */ | |
case DW_FORM_string: | |
attr_value->type = DWARF_VALUE_STR; | |
attr_value->str = reinterpret_cast<const char*>(prop); | |
attr_value->encoded_size = strlen(attr_value->str) + 1; | |
break; | |
/* Property is an offset of a string contained in .debug_str section. | |
* We will process the reference here, converting it into the actual | |
* string value. */ | |
case DW_FORM_strp: | |
attr_value->type = DWARF_VALUE_STR; | |
if (elf_file_->is_DWARF_64()) { | |
Elf_Xword str_offset = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop)); | |
attr_value->str = elf_file_->get_debug_str(str_offset); | |
attr_value->encoded_size = 8; | |
} else { | |
Elf_Word str_offset = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop)); | |
attr_value->str = elf_file_->get_debug_str(str_offset); | |
attr_value->encoded_size = 4; | |
} | |
break; | |
/* Property is an address. */ | |
case DW_FORM_addr: | |
if (addr_sizeof_ == 4) { | |
attr_value->type = DWARF_VALUE_PTR32; | |
attr_value->u32 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop)); | |
} else { | |
attr_value->type = DWARF_VALUE_PTR64; | |
attr_value->u64 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop)); | |
} | |
attr_value->encoded_size = addr_sizeof_; | |
break; | |
/* Reference from the beginning of .debug_info section. */ | |
case DW_FORM_ref_addr: | |
/* DWARF3+ requires that encoding size of this property must be 4 bytes | |
* in 32-bit DWARF, and 8 bytes in 64-bit DWARF, while DWARF2- requires | |
* encoding size to be equal to CU's pointer size. */ | |
if (is_DWARF3_or_higher()) { | |
if (elf_file_->is_DWARF_64()) { | |
attr_value->type = DWARF_VALUE_U64; | |
attr_value->u64 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop)); | |
attr_value->encoded_size = 4; | |
} else { | |
attr_value->type = DWARF_VALUE_U32; | |
attr_value->u32 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop)); | |
attr_value->encoded_size = 8; | |
} | |
} else { | |
if (addr_sizeof_ == 4) { | |
attr_value->type = DWARF_VALUE_U32; | |
attr_value->u32 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop)); | |
} else { | |
attr_value->type = DWARF_VALUE_U64; | |
attr_value->u64 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop)); | |
} | |
attr_value->encoded_size = addr_sizeof_; | |
} | |
break; | |
/* Reference to a section, other than .debug_info, or .debug_str */ | |
case DW_FORM_sec_offset: | |
if (elf_file_->is_DWARF_64()) { | |
attr_value->type = DWARF_VALUE_U64; | |
attr_value->u64 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Xword*>(prop)); | |
attr_value->encoded_size = 4; | |
} else { | |
attr_value->type = DWARF_VALUE_U32; | |
attr_value->u32 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop)); | |
attr_value->encoded_size = 8; | |
} | |
break; | |
/* This is a replacement for DW_FORM_flag, which doesn't consume memory | |
* in .debug_info section, and only by the fact of its existence it is | |
* equal to DW_FORM_flag with value set to 1. */ | |
case DW_FORM_flag_present: | |
attr_value->type = DWARF_VALUE_U8; | |
attr_value->u8 = 1; | |
attr_value->encoded_size = 0; | |
break; | |
/* Encodes the actual form to be used. */ | |
case DW_FORM_indirect: | |
// Starts with ULEB128 | |
prop = reinterpret_cast<const Elf_Byte*> | |
(reinterpret_cast<const Dwarf_Leb128*> | |
(prop)->process_unsigned(&tmp_val)); | |
/* ULEB128 encodes the actual form to be used to process this entry. */ | |
process_attrib(prop, tmp_val.u16, attr_value); | |
attr_value->encoded_size += tmp_val.encoded_size; | |
break; | |
/* This form is defined for DWARF4, and has no documentation whatsoever. */ | |
case DW_FORM_exprloc: | |
default: | |
attr_value->type = DWARF_VALUE_U32; | |
attr_value->u32 = | |
elf_file_->pull_val(reinterpret_cast<const Elf_Word*>(prop)); | |
attr_value->encoded_size = 4; | |
break; | |
} | |
return prop + attr_value->encoded_size; | |
} | |
void DwarfCU::dump() const { | |
printf("\n\n>>>>>>>>>>>>>>> CU %p (version %u, address size %u)\n", | |
cu_die_->die(), static_cast<Elf_Word>(version_), | |
static_cast<Elf_Word>(addr_sizeof_)); | |
printf(">>>>> Build dir path: %s\n", comp_dir_path()); | |
printf(">>>>> Build file path: %s\n", rel_cu_path()); | |
if (cu_die_ != NULL) { | |
cu_die_->dump(false); | |
} | |
} | |
//============================================================================= | |
// DwarfCUImpl implementation | |
//============================================================================= | |
template <typename Dwarf_CUHdr, typename Dwarf_Off> | |
DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::DwarfCUImpl(ElfFile* elf, | |
const Dwarf_CUHdr* hdr) | |
: DwarfCU(elf), | |
cu_header_(hdr) { | |
/* Cache CU's DIE abbreviation descriptor in the array. This MUST be done | |
* BEFORE first call to array's cache_to() method. */ | |
const Dwarf_Abbr_DIE* cu_abbr_die = reinterpret_cast<const Dwarf_Abbr_DIE*> | |
(INC_CPTR(elf->get_debug_abbrev_data(), | |
elf->pull_val(hdr->abbrev_offset))); | |
abbrs_.add(cu_abbr_die); | |
cu_size_ = elf->pull_val(hdr->size_hdr.size); | |
version_ = elf->pull_val(hdr->version); | |
addr_sizeof_ = hdr->address_size; | |
memset(&stmtl_header_, 0, sizeof(stmtl_header_)); | |
} | |
template <typename Dwarf_CUHdr, typename Dwarf_Off> | |
bool DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::parse( | |
const DwarfParseContext* parse_context, | |
const void** next_cu_die) { | |
/* Start parsing with the DIE for this CU. */ | |
if (process_DIE(parse_context, get_DIE(), NULL) == NULL) { | |
return false; | |
} | |
/* CU area size (thus, next CU header offset) in .debug_info section equals | |
* to CU size, plus number of bytes, required to encode CU size in CU header | |
* (4 for 32-bit CU, and 12 for 64-bit CU. */ | |
*next_cu_die = | |
INC_CPTR(cu_header_, cu_size_ + ELFF_FIELD_OFFSET(Dwarf_CUHdr, version)); | |
return true; | |
} | |
template <typename Dwarf_CUHdr, typename Dwarf_Off> | |
const Elf_Byte* DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::process_DIE( | |
const DwarfParseContext* parse_context, | |
const Dwarf_DIE* die, | |
DIEObject* parent_obj) { | |
while (is_attrib_ptr_valid(die) && !die->is_separator()) { | |
Dwarf_AbbrNum abbr_num; | |
Dwarf_Tag die_tag; | |
Elf_Word sibling_off = 0; | |
/* Get DIE's abbreviation number, and advance to DIE's properties. */ | |
const Elf_Byte* die_attr = die->process(&abbr_num); | |
/* Get abbreviation for the current DIE. */ | |
const Dwarf_Abbr_DIE* die_abbr = abbrs_.cache_to(abbr_num); | |
if (die_abbr == NULL) { | |
return NULL; | |
} | |
/* Get base DIE properties, and advance to the DIE's | |
* attribute descriptors. */ | |
const Dwarf_Abbr_AT* at_abbr = die_abbr->process(NULL, &die_tag); | |
/* Instantiate DIE object for this DIE, and get list of properties, | |
* that should be collected while processing that DIE. */ | |
DIEObject* die_obj = | |
create_die_object(parse_context, die, parent_obj, die_tag); | |
if (die_obj == NULL && errno != 0) { | |
return NULL; | |
} | |
if (die_obj != NULL) { | |
if (parent_obj != NULL) { | |
/* Update list of parent's children. */ | |
die_obj->link_sibling(parent_obj->last_child()); | |
parent_obj->link_child(die_obj); | |
} else { | |
/* NULL parent object is allowed only for CU DIE itself. */ | |
assert(cu_die_ == NULL && die_tag == DW_TAG_compile_unit); | |
if (cu_die_ == NULL && die_tag != DW_TAG_compile_unit) { | |
_set_errno(EINVAL); | |
return NULL; | |
} | |
cu_die_ = die_obj; | |
/* This CU DIE object will be used as a parent for all DIE | |
* objects, created in this method. */ | |
parent_obj = cu_die_; | |
} | |
} | |
// Loop through all DIE properties. | |
while (elf_file_->is_valid_abbr_ptr(at_abbr, sizeof(Dwarf_Abbr_AT)) && | |
!at_abbr->is_separator()) { | |
Dwarf_At at_value; | |
Dwarf_Form at_form; | |
Dwarf_Value attr_value; | |
// Obtain next property value. | |
at_abbr = at_abbr->process(&at_value, &at_form); | |
die_attr = process_attrib(die_attr, at_form, &attr_value); | |
if (at_value == DW_AT_sibling) { | |
/* DW_AT_sibling means that next DIE is a child of the one that's | |
* being currently processed. We need to cache value of this property | |
* in order to correctly calculate next sibling of this DIE after | |
* child's DIE has been processed. */ | |
assert(sibling_off == 0); | |
sibling_off = attr_value.u32; | |
} | |
} | |
/* Next DIE immediately follows last property for the current DIE. */ | |
die = reinterpret_cast<const Dwarf_DIE*>(die_attr); | |
if (sibling_off != 0) { | |
// Process child DIE. | |
process_DIE(parse_context, die, die_obj != NULL ? die_obj : parent_obj); | |
// Next sibling DIE offset is relative to this CU's header beginning. | |
die = INC_CPTR_T(Dwarf_DIE, cu_header_, sibling_off); | |
} | |
} | |
return INC_CPTR_T(Elf_Byte, die, 1); | |
} | |
template <typename Dwarf_CUHdr, typename Dwarf_Off> | |
DIEObject* DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::create_die_object( | |
const DwarfParseContext* parse_context, | |
const Dwarf_DIE* die, | |
DIEObject* parent, | |
Dwarf_Tag tag) { | |
DIEObject* ret = NULL; | |
/* We will always create a DIE object for CU DIE. */ | |
if (tag == DW_TAG_compile_unit || collect_die(parse_context, tag)) { | |
ret = new(elf_file_) DIEObject(die, this, parent); | |
assert(ret != NULL); | |
if (ret == NULL) { | |
_set_errno(ENOMEM); | |
} | |
} else { | |
_set_errno(0); | |
} | |
return ret; | |
} | |
template <typename Dwarf_CUHdr, typename Dwarf_Off> | |
bool DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::init_stmtl() { | |
if (stmtl_header_.unit_length != 0) { | |
return true; | |
} | |
assert(cu_die_ != NULL); | |
if (cu_die_ == NULL) { | |
_set_errno(EINVAL); | |
return false; | |
} | |
DIEAttrib stmtl; | |
if (!cu_die()->get_attrib(DW_AT_stmt_list, &stmtl)) { | |
_set_errno(EINVAL); | |
return false; | |
} | |
const void* stmtl_start = | |
INC_CPTR(elf_file()->get_debug_line_data(), stmtl.value()->u32); | |
if (*reinterpret_cast<const Elf_Word*>(stmtl_start) == 0xFFFFFFFF) { | |
cache_stmtl<Dwarf64_STMTLHdr>(reinterpret_cast<const Dwarf64_STMTLHdr*>(stmtl_start)); | |
} else { | |
cache_stmtl<Dwarf32_STMTLHdr>(reinterpret_cast<const Dwarf32_STMTLHdr*>(stmtl_start)); | |
} | |
return true; | |
} | |
template <typename Dwarf_CUHdr, typename Dwarf_Off> | |
bool DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::get_pc_address_file_info( | |
Elf_Xword address, | |
Dwarf_AddressInfo* info) { | |
/* Make sure STMTL header is cached. */ | |
if (!init_stmtl()) { | |
return false; | |
} | |
/* Flags address match, that should trigger return next time | |
* source line gets adjusted. */ | |
bool found = false; | |
/* Create new state machine. */ | |
DwarfStateMachine state(stmtl_header_.default_is_stmt != 0); | |
/* Start the "Line Number Program" */ | |
const Elf_Byte* go = stmtl_header_.start; | |
while (go < stmtl_header_.end) { | |
const Elf_Byte op = *go; | |
go++; | |
if (op == 0) { | |
/* This is an extended opcode. */ | |
Dwarf_Value op_size; | |
/* First ULEB128 contains opcode size, (excluding ULEB128 itself). */ | |
go = reinterpret_cast<const Elf_Byte*> | |
(reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&op_size)); | |
/* Next is the extended opcode. */ | |
const Elf_Byte* ex_op_ptr = go; | |
switch (*ex_op_ptr) { | |
case DW_LNE_end_sequence: | |
state.end_sequence_ = true; | |
state.reset(stmtl_header_.default_is_stmt != 0); | |
found = false; | |
break; | |
case DW_LNE_set_address: { | |
Elf_Xword prev_address = state.address_; | |
if (is_CU_address_64()) { | |
state.address_ = | |
elf_file()->pull_val(reinterpret_cast<const Elf_Xword*>(ex_op_ptr + 1)); | |
} else { | |
state.address_ = | |
elf_file()->pull_val(reinterpret_cast<const Elf_Word*>(ex_op_ptr + 1)); | |
} | |
if (prev_address != 0 && | |
address >= prev_address && address < state.address_) { | |
return set_source_info(&state, info); | |
} else if (address == state.address_) { | |
found = true; | |
} | |
break; | |
} | |
case DW_LNE_define_file: { | |
/* Parameters start with the directly encoded zero-terminated | |
* file name. */ | |
state.set_file_info_ = INC_CPTR_T(Dwarf_STMTL_FileDesc, ex_op_ptr, 1); | |
assert(state.set_file_info_ != NULL); | |
if (state.set_file_info_ != NULL) { | |
ex_op_ptr = reinterpret_cast<const Elf_Byte*>(state.set_file_info_->process(NULL)); | |
} | |
break; | |
} | |
case DW_LNE_set_discriminator: { | |
Dwarf_Value discr_val; | |
/* One parameter: discriminator's ULEB128 value. */ | |
reinterpret_cast<const Dwarf_Leb128*>(ex_op_ptr + 1)->process_unsigned(&discr_val); | |
state.discriminator_ = discr_val.u32; | |
break; | |
} | |
default: | |
assert(0); | |
return false; | |
} | |
go += op_size.u32; | |
} else if (op < stmtl_header_.opcode_base) { | |
/* This is a standard opcode. */ | |
switch (op) { | |
case DW_LNS_copy: | |
/* No parameters. */ | |
state.basic_block_ = false; | |
state.prologue_end_ = false; | |
state.epilogue_begin_ = false; | |
break; | |
case DW_LNS_advance_pc: { | |
/* One parameter: ULEB128 value to add to the current address value | |
* in the state machine. */ | |
Dwarf_Value addr_add; | |
go = reinterpret_cast<const Elf_Byte*> | |
(reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&addr_add)); | |
Elf_Xword prev_address = state.address_; | |
state.address_ += addr_add.u64; | |
if (prev_address != 0 && | |
address >= prev_address && address < state.address_) { | |
return set_source_info(&state, info); | |
} else if (address == state.address_) { | |
found = true; | |
} | |
break; | |
} | |
case DW_LNS_advance_line: { | |
/* One parameter: signed LEB128 value to add to the current line | |
* number in the state machine. */ | |
Dwarf_Value line_add; | |
go = reinterpret_cast<const Elf_Byte*> | |
(reinterpret_cast<const Dwarf_Leb128*>(go)->process_signed(&line_add)); | |
state.line_ += line_add.s32; | |
if (found) { | |
return set_source_info(&state, info); | |
} | |
break; | |
} | |
case DW_LNS_set_file: { | |
/* One parameter: ULEB128 value encoding current file number. */ | |
Dwarf_Value file_num; | |
go = reinterpret_cast<const Elf_Byte*> | |
(reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&file_num)); | |
state.file_ = file_num.u32; | |
/* This operation should discard previously saved file information. */ | |
state.set_file_info_ = NULL; | |
break; | |
} | |
case DW_LNS_set_column: { | |
/* One parameter: ULEB128 value encoding current column number. */ | |
Dwarf_Value column_num; | |
go = reinterpret_cast<const Elf_Byte*> | |
(reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&column_num)); | |
state.column_ = column_num.u32; | |
break; | |
} | |
case DW_LNS_negate_stmt: | |
/* No parameters. */ | |
state.is_stmt_ = !state.is_stmt_; | |
break; | |
case DW_LNS_set_basic_block: | |
/* No parameters. */ | |
state.basic_block_ = true; | |
break; | |
case DW_LNS_const_add_pc: { | |
Elf_Xword prev_address = state.address_; | |
/* No parameters. This operation does the same thing, as special | |
* opcode 255 would do to the current address. */ | |
Elf_Word adjusted = | |
static_cast<Elf_Word>(255) - stmtl_header_.opcode_base; | |
state.address_ += (adjusted / stmtl_header_.line_range) * | |
stmtl_header_.min_instruction_len; | |
if (prev_address != 0 && | |
address >= prev_address && address < state.address_) { | |
return set_source_info(&state, info); | |
} else if (address == state.address_) { | |
found = true; | |
} | |
break; | |
} | |
case DW_LNS_fixed_advance_pc: { | |
Elf_Xword prev_address = state.address_; | |
/* One parameter: directly encoded 16-bit value to add to the | |
* current address. */ | |
state.address_ += | |
elf_file()->pull_val(reinterpret_cast<const Elf_Half*>(go)); | |
if (prev_address != 0 && | |
address >= prev_address && address < state.address_) { | |
return set_source_info(&state, info); | |
} else if (address == state.address_) { | |
found = true; | |
} | |
go += sizeof(Elf_Half); | |
break; | |
} | |
case DW_LNS_set_prologue_end: | |
/* No parameters. */ | |
state.prologue_end_ = true; | |
break; | |
case DW_LNS_set_epilogue_begin: | |
/* No parameters. */ | |
state.epilogue_begin_ = true; | |
break; | |
case DW_LNS_set_isa: { | |
/* One parameter: ISA value encoded as ULEB128. */ | |
Dwarf_Value isa_val; | |
go = reinterpret_cast<const Elf_Byte*> | |
(reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&isa_val)); | |
state.isa_ = isa_val.u32; | |
break; | |
} | |
default: | |
/* Unknown opcode. Just skip it. */ | |
for (Elf_Byte uleb = 0; | |
uleb < stmtl_header_.standard_opcode_lengths[op - 1]; uleb++) { | |
Dwarf_Value tmp; | |
go = reinterpret_cast<const Elf_Byte*> | |
(reinterpret_cast<const Dwarf_Leb128*>(go)->process_unsigned(&tmp)); | |
} | |
break; | |
} | |
} else { | |
Elf_Xword prev_address = state.address_; | |
/* This is a special opcode. */ | |
const Elf_Word adjusted = op - stmtl_header_.opcode_base; | |
/* Advance address. */ | |
state.address_ += (adjusted / stmtl_header_.line_range) * | |
stmtl_header_.min_instruction_len; | |
if (prev_address != 0 && | |
address >= prev_address && address < state.address_) { | |
return set_source_info(&state, info); | |
} | |
/* Advance line. */ | |
state.line_ += stmtl_header_.line_base + | |
(adjusted % stmtl_header_.line_range); | |
if (state.address_ == address) { | |
return set_source_info(&state, info); | |
} | |
/* Do the woodoo. */ | |
state.basic_block_ = false; | |
state.prologue_end_ = false; | |
state.epilogue_begin_ = false; | |
} | |
} | |
return false; | |
} | |
template <typename Dwarf_CUHdr, typename Dwarf_Off> | |
const Dwarf_STMTL_FileDesc* DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::get_stmt_file_info( | |
Elf_Word index) { | |
/* Index must be 1-based. */ | |
if (index == 0) { | |
return NULL; | |
} | |
const Dwarf_STMTL_FileDesc* cur_desc = stmtl_header_.file_infos; | |
while (index != 1 && !cur_desc->is_last_entry()) { | |
cur_desc = cur_desc->process(NULL); | |
index--; | |
} | |
assert(!cur_desc->is_last_entry()); | |
return cur_desc->is_last_entry() ? NULL : cur_desc; | |
} | |
template <typename Dwarf_CUHdr, typename Dwarf_Off> | |
const char* DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::get_stmt_dir_name( | |
Elf_Word dir_index) { | |
if (dir_index == 0) { | |
/* Requested is current compilation directory. */ | |
return comp_dir_path(); | |
} | |
if (dir_index > stmtl_header_.inc_dir_num) { | |
return NULL; | |
} | |
const char* cur_dir = stmtl_header_.include_directories; | |
while (dir_index != 1) { | |
cur_dir += strlen(cur_dir) + 1; | |
dir_index--; | |
} | |
return cur_dir; | |
} | |
template <typename Dwarf_CUHdr, typename Dwarf_Off> | |
bool DwarfCUImpl<Dwarf_CUHdr, Dwarf_Off>::set_source_info( | |
const DwarfStateMachine* state, | |
Dwarf_AddressInfo* info) { | |
info->line_number = state->line_; | |
const Dwarf_STMTL_FileDesc* file_info = state->set_file_info_; | |
if (file_info == NULL) { | |
file_info = get_stmt_file_info(state->file_); | |
if (file_info == NULL) { | |
info->file_name = rel_cu_path(); | |
info->dir_name = comp_dir_path(); | |
return true; | |
} | |
} | |
info->file_name = file_info->get_file_name(); | |
const Elf_Word dir_index = file_info->get_dir_index(); | |
info->dir_name = get_stmt_dir_name(dir_index); | |
return true; | |
} | |