| // Copyright 2016 The Tulsi Authors. All rights reserved. |
| // |
| // 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. |
| |
| #include "dwarf_string_patcher.h" |
| |
| #include "dwarf_buffer_reader.h" |
| #include "mach_o_file.h" |
| |
| |
| namespace post_processor { |
| |
| namespace { |
| |
| const char *kSegment = "__DWARF"; |
| |
| const char *kAbbreviationSection = "__debug_abbrev"; |
| const char *kInfoSection = "__debug_info"; |
| const char *kLineInfoSection = "__debug_line"; |
| const char *kStringSection = "__debug_str"; |
| |
| enum DW_FORM { |
| DW_FORM_addr = 0x01, |
| DW_FORM_block2 = 0x03, |
| DW_FORM_block4 = 0x04, |
| DW_FORM_data2 = 0x05, |
| DW_FORM_data4 = 0x06, |
| DW_FORM_data8 = 0x07, |
| DW_FORM_string = 0x08, |
| DW_FORM_block = 0x09, |
| DW_FORM_block1 = 0x0a, |
| DW_FORM_data1 = 0x0b, |
| DW_FORM_flag = 0x0c, |
| DW_FORM_sdata = 0x0d, |
| DW_FORM_strp = 0x0e, |
| DW_FORM_udata = 0x0f, |
| DW_FORM_ref_addr = 0x10, |
| DW_FORM_ref1 = 0x11, |
| DW_FORM_ref2 = 0x12, |
| DW_FORM_ref4 = 0x13, |
| DW_FORM_ref8 = 0x14, |
| DW_FORM_ref_udata = 0x15, |
| DW_FORM_indirect = 0x16, |
| }; |
| |
| enum DataSize { |
| DataSize_DWORD, |
| DataSize_QWORD, |
| }; |
| |
| inline bool PatchString(std::string *value, |
| size_t value_len, |
| size_t old_prefix_length, |
| const std::string::const_iterator &old_prefix_begin, |
| const std::string::const_iterator &old_prefix_end, |
| const std::string &new_prefix); |
| |
| inline ReturnCode PatchfoAttributeValue( |
| std::function<bool(uint64_t, size_t)> write_func, |
| const std::map<size_t, size_t> &string_relocation_table, |
| DWARFBufferReader *reader, |
| uint64_t form_code, |
| uint8_t address_size, |
| uint16_t dwarf_version, |
| std::function<bool(uint64_t *)> read_func); |
| |
| // Updates size information contained in a DWARF line info header. T must be |
| // uint64_t if the unit length is 64-bit or uint32_t if it is 32-bit. |
| template <typename T> |
| void UpdateLineInfoSizeInfo(uint8_t *existing_data_ptr, |
| size_t compilation_unit_length_offset, |
| size_t header_length_offset, |
| bool swap_byte_ordering, |
| uint64_t new_compilation_unit_length, |
| uint64_t new_header_length); |
| } // namespace |
| |
| ReturnCode DWARFStringPatcher::Patch(post_processor::MachOFile *f) { |
| assert(f); |
| |
| ReturnCode retval = PatchLineInfoSection(f); |
| if (retval != ERR_OK) { |
| return retval; |
| } |
| |
| // Patch the string table and any references into it. |
| VerbosePrint("Processing string section.\n"); |
| size_t data_length; |
| |
| // Note that a NULL is added to the section data buffer to ensure that the |
| // table can be processed in a predictable manner (DWARF string tables |
| // generally omit the final NULL terminator and use the section size to |
| // delimit the final string). |
| std::unique_ptr<uint8_t[]> &&string_data = |
| f->ReadSectionData(kSegment, |
| kStringSection, |
| &data_length, |
| 1 /* null terminate the data */); |
| if (!string_data) { |
| fprintf(stderr, "Warning: Failed to find __debug_str section.\n"); |
| return ERR_OK; |
| } |
| |
| bool data_was_modified = false; |
| |
| // Handle the simple in-place update case. |
| if (new_prefix_.length() <= old_prefix_.length()) { |
| UpdateStringSectionInPlace(reinterpret_cast<char *>(string_data.get()), |
| data_length, |
| &data_was_modified); |
| if (data_was_modified) { |
| VerbosePrint("Updating string section in-place.\n"); |
| // Remove the trailing null. |
| --data_length; |
| return f->WriteSectionData(kSegment, |
| kStringSection, |
| std::move(string_data), |
| data_length); |
| } |
| return ERR_OK; |
| } |
| |
| // At this point a full table replacement is required. This necessitates |
| // rewriting the string table itself, then walking through the other DWARF |
| // sections and updating any string references to point at their new |
| // locations. |
| |
| size_t new_data_length = data_length; |
| std::map<size_t, size_t> string_relocation_table; |
| std::unique_ptr<uint8_t[]> &&new_data = RewriteStringSection( |
| reinterpret_cast<char *>(string_data.get()), |
| data_length, |
| &string_relocation_table, |
| &new_data_length, |
| &data_was_modified); |
| if (!data_was_modified) { |
| return ERR_OK; |
| } |
| |
| // The last entry need not be null terminated. |
| --new_data_length; |
| VerbosePrint("Rewriting string section.\n"); |
| retval = f->WriteSectionData(kSegment, |
| kStringSection, |
| std::move(new_data), |
| new_data_length); |
| if (retval != ERR_OK && retval != ERR_WRITE_DEFERRED) { |
| return retval; |
| } |
| |
| std::map<size_t, AbbreviationTable> abbreviation_table_map; |
| retval = ProcessAbbrevSection(*f, &abbreviation_table_map); |
| if (retval != ERR_OK) { |
| return retval; |
| } |
| |
| return PatchInfoSection(f, string_relocation_table, abbreviation_table_map); |
| } |
| |
| void DWARFStringPatcher::UpdateStringSectionInPlace( |
| char *data, |
| size_t data_length, |
| bool *data_was_modified) { |
| assert(data && data_was_modified); |
| |
| *data_was_modified = false; |
| size_t old_prefix_length = old_prefix_.length(); |
| const char *old_prefix_cstr = old_prefix_.c_str(); |
| size_t new_prefix_length = new_prefix_.length(); |
| const char *new_prefix_cstr = new_prefix_.c_str(); |
| |
| // The data table is an offset-indexed contiguous array of null terminated |
| // ASCII or UTF-8 strings, so strings whose lengths are being reduced or |
| // maintained may be modified in place without changing the run-time |
| // behavior. |
| // TODO(abaire): Support UTF-8. |
| char *start = data; |
| char *end = start + data_length; |
| while (start < end) { |
| size_t entry_length = strlen(start); |
| if (entry_length >= old_prefix_length && |
| !memcmp(start, old_prefix_cstr, old_prefix_length)) { |
| *data_was_modified = true; |
| size_t suffix_length = entry_length - old_prefix_length; |
| memcpy(start, new_prefix_cstr, new_prefix_length); |
| memmove(start + new_prefix_length, |
| start + old_prefix_length, |
| suffix_length); |
| start[new_prefix_length + suffix_length] = 0; |
| } |
| start += entry_length + 1; |
| } |
| } |
| |
| std::unique_ptr<uint8_t[]> DWARFStringPatcher::RewriteStringSection( |
| char *data, |
| size_t data_length, |
| std::map<size_t, size_t> *relocation_table, |
| size_t *new_data_length, |
| bool *data_was_modified) { |
| assert(data && relocation_table && new_data_length && data_was_modified); |
| |
| relocation_table->clear(); |
| *data_was_modified = false; |
| auto old_prefix_begin = old_prefix_.begin(); |
| auto old_prefix_end = old_prefix_.end(); |
| size_t old_prefix_length = old_prefix_.length(); |
| size_t delta_length = new_prefix_.length() - old_prefix_.length(); |
| |
| std::list<std::string> new_string_table; |
| *new_data_length = 0; |
| size_t original_offset = 0; |
| size_t new_offset = 0; |
| char *start = data; |
| char *end = start + data_length; |
| while (start < end) { |
| std::string entry(start); |
| size_t len = entry.length(); |
| size_t len_plus_one = len + 1; |
| start += len_plus_one; |
| *new_data_length += len_plus_one; |
| |
| if (PatchString(&entry, |
| len, |
| old_prefix_length, |
| old_prefix_begin, |
| old_prefix_end, |
| new_prefix_)) { |
| *data_was_modified = true; |
| *new_data_length += delta_length; |
| } |
| new_string_table.push_back(entry); |
| |
| (*relocation_table)[original_offset] = new_offset; |
| original_offset += len_plus_one; |
| new_offset += entry.size() + 1; |
| } |
| |
| std::unique_ptr<uint8_t[]> new_data(new uint8_t[*new_data_length]); |
| uint8_t *offset = new_data.get(); |
| for (auto str : new_string_table) { |
| auto str_length = str.length(); |
| memcpy(offset, str.c_str(), str_length); |
| offset += str_length + 1; |
| } |
| |
| return new_data; |
| } |
| |
| ReturnCode DWARFStringPatcher::ProcessAbbrevSection( |
| const MachOFile &f, |
| std::map<size_t, AbbreviationTable> *table_map) const { |
| assert(table_map); |
| VerbosePrint("Processing abbreviation section.\n"); |
| |
| size_t data_length; |
| std::unique_ptr<uint8_t[]> &&data = f.ReadSectionData(kSegment, |
| kAbbreviationSection, |
| &data_length); |
| if (!data) { |
| fprintf(stderr, "Warning: Failed to find __debug_abbrev section.\n"); |
| return ERR_OK; |
| } |
| |
| DWARFBufferReader reader(data.get(), |
| data_length, |
| f.swap_byte_ordering()); |
| |
| size_t cur_table_offset = 0; |
| |
| while (reader.bytes_remaining()) { |
| Abbreviation abbreviation; |
| bool end_of_table; |
| ReturnCode retval = ProcessAbbreviation(&reader, |
| &abbreviation, |
| &end_of_table); |
| if (retval != ERR_OK) { |
| return retval; |
| } |
| |
| if (end_of_table) { |
| cur_table_offset = reader.read_position(); |
| } else { |
| auto table_map_it = table_map->find(cur_table_offset); |
| if (table_map_it == table_map->end()) { |
| auto result = table_map->insert( |
| std::make_pair(cur_table_offset, AbbreviationTable())); |
| table_map_it = result.first; |
| } |
| AbbreviationTable &table = table_map_it->second; |
| table[abbreviation.abbreviation_code] = std::move(abbreviation); |
| } |
| } |
| |
| return ERR_OK; |
| } |
| |
| ReturnCode DWARFStringPatcher::ProcessAbbreviation(DWARFBufferReader *reader, |
| Abbreviation *out, |
| bool *end_of_table) const { |
| assert(reader && out && end_of_table); |
| if (!reader->ReadULEB128(&out->abbreviation_code)) { |
| fprintf(stderr, "Failed to read DWARF abbreviation table.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| *end_of_table = (out->abbreviation_code == 0); |
| if (*end_of_table) { |
| return ERR_OK; |
| } |
| |
| if (!reader->ReadULEB128(&out->tag)) { |
| fprintf(stderr, "Failed to read DWARF abbreviation table.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| uint8_t has_children; |
| if (!reader->ReadByte(&has_children)) { |
| fprintf(stderr, "Failed to read DWARF abbreviation table.\n"); |
| return ERR_INVALID_FILE; |
| } |
| out->has_children = has_children != 0; |
| |
| out->attributes.clear(); |
| while (true) { |
| uint64_t name, form; |
| if (!reader->ReadULEB128(&name) || !reader->ReadULEB128(&form)) { |
| fprintf(stderr, "Failed to read DWARF abbreviation table.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| if (name == 0 && form == 0) { |
| break; |
| } |
| |
| out->attributes.push_back(Attribute(name, form)); |
| } |
| |
| return ERR_OK; |
| } |
| |
| ReturnCode DWARFStringPatcher::PatchInfoSection( |
| MachOFile *f, |
| const std::map<size_t, size_t> &string_relocation_table, |
| const std::map<size_t, AbbreviationTable> &abbreviation_table_map) const { |
| assert(f); |
| VerbosePrint("Patching info section.\n"); |
| |
| size_t data_length; |
| std::unique_ptr<uint8_t[]> &&data = |
| f->ReadSectionData(kSegment, |
| kInfoSection, |
| &data_length); |
| if (!data) { |
| fprintf(stderr, "Failed to find __debug_info section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| bool data_was_modified = false; |
| DWARFBufferReader reader(data.get(), |
| data_length, |
| f->swap_byte_ordering()); |
| |
| while (reader.bytes_remaining() > 0) { |
| uint64_t compilation_unit_length; |
| uint32_t compilation_unit_length_32; |
| if (!reader.ReadDWORD(&compilation_unit_length_32)) { |
| fprintf(stderr, "Failed to read DWARF info section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| std::function<bool(uint64_t *)> read_func; |
| std::function<bool(uint64_t, size_t)> write_func; |
| |
| if (compilation_unit_length_32 & 0x80000000) { |
| if (!reader.ReadQWORD(&compilation_unit_length)) { |
| fprintf(stderr, "Failed to read DWARF info section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| read_func = [&](uint64_t *out) -> bool { |
| return reader.ReadQWORD(out); |
| }; |
| write_func = [&](uint64_t value, size_t offset) -> bool { |
| uint64_t *target = reinterpret_cast<uint64_t*>(data.get() + offset); |
| if (f->swap_byte_ordering()) { |
| OSSwapInt64(value); |
| } |
| *target = value; |
| data_was_modified = true; |
| return true; |
| }; |
| } else { |
| compilation_unit_length = compilation_unit_length_32; |
| read_func = [&](uint64_t *out) -> bool { |
| uint32_t val; |
| if (!reader.ReadDWORD(&val)) { |
| return false; |
| } |
| *out = val; |
| return true; |
| }; |
| write_func = [&](uint64_t value, size_t offset) -> bool { |
| uint32_t actual_value = static_cast<uint32_t>(value); |
| uint32_t *target = reinterpret_cast<uint32_t*>(data.get() + offset); |
| if (f->swap_byte_ordering()) { |
| OSSwapInt32(actual_value); |
| } |
| *target = actual_value; |
| data_was_modified = true; |
| return true; |
| }; |
| } |
| |
| size_t unit_end_position = reader.read_position() + compilation_unit_length; |
| |
| uint16_t dwarf_version; |
| if (!reader.ReadWORD(&dwarf_version)) { |
| fprintf(stderr, "Failed to read DWARF info section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| uint64_t abbrev_offset; |
| if (!read_func(&abbrev_offset)) { |
| return ERR_INVALID_FILE; |
| } |
| uint8_t address_size; |
| if (!reader.ReadByte(&address_size)) { |
| fprintf(stderr, "Failed to read DWARF info section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| auto abbreviation_table_it = abbreviation_table_map.find(abbrev_offset); |
| if (abbreviation_table_it == abbreviation_table_map.end()) { |
| fprintf(stderr, |
| "Invalid abbreviation table reference %llu in DWARF info " |
| "section.\n", |
| abbrev_offset); |
| return ERR_INVALID_FILE; |
| } |
| const AbbreviationTable &abbreviation_table = abbreviation_table_it->second; |
| |
| while (reader.read_position() < unit_end_position) { |
| uint64_t abbrev_code; |
| if (!reader.ReadULEB128(&abbrev_code)) { |
| fprintf(stderr, "Failed to read DWARF info section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| if (abbrev_code == 0) { |
| // Skip this null padding entry. |
| continue; |
| } |
| |
| auto abbreviation_it = abbreviation_table.find(abbrev_code); |
| if (abbreviation_it == abbreviation_table.end()) { |
| fprintf(stderr, "Failed to read DWARF info section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| const Abbreviation &abbreviation = abbreviation_it->second; |
| for (auto &attribute : abbreviation.attributes) { |
| ReturnCode retval = PatchfoAttributeValue(write_func, |
| string_relocation_table, |
| &reader, |
| attribute.second, |
| address_size, |
| dwarf_version, |
| read_func); |
| if (retval != ERR_OK) { |
| fprintf(stderr, "Invalid entry in DWARF info section.\n"); |
| return retval; |
| } |
| } |
| } |
| } |
| |
| if (!data_was_modified) { |
| return ERR_OK; |
| } |
| |
| return f->WriteSectionData(kSegment, |
| kInfoSection, |
| std::move(data), |
| data_length); |
| } |
| |
| ReturnCode DWARFStringPatcher::PatchLineInfoSection(MachOFile *f) { |
| assert(f); |
| VerbosePrint("Patching line info section.\n"); |
| size_t data_length; |
| std::unique_ptr<uint8_t[]> &&data = |
| f->ReadSectionData(kSegment, |
| kLineInfoSection, |
| &data_length); |
| if (!data) { |
| fprintf(stderr, "Warning: Failed to find __debug_line section.\n"); |
| return ERR_OK; |
| } |
| |
| std::list<LineInfoPatch> patch_actions; |
| size_t patched_section_size_increase = 0; |
| ReturnCode retval = ProcessLineInfoData(data.get(), |
| data_length, |
| f->swap_byte_ordering(), |
| &patch_actions, |
| &patched_section_size_increase); |
| if (retval != ERR_OK) { |
| return retval; |
| } |
| |
| if (patch_actions.empty()) { |
| return ERR_OK; |
| } |
| |
| // If the section does not need to be resized, patches can simply be applied |
| // in place without adjusting any lengths (string tables are never reduced in |
| // size). |
| if (!patched_section_size_increase) { |
| return ApplyLineInfoPatchesInPlace(f, |
| std::move(data), |
| data_length, |
| patch_actions); |
| } |
| |
| size_t new_data_size = data_length + patched_section_size_increase; |
| return ApplyLineInfoPatches(f, |
| std::move(data), |
| data_length, |
| new_data_size, |
| patch_actions); |
| } |
| |
| ReturnCode DWARFStringPatcher::ProcessLineInfoData( |
| uint8_t *data, |
| size_t data_length, |
| bool swap_byte_ordering, |
| std::list<LineInfoPatch> *patch_actions, |
| size_t *patched_section_size_increase) { |
| assert(data && patch_actions && patched_section_size_increase); |
| DWARFBufferReader reader(data, |
| data_length, |
| swap_byte_ordering); |
| |
| auto old_prefix_begin = old_prefix_.begin(); |
| auto old_prefix_end = old_prefix_.end(); |
| size_t old_prefix_length = old_prefix_.length(); |
| |
| // The number of additional bytes required by the patched strings. |
| *patched_section_size_increase = 0; |
| |
| while (reader.bytes_remaining() > 0) { |
| size_t compilation_unit_length_offset = reader.read_position(); |
| uint64_t compilation_unit_length; |
| uint32_t compilation_unit_length_32; |
| if (!reader.ReadDWORD(&compilation_unit_length_32)) { |
| fprintf(stderr, "Failed to read DWARF line section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| std::function<bool(uint64_t *)> read_func; |
| if (compilation_unit_length_32 == 0xffffffff) { |
| if (!reader.ReadQWORD(&compilation_unit_length)) { |
| fprintf(stderr, "Failed to read DWARF line section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| read_func = [&](uint64_t *out) -> bool { |
| return reader.ReadQWORD(out); |
| }; |
| } else { |
| compilation_unit_length = compilation_unit_length_32; |
| read_func = [&](uint64_t *out) -> bool { |
| uint32_t val; |
| if (!reader.ReadDWORD(&val)) { |
| return false; |
| } |
| *out = val; |
| return true; |
| }; |
| } |
| size_t end_offset = reader.read_position() + compilation_unit_length; |
| |
| uint16_t version; |
| if (!reader.ReadWORD(&version)) { |
| fprintf(stderr, "Failed to read DWARF line section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| size_t header_length_offset = reader.read_position(); |
| uint64_t header_length; |
| if (!read_func(&header_length)) { |
| fprintf(stderr, "Failed to read DWARF line section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| // Skip the minimum_instruction_length (ubyte). |
| size_t bytes_to_skip = 1; |
| |
| if (version == 4) { |
| // Skip maximum_operations_per_instruction. |
| ++bytes_to_skip; |
| } |
| |
| // Skip default_is_stmt, line_base, and line_range, each a ubyte. |
| bytes_to_skip += 3; |
| reader.SkipForward(bytes_to_skip); |
| |
| uint8_t opcode_base; |
| if (!reader.ReadByte(&opcode_base)) { |
| fprintf(stderr, "Failed to read DWARF line section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| |
| // Skip standard_opcode_lengths, each a ubyte from opcode 1 to |
| // opcode_base - 1. |
| reader.SkipForward(static_cast<size_t>(opcode_base) - 1); |
| |
| // Parse the directory table, a set of contiguous ASCIIZ values followed by |
| // a null. |
| size_t string_table_start_offset = reader.read_position(); |
| bool data_was_modified = false; |
| std::list<std::string> patched_string_table; |
| size_t new_string_table_length = 1; // Count the null termination byte. |
| while (1) { |
| std::string entry; |
| if (!reader.ReadASCIIZ(&entry)) { |
| fprintf(stderr, "Failed to read DWARF line section.\n"); |
| return ERR_INVALID_FILE; |
| } |
| if (entry.empty()) { |
| break; |
| } |
| |
| if (PatchString(&entry, |
| entry.length(), |
| old_prefix_length, |
| old_prefix_begin, |
| old_prefix_end, |
| new_prefix_)) { |
| data_was_modified = true; |
| } |
| patched_string_table.push_back(entry); |
| new_string_table_length += entry.length() + 1; |
| } |
| |
| if (data_was_modified) { |
| size_t string_table_length = |
| reader.read_position() - string_table_start_offset; |
| |
| if (new_string_table_length == string_table_length - 1) { |
| // If the new string table is exactly one byte shorter than the old, the |
| // table must be grown by two bytes (as an empty string signifies the |
| // end of the table). |
| patched_string_table.push_back("!"); |
| new_string_table_length += 2; |
| } else if (new_string_table_length < string_table_length) { |
| // Whenever possible, the string table is patched in place by adding a |
| // padding string. |
| size_t padded_string_length = |
| string_table_length - new_string_table_length - 1; |
| patched_string_table.push_back(std::string(padded_string_length, '!')); |
| new_string_table_length = string_table_length; |
| } |
| |
| std::unique_ptr<uint8_t[]> new_string_table( |
| new uint8_t[new_string_table_length]); |
| char *buf = reinterpret_cast<char *>(new_string_table.get()); |
| for (const auto &s : patched_string_table) { |
| size_t len = s.size(); |
| memcpy(buf, s.data(), len); |
| buf += len; |
| *buf = 0; |
| ++buf; |
| } |
| new_string_table[new_string_table_length - 1] = 0; |
| |
| patch_actions->push_back(LineInfoPatch { |
| compilation_unit_length, |
| compilation_unit_length_offset, |
| header_length, |
| header_length_offset, |
| string_table_start_offset, |
| string_table_length, |
| std::move(new_string_table), |
| new_string_table_length |
| }); |
| |
| *patched_section_size_increase += |
| new_string_table_length - string_table_length; |
| } |
| |
| reader.SeekToOffset(end_offset); |
| } |
| return ERR_OK; |
| } |
| |
| ReturnCode DWARFStringPatcher::ApplyLineInfoPatchesInPlace( |
| MachOFile *f, |
| std::unique_ptr<uint8_t[]> data, |
| size_t data_length, |
| const std::list<LineInfoPatch> &patch_actions) const { |
| assert(f); |
| VerbosePrint("Updating line info section in-place.\n"); |
| uint8_t *data_ptr = data.get(); |
| for (const auto &action : patch_actions) { |
| memcpy(data_ptr + action.string_table_start_offset, |
| action.new_string_table.get(), |
| action.string_table_length); |
| } |
| |
| return f->WriteSectionData(kSegment, |
| kLineInfoSection, |
| std::move(data), |
| data_length); |
| } |
| |
| ReturnCode DWARFStringPatcher::ApplyLineInfoPatches( |
| MachOFile *f, |
| std::unique_ptr<uint8_t[]> existing_data, |
| size_t data_length, |
| size_t new_data_length, |
| const std::list<LineInfoPatch> &patch_actions) const { |
| assert(f); |
| |
| VerbosePrint("Rewriting line info section.\n"); |
| std::unique_ptr<uint8_t[]> new_data(new uint8_t[new_data_length]); |
| uint8_t *existing_data_ptr = existing_data.get(); |
| uint8_t *next_copy_start = existing_data_ptr; |
| uint8_t *new_data_ptr = new_data.get(); |
| for (const auto &a : patch_actions) { |
| size_t string_table_delta_size = |
| a.new_string_table_length - a.string_table_length; |
| uint64_t new_compilation_unit_length = |
| a.compilation_unit_length + string_table_delta_size; |
| uint64_t new_header_length = a.header_length + string_table_delta_size; |
| |
| bool existing_data_is_64_bit = a.compilation_unit_length > 0xffffffff; |
| bool new_data_is_64_bit = new_compilation_unit_length > 0xffffffff; |
| if (existing_data_is_64_bit != new_data_is_64_bit) { |
| fprintf(stderr, |
| "ERROR: compilation unit growth past the 32-bit boundary is not " |
| "implemented.\n"); |
| return ERR_NOT_IMPLEMENTED; |
| } |
| |
| // Patch the data sizes prior to copying the block. |
| if (existing_data_is_64_bit) { |
| UpdateLineInfoSizeInfo<uint64_t>(existing_data_ptr, |
| a.compilation_unit_length_offset, |
| a.header_length_offset, |
| f->swap_byte_ordering(), |
| new_compilation_unit_length, |
| new_header_length); |
| } else { |
| UpdateLineInfoSizeInfo<uint32_t>(existing_data_ptr, |
| a.compilation_unit_length_offset, |
| a.header_length_offset, |
| f->swap_byte_ordering(), |
| new_compilation_unit_length, |
| new_header_length); |
| } |
| |
| // Copy everything from the end of the previous string table to the |
| // beginning of this unit's string table. |
| uint8_t *existing_string_table = |
| existing_data_ptr + a.string_table_start_offset; |
| size_t copy_len = existing_string_table - next_copy_start; |
| memcpy(new_data_ptr, next_copy_start, copy_len); |
| new_data_ptr += copy_len; |
| next_copy_start = existing_string_table + a.string_table_length; |
| |
| // Install the new string table. |
| memcpy(new_data_ptr, a.new_string_table.get(), a.new_string_table_length); |
| new_data_ptr += a.new_string_table_length; |
| } |
| |
| // Copy any remaining data. |
| size_t remaining_len = (existing_data_ptr + data_length) - next_copy_start; |
| if (remaining_len) { |
| memcpy(new_data_ptr, next_copy_start, remaining_len); |
| } |
| |
| return f->WriteSectionData(kSegment, |
| kLineInfoSection, |
| std::move(new_data), |
| new_data_length); |
| } |
| |
| namespace { |
| |
| inline bool PatchString(std::string *value, |
| size_t value_len, |
| size_t old_prefix_length, |
| const std::string::const_iterator &old_prefix_begin, |
| const std::string::const_iterator &old_prefix_end, |
| const std::string &new_prefix) { |
| assert(value); |
| if (value_len < old_prefix_length || |
| !std::equal(old_prefix_begin, old_prefix_end, value->begin())) { |
| return false; |
| } |
| value->replace(0, old_prefix_length, new_prefix); |
| return true; |
| } |
| |
| inline ReturnCode PatchfoAttributeValue( |
| std::function<bool(uint64_t, size_t)> write_func, |
| const std::map<size_t, size_t> &string_relocation_table, |
| DWARFBufferReader *reader, |
| uint64_t form_code, |
| uint8_t address_size, |
| uint16_t dwarf_version, |
| std::function<bool(uint64_t *)> read_func) { |
| assert(reader); |
| |
| switch (form_code) { |
| case DW_FORM_addr: |
| reader->SkipForward(address_size); |
| return ERR_OK; |
| |
| case DW_FORM_block2: { |
| uint16_t block_len; |
| if (!reader->ReadWORD(&block_len)) { |
| return ERR_INVALID_FILE; |
| } |
| reader->SkipForward(block_len); |
| return ERR_OK; |
| } |
| |
| case DW_FORM_block4: { |
| uint32_t block_len; |
| if (!reader->ReadDWORD(&block_len)) { |
| return ERR_INVALID_FILE; |
| } |
| reader->SkipForward(block_len); |
| return ERR_OK; |
| } |
| |
| case DW_FORM_data1: |
| case DW_FORM_ref1: |
| case DW_FORM_flag: |
| reader->SkipForward(1); |
| return ERR_OK; |
| |
| case DW_FORM_data2: |
| case DW_FORM_ref2: |
| reader->SkipForward(2); |
| return ERR_OK; |
| |
| case DW_FORM_data4: |
| case DW_FORM_ref4: |
| reader->SkipForward(4); |
| return ERR_OK; |
| |
| case DW_FORM_data8: |
| case DW_FORM_ref8: |
| reader->SkipForward(8); |
| return ERR_OK; |
| |
| case DW_FORM_string: { |
| std::string str; |
| if (!reader->ReadASCIIZ(&str)) { |
| return ERR_INVALID_FILE; |
| } |
| return ERR_OK; |
| } |
| |
| case DW_FORM_block: { |
| uint64_t block_len; |
| if (!reader->ReadULEB128(&block_len)) { |
| return ERR_INVALID_FILE; |
| } |
| reader->SkipForward(block_len); |
| return ERR_OK; |
| } |
| |
| case DW_FORM_block1: { |
| uint8_t block_len; |
| if (!reader->ReadByte(&block_len)) { |
| return ERR_INVALID_FILE; |
| } |
| reader->SkipForward(block_len); |
| return ERR_OK; |
| } |
| |
| case DW_FORM_sdata: { |
| // TODO(abaire): Should be a signed LEB128 (if this data is ever used). |
| uint64_t data; |
| if (!reader->ReadULEB128(&data)) { |
| return ERR_INVALID_FILE; |
| } |
| return ERR_OK; |
| } |
| |
| case DW_FORM_strp: { |
| size_t pos = reader->read_position(); |
| uint64_t string_offset; |
| if (!read_func(&string_offset)) { |
| return ERR_INVALID_FILE; |
| } |
| |
| // TODO(abaire): Patch the offset; |
| auto table_relocation = string_relocation_table.find(string_offset); |
| if (table_relocation == string_relocation_table.end()) { |
| fprintf(stderr, |
| "Failed to relocate string offset %llu.\n", |
| string_offset); |
| return ERR_INVALID_FILE; |
| } |
| |
| size_t new_offset = table_relocation->second; |
| if (new_offset != string_offset) { |
| if (!write_func(new_offset, pos)) { |
| return ERR_WRITE_FAILED; |
| } |
| } |
| return ERR_OK; |
| } |
| |
| case DW_FORM_udata: |
| case DW_FORM_ref_udata: { |
| uint64_t data; |
| if (!reader->ReadULEB128(&data)) { |
| return ERR_INVALID_FILE; |
| } |
| return ERR_OK; |
| } |
| |
| case DW_FORM_ref_addr: { |
| if (dwarf_version <= 2) { |
| reader->SkipForward(address_size); |
| return ERR_OK; |
| } |
| uint64_t addr; |
| if (!read_func(&addr)) { |
| return ERR_INVALID_FILE; |
| } |
| return ERR_OK; |
| } |
| |
| case DW_FORM_indirect: { |
| uint64_t real_encoding; |
| if (!reader->ReadULEB128(&real_encoding)) { |
| return ERR_INVALID_FILE; |
| } |
| return PatchfoAttributeValue(write_func, |
| string_relocation_table, |
| reader, |
| real_encoding, |
| address_size, |
| dwarf_version, |
| read_func); |
| } |
| |
| default: |
| fprintf(stderr, "Unknown attribute form 0x%llX\n", form_code); |
| return ERR_NOT_IMPLEMENTED; |
| } |
| |
| return ERR_NOT_IMPLEMENTED; |
| } |
| |
| template <typename T> |
| inline void UpdateLineInfoSizeInfo(uint8_t *existing_data_ptr, |
| size_t compilation_unit_length_offset, |
| size_t header_length_offset, |
| bool swap_byte_ordering, |
| uint64_t new_compilation_unit_length, |
| uint64_t new_header_length) { |
| T *unit_length_ptr = reinterpret_cast<T *>( |
| existing_data_ptr + compilation_unit_length_offset); |
| T *header_length_ptr = reinterpret_cast<T *>( |
| existing_data_ptr + header_length_offset); |
| |
| memcpy(unit_length_ptr, &new_compilation_unit_length, sizeof(T)); |
| memcpy(header_length_ptr, &new_header_length, sizeof(T)); |
| if (swap_byte_ordering) { |
| if (sizeof(T) == sizeof(uint64_t)) { |
| OSSwapInt64(*unit_length_ptr); |
| OSSwapInt64(*header_length_ptr); |
| } else { |
| OSSwapInt32(*unit_length_ptr); |
| OSSwapInt32(*header_length_ptr); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| } // namespace post_processor |