aboutsummaryrefslogtreecommitdiffstats
path: root/elff/dwarf_cu.cc
diff options
context:
space:
mode:
authorVladimir Chtchetkine <vchtchetkine@google.com>2010-02-16 10:38:35 -0800
committerVladimir Chtchetkine <vchtchetkine@google.com>2010-02-18 15:22:07 -0800
commit5389aa19033153c09556d1362a8b8a56abccb8f5 (patch)
tree5d731effe5bd5d2f162f06aadec7212045eaef3d /elff/dwarf_cu.cc
parent76dbca0489ab98a46f2954bc7b77c3df6f9d8264 (diff)
downloadexternal_qemu-5389aa19033153c09556d1362a8b8a56abccb8f5.zip
external_qemu-5389aa19033153c09556d1362a8b8a56abccb8f5.tar.gz
external_qemu-5389aa19033153c09556d1362a8b8a56abccb8f5.tar.bz2
Merge memory checking from sandbox
Change-id: Ibce845d0
Diffstat (limited to 'elff/dwarf_cu.cc')
-rw-r--r--elff/dwarf_cu.cc758
1 files changed, 758 insertions, 0 deletions
diff --git a/elff/dwarf_cu.cc b/elff/dwarf_cu.cc
new file mode 100644
index 0000000..8e7da98
--- /dev/null
+++ b/elff/dwarf_cu.cc
@@ -0,0 +1,758 @@
+/* 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;
+}
+