aboutsummaryrefslogtreecommitdiff
path: root/src/common/linux/dump_symbols.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/linux/dump_symbols.cc')
-rw-r--r--src/common/linux/dump_symbols.cc328
1 files changed, 220 insertions, 108 deletions
diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc
index b7e77ab7..b436f765 100644
--- a/src/common/linux/dump_symbols.cc
+++ b/src/common/linux/dump_symbols.cc
@@ -1,5 +1,4 @@
-// Copyright (c) 2011 Google Inc.
-// All rights reserved.
+// Copyright 2011 Google LLC
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
@@ -11,7 +10,7 @@
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
-// * Neither the name of Google Inc. nor the names of its
+// * Neither the name of Google LLC nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
@@ -47,8 +46,8 @@
#include <sys/mman.h>
#include <sys/stat.h>
#include <unistd.h>
+#include <zlib.h>
-#include <iostream>
#include <set>
#include <string>
#include <utility>
@@ -87,11 +86,11 @@ using google_breakpad::DwarfRangeListHandler;
using google_breakpad::ElfClass;
using google_breakpad::ElfClass32;
using google_breakpad::ElfClass64;
-using google_breakpad::FileID;
+using google_breakpad::elf::FileID;
using google_breakpad::FindElfSectionByName;
using google_breakpad::GetOffset;
using google_breakpad::IsValidElf;
-using google_breakpad::kDefaultBuildIdSize;
+using google_breakpad::elf::kDefaultBuildIdSize;
using google_breakpad::Module;
using google_breakpad::PageAllocator;
#ifndef NO_STABS_SUPPORT
@@ -144,7 +143,7 @@ class MmapWrapper {
munmap(base_, size_);
}
}
- void set(void *mapped_address, size_t mapped_size) {
+ void set(void* mapped_address, size_t mapped_size) {
is_set_ = true;
base_ = mapped_address;
size_ = mapped_size;
@@ -228,62 +227,121 @@ bool LoadStabs(const typename ElfClass::Ehdr* elf_header,
#endif // NO_STABS_SUPPORT
// A range handler that accepts rangelist data parsed by
-// dwarf2reader::RangeListReader and populates a range vector (typically
+// google_breakpad::RangeListReader and populates a range vector (typically
// owned by a function) with the results.
class DumperRangesHandler : public DwarfCUToModule::RangesHandler {
public:
- DumperRangesHandler(const uint8_t *buffer, uint64_t size,
- dwarf2reader::ByteReader* reader)
- : buffer_(buffer), size_(size), reader_(reader) { }
-
- bool ReadRanges(uint64_t offset, Module::Address base_address,
- vector<Module::Range>* ranges) {
- DwarfRangeListHandler handler(base_address, ranges);
- dwarf2reader::RangeListReader rangelist_reader(buffer_, size_, reader_,
- &handler);
-
- return rangelist_reader.ReadRangeList(offset);
+ DumperRangesHandler(google_breakpad::ByteReader* reader) :
+ reader_(reader) { }
+
+ bool ReadRanges(
+ enum google_breakpad::DwarfForm form, uint64_t data,
+ google_breakpad::RangeListReader::CURangesInfo* cu_info,
+ vector<Module::Range>* ranges) {
+ DwarfRangeListHandler handler(ranges);
+ google_breakpad::RangeListReader range_list_reader(reader_, cu_info,
+ &handler);
+ return range_list_reader.ReadRanges(form, data);
}
private:
- const uint8_t *buffer_;
- uint64_t size_;
- dwarf2reader::ByteReader* reader_;
+ google_breakpad::ByteReader* reader_;
};
// A line-to-module loader that accepts line number info parsed by
-// dwarf2reader::LineInfo and populates a Module and a line vector
+// google_breakpad::LineInfo and populates a Module and a line vector
// with the results.
class DumperLineToModule: public DwarfCUToModule::LineToModuleHandler {
public:
// Create a line-to-module converter using BYTE_READER.
- explicit DumperLineToModule(dwarf2reader::ByteReader *byte_reader)
+ explicit DumperLineToModule(google_breakpad::ByteReader* byte_reader)
: byte_reader_(byte_reader) { }
void StartCompilationUnit(const string& compilation_dir) {
compilation_dir_ = compilation_dir;
}
- void ReadProgram(const uint8_t *program, uint64_t length,
- Module* module, std::vector<Module::Line>* lines) {
- DwarfLineToModule handler(module, compilation_dir_, lines);
- dwarf2reader::LineInfo parser(program, length, byte_reader_, &handler);
+ void ReadProgram(const uint8_t* program,
+ uint64_t length,
+ const uint8_t* string_section,
+ uint64_t string_section_length,
+ const uint8_t* line_string_section,
+ uint64_t line_string_section_length,
+ Module* module,
+ std::vector<Module::Line>* lines,
+ std::map<uint32_t, Module::File*>* files) {
+ DwarfLineToModule handler(module, compilation_dir_, lines, files);
+ google_breakpad::LineInfo parser(program, length, byte_reader_,
+ string_section, string_section_length,
+ line_string_section,
+ line_string_section_length,
+ &handler);
parser.Start();
}
private:
string compilation_dir_;
- dwarf2reader::ByteReader *byte_reader_;
+ google_breakpad::ByteReader* byte_reader_;
};
template<typename ElfClass>
+bool IsCompressedHeader(const typename ElfClass::Shdr* section) {
+ return (section->sh_flags & SHF_COMPRESSED) != 0;
+}
+
+template<typename ElfClass>
+uint32_t GetCompressionHeader(
+ typename ElfClass::Chdr& compression_header,
+ const uint8_t* content, uint64_t size) {
+ const typename ElfClass::Chdr* header =
+ reinterpret_cast<const typename ElfClass::Chdr *>(content);
+
+ if (size < sizeof (*header)) {
+ return 0;
+ }
+
+ compression_header = *header;
+ return sizeof (*header);
+}
+
+std::pair<uint8_t *, uint64_t> UncompressSectionContents(
+ const uint8_t* compressed_buffer, uint64_t compressed_size, uint64_t uncompressed_size) {
+ z_stream stream;
+ memset(&stream, 0, sizeof stream);
+
+ stream.avail_in = compressed_size;
+ stream.avail_out = uncompressed_size;
+ stream.next_in = const_cast<uint8_t *>(compressed_buffer);
+
+ google_breakpad::scoped_array<uint8_t> uncompressed_buffer(
+ new uint8_t[uncompressed_size]);
+
+ int status = inflateInit(&stream);
+ while (stream.avail_in != 0 && status == Z_OK) {
+ stream.next_out =
+ uncompressed_buffer.get() + uncompressed_size - stream.avail_out;
+
+ if ((status = inflate(&stream, Z_FINISH)) != Z_STREAM_END) {
+ break;
+ }
+
+ status = inflateReset(&stream);
+ }
+
+ return inflateEnd(&stream) != Z_OK || status != Z_OK || stream.avail_out != 0
+ ? std::make_pair(nullptr, 0)
+ : std::make_pair(uncompressed_buffer.release(), uncompressed_size);
+}
+
+template<typename ElfClass>
bool LoadDwarf(const string& dwarf_filename,
const typename ElfClass::Ehdr* elf_header,
const bool big_endian,
bool handle_inter_cu_refs,
+ bool handle_inline,
Module* module) {
typedef typename ElfClass::Shdr Shdr;
- const dwarf2reader::Endianness endianness = big_endian ?
- dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
- dwarf2reader::ByteReader byte_reader(endianness);
+ const google_breakpad::Endianness endianness = big_endian ?
+ google_breakpad::ENDIANNESS_BIG : google_breakpad::ENDIANNESS_LITTLE;
+ google_breakpad::ByteReader byte_reader(endianness);
// Construct a context for this file.
DwarfCUToModule::FileContext file_context(dwarf_filename,
@@ -300,29 +358,44 @@ bool LoadDwarf(const string& dwarf_filename,
string name = GetOffset<ElfClass, char>(elf_header,
section_names->sh_offset) +
section->sh_name;
- const uint8_t *contents = GetOffset<ElfClass, uint8_t>(elf_header,
+ const uint8_t* contents = GetOffset<ElfClass, uint8_t>(elf_header,
section->sh_offset);
- file_context.AddSectionToSectionMap(name, contents, section->sh_size);
- }
+ uint64_t size = section->sh_size;
+
+ if (!IsCompressedHeader<ElfClass>(section)) {
+ file_context.AddSectionToSectionMap(name, contents, size);
+ continue;
+ }
+
+ typename ElfClass::Chdr chdr;
- // Optional .debug_ranges reader
- scoped_ptr<DumperRangesHandler> ranges_handler;
- dwarf2reader::SectionMap::const_iterator ranges_entry =
- file_context.section_map().find(".debug_ranges");
- if (ranges_entry != file_context.section_map().end()) {
- const std::pair<const uint8_t *, uint64_t>& ranges_section =
- ranges_entry->second;
- ranges_handler.reset(
- new DumperRangesHandler(ranges_section.first, ranges_section.second,
- &byte_reader));
+ uint32_t compression_header_size =
+ GetCompressionHeader<ElfClass>(chdr, contents, size);
+
+ if (compression_header_size == 0 || chdr.ch_size == 0) {
+ continue;
+ }
+
+ contents += compression_header_size;
+ size -= compression_header_size;
+
+ std::pair<uint8_t *, uint64_t> uncompressed =
+ UncompressSectionContents(contents, size, chdr.ch_size);
+
+ if (uncompressed.first != nullptr && uncompressed.second != 0) {
+ file_context.AddManagedSectionToSectionMap(name, uncompressed.first, uncompressed.second);
+ }
}
+ // .debug_ranges and .debug_rnglists reader
+ DumperRangesHandler ranges_handler(&byte_reader);
+
// Parse all the compilation units in the .debug_info section.
DumperLineToModule line_to_module(&byte_reader);
- dwarf2reader::SectionMap::const_iterator debug_info_entry =
+ google_breakpad::SectionMap::const_iterator debug_info_entry =
file_context.section_map().find(".debug_info");
assert(debug_info_entry != file_context.section_map().end());
- const std::pair<const uint8_t *, uint64_t>& debug_info_section =
+ const std::pair<const uint8_t*, uint64_t>& debug_info_section =
debug_info_entry->second;
// This should never have been called if the file doesn't have a
// .debug_info section.
@@ -333,11 +406,11 @@ bool LoadDwarf(const string& dwarf_filename,
// data that was found.
DwarfCUToModule::WarningReporter reporter(dwarf_filename, offset);
DwarfCUToModule root_handler(&file_context, &line_to_module,
- ranges_handler.get(), &reporter);
+ &ranges_handler, &reporter, handle_inline);
// Make a Dwarf2Handler that drives the DIEHandler.
- dwarf2reader::DIEDispatcher die_dispatcher(&root_handler);
+ google_breakpad::DIEDispatcher die_dispatcher(&root_handler);
// Make a DWARF parser for the compilation unit at OFFSET.
- dwarf2reader::CompilationUnit reader(dwarf_filename,
+ google_breakpad::CompilationUnit reader(dwarf_filename,
file_context.section_map(),
offset,
&byte_reader,
@@ -397,18 +470,18 @@ bool LoadDwarfCFI(const string& dwarf_filename,
return false;
}
- const dwarf2reader::Endianness endianness = big_endian ?
- dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
+ const google_breakpad::Endianness endianness = big_endian ?
+ google_breakpad::ENDIANNESS_BIG : google_breakpad::ENDIANNESS_LITTLE;
// Find the call frame information and its size.
- const uint8_t *cfi =
+ const uint8_t* cfi =
GetOffset<ElfClass, uint8_t>(elf_header, section->sh_offset);
size_t cfi_size = section->sh_size;
// Plug together the parser, handler, and their entourages.
DwarfCFIToModule::Reporter module_reporter(dwarf_filename, section_name);
DwarfCFIToModule handler(module, register_names, &module_reporter);
- dwarf2reader::ByteReader byte_reader(endianness);
+ google_breakpad::ByteReader byte_reader(endianness);
byte_reader.SetAddressSize(ElfClass::kAddrSize);
@@ -420,11 +493,44 @@ bool LoadDwarfCFI(const string& dwarf_filename,
if (text_section)
byte_reader.SetTextBase(text_section->sh_addr);
- dwarf2reader::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename,
+ google_breakpad::CallFrameInfo::Reporter dwarf_reporter(dwarf_filename,
section_name);
- dwarf2reader::CallFrameInfo parser(cfi, cfi_size,
- &byte_reader, &handler, &dwarf_reporter,
- eh_frame);
+ if (!IsCompressedHeader<ElfClass>(section)) {
+ google_breakpad::CallFrameInfo parser(cfi, cfi_size,
+ &byte_reader, &handler,
+ &dwarf_reporter, eh_frame);
+ parser.Start();
+ return true;
+ }
+
+ typename ElfClass::Chdr chdr;
+ uint32_t compression_header_size =
+ GetCompressionHeader<ElfClass>(chdr, cfi, cfi_size);
+
+ if (compression_header_size == 0 || chdr.ch_size == 0) {
+ fprintf(stderr, "%s: decompression failed at header\n",
+ dwarf_filename.c_str());
+ return false;
+ }
+ if (compression_header_size > cfi_size) {
+ fprintf(stderr, "%s: decompression error, compression_header too large\n",
+ dwarf_filename.c_str());
+ return false;
+ }
+
+ cfi += compression_header_size;
+ cfi_size -= compression_header_size;
+
+ std::pair<uint8_t *, uint64_t> uncompressed =
+ UncompressSectionContents(cfi, cfi_size, chdr.ch_size);
+
+ if (uncompressed.first == nullptr || uncompressed.second == 0) {
+ fprintf(stderr, "%s: decompression failed\n", dwarf_filename.c_str());
+ return false;
+ }
+ google_breakpad::CallFrameInfo parser(uncompressed.first, uncompressed.second,
+ &byte_reader, &handler, &dwarf_reporter,
+ eh_frame);
parser.Start();
return true;
}
@@ -489,13 +595,13 @@ bool IsSameFile(const char* left_abspath, const string& right_path) {
// Read the .gnu_debuglink and get the debug file name. If anything goes
// wrong, return an empty string.
-string ReadDebugLink(const uint8_t *debuglink,
+string ReadDebugLink(const uint8_t* debuglink,
const size_t debuglink_size,
const bool big_endian,
const string& obj_file,
const std::vector<string>& debug_dirs) {
// Include '\0' + CRC32 (4 bytes).
- size_t debuglink_len = strlen(reinterpret_cast<const char *>(debuglink)) + 5;
+ size_t debuglink_len = strlen(reinterpret_cast<const char*>(debuglink)) + 5;
debuglink_len = 4 * ((debuglink_len + 3) / 4); // Round up to 4 bytes.
// Sanity check.
@@ -517,7 +623,7 @@ string ReadDebugLink(const uint8_t *debuglink,
for (it = debug_dirs.begin(); it < debug_dirs.end(); ++it) {
const string& debug_dir = *it;
debuglink_path = debug_dir + "/" +
- reinterpret_cast<const char *>(debuglink);
+ reinterpret_cast<const char*>(debuglink);
// There is the annoying case of /path/to/foo.so having foo.so as the
// debug link file name. Thus this may end up opening /path/to/foo.so again,
@@ -533,9 +639,9 @@ string ReadDebugLink(const uint8_t *debuglink,
FDWrapper debuglink_fd_wrapper(debuglink_fd);
// The CRC is the last 4 bytes in |debuglink|.
- const dwarf2reader::Endianness endianness = big_endian ?
- dwarf2reader::ENDIANNESS_BIG : dwarf2reader::ENDIANNESS_LITTLE;
- dwarf2reader::ByteReader byte_reader(endianness);
+ const google_breakpad::Endianness endianness = big_endian ?
+ google_breakpad::ENDIANNESS_BIG : google_breakpad::ENDIANNESS_LITTLE;
+ google_breakpad::ByteReader byte_reader(endianness);
uint32_t expected_crc =
byte_reader.ReadFourBytes(&debuglink[debuglink_size - 4]);
@@ -591,7 +697,7 @@ class LoadSymbolsInfo {
// Keeps track of which sections have been loaded so sections don't
// accidentally get loaded twice from two different files.
- void LoadedSection(const string &section) {
+ void LoadedSection(const string& section) {
if (loaded_sections_.count(section) == 0) {
loaded_sections_.insert(section);
} else {
@@ -602,7 +708,7 @@ class LoadSymbolsInfo {
// The ELF file and linked debug file are expected to have the same preferred
// loading address.
- void set_loading_addr(Addr addr, const string &filename) {
+ void set_loading_addr(Addr addr, const string& filename) {
if (!has_loading_addr_) {
loading_addr_ = addr;
loaded_file_ = filename;
@@ -679,11 +785,12 @@ bool LoadSymbols(const string& obj_file,
const Shdr* section_names = sections + elf_header->e_shstrndx;
const char* names =
GetOffset<ElfClass, char>(elf_header, section_names->sh_offset);
- const char *names_end = names + section_names->sh_size;
+ const char* names_end = names + section_names->sh_size;
bool found_debug_info_section = false;
bool found_usable_info = false;
- if (options.symbol_data != ONLY_CFI) {
+ if ((options.symbol_data & SYMBOLS_AND_FILES) ||
+ (options.symbol_data & INLINES)) {
#ifndef NO_STABS_SUPPORT
// Look for STABS debugging information, and load it if present.
const Shdr* stab_section =
@@ -705,32 +812,6 @@ bool LoadSymbols(const string& obj_file,
}
#endif // NO_STABS_SUPPORT
- // Look for DWARF debugging information, and load it if present.
- const Shdr* dwarf_section =
- FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS,
- sections, names, names_end,
- elf_header->e_shnum);
-
- // .debug_info section type is SHT_PROGBITS for mips on pnacl toolchains,
- // but MIPS_DWARF for regular gnu toolchains, so both need to be checked
- if (elf_header->e_machine == EM_MIPS && !dwarf_section) {
- dwarf_section =
- FindElfSectionByName<ElfClass>(".debug_info", SHT_MIPS_DWARF,
- sections, names, names_end,
- elf_header->e_shnum);
- }
-
- if (dwarf_section) {
- found_debug_info_section = true;
- found_usable_info = true;
- info->LoadedSection(".debug_info");
- if (!LoadDwarf<ElfClass>(obj_file, elf_header, big_endian,
- options.handle_inter_cu_refs, module)) {
- fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
- "DWARF debugging information\n", obj_file.c_str());
- }
- }
-
// See if there are export symbols available.
const Shdr* symtab_section =
FindElfSectionByName<ElfClass>(".symtab", SHT_SYMTAB,
@@ -788,9 +869,38 @@ bool LoadSymbols(const string& obj_file,
found_usable_info = found_usable_info || result;
}
}
+
+ // Only Load .debug_info after loading symbol table to avoid duplicate
+ // PUBLIC records.
+ // Look for DWARF debugging information, and load it if present.
+ const Shdr* dwarf_section =
+ FindElfSectionByName<ElfClass>(".debug_info", SHT_PROGBITS,
+ sections, names, names_end,
+ elf_header->e_shnum);
+
+ // .debug_info section type is SHT_PROGBITS for mips on pnacl toolchains,
+ // but MIPS_DWARF for regular gnu toolchains, so both need to be checked
+ if (elf_header->e_machine == EM_MIPS && !dwarf_section) {
+ dwarf_section =
+ FindElfSectionByName<ElfClass>(".debug_info", SHT_MIPS_DWARF,
+ sections, names, names_end,
+ elf_header->e_shnum);
+ }
+
+ if (dwarf_section) {
+ found_debug_info_section = true;
+ found_usable_info = true;
+ info->LoadedSection(".debug_info");
+ if (!LoadDwarf<ElfClass>(obj_file, elf_header, big_endian,
+ options.handle_inter_cu_refs,
+ options.symbol_data & INLINES, module)) {
+ fprintf(stderr, "%s: \".debug_info\" section found, but failed to load "
+ "DWARF debugging information\n", obj_file.c_str());
+ }
+ }
}
- if (options.symbol_data != NO_CFI) {
+ if (options.symbol_data & CFI) {
// Dwarf Call Frame Information (CFI) is actually independent from
// the other DWARF debugging information, and can be used alone.
const Shdr* dwarf_cfi_section =
@@ -859,7 +969,7 @@ bool LoadSymbols(const string& obj_file,
names_end, elf_header->e_shnum);
if (gnu_debuglink_section) {
if (!info->debug_dirs().empty()) {
- const uint8_t *debuglink_contents =
+ const uint8_t* debuglink_contents =
GetOffset<ElfClass, uint8_t>(elf_header,
gnu_debuglink_section->sh_offset);
string debuglink_file =
@@ -947,7 +1057,8 @@ template<typename ElfClass>
bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header,
const string& obj_filename,
const string& obj_os,
- scoped_ptr<Module>& module) {
+ scoped_ptr<Module>& module,
+ bool enable_multiple_field) {
PageAllocator allocator;
wasteful_vector<uint8_t> identifier(&allocator, kDefaultBuildIdSize);
if (!FileID::ElfFileIdentifierFromMappedFile(elf_header, identifier)) {
@@ -956,7 +1067,7 @@ bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header,
return false;
}
- const char *architecture = ElfArchitecture<ElfClass>(elf_header);
+ const char* architecture = ElfArchitecture<ElfClass>(elf_header);
if (!architecture) {
fprintf(stderr, "%s: unrecognized ELF machine architecture: %d\n",
obj_filename.c_str(), elf_header->e_machine);
@@ -976,7 +1087,8 @@ bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header,
// This is just the raw Build ID in hex.
string code_id = FileID::ConvertIdentifierToString(identifier);
- module.reset(new Module(name, obj_os, architecture, id, code_id));
+ module.reset(new Module(name, obj_os, architecture, id, code_id,
+ enable_multiple_field));
return true;
}
@@ -993,8 +1105,8 @@ bool ReadSymbolDataElfClass(const typename ElfClass::Ehdr* elf_header,
*out_module = NULL;
scoped_ptr<Module> module;
- if (!InitModuleForElfClass<ElfClass>(elf_header, obj_filename, obj_os,
- module)) {
+ if (!InitModuleForElfClass<ElfClass>(elf_header, obj_filename, obj_os, module,
+ options.enable_multiple_field)) {
return false;
}
@@ -1066,12 +1178,12 @@ bool ReadSymbolDataInternal(const uint8_t* obj_file,
return false;
}
-bool WriteSymbolFile(const string &load_path,
- const string &obj_file,
- const string &obj_os,
+bool WriteSymbolFile(const string& load_path,
+ const string& obj_file,
+ const string& obj_os,
const std::vector<string>& debug_dirs,
const DumpOptions& options,
- std::ostream &sym_stream) {
+ std::ostream& sym_stream) {
Module* module;
if (!ReadSymbolData(load_path, obj_file, obj_os, debug_dirs, options,
&module))
@@ -1088,7 +1200,7 @@ bool WriteSymbolFile(const string &load_path,
bool WriteSymbolFileHeader(const string& load_path,
const string& obj_file,
const string& obj_os,
- std::ostream &sym_stream) {
+ std::ostream& sym_stream) {
MmapWrapper map_wrapper;
void* elf_header = NULL;
if (!LoadELF(load_path, &map_wrapper, &elf_header)) {
@@ -1106,14 +1218,14 @@ bool WriteSymbolFileHeader(const string& load_path,
if (elfclass == ELFCLASS32) {
if (!InitModuleForElfClass<ElfClass32>(
reinterpret_cast<const Elf32_Ehdr*>(elf_header), obj_file, obj_os,
- module)) {
+ module, /*enable_multiple_field=*/false)) {
fprintf(stderr, "Failed to load ELF module: %s\n", obj_file.c_str());
return false;
}
} else if (elfclass == ELFCLASS64) {
if (!InitModuleForElfClass<ElfClass64>(
reinterpret_cast<const Elf64_Ehdr*>(elf_header), obj_file, obj_os,
- module)) {
+ module, /*enable_multiple_field=*/false)) {
fprintf(stderr, "Failed to load ELF module: %s\n", obj_file.c_str());
return false;
}