diff options
author | David Srbecky <dsrbecky@google.com> | 2015-04-08 19:37:39 +0100 |
---|---|---|
committer | David Srbecky <dsrbecky@google.com> | 2015-04-09 23:55:05 +0100 |
commit | b536247b1ce5de640eec81dddac47802cd074363 (patch) | |
tree | 22568fd953bfc92deb3378c3d5b31fb04b9948cc | |
parent | 8b3c1b6526ff6b44dd724826b8e9e6d0a2067f6e (diff) | |
download | art-b536247b1ce5de640eec81dddac47802cd074363.tar.gz art-b536247b1ce5de640eec81dddac47802cd074363.tar.bz2 art-b536247b1ce5de640eec81dddac47802cd074363.zip |
Extend the DWARF library to support .debug_info section.
Change-Id: I9916abd8db227e7a73a3311294e675be5222a709
-rw-r--r-- | compiler/cfi_test.h | 9 | ||||
-rw-r--r-- | compiler/dwarf/debug_frame_writer.h | 118 | ||||
-rw-r--r-- | compiler/dwarf/debug_info_entry_writer.h | 248 | ||||
-rw-r--r-- | compiler/dwarf/debug_line_writer.h | 87 | ||||
-rw-r--r-- | compiler/dwarf/dwarf_test.cc | 87 | ||||
-rw-r--r-- | compiler/dwarf/headers.h | 167 | ||||
-rw-r--r-- | compiler/dwarf/writer.h | 14 | ||||
-rw-r--r-- | compiler/elf_writer_quick.cc | 218 | ||||
-rw-r--r-- | runtime/leb128.h | 13 | ||||
-rw-r--r-- | runtime/leb128_test.cc | 19 |
10 files changed, 571 insertions, 409 deletions
diff --git a/compiler/cfi_test.h b/compiler/cfi_test.h index f550395dad..918179290b 100644 --- a/compiler/cfi_test.h +++ b/compiler/cfi_test.h @@ -22,8 +22,8 @@ #include <sstream> #include "arch/instruction_set.h" -#include "dwarf/debug_frame_writer.h" #include "dwarf/dwarf_test.h" +#include "dwarf/headers.h" #include "disassembler/disassembler.h" #include "gtest/gtest.h" @@ -43,9 +43,10 @@ class CFITest : public dwarf::DwarfTest { HexDump(f, actual_cfi); fprintf(f, "\n};\n"); // Pretty-print CFI opcodes. - dwarf::DebugFrameWriter<> eh_frame(&eh_frame_data_, false); - eh_frame.WriteCIE(dwarf::Reg(8), {}); - eh_frame.WriteFDE(0, actual_asm.size(), actual_cfi.data(), actual_cfi.size()); + constexpr bool is64bit = false; + dwarf::DebugFrameOpCodeWriter<> initial_opcodes; + dwarf::WriteEhFrameCIE(is64bit, dwarf::Reg(8), initial_opcodes, &eh_frame_data_); + dwarf::WriteEhFrameFDE(is64bit, 0, 0, actual_asm.size(), &actual_cfi, &eh_frame_data_); ReformatCfi(Objdump(false, "-W"), &lines); // Pretty-print assembly. auto* opts = new DisassemblerOptions(false, actual_asm.data(), true); diff --git a/compiler/dwarf/debug_frame_writer.h b/compiler/dwarf/debug_frame_writer.h deleted file mode 100644 index 3502906f59..0000000000 --- a/compiler/dwarf/debug_frame_writer.h +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_ -#define ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_ - -#include "debug_frame_opcode_writer.h" -#include "dwarf.h" -#include "writer.h" - -namespace art { -namespace dwarf { - -// Writer for the .eh_frame section (which extends .debug_frame specification). -template<typename Allocator = std::allocator<uint8_t>> -class DebugFrameWriter FINAL : private Writer<Allocator> { - public: - void WriteCIE(Reg return_address_register, - const uint8_t* initial_opcodes, - int initial_opcodes_size) { - DCHECK(cie_header_start_ == ~0u); - cie_header_start_ = this->data()->size(); - if (use_64bit_address_) { - // TODO: This is not related to being 64bit. - this->PushUint32(0xffffffff); - this->PushUint64(0); // Length placeholder. - this->PushUint64(0); // CIE id. - } else { - this->PushUint32(0); // Length placeholder. - this->PushUint32(0); // CIE id. - } - this->PushUint8(1); // Version. - this->PushString("zR"); - this->PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor); - this->PushSleb128(DebugFrameOpCodeWriter<Allocator>::kDataAlignmentFactor); - this->PushUleb128(return_address_register.num()); // ubyte in DWARF2. - this->PushUleb128(1); // z: Augmentation data size. - if (use_64bit_address_) { - this->PushUint8(0x04); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8). - } else { - this->PushUint8(0x03); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4). - } - this->PushData(initial_opcodes, initial_opcodes_size); - this->Pad(use_64bit_address_ ? 8 : 4); - if (use_64bit_address_) { - this->UpdateUint64(cie_header_start_ + 4, this->data()->size() - cie_header_start_ - 12); - } else { - this->UpdateUint32(cie_header_start_, this->data()->size() - cie_header_start_ - 4); - } - } - - void WriteCIE(Reg return_address_register, - const DebugFrameOpCodeWriter<Allocator>& opcodes) { - WriteCIE(return_address_register, opcodes.data()->data(), opcodes.data()->size()); - } - - void WriteFDE(uint64_t initial_address, - uint64_t address_range, - const uint8_t* unwind_opcodes, - int unwind_opcodes_size) { - DCHECK(cie_header_start_ != ~0u); - size_t fde_header_start = this->data()->size(); - if (use_64bit_address_) { - // TODO: This is not related to being 64bit. - this->PushUint32(0xffffffff); - this->PushUint64(0); // Length placeholder. - this->PushUint64(this->data()->size() - cie_header_start_); // 'CIE_pointer' - } else { - this->PushUint32(0); // Length placeholder. - this->PushUint32(static_cast<uint32_t>(this->data()->size() - cie_header_start_)); // 'CIE_pointer' - } - if (use_64bit_address_) { - this->PushUint64(initial_address); - this->PushUint64(address_range); - } else { - this->PushUint32(initial_address); - this->PushUint32(address_range); - } - this->PushUleb128(0); // Augmentation data size. - this->PushData(unwind_opcodes, unwind_opcodes_size); - this->Pad(use_64bit_address_ ? 8 : 4); - if (use_64bit_address_) { - this->UpdateUint64(fde_header_start + 4, this->data()->size() - fde_header_start - 12); - } else { - this->UpdateUint32(fde_header_start, this->data()->size() - fde_header_start - 4); - } - } - - DebugFrameWriter(std::vector<uint8_t, Allocator>* buffer, bool use_64bit_address) - : Writer<Allocator>(buffer), - use_64bit_address_(use_64bit_address), - cie_header_start_(~0u) { - } - - private: - bool use_64bit_address_; - size_t cie_header_start_; - - DISALLOW_COPY_AND_ASSIGN(DebugFrameWriter); -}; - -} // namespace dwarf -} // namespace art - -#endif // ART_COMPILER_DWARF_DEBUG_FRAME_WRITER_H_ diff --git a/compiler/dwarf/debug_info_entry_writer.h b/compiler/dwarf/debug_info_entry_writer.h new file mode 100644 index 0000000000..c0350b6f8a --- /dev/null +++ b/compiler/dwarf/debug_info_entry_writer.h @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_ +#define ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_ + +#include <unordered_map> + +#include "dwarf.h" +#include "leb128.h" +#include "writer.h" + +namespace art { +namespace dwarf { + +// 32-bit FNV-1a hash function which we use to find duplicate abbreviations. +// See http://en.wikipedia.org/wiki/Fowler%E2%80%93Noll%E2%80%93Vo_hash_function +template< typename Allocator > +struct FNVHash { + size_t operator()(const std::vector<uint8_t, Allocator>& v) const { + uint32_t hash = 2166136261u; + for (size_t i = 0; i < v.size(); i++) { + hash = (hash ^ v[i]) * 16777619u; + } + return hash; + } +}; + +/* + * Writer for debug information entries (DIE). + * It also handles generation of abbreviations. + * + * Usage: + * StartTag(DW_TAG_compile_unit, DW_CHILDREN_yes); + * WriteStrp(DW_AT_producer, "Compiler name", debug_str); + * StartTag(DW_TAG_subprogram, DW_CHILDREN_no); + * WriteStrp(DW_AT_name, "Foo", debug_str); + * EndTag(); + * EndTag(); + */ +template< typename Allocator = std::allocator<uint8_t> > +class DebugInfoEntryWriter FINAL : private Writer<Allocator> { + public: + // Start debugging information entry. + void StartTag(Tag tag, Children children) { + DCHECK(has_children) << "This tag can not have nested tags"; + if (inside_entry_) { + // Write abbrev code for the previous entry. + this->UpdateUleb128(abbrev_code_offset_, EndAbbrev()); + inside_entry_ = false; + } + StartAbbrev(tag, children); + // Abbrev code placeholder of sufficient size. + abbrev_code_offset_ = this->data()->size(); + this->PushUleb128(NextAbbrevCode()); + depth_++; + inside_entry_ = true; + has_children = (children == DW_CHILDREN_yes); + } + + // End debugging information entry. + void EndTag() { + DCHECK_GT(depth_, 0); + if (inside_entry_) { + // Write abbrev code for this tag. + this->UpdateUleb128(abbrev_code_offset_, EndAbbrev()); + inside_entry_ = false; + } + if (has_children) { + this->PushUint8(0); // End of children. + } + depth_--; + has_children = true; // Parent tag obviously has children. + } + + void WriteAddr(Attribute attrib, uint64_t value) { + AddAbbrevAttribute(attrib, DW_FORM_addr); + if (is64bit_) { + this->PushUint64(value); + } else { + this->PushUint32(value); + } + } + + void WriteBlock(Attribute attrib, const void* ptr, int size) { + AddAbbrevAttribute(attrib, DW_FORM_block); + this->PushUleb128(size); + this->PushData(ptr, size); + } + + void WriteData1(Attribute attrib, uint8_t value) { + AddAbbrevAttribute(attrib, DW_FORM_data1); + this->PushUint8(value); + } + + void WriteData2(Attribute attrib, uint16_t value) { + AddAbbrevAttribute(attrib, DW_FORM_data2); + this->PushUint16(value); + } + + void WriteData4(Attribute attrib, uint32_t value) { + AddAbbrevAttribute(attrib, DW_FORM_data4); + this->PushUint32(value); + } + + void WriteData8(Attribute attrib, uint64_t value) { + AddAbbrevAttribute(attrib, DW_FORM_data8); + this->PushUint64(value); + } + + void WriteSdata(Attribute attrib, int value) { + AddAbbrevAttribute(attrib, DW_FORM_sdata); + this->PushSleb128(value); + } + + void WriteUdata(Attribute attrib, int value) { + AddAbbrevAttribute(attrib, DW_FORM_udata); + this->PushUleb128(value); + } + + void WriteUdata(Attribute attrib, uint32_t value) { + AddAbbrevAttribute(attrib, DW_FORM_udata); + this->PushUleb128(value); + } + + void WriteFlag(Attribute attrib, bool value) { + AddAbbrevAttribute(attrib, DW_FORM_flag); + this->PushUint8(value ? 1 : 0); + } + + void WriteRef4(Attribute attrib, int cu_offset) { + AddAbbrevAttribute(attrib, DW_FORM_ref4); + this->PushUint32(cu_offset); + } + + void WriteRef(Attribute attrib, int cu_offset) { + AddAbbrevAttribute(attrib, DW_FORM_ref_udata); + this->PushUleb128(cu_offset); + } + + void WriteString(Attribute attrib, const char* value) { + AddAbbrevAttribute(attrib, DW_FORM_string); + this->PushString(value); + } + + void WriteStrp(Attribute attrib, int address) { + AddAbbrevAttribute(attrib, DW_FORM_strp); + this->PushUint32(address); + } + + void WriteStrp(Attribute attrib, const char* value, std::vector<uint8_t>* debug_str) { + AddAbbrevAttribute(attrib, DW_FORM_strp); + int address = debug_str->size(); + debug_str->insert(debug_str->end(), value, value + strlen(value) + 1); + this->PushUint32(address); + } + + bool is64bit() const { return is64bit_; } + + using Writer<Allocator>::data; + + DebugInfoEntryWriter(bool is64bitArch, + std::vector<uint8_t, Allocator>* debug_abbrev, + const Allocator& alloc = Allocator()) + : Writer<Allocator>(&entries_), + debug_abbrev_(debug_abbrev), + current_abbrev_(alloc), + abbrev_codes_(alloc), + entries_(alloc), + is64bit_(is64bitArch) { + debug_abbrev_.PushUint8(0); // Add abbrev table terminator. + } + + ~DebugInfoEntryWriter() { + DCHECK_EQ(depth_, 0); + } + + private: + // Start abbreviation declaration. + void StartAbbrev(Tag tag, Children children) { + DCHECK(!inside_entry_); + current_abbrev_.clear(); + EncodeUnsignedLeb128(¤t_abbrev_, tag); + current_abbrev_.push_back(children); + } + + // Add attribute specification. + void AddAbbrevAttribute(Attribute name, Form type) { + DCHECK(inside_entry_) << "Call StartTag before adding attributes."; + EncodeUnsignedLeb128(¤t_abbrev_, name); + EncodeUnsignedLeb128(¤t_abbrev_, type); + } + + int NextAbbrevCode() { + return 1 + abbrev_codes_.size(); + } + + // End abbreviation declaration and return its code. + int EndAbbrev() { + DCHECK(inside_entry_); + auto it = abbrev_codes_.insert(std::make_pair(std::move(current_abbrev_), + NextAbbrevCode())); + int abbrev_code = it.first->second; + if (UNLIKELY(it.second)) { // Inserted new entry. + const std::vector<uint8_t, Allocator>& abbrev = it.first->first; + debug_abbrev_.Pop(); // Remove abbrev table terminator. + debug_abbrev_.PushUleb128(abbrev_code); + debug_abbrev_.PushData(abbrev.data(), abbrev.size()); + debug_abbrev_.PushUint8(0); // Attribute list end. + debug_abbrev_.PushUint8(0); // Attribute list end. + debug_abbrev_.PushUint8(0); // Add abbrev table terminator. + } + return abbrev_code; + } + + private: + // Fields for writing and deduplication of abbrevs. + Writer<Allocator> debug_abbrev_; + std::vector<uint8_t, Allocator> current_abbrev_; + std::unordered_map<std::vector<uint8_t, Allocator>, int, + FNVHash<Allocator> > abbrev_codes_; + + // Fields for writing of debugging information entries. + std::vector<uint8_t, Allocator> entries_; + bool is64bit_; + int depth_ = 0; + size_t abbrev_code_offset_ = 0; // Location to patch once we know the code. + bool inside_entry_ = false; // Entry ends at first child (if any). + bool has_children = true; +}; + +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_DEBUG_INFO_ENTRY_WRITER_H_ diff --git a/compiler/dwarf/debug_line_writer.h b/compiler/dwarf/debug_line_writer.h deleted file mode 100644 index 4b7d8d9d92..0000000000 --- a/compiler/dwarf/debug_line_writer.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_ -#define ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_ - -#include "debug_line_opcode_writer.h" -#include "dwarf.h" -#include "writer.h" -#include <string> - -namespace art { -namespace dwarf { - -// Writer for the .debug_line section (DWARF-3). -template<typename Allocator = std::allocator<uint8_t>> -class DebugLineWriter FINAL : private Writer<Allocator> { - public: - struct FileEntry { - std::string file_name; - int directory_index; - int modification_time; - int file_size; - }; - - void WriteTable(const std::vector<std::string>& include_directories, - const std::vector<FileEntry>& files, - const DebugLineOpCodeWriter<Allocator>& opcodes) { - size_t header_start = this->data()->size(); - this->PushUint32(0); // Section-length placeholder. - // Claim DWARF-2 version even though we use some DWARF-3 features. - // DWARF-2 consumers will ignore the unknown opcodes. - // This is what clang currently does. - this->PushUint16(2); // .debug_line version. - size_t header_length_pos = this->data()->size(); - this->PushUint32(0); // Header-length placeholder. - this->PushUint8(1 << opcodes.GetCodeFactorBits()); - this->PushUint8(DebugLineOpCodeWriter<Allocator>::kDefaultIsStmt ? 1 : 0); - this->PushInt8(DebugLineOpCodeWriter<Allocator>::kLineBase); - this->PushUint8(DebugLineOpCodeWriter<Allocator>::kLineRange); - this->PushUint8(DebugLineOpCodeWriter<Allocator>::kOpcodeBase); - static const int opcode_lengths[DebugLineOpCodeWriter<Allocator>::kOpcodeBase] = { - 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 }; - for (int i = 1; i < DebugLineOpCodeWriter<Allocator>::kOpcodeBase; i++) { - this->PushUint8(opcode_lengths[i]); - } - for (const std::string& directory : include_directories) { - this->PushData(directory.data(), directory.size() + 1); - } - this->PushUint8(0); // Terminate include_directories list. - for (const FileEntry& file : files) { - this->PushData(file.file_name.data(), file.file_name.size() + 1); - this->PushUleb128(file.directory_index); - this->PushUleb128(file.modification_time); - this->PushUleb128(file.file_size); - } - this->PushUint8(0); // Terminate file list. - this->UpdateUint32(header_length_pos, this->data()->size() - header_length_pos - 4); - this->PushData(opcodes.data()->data(), opcodes.data()->size()); - this->UpdateUint32(header_start, this->data()->size() - header_start - 4); - } - - explicit DebugLineWriter(std::vector<uint8_t, Allocator>* buffer) - : Writer<Allocator>(buffer) { - } - - private: - DISALLOW_COPY_AND_ASSIGN(DebugLineWriter); -}; - -} // namespace dwarf -} // namespace art - -#endif // ART_COMPILER_DWARF_DEBUG_LINE_WRITER_H_ diff --git a/compiler/dwarf/dwarf_test.cc b/compiler/dwarf/dwarf_test.cc index 2b051c9e12..ec18e96b4b 100644 --- a/compiler/dwarf/dwarf_test.cc +++ b/compiler/dwarf/dwarf_test.cc @@ -17,9 +17,9 @@ #include "dwarf_test.h" #include "dwarf/debug_frame_opcode_writer.h" -#include "dwarf/debug_frame_writer.h" +#include "dwarf/debug_info_entry_writer.h" #include "dwarf/debug_line_opcode_writer.h" -#include "dwarf/debug_line_writer.h" +#include "dwarf/headers.h" #include "gtest/gtest.h" namespace art { @@ -118,23 +118,20 @@ TEST_F(DwarfTest, DebugFrame) { DW_CHECK_NEXT("DW_CFA_restore: r2 (edx)"); DW_CHECK_NEXT("DW_CFA_restore: r5 (ebp)"); - DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit); DebugFrameOpCodeWriter<> initial_opcodes; - eh_frame.WriteCIE(Reg(is64bit ? 16 : 8), // Return address register. - initial_opcodes); // Initial opcodes. - eh_frame.WriteFDE(0x01000000, 0x01000000, - opcodes.data()->data(), opcodes.data()->size()); + WriteEhFrameCIE(is64bit, Reg(is64bit ? 16 : 8), initial_opcodes, &eh_frame_data_); + WriteEhFrameFDE(is64bit, 0, 0x01000000, 0x01000000, opcodes.data(), &eh_frame_data_); CheckObjdumpOutput(is64bit, "-W"); } // TODO: objdump seems to have trouble with 64bit CIE length. TEST_F(DwarfTest, DISABLED_DebugFrame64) { - const bool is64bit = true; - DebugFrameWriter<> eh_frame(&eh_frame_data_, is64bit); - DebugFrameOpCodeWriter<> no_opcodes; - eh_frame.WriteCIE(Reg(16), no_opcodes); - eh_frame.WriteFDE(0x0100000000000000, 0x0200000000000000, - no_opcodes.data()->data(), no_opcodes.data()->size()); + constexpr bool is64bit = true; + DebugFrameOpCodeWriter<> initial_opcodes; + WriteEhFrameCIE(is64bit, Reg(16), initial_opcodes, &eh_frame_data_); + DebugFrameOpCodeWriter<> opcodes; + WriteEhFrameFDE(is64bit, 0, 0x0100000000000000, 0x0200000000000000, + opcodes.data(), &eh_frame_data_); DW_CHECK("FDE cie=00000000 pc=100000000000000..300000000000000"); CheckObjdumpOutput(is64bit, "-W"); } @@ -148,7 +145,7 @@ TEST_F(DwarfTest, DebugLine) { include_directories.push_back("/path/to/source"); DW_CHECK("/path/to/source"); - std::vector<DebugLineWriter<>::FileEntry> files { + std::vector<FileEntry> files { { "file0.c", 0, 1000, 2000 }, { "file1.c", 1, 1000, 2000 }, { "file2.c", 1, 1000, 2000 }, @@ -187,8 +184,7 @@ TEST_F(DwarfTest, DebugLine) { DW_CHECK_NEXT("Entry\tDir\tTime\tSize\tName"); DW_CHECK_NEXT("1\t0\t1000\t2000\tfile.c"); - DebugLineWriter<> debug_line(&debug_line_data_); - debug_line.WriteTable(include_directories, files, opcodes); + WriteDebugLineTable(include_directories, files, opcodes, &debug_line_data_); CheckObjdumpOutput(is64bit, "-W"); } @@ -222,14 +218,63 @@ TEST_F(DwarfTest, DebugLineSpecialOpcodes) { EXPECT_LT(opcodes.data()->size(), num_rows * 3); std::vector<std::string> directories; - std::vector<DebugLineWriter<>::FileEntry> files { - { "file.c", 0, 1000, 2000 }, - }; - DebugLineWriter<> debug_line(&debug_line_data_); - debug_line.WriteTable(directories, files, opcodes); + std::vector<FileEntry> files { { "file.c", 0, 1000, 2000 } }; // NOLINT + WriteDebugLineTable(directories, files, opcodes, &debug_line_data_); CheckObjdumpOutput(is64bit, "-W -WL"); } +TEST_F(DwarfTest, DebugInfo) { + constexpr bool is64bit = false; + DebugInfoEntryWriter<> info(is64bit, &debug_abbrev_data_); + DW_CHECK("Contents of the .debug_info section:"); + info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes); + DW_CHECK("Abbrev Number: 1 (DW_TAG_compile_unit)"); + info.WriteStrp(dwarf::DW_AT_producer, "Compiler name", &debug_str_data_); + DW_CHECK_NEXT("DW_AT_producer : (indirect string, offset: 0x0): Compiler name"); + info.WriteAddr(dwarf::DW_AT_low_pc, 0x01000000); + DW_CHECK_NEXT("DW_AT_low_pc : 0x1000000"); + info.WriteAddr(dwarf::DW_AT_high_pc, 0x02000000); + DW_CHECK_NEXT("DW_AT_high_pc : 0x2000000"); + info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no); + DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)"); + info.WriteStrp(dwarf::DW_AT_name, "Foo", &debug_str_data_); + DW_CHECK_NEXT("DW_AT_name : (indirect string, offset: 0xe): Foo"); + info.WriteAddr(dwarf::DW_AT_low_pc, 0x01010000); + DW_CHECK_NEXT("DW_AT_low_pc : 0x1010000"); + info.WriteAddr(dwarf::DW_AT_high_pc, 0x01020000); + DW_CHECK_NEXT("DW_AT_high_pc : 0x1020000"); + info.EndTag(); // DW_TAG_subprogram + info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no); + DW_CHECK("Abbrev Number: 2 (DW_TAG_subprogram)"); + info.WriteStrp(dwarf::DW_AT_name, "Bar", &debug_str_data_); + DW_CHECK_NEXT("DW_AT_name : (indirect string, offset: 0x12): Bar"); + info.WriteAddr(dwarf::DW_AT_low_pc, 0x01020000); + DW_CHECK_NEXT("DW_AT_low_pc : 0x1020000"); + info.WriteAddr(dwarf::DW_AT_high_pc, 0x01030000); + DW_CHECK_NEXT("DW_AT_high_pc : 0x1030000"); + info.EndTag(); // DW_TAG_subprogram + info.EndTag(); // DW_TAG_compile_unit + // Test that previous list was properly terminated and empty children. + info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes); + info.EndTag(); // DW_TAG_compile_unit + + // The abbrev table is just side product, but check it as well. + DW_CHECK("Abbrev Number: 3 (DW_TAG_compile_unit)"); + DW_CHECK("Contents of the .debug_abbrev section:"); + DW_CHECK("1 DW_TAG_compile_unit [has children]"); + DW_CHECK_NEXT("DW_AT_producer DW_FORM_strp"); + DW_CHECK_NEXT("DW_AT_low_pc DW_FORM_addr"); + DW_CHECK_NEXT("DW_AT_high_pc DW_FORM_addr"); + DW_CHECK("2 DW_TAG_subprogram [no children]"); + DW_CHECK_NEXT("DW_AT_name DW_FORM_strp"); + DW_CHECK_NEXT("DW_AT_low_pc DW_FORM_addr"); + DW_CHECK_NEXT("DW_AT_high_pc DW_FORM_addr"); + DW_CHECK("3 DW_TAG_compile_unit [has children]"); + + dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info, &debug_info_data_); + CheckObjdumpOutput(is64bit, "-W"); +} + #endif // HAVE_ANDROID_OS } // namespace dwarf diff --git a/compiler/dwarf/headers.h b/compiler/dwarf/headers.h new file mode 100644 index 0000000000..d866b91ae7 --- /dev/null +++ b/compiler/dwarf/headers.h @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_DWARF_HEADERS_H_ +#define ART_COMPILER_DWARF_HEADERS_H_ + +#include "debug_frame_opcode_writer.h" +#include "debug_info_entry_writer.h" +#include "debug_line_opcode_writer.h" +#include "register.h" +#include "writer.h" + +namespace art { +namespace dwarf { + +// Write common information entry (CIE) to .eh_frame section. +template<typename Allocator> +void WriteEhFrameCIE(bool is64bit, Reg return_address_register, + const DebugFrameOpCodeWriter<Allocator>& opcodes, + std::vector<uint8_t>* eh_frame) { + Writer<> writer(eh_frame); + size_t cie_header_start_ = writer.data()->size(); + if (is64bit) { + // TODO: This is not related to being 64bit. + writer.PushUint32(0xffffffff); + writer.PushUint64(0); // Length placeholder. + writer.PushUint64(0); // CIE id. + } else { + writer.PushUint32(0); // Length placeholder. + writer.PushUint32(0); // CIE id. + } + writer.PushUint8(1); // Version. + writer.PushString("zR"); + writer.PushUleb128(DebugFrameOpCodeWriter<Allocator>::kCodeAlignmentFactor); + writer.PushSleb128(DebugFrameOpCodeWriter<Allocator>::kDataAlignmentFactor); + writer.PushUleb128(return_address_register.num()); // ubyte in DWARF2. + writer.PushUleb128(1); // z: Augmentation data size. + if (is64bit) { + writer.PushUint8(0x04); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata8). + } else { + writer.PushUint8(0x03); // R: ((DW_EH_PE_absptr << 4) | DW_EH_PE_udata4). + } + writer.PushData(opcodes.data()); + writer.Pad(is64bit ? 8 : 4); + if (is64bit) { + writer.UpdateUint64(cie_header_start_ + 4, writer.data()->size() - cie_header_start_ - 12); + } else { + writer.UpdateUint32(cie_header_start_, writer.data()->size() - cie_header_start_ - 4); + } +} + +// Write frame description entry (FDE) to .eh_frame section. +template<typename Allocator> +void WriteEhFrameFDE(bool is64bit, size_t cie_offset, + uint64_t initial_address, uint64_t address_range, + const std::vector<uint8_t, Allocator>* opcodes, + std::vector<uint8_t>* eh_frame) { + Writer<> writer(eh_frame); + size_t fde_header_start = writer.data()->size(); + if (is64bit) { + // TODO: This is not related to being 64bit. + writer.PushUint32(0xffffffff); + writer.PushUint64(0); // Length placeholder. + uint64_t cie_pointer = writer.data()->size() - cie_offset; + writer.PushUint64(cie_pointer); + } else { + writer.PushUint32(0); // Length placeholder. + uint32_t cie_pointer = writer.data()->size() - cie_offset; + writer.PushUint32(cie_pointer); + } + if (is64bit) { + writer.PushUint64(initial_address); + writer.PushUint64(address_range); + } else { + writer.PushUint32(initial_address); + writer.PushUint32(address_range); + } + writer.PushUleb128(0); // Augmentation data size. + writer.PushData(opcodes); + writer.Pad(is64bit ? 8 : 4); + if (is64bit) { + writer.UpdateUint64(fde_header_start + 4, writer.data()->size() - fde_header_start - 12); + } else { + writer.UpdateUint32(fde_header_start, writer.data()->size() - fde_header_start - 4); + } +} + +// Write compilation unit (CU) to .debug_info section. +template<typename Allocator> +void WriteDebugInfoCU(uint32_t debug_abbrev_offset, + const DebugInfoEntryWriter<Allocator>& entries, + std::vector<uint8_t>* debug_info) { + Writer<> writer(debug_info); + size_t start = writer.data()->size(); + writer.PushUint32(0); // Length placeholder. + writer.PushUint16(3); // Version. + writer.PushUint32(debug_abbrev_offset); + writer.PushUint8(entries.is64bit() ? 8 : 4); + writer.PushData(entries.data()); + writer.UpdateUint32(start, writer.data()->size() - start - 4); +} + +struct FileEntry { + std::string file_name; + int directory_index; + int modification_time; + int file_size; +}; + +// Write line table to .debug_line section. +template<typename Allocator> +void WriteDebugLineTable(const std::vector<std::string>& include_directories, + const std::vector<FileEntry>& files, + const DebugLineOpCodeWriter<Allocator>& opcodes, + std::vector<uint8_t>* debug_line) { + Writer<> writer(debug_line); + size_t header_start = writer.data()->size(); + writer.PushUint32(0); // Section-length placeholder. + // Claim DWARF-2 version even though we use some DWARF-3 features. + // DWARF-2 consumers will ignore the unknown opcodes. + // This is what clang currently does. + writer.PushUint16(2); // .debug_line version. + size_t header_length_pos = writer.data()->size(); + writer.PushUint32(0); // Header-length placeholder. + writer.PushUint8(1 << opcodes.GetCodeFactorBits()); + writer.PushUint8(DebugLineOpCodeWriter<Allocator>::kDefaultIsStmt ? 1 : 0); + writer.PushInt8(DebugLineOpCodeWriter<Allocator>::kLineBase); + writer.PushUint8(DebugLineOpCodeWriter<Allocator>::kLineRange); + writer.PushUint8(DebugLineOpCodeWriter<Allocator>::kOpcodeBase); + static const int opcode_lengths[DebugLineOpCodeWriter<Allocator>::kOpcodeBase] = { + 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1 }; + for (int i = 1; i < DebugLineOpCodeWriter<Allocator>::kOpcodeBase; i++) { + writer.PushUint8(opcode_lengths[i]); + } + for (const std::string& directory : include_directories) { + writer.PushData(directory.data(), directory.size() + 1); + } + writer.PushUint8(0); // Terminate include_directories list. + for (const FileEntry& file : files) { + writer.PushData(file.file_name.data(), file.file_name.size() + 1); + writer.PushUleb128(file.directory_index); + writer.PushUleb128(file.modification_time); + writer.PushUleb128(file.file_size); + } + writer.PushUint8(0); // Terminate file list. + writer.UpdateUint32(header_length_pos, writer.data()->size() - header_length_pos - 4); + writer.PushData(opcodes.data()->data(), opcodes.data()->size()); + writer.UpdateUint32(header_start, writer.data()->size() - header_start - 4); +} + +} // namespace dwarf +} // namespace art + +#endif // ART_COMPILER_DWARF_HEADERS_H_ diff --git a/compiler/dwarf/writer.h b/compiler/dwarf/writer.h index d8e29f0986..3b9c55866a 100644 --- a/compiler/dwarf/writer.h +++ b/compiler/dwarf/writer.h @@ -116,6 +116,11 @@ class Writer { data_->insert(data_->end(), p, p + size); } + template<typename Allocator2> + void PushData(const std::vector<uint8_t, Allocator2>* buffer) { + data_->insert(data_->end(), buffer->begin(), buffer->end()); + } + void UpdateUint32(size_t offset, uint32_t value) { DCHECK_LT(offset + 3, data_->size()); (*data_)[offset + 0] = (value >> 0) & 0xFF; @@ -136,6 +141,15 @@ class Writer { (*data_)[offset + 7] = (value >> 56) & 0xFF; } + void UpdateUleb128(size_t offset, uint32_t value) { + DCHECK_LE(offset + UnsignedLeb128Size(value), data_->size()); + UpdateUnsignedLeb128(data_->data() + offset, value); + } + + void Pop() { + return data_->pop_back(); + } + void Pad(int alignment) { DCHECK_NE(alignment, 0); data_->resize(RoundUp(data_->size(), alignment), 0); diff --git a/compiler/elf_writer_quick.cc b/compiler/elf_writer_quick.cc index 2756af1822..f780deadcc 100644 --- a/compiler/elf_writer_quick.cc +++ b/compiler/elf_writer_quick.cc @@ -26,8 +26,7 @@ #include "driver/compiler_driver.h" #include "driver/compiler_options.h" #include "dwarf.h" -#include "dwarf/debug_frame_writer.h" -#include "dwarf/debug_line_writer.h" +#include "dwarf/headers.h" #include "elf_builder.h" #include "elf_file.h" #include "elf_utils.h" @@ -40,42 +39,6 @@ namespace art { -static void PushByte(std::vector<uint8_t>* buf, int data) { - buf->push_back(data & 0xff); -} - -static uint32_t PushStr(std::vector<uint8_t>* buf, const char* str, const char* def = nullptr) { - if (str == nullptr) { - str = def; - } - - uint32_t offset = buf->size(); - for (size_t i = 0; str[i] != '\0'; ++i) { - buf->push_back(str[i]); - } - buf->push_back('\0'); - return offset; -} - -static uint32_t PushStr(std::vector<uint8_t>* buf, const std::string &str) { - uint32_t offset = buf->size(); - buf->insert(buf->end(), str.begin(), str.end()); - buf->push_back('\0'); - return offset; -} - -static void UpdateWord(std::vector<uint8_t>* buf, int offset, int data) { - (*buf)[offset+0] = data; - (*buf)[offset+1] = data >> 8; - (*buf)[offset+2] = data >> 16; - (*buf)[offset+3] = data >> 24; -} - -static void PushHalf(std::vector<uint8_t>* buf, int data) { - buf->push_back(data & 0xff); - buf->push_back((data >> 8) & 0xff); -} - template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr> @@ -90,7 +53,8 @@ bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, return elf_writer.Write(oat_writer, dex_files, android_root, is_host); } -void WriteCIE(dwarf::DebugFrameWriter<>* cfi, InstructionSet isa) { +void WriteCIE(InstructionSet isa, std::vector<uint8_t>* eh_frame) { + const bool is64bit = Is64BitInstructionSet(isa); // Scratch registers should be marked as undefined. This tells the // debugger that its value in the previous frame is not recoverable. switch (isa) { @@ -115,7 +79,7 @@ void WriteCIE(dwarf::DebugFrameWriter<>* cfi, InstructionSet isa) { } } auto return_address_reg = dwarf::Reg::ArmCore(14); // R14(LR). - cfi->WriteCIE(return_address_reg, opcodes); + dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); return; } case kArm64: { @@ -138,7 +102,7 @@ void WriteCIE(dwarf::DebugFrameWriter<>* cfi, InstructionSet isa) { } } auto return_address_reg = dwarf::Reg::Arm64Core(30); // R30(LR). - cfi->WriteCIE(return_address_reg, opcodes); + dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); return; } case kMips: @@ -154,7 +118,7 @@ void WriteCIE(dwarf::DebugFrameWriter<>* cfi, InstructionSet isa) { } } auto return_address_reg = dwarf::Reg::MipsCore(31); // R31(RA). - cfi->WriteCIE(return_address_reg, opcodes); + dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); return; } case kX86: { @@ -176,7 +140,7 @@ void WriteCIE(dwarf::DebugFrameWriter<>* cfi, InstructionSet isa) { opcodes.Undefined(dwarf::Reg::X86Fp(reg)); } auto return_address_reg = dwarf::Reg::X86Core(8); // R8(EIP). - cfi->WriteCIE(return_address_reg, opcodes); + dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); return; } case kX86_64: { @@ -202,7 +166,7 @@ void WriteCIE(dwarf::DebugFrameWriter<>* cfi, InstructionSet isa) { } } auto return_address_reg = dwarf::Reg::X86_64Core(16); // R16(RIP). - cfi->WriteCIE(return_address_reg, opcodes); + dwarf::WriteEhFrameCIE(is64bit, return_address_reg, opcodes, eh_frame); return; } case kNone: @@ -298,123 +262,29 @@ bool ElfWriterQuick<Elf_Word, Elf_Sword, Elf_Addr, Elf_Dyn, * @param dbg_str Debug strings. */ static void FillInCFIInformation(OatWriter* oat_writer, - std::vector<uint8_t>* dbg_info, - std::vector<uint8_t>* dbg_abbrev, - std::vector<uint8_t>* dbg_str, - std::vector<uint8_t>* dbg_line, + std::vector<uint8_t>* debug_info_data, + std::vector<uint8_t>* debug_abbrev_data, + std::vector<uint8_t>* debug_str_data, + std::vector<uint8_t>* debug_line_data, uint32_t text_section_offset) { const std::vector<OatWriter::DebugInfo>& method_infos = oat_writer->GetCFIMethodInfo(); - uint32_t producer_str_offset = PushStr(dbg_str, "Android dex2oat"); - - constexpr bool use_64bit_addresses = false; - - // Create the debug_abbrev section with boilerplate information. - // We only care about low_pc and high_pc right now for the compilation - // unit and methods. - - // Tag 1: Compilation unit: DW_TAG_compile_unit. - PushByte(dbg_abbrev, 1); - PushByte(dbg_abbrev, dwarf::DW_TAG_compile_unit); - - // There are children (the methods). - PushByte(dbg_abbrev, dwarf::DW_CHILDREN_yes); - - // DW_AT_producer DW_FORM_data1. - // REVIEW: we can get rid of dbg_str section if - // DW_FORM_string (immediate string) was used everywhere instead of - // DW_FORM_strp (ref to string from .debug_str section). - // DW_FORM_strp makes sense only if we reuse the strings. - PushByte(dbg_abbrev, dwarf::DW_AT_producer); - PushByte(dbg_abbrev, dwarf::DW_FORM_strp); - - // DW_LANG_Java DW_FORM_data1. - PushByte(dbg_abbrev, dwarf::DW_AT_language); - PushByte(dbg_abbrev, dwarf::DW_FORM_data1); - - // DW_AT_low_pc DW_FORM_addr. - PushByte(dbg_abbrev, dwarf::DW_AT_low_pc); - PushByte(dbg_abbrev, dwarf::DW_FORM_addr); - - // DW_AT_high_pc DW_FORM_addr. - PushByte(dbg_abbrev, dwarf::DW_AT_high_pc); - PushByte(dbg_abbrev, dwarf::DW_FORM_addr); - - if (dbg_line != nullptr) { - // DW_AT_stmt_list DW_FORM_sec_offset. - PushByte(dbg_abbrev, dwarf::DW_AT_stmt_list); - PushByte(dbg_abbrev, dwarf::DW_FORM_data4); - } - - // End of DW_TAG_compile_unit. - PushByte(dbg_abbrev, 0); // DW_AT. - PushByte(dbg_abbrev, 0); // DW_FORM. - - // Tag 2: Compilation unit: DW_TAG_subprogram. - PushByte(dbg_abbrev, 2); - PushByte(dbg_abbrev, dwarf::DW_TAG_subprogram); - - // There are no children. - PushByte(dbg_abbrev, dwarf::DW_CHILDREN_no); - - // Name of the method. - PushByte(dbg_abbrev, dwarf::DW_AT_name); - PushByte(dbg_abbrev, dwarf::DW_FORM_strp); - - // DW_AT_low_pc DW_FORM_addr. - PushByte(dbg_abbrev, dwarf::DW_AT_low_pc); - PushByte(dbg_abbrev, dwarf::DW_FORM_addr); - - // DW_AT_high_pc DW_FORM_addr. - PushByte(dbg_abbrev, dwarf::DW_AT_high_pc); - PushByte(dbg_abbrev, dwarf::DW_FORM_addr); - - // End of DW_TAG_subprogram. - PushByte(dbg_abbrev, 0); // DW_AT. - PushByte(dbg_abbrev, 0); // DW_FORM. - - // End of abbrevs for compilation unit - PushByte(dbg_abbrev, 0); - - // Start the debug_info section with the header information - // 'unit_length' will be filled in later. - int cunit_length = dbg_info->size(); - Push32(dbg_info, 0); - - // 'version' - 3. - PushHalf(dbg_info, 3); - - // Offset into .debug_abbrev section (always 0). - Push32(dbg_info, 0); - - // Address size: 4 or 8. - PushByte(dbg_info, use_64bit_addresses ? 8 : 4); - - // Start the description for the compilation unit. - // This uses tag 1. - PushByte(dbg_info, 1); - - // The producer is Android dex2oat. - Push32(dbg_info, producer_str_offset); - - // The language is Java. - PushByte(dbg_info, dwarf::DW_LANG_Java); - - // low_pc and high_pc. uint32_t cunit_low_pc = static_cast<uint32_t>(-1); uint32_t cunit_high_pc = 0; for (auto method_info : method_infos) { cunit_low_pc = std::min(cunit_low_pc, method_info.low_pc_); cunit_high_pc = std::max(cunit_high_pc, method_info.high_pc_); } - Push32(dbg_info, cunit_low_pc + text_section_offset); - Push32(dbg_info, cunit_high_pc + text_section_offset); - if (dbg_line != nullptr) { - // Line number table offset. - Push32(dbg_info, dbg_line->size()); + dwarf::DebugInfoEntryWriter<> info(false /* 32 bit */, debug_abbrev_data); + info.StartTag(dwarf::DW_TAG_compile_unit, dwarf::DW_CHILDREN_yes); + info.WriteStrp(dwarf::DW_AT_producer, "Android dex2oat", debug_str_data); + info.WriteData1(dwarf::DW_AT_language, dwarf::DW_LANG_Java); + info.WriteAddr(dwarf::DW_AT_low_pc, cunit_low_pc + text_section_offset); + info.WriteAddr(dwarf::DW_AT_high_pc, cunit_high_pc + text_section_offset); + if (debug_line_data != nullptr) { + info.WriteData4(dwarf::DW_AT_stmt_list, debug_line_data->size()); } - for (auto method_info : method_infos) { std::string method_name = PrettyMethod(method_info.dex_method_index_, *method_info.dex_file_, true); @@ -423,17 +293,16 @@ static void FillInCFIInformation(OatWriter* oat_writer, // so that it will show up in a debuggerd crash report. method_name += " [ DEDUPED ]"; } - - // Start a new TAG: subroutine (2). - PushByte(dbg_info, 2); - - // Enter name, low_pc, high_pc. - Push32(dbg_info, PushStr(dbg_str, method_name)); - Push32(dbg_info, method_info.low_pc_ + text_section_offset); - Push32(dbg_info, method_info.high_pc_ + text_section_offset); + info.StartTag(dwarf::DW_TAG_subprogram, dwarf::DW_CHILDREN_no); + info.WriteStrp(dwarf::DW_AT_name, method_name.data(), debug_str_data); + info.WriteAddr(dwarf::DW_AT_low_pc, method_info.low_pc_ + text_section_offset); + info.WriteAddr(dwarf::DW_AT_high_pc, method_info.high_pc_ + text_section_offset); + info.EndTag(); // DW_TAG_subprogram } + info.EndTag(); // DW_TAG_compile_unit + dwarf::WriteDebugInfoCU(0 /* debug_abbrev_offset */, info, debug_info_data); - if (dbg_line != nullptr) { + if (debug_line_data != nullptr) { // TODO: in gdb info functions <regexp> - reports Java functions, but // source file is <unknown> because .debug_line is formed as one // compilation unit. To fix this it is possible to generate @@ -441,7 +310,7 @@ static void FillInCFIInformation(OatWriter* oat_writer, // Each of the these compilation units can have several non-adjacent // method ranges. - std::vector<dwarf::DebugLineWriter<>::FileEntry> files; + std::vector<dwarf::FileEntry> files; std::unordered_map<std::string, size_t> files_map; std::vector<std::string> directories; std::unordered_map<std::string, size_t> directories_map; @@ -465,7 +334,7 @@ static void FillInCFIInformation(OatWriter* oat_writer, break; } - dwarf::DebugLineOpCodeWriter<> opcodes(use_64bit_addresses, code_factor_bits_); + dwarf::DebugLineOpCodeWriter<> opcodes(false /* 32bit */, code_factor_bits_); opcodes.SetAddress(text_section_offset + cunit_low_pc); if (isa != -1) { opcodes.SetISA(isa); @@ -529,7 +398,7 @@ static void FillInCFIInformation(OatWriter* oat_writer, if (it2 == files_map.end()) { file_index = 1 + files.size(); files_map.emplace(full_path, file_index); - files.push_back(dwarf::DebugLineWriter<>::FileEntry { + files.push_back(dwarf::FileEntry { file_name, directory_index, 0, // Modification time - NA. @@ -574,19 +443,10 @@ static void FillInCFIInformation(OatWriter* oat_writer, opcodes.AddRow(low_pc, 0); } } - opcodes.AdvancePC(text_section_offset + cunit_high_pc); opcodes.EndSequence(); - - dwarf::DebugLineWriter<> dbg_line_writer(dbg_line); - dbg_line_writer.WriteTable(directories, files, opcodes); + dwarf::WriteDebugLineTable(directories, files, opcodes, debug_line_data); } - - // One byte terminator. - PushByte(dbg_info, 0); - - // We have now walked all the methods. Fill in lengths. - UpdateWord(dbg_info, cunit_length, dbg_info->size() - cunit_length - 4); } template <typename Elf_Word, typename Elf_Sword, typename Elf_Addr, @@ -599,9 +459,9 @@ static void WriteDebugSymbols(const CompilerDriver* compiler_driver, Elf_Sym, Elf_Ehdr, Elf_Phdr, Elf_Shdr>* builder, OatWriter* oat_writer) { std::vector<uint8_t> cfi_data; - bool is_64bit = Is64BitInstructionSet(compiler_driver->GetInstructionSet()); - dwarf::DebugFrameWriter<> cfi(&cfi_data, is_64bit); - WriteCIE(&cfi, compiler_driver->GetInstructionSet()); + const int cie_offset = 0; + bool is64bit = Is64BitInstructionSet(compiler_driver->GetInstructionSet()); + WriteCIE(compiler_driver->GetInstructionSet(), &cfi_data); Elf_Addr text_section_address = builder->GetTextBuilder().GetSection()->sh_addr; @@ -632,12 +492,12 @@ static void WriteDebugSymbols(const CompilerDriver* compiler_driver, // Include FDE for compiled method, if possible. DCHECK(it->compiled_method_ != nullptr); - const SwapVector<uint8_t>* unwind_opcodes = it->compiled_method_->GetCFIInfo(); - if (unwind_opcodes != nullptr) { + const SwapVector<uint8_t>* opcodes = it->compiled_method_->GetCFIInfo(); + if (opcodes != nullptr) { // TUNING: The headers take a lot of space. Can we have 1 FDE per file? // TUNING: Some tools support compressed DWARF sections (.zdebug_*). - cfi.WriteFDE(text_section_address + it->low_pc_, it->high_pc_ - it->low_pc_, - unwind_opcodes->data(), unwind_opcodes->size()); + dwarf::WriteEhFrameFDE(is64bit, cie_offset, text_section_address + it->low_pc_, + it->high_pc_ - it->low_pc_, opcodes, &cfi_data); } } diff --git a/runtime/leb128.h b/runtime/leb128.h index d36b690aa1..2e27b8ea3d 100644 --- a/runtime/leb128.h +++ b/runtime/leb128.h @@ -136,6 +136,19 @@ static inline void EncodeUnsignedLeb128(std::vector<uint8_t, Allocator>* dest, u dest->push_back(out); } +// Overwrite encoded Leb128 with a new value. The new value must be less than +// or equal to the old value to ensure that it fits the allocated space. +static inline void UpdateUnsignedLeb128(uint8_t* dest, uint32_t value) { + const uint8_t* old_end = dest; + uint32_t old_value = DecodeUnsignedLeb128(&old_end); + DCHECK_LE(value, old_value); + for (uint8_t* end = EncodeUnsignedLeb128(dest, value); end < old_end; end++) { + // Use longer encoding than necessary to fill the allocated space. + end[-1] |= 0x80; + end[0] = 0; + } +} + static inline uint8_t* EncodeSignedLeb128(uint8_t* dest, int32_t value) { uint32_t extra_bits = static_cast<uint32_t>(value ^ (value >> 31)) >> 6; uint8_t out = value & 0x7f; diff --git a/runtime/leb128_test.cc b/runtime/leb128_test.cc index 5d157dc9e5..87e13ff693 100644 --- a/runtime/leb128_test.cc +++ b/runtime/leb128_test.cc @@ -252,6 +252,25 @@ TEST(Leb128Test, SignedStream) { EXPECT_EQ(data_size, static_cast<size_t>(encoded_data_ptr - encoded_data)); } +TEST(Leb128Test, UnsignedUpdate) { + for (size_t i = 0; i < arraysize(uleb128_tests); ++i) { + for (size_t j = 0; j < arraysize(uleb128_tests); ++j) { + uint32_t old_value = uleb128_tests[i].decoded; + uint32_t new_value = uleb128_tests[j].decoded; + // We can only make the encoded value smaller. + if (new_value <= old_value) { + uint8_t encoded_data[5]; + uint8_t* old_end = EncodeUnsignedLeb128(encoded_data, old_value); + UpdateUnsignedLeb128(encoded_data, new_value); + const uint8_t* new_end = encoded_data; + EXPECT_EQ(DecodeUnsignedLeb128(&new_end), new_value); + // Even if the new value needs fewer bytes, we should fill the space. + EXPECT_EQ(new_end, old_end); + } + } + } +} + TEST(Leb128Test, Speed) { std::unique_ptr<Histogram<uint64_t>> enc_hist(new Histogram<uint64_t>("Leb128EncodeSpeedTest", 5)); std::unique_ptr<Histogram<uint64_t>> dec_hist(new Histogram<uint64_t>("Leb128DecodeSpeedTest", 5)); |