Skip to content

Commit

Permalink
Adds a Flag to Perserve Address Offset in dump_syms
Browse files Browse the repository at this point in the history
By default, the .code section start address is normalized to zero,
and subsequently the address of all functions and lines are adjusted
accordingly. This change adds a command line flag that perserves the
original addresses. This is a requirement for those that may wish to use
the crash resolver, but do not have a minidump file to work with (most commonly in firmware).

TEST=make check
BUG=b:328511413

Change-Id: Id7c477196ffed7fd319029b28ed2607192249c6c
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/5519154
Reviewed-by: Rob Barnes <robbarnes@google.com>
Reviewed-by: Ivan Penkov <ivanpe@google.com>
Reviewed-by: Ivan Penkov <ivanpe@chromium.org>
  • Loading branch information
Sean Carpenter authored and Ivan Penkov committed May 8, 2024
1 parent f88a1aa commit 6fe410d
Show file tree
Hide file tree
Showing 7 changed files with 76 additions and 17 deletions.
2 changes: 1 addition & 1 deletion src/common/linux/dump_symbols.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1280,7 +1280,7 @@ bool WriteSymbolFile(const string& load_path,
&module))
return false;

bool result = module->Write(sym_stream, options.symbol_data);
bool result = module->Write(sym_stream, options.symbol_data, options.preserve_load_address);
delete module;
return result;
}
Expand Down
7 changes: 5 additions & 2 deletions src/common/linux/dump_symbols.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,14 +48,17 @@ class Module;
struct DumpOptions {
DumpOptions(SymbolData symbol_data,
bool handle_inter_cu_refs,
bool enable_multiple_field)
bool enable_multiple_field,
bool preserve_load_address)
: symbol_data(symbol_data),
handle_inter_cu_refs(handle_inter_cu_refs),
enable_multiple_field(enable_multiple_field) {}
enable_multiple_field(enable_multiple_field),
preserve_load_address(preserve_load_address) {}

SymbolData symbol_data;
bool handle_inter_cu_refs;
bool enable_multiple_field;
bool preserve_load_address;
};

