/* 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(hdr) == 0xFFFFFFFF) { ret = new(elf) DwarfCUImpl (elf, reinterpret_cast(hdr)); } else { ret = new(elf) DwarfCUImpl (elf, reinterpret_cast(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(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(prop)); attr_value->block.block_ptr = prop + 2; attr_value->encoded_size = static_cast(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(prop)); attr_value->block.block_ptr = prop + 4; attr_value->encoded_size = static_cast(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(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(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(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(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(prop)); attr_value->encoded_size = 8; break; /* Property is signed LEB128 value. */ case DW_FORM_sdata: reinterpret_cast(prop)->process_signed(attr_value); break; /* Property is unsigned LEB128 value. */ case DW_FORM_ref_udata: case DW_FORM_udata: reinterpret_cast(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(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(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(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(prop)); } else { attr_value->type = DWARF_VALUE_PTR64; attr_value->u64 = elf_file_->pull_val(reinterpret_cast(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(prop)); attr_value->encoded_size = 4; } else { attr_value->type = DWARF_VALUE_U32; attr_value->u32 = elf_file_->pull_val(reinterpret_cast(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(prop)); } else { attr_value->type = DWARF_VALUE_U64; attr_value->u64 = elf_file_->pull_val(reinterpret_cast(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(prop)); attr_value->encoded_size = 4; } else { attr_value->type = DWARF_VALUE_U32; attr_value->u32 = elf_file_->pull_val(reinterpret_cast(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 (reinterpret_cast (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(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(version_), static_cast(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 DwarfCUImpl::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 (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 bool DwarfCUImpl::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 const Elf_Byte* DwarfCUImpl::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(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 DIEObject* DwarfCUImpl::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 bool DwarfCUImpl::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(stmtl_start) == 0xFFFFFFFF) { cache_stmtl(reinterpret_cast(stmtl_start)); } else { cache_stmtl(reinterpret_cast(stmtl_start)); } return true; } template bool DwarfCUImpl::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 (reinterpret_cast(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(ex_op_ptr + 1)); } else { state.address_ = elf_file()->pull_val(reinterpret_cast(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(state.set_file_info_->process(NULL)); } break; } case DW_LNE_set_discriminator: { Dwarf_Value discr_val; /* One parameter: discriminator's ULEB128 value. */ reinterpret_cast(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 (reinterpret_cast(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 (reinterpret_cast(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 (reinterpret_cast(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 (reinterpret_cast(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(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(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 (reinterpret_cast(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 (reinterpret_cast(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 const Dwarf_STMTL_FileDesc* DwarfCUImpl::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 const char* DwarfCUImpl::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 bool DwarfCUImpl::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; }