blob: d7b96171e8ba5eeec4bb644b9cf9fc12a25d87e8 [file] [log] [blame]
// 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