// Find all the debugging information in OBJ_FILE, an ELF executable
Expand Down
6 changes: 3 additions & 3 deletions src/common/linux/dump_symbols_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ TYPED_TEST(DumpSymbols, Invalid) {
Elf32_Ehdr header;
memset(&header, 0, sizeof(header));
Module* module;
DumpOptions options(ALL_SYMBOL_DATA, true, false);
DumpOptions options(ALL_SYMBOL_DATA, true, false, false);
EXPECT_FALSE(ReadSymbolDataInternal(reinterpret_cast<uint8_t*>(&header),
"foo",
"Linux",
Expand Down Expand Up @@ -132,7 +132,7 @@ TYPED_TEST(DumpSymbols, SimplePublic) {
this->GetElfContents(elf);

Module* module;
DumpOptions options(ALL_SYMBOL_DATA, true, false);
DumpOptions options(ALL_SYMBOL_DATA, true, false, false);
EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata,
"foo",
"Linux",
Expand Down Expand Up @@ -189,7 +189,7 @@ TYPED_TEST(DumpSymbols, SimpleBuildID) {
this->GetElfContents(elf);

Module* module;
DumpOptions options(ALL_SYMBOL_DATA, true, false);
DumpOptions options(ALL_SYMBOL_DATA, true, false, false);
EXPECT_TRUE(ReadSymbolDataInternal(this->elfdata,
"foo",
"Linux",
Expand Down
23 changes: 15 additions & 8 deletions src/common/module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ bool Module::AddressIsInModule(Address address) const {
return false;
}

bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
bool Module::Write(std::ostream& stream, SymbolData symbol_data, bool preserve_load_address) {
stream << "MODULE " << os_ << " " << architecture_ << " "
<< id_ << " " << name_ << "\n";
if (!stream.good())
Expand All @@ -390,6 +390,13 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
stream << "INFO CODE_ID " << code_id_ << "\n";
}

// load_address is subtracted from each line. If we use zero instead, we
// preserve the original addresses present in the ELF binary.
Address load_offset = load_address_;
if (preserve_load_address) {
load_offset = 0;
}

if (symbol_data & SYMBOLS_AND_FILES) {
// Get all referenced inline origins.
set<InlineOrigin*, InlineOriginCompare> inline_origins;
Expand All @@ -406,13 +413,13 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
return ReportError();
}
}

// Write out inline origins.
for (InlineOrigin* origin : inline_origins) {
stream << "INLINE_ORIGIN " << origin->id << " " << origin->name << "\n";
if (!stream.good())
return ReportError();
}

// Write out functions and their inlines and lines.
for (FunctionSet::const_iterator func_it = functions_.begin();
func_it != functions_.end(); ++func_it) {
Expand All @@ -421,7 +428,7 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
for (auto range_it = func->ranges.cbegin();
range_it != func->ranges.cend(); ++range_it) {
stream << "FUNC " << (func->is_multiple ? "m " : "") << hex
<< (range_it->address - load_address_) << " " << range_it->size
<< (range_it->address - load_offset) << " " << range_it->size
<< " " << func->parameter_size << " " << func->name << dec
<< "\n";

Expand All @@ -434,7 +441,7 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
stream << in->inline_nest_level << " " << in->call_site_line << " "
<< in->getCallSiteFileID() << " " << in->origin->id << hex;
for (const Range& r : in->ranges)
stream << " " << (r.address - load_address_) << " " << r.size;
stream << " " << (r.address - load_offset) << " " << r.size;
stream << dec << "\n";
};
Module::Inline::InlineDFS(func->inlines, write_inline);
Expand All @@ -445,7 +452,7 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
(line_it->address >= range_it->address) &&
(line_it->address < (range_it->address + range_it->size))) {
stream << hex
<< (line_it->address - load_address_) << " "
<< (line_it->address - load_offset) << " "
<< line_it->size << " "
<< dec
<< line_it->number << " "
Expand All @@ -464,7 +471,7 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
extern_it != externs_.end(); ++extern_it) {
Extern* ext = extern_it->get();
stream << "PUBLIC " << (ext->is_multiple ? "m " : "") << hex
<< (ext->address - load_address_) << " 0 " << ext->name << dec
<< (ext->address - load_offset) << " 0 " << ext->name << dec
<< "\n";
}
}
Expand All @@ -475,7 +482,7 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
frame_it != stack_frame_entries_.end(); ++frame_it) {
StackFrameEntry* entry = frame_it->get();
stream << "STACK CFI INIT " << hex
<< (entry->address - load_address_) << " "
<< (entry->address - load_offset) << " "
<< entry->size << " " << dec;
if (!stream.good()
|| !WriteRuleMap(entry->initial_rules, stream))
Expand All @@ -487,7 +494,7 @@ bool Module::Write(std::ostream& stream, SymbolData symbol_data) {
for (RuleChangeMap::const_iterator delta_it = entry->rule_changes.begin();
delta_it != entry->rule_changes.end(); ++delta_it) {
stream << "STACK CFI " << hex
<< (delta_it->first - load_address_) << " " << dec;
<< (delta_it->first - load_offset) << " " << dec;
if (!stream.good()
|| !WriteRuleMap(delta_it->second, stream))
return ReportError();
Expand Down
5 changes: 3 additions & 2 deletions src/common/module.h
Original file line number Diff line number Diff line change
Expand Up @@ -421,8 +421,9 @@ class Module {
// If symbol_data is CFI then:
// - all CFI records.
// Addresses in the output are all relative to the load address
// established by SetLoadAddress.
bool Write(std::ostream& stream, SymbolData symbol_data);
// established by SetLoadAddress, unless preserve_load_address
// is equal to true, in which case each address will remain unchanged.
bool Write(std::ostream& stream, SymbolData symbol_data, bool preserve_load_address = false);

// Place the name in the global set of strings. Return a StringView points to
// a string inside the pool.
Expand Down
43 changes: 43 additions & 0 deletions src/common/module_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,49 @@ TEST(Module, WriteRelativeLoadAddress) {
contents.c_str());
}

TEST(Module, WritePreserveLoadAddress) {
stringstream s;
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);
// Set the load address to something. Doesn't matter what.
// The goal of this test is to demonstrate that the load
// address does not impact any of the generated addresses
// when the preserve_load_address option is equal to true.
m.SetLoadAddress(0x1337ULL);

Module::File* file = m.FindFile("filename-a.cc");
Module::Function* function = new Module::Function(
"do_stuff", 0x110ULL);
Module::Range range(0x110ULL, 0x210ULL);
function->ranges.push_back(range);
function->parameter_size = 0x50ULL;
Module::Line line1 = { 0x110ULL, 0x1ULL,
file, 20ULL };
function->lines.push_back(line1);
m.AddFunction(function);

// Some stack information.
auto entry = std::make_unique<Module::StackFrameEntry>();
entry->address = 0x200ULL;
entry->size = 0x55ULL;
entry->initial_rules[".cfa"] = "some call frame info";
entry->rule_changes[0x201ULL][".s0"] =
"some rules change call frame info";
m.AddStackFrameEntry(std::move(entry));

bool preserve_load_address = true;
m.Write(s, ALL_SYMBOL_DATA, preserve_load_address);
string contents = s.str();
EXPECT_STREQ("MODULE os-name architecture id-string name with spaces\n"
"FILE 0 filename-a.cc\n"
"FUNC 110 210 50 do_stuff\n"
"110 1 20 0\n"
"STACK CFI INIT 200 55"
" .cfa: some call frame info\n"
"STACK CFI 201"
" .s0: some rules change call frame info\n",
contents.c_str());
}

TEST(Module, WriteOmitUnusedFiles) {
Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID);

Expand Down
7 changes: 6 additions & 1 deletion src/tools/linux/dump_syms/dump_syms.cc
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ int usage(const char* self) {
google_breakpad::BaseName(self).c_str());
fprintf(stderr, "Options:\n");
fprintf(stderr, " -i: Output module header information only.\n");
fprintf(stderr, " -a Preserve the load address - Do not normalize "
"the load address to zero.\n");
fprintf(stderr, " -c Do not generate CFI section\n");
fprintf(stderr, " -d Generate INLINE/INLINE_ORIGIN records\n");
fprintf(stderr, " -r Do not handle inter-compilation "
Expand All @@ -69,6 +71,7 @@ int usage(const char* self) {
int main(int argc, char** argv) {
if (argc < 2)
return usage(argv[0]);
bool preserve_load_address = false;
bool header_only = false;
bool cfi = true;
bool handle_inlines = false;
Expand All @@ -82,6 +85,8 @@ int main(int argc, char** argv) {
argv[arg_index][0] == '-') {
if (strcmp("-i", argv[arg_index]) == 0) {
header_only = true;
} else if (strcmp("-a", argv[arg_index]) == 0) {
preserve_load_address = true;
} else if (strcmp("-c", argv[arg_index]) == 0) {
cfi = false;
} else if (strcmp("-d", argv[arg_index]) == 0) {
Expand Down Expand Up @@ -143,7 +148,7 @@ int main(int argc, char** argv) {
SymbolData symbol_data = (handle_inlines ? INLINES : NO_DATA) |
(cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES;
google_breakpad::DumpOptions options(symbol_data, handle_inter_cu_refs,
enable_multiple_field);
enable_multiple_field, preserve_load_address);
if (!WriteSymbolFile(binary, obj_name, obj_os, debug_dirs, options,
std::cout)) {
fprintf(saved_stderr, "Failed to write symbol file.\n");
Expand Down

0 comments on commit 6fe410d

Please sign in to comment.