diff options
author | Erwin Jansen <jansene@google.com> | 2023-04-24 11:22:12 -0700 |
---|---|---|
committer | Erwin Jansen <jansene@google.com> | 2023-04-24 11:22:12 -0700 |
commit | dbe52e2e00172fe95e1c3c0de2637d968bfda387 (patch) | |
tree | 6e63001ba5e7300844e1864fc8519c27c8db452c | |
parent | 9a8a410e97d310f449913df41ada7445420f9cc8 (diff) | |
parent | f548d75c9fa8bc062a580dbe5edb2fae2106d5c9 (diff) | |
download | google-breakpad-emu-34-3-release.tar.gz |
Merge remote-tracking branch 'aosp/upstream-main'netsim-devemu-prebuiltsemu-master-devemu-devemu-35-1-releaseemu-34-releaseemu-34-devemu-34-3-releaseemu-34-2-releaseemu-34-2-devemu-33-devaosp-emu-34-dev
This merges the latest changes from breakpad. This might help with the
upload failures we are seeing on MacOs.
Bug: 279454764
Change-Id: I0cc4f1cf0f73a3a790dfb8e947bbd634fc09abc4
-rw-r--r-- | src/common/dwarf_cfi_to_module.cc | 23 | ||||
-rw-r--r-- | src/common/dwarf_cfi_to_module.h | 3 | ||||
-rw-r--r-- | src/common/dwarf_cfi_to_module_unittest.cc | 12 | ||||
-rw-r--r-- | src/common/dwarf_cu_to_module.cc | 32 | ||||
-rw-r--r-- | src/common/dwarf_cu_to_module_unittest.cc | 13 | ||||
-rw-r--r-- | src/common/linux/dump_symbols.cc | 4 | ||||
-rw-r--r-- | src/common/linux/memory_mapped_file.cc | 7 | ||||
-rw-r--r-- | src/common/mac/arch_utilities.cc | 97 | ||||
-rw-r--r-- | src/common/mac/arch_utilities.h | 26 | ||||
-rw-r--r-- | src/common/mac/dump_syms.cc | 129 | ||||
-rw-r--r-- | src/common/mac/dump_syms.h | 48 | ||||
-rw-r--r-- | src/common/mac/macho_walker.cc | 9 | ||||
-rw-r--r-- | src/common/module.cc | 15 | ||||
-rw-r--r-- | src/common/module.h | 16 | ||||
-rw-r--r-- | src/common/module_unittest.cc | 31 | ||||
-rw-r--r-- | src/processor/minidump.cc | 9 | ||||
-rw-r--r-- | src/processor/minidump_processor.cc | 10 | ||||
-rw-r--r-- | src/processor/minidump_stackwalk.cc | 12 | ||||
-rw-r--r-- | src/processor/stackwalk_common.cc | 73 | ||||
-rw-r--r-- | src/processor/stackwalk_common.h | 1 | ||||
-rw-r--r-- | src/tools/mac/dump_syms/dump_syms_tool.cc | 84 | ||||
-rw-r--r-- | src/tools/mac/dump_syms/macho_dump.cc | 206 |
22 files changed, 400 insertions, 460 deletions
diff --git a/src/common/dwarf_cfi_to_module.cc b/src/common/dwarf_cfi_to_module.cc index 287c851e..7e04d3c5 100644 --- a/src/common/dwarf_cfi_to_module.cc +++ b/src/common/dwarf_cfi_to_module.cc @@ -147,6 +147,29 @@ vector<string> DwarfCFIToModule::RegisterNames::MIPS() { sizeof(kRegisterNames) / sizeof(kRegisterNames[0])); } +vector<string> DwarfCFIToModule::RegisterNames::RISCV() { + static const char *const names[] = { + "x0", "x1", "x2", "x3", "x4", "x5", "x6", "x7", + "x8", "x9", "x10", "x11", "x12", "x13", "x14", "x15", + "x16", "x17", "x18", "x19", "x20", "x21", "x22", "x23", + "x24", "x25", "x26", "x27", "x28", "x29", "x30", "x31", + "f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", + "f8", "f9", "f10", "f11", "f12", "f13", "f14", "f15", + "f16", "f17", "f18", "f19", "f20", "f21", "f22", "f23", + "f24", "f25", "f26", "f27", "f28", "f29", "f30", "f31", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "", "", "", "", "", "", "", "", + "v0", "v1", "v2", "v3", "v4", "v5", "v6", "v7", + "v8", "v9", "v10", "v11", "v12", "v13", "v14", "v15", + "v16", "v17", "v18", "v19", "v20", "v21", "v22", "v23", + "v24", "v25", "v26", "v27", "v28", "v29", "v30", "v31" + }; + + return MakeVector(names, sizeof(names) / sizeof(names[0])); +} + bool DwarfCFIToModule::Entry(size_t offset, uint64_t address, uint64_t length, uint8_t version, const string& augmentation, unsigned return_address) { diff --git a/src/common/dwarf_cfi_to_module.h b/src/common/dwarf_cfi_to_module.h index 42b618d5..19297db9 100644 --- a/src/common/dwarf_cfi_to_module.h +++ b/src/common/dwarf_cfi_to_module.h @@ -114,6 +114,9 @@ class DwarfCFIToModule: public CallFrameInfo::Handler { // MIPS. static vector<string> MIPS(); + // RISC-V. + static vector<string> RISCV(); + private: // Given STRINGS, an array of C strings with SIZE elements, return an // equivalent vector<string>. diff --git a/src/common/dwarf_cfi_to_module_unittest.cc b/src/common/dwarf_cfi_to_module_unittest.cc index 43b5e7c4..b407edd0 100644 --- a/src/common/dwarf_cfi_to_module_unittest.cc +++ b/src/common/dwarf_cfi_to_module_unittest.cc @@ -307,3 +307,15 @@ TEST(RegisterNames, X86_64) { EXPECT_EQ("$rsp", names[7]); EXPECT_EQ("$rip", names[16]); } + +TEST(RegisterNames, RISCV) { + vector<string> names = DwarfCFIToModule::RegisterNames::RISCV(); + + EXPECT_EQ("x0", names[0]); + EXPECT_EQ("x31", names[31]); + EXPECT_EQ("f0", names[32]); + EXPECT_EQ("f31", names[63]); + EXPECT_EQ("v0", names[96]); + EXPECT_EQ("v31", names[127]); +} + diff --git a/src/common/dwarf_cu_to_module.cc b/src/common/dwarf_cu_to_module.cc index 43b468c1..708ed143 100644 --- a/src/common/dwarf_cu_to_module.cc +++ b/src/common/dwarf_cu_to_module.cc @@ -330,7 +330,10 @@ class DwarfCUToModule::GenericDIEHandler: public DIEHandler { // Use this from EndAttributes member functions, not ProcessAttribute* // functions; only the former can be sure that all the DIE's attributes // have been seen. - StringView ComputeQualifiedName(); + // + // On return, if has_qualified_name is non-NULL, *has_qualified_name is set to + // true if the DIE includes a fully-qualified name, false otherwise. + StringView ComputeQualifiedName(bool* has_qualified_name); CUContext* cu_context_; DIEContext* parent_context_; @@ -466,7 +469,8 @@ void DwarfCUToModule::GenericDIEHandler::ProcessAttributeString( } } -StringView DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() { +StringView DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName( + bool* has_qualified_name) { // Use the demangled name, if one is available. Demangled names are // preferable to those inferred from the DWARF structure because they // include argument types. @@ -482,6 +486,15 @@ StringView DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() { StringView* unqualified_name = nullptr; StringView* enclosing_name = nullptr; if (!qualified_name) { + if (has_qualified_name) { + // dSYMs built with -gmlt do not include the DW_AT_linkage_name + // with the unmangled symbol, but rather include it in the + // LC_SYMTAB STABS, which end up in the externs of the module. + // + // Remember this so the Module can copy over the extern name later. + *has_qualified_name = false; + } + // Find the unqualified name. If the DIE has its own DW_AT_name // attribute, then use that; otherwise, check the specification. if (!name_attribute_.empty()) { @@ -500,6 +513,10 @@ StringView DwarfCUToModule::GenericDIEHandler::ComputeQualifiedName() { } else if (parent_context_) { enclosing_name = &parent_context_->name; } + } else { + if (has_qualified_name) { + *has_qualified_name = true; + } } // Prepare the return value before upcoming mutations possibly invalidate the @@ -722,7 +739,8 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { ranges_form_(DW_FORM_sec_offset), ranges_data_(0), inline_(false), - handle_inline_(handle_inline) {} + handle_inline_(handle_inline), + has_qualified_name_(false) {} void ProcessAttributeUnsigned(enum DwarfAttribute attr, enum DwarfForm form, @@ -745,6 +763,7 @@ class DwarfCUToModule::FuncHandler: public GenericDIEHandler { bool inline_; vector<unique_ptr<Module::Inline>> child_inlines_; bool handle_inline_; + bool has_qualified_name_; DIEContext child_context_; // A context for our children. }; @@ -808,7 +827,7 @@ DIEHandler* DwarfCUToModule::FuncHandler::FindChildHandler( bool DwarfCUToModule::FuncHandler::EndAttributes() { // Compute our name, and record a specification, if appropriate. - name_ = ComputeQualifiedName(); + name_ = ComputeQualifiedName(&has_qualified_name_); if (name_.empty() && abstract_origin_) { name_ = abstract_origin_->name; } @@ -881,6 +900,9 @@ void DwarfCUToModule::FuncHandler::Finish() { scoped_ptr<Module::Function> func(new Module::Function(name, low_pc_)); func->ranges = ranges; func->parameter_size = 0; + // If the name was unqualified, prefer the Extern name if there's a mismatch + // (the Extern name will be fully-qualified in that case). + func->prefer_extern_name = !has_qualified_name_; if (func->address) { // If the function address is zero this is a sign that this function // description is just empty debug data and should just be discarded. @@ -915,7 +937,7 @@ void DwarfCUToModule::FuncHandler::Finish() { } bool DwarfCUToModule::NamedScopeHandler::EndAttributes() { - child_context_.name = ComputeQualifiedName(); + child_context_.name = ComputeQualifiedName(NULL); if (child_context_.name.empty() && no_specification) { cu_context_->reporter->UnknownSpecification(offset_, specification_offset_); } diff --git a/src/common/dwarf_cu_to_module_unittest.cc b/src/common/dwarf_cu_to_module_unittest.cc index 7ab2f15c..134b2c24 100644 --- a/src/common/dwarf_cu_to_module_unittest.cc +++ b/src/common/dwarf_cu_to_module_unittest.cc @@ -267,6 +267,10 @@ class CUFixtureBase { void TestFunction(int i, const string& name, Module::Address address, Module::Address size); + // Test that the I'th function (ordered by address) in the module + // this.module_ has the given prefer_extern_name. + void TestFunctionPreferExternName(int i, bool prefer_extern_name); + // Test that the number of source lines owned by the I'th function // in the module this.module_ is equal to EXPECTED. void TestLineCount(int i, size_t expected); @@ -615,6 +619,15 @@ void CUFixtureBase::TestFunction(int i, const string& name, EXPECT_EQ(0U, function->parameter_size); } +void CUFixtureBase::TestFunctionPreferExternName(int i, + bool prefer_extern_name) { + FillFunctions(); + ASSERT_LT((size_t)i, functions_.size()); + + Module::Function* function = functions_[i]; + EXPECT_EQ(prefer_extern_name, function->prefer_extern_name); +} + void CUFixtureBase::TestLineCount(int i, size_t expected) { FillFunctions(); ASSERT_LT((size_t) i, functions_.size()); diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 48e4c926..8179663b 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -449,6 +449,9 @@ bool DwarfCFIRegisterNames(const typename ElfClass::Ehdr* elf_header, case EM_X86_64: *register_names = DwarfCFIToModule::RegisterNames::X86_64(); return true; + case EM_RISCV: + *register_names = DwarfCFIToModule::RegisterNames::RISCV(); + return true; default: return false; } @@ -1022,6 +1025,7 @@ const char* ElfArchitecture(const typename ElfClass::Ehdr* elf_header) { case EM_SPARC: return "sparc"; case EM_SPARCV9: return "sparcv9"; case EM_X86_64: return "x86_64"; + case EM_RISCV: return "riscv"; default: return NULL; } } diff --git a/src/common/linux/memory_mapped_file.cc b/src/common/linux/memory_mapped_file.cc index 568312cf..a7b96eb5 100644 --- a/src/common/linux/memory_mapped_file.cc +++ b/src/common/linux/memory_mapped_file.cc @@ -61,8 +61,11 @@ MemoryMappedFile::~MemoryMappedFile() { bool MemoryMappedFile::Map(const char* path, size_t offset) { Unmap(); - - int fd = sys_open(path, O_RDONLY, 0); + // Based on https://pubs.opengroup.org/onlinepubs/7908799/xsh/open.html + // If O_NONBLOCK is set: The open() function will return without blocking + // for the device to be ready or available. Setting this value will provent + // hanging if file is not avilable. + int fd = sys_open(path, O_RDONLY | O_NONBLOCK, 0); if (fd == -1) { return false; } diff --git a/src/common/mac/arch_utilities.cc b/src/common/mac/arch_utilities.cc index 543c7c4a..febf8a22 100644 --- a/src/common/mac/arch_utilities.cc +++ b/src/common/mac/arch_utilities.cc @@ -53,86 +53,44 @@ #define CPU_SUBTYPE_ARM64_E (static_cast<cpu_subtype_t>(2)) #endif // CPU_SUBTYPE_ARM64_E -namespace { - -const NXArchInfo* ArchInfo_arm64(cpu_subtype_t cpu_subtype) { - const char* name = NULL; - switch (cpu_subtype) { - case CPU_SUBTYPE_ARM64_ALL: - name = "arm64"; - break; - case CPU_SUBTYPE_ARM64_E: - name = "arm64e"; - break; - default: - return NULL; - } - - NXArchInfo* arm64 = new NXArchInfo; - *arm64 = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM, - CPU_SUBTYPE_ARM_V7); - arm64->name = name; - arm64->cputype = CPU_TYPE_ARM64; - arm64->cpusubtype = cpu_subtype; - arm64->description = "arm 64"; - return arm64; -} - -const NXArchInfo* ArchInfo_armv7s() { - NXArchInfo* armv7s = new NXArchInfo; - *armv7s = *NXGetArchInfoFromCpuType(CPU_TYPE_ARM, - CPU_SUBTYPE_ARM_V7); - armv7s->name = "armv7s"; - armv7s->cpusubtype = CPU_SUBTYPE_ARM_V7S; - armv7s->description = "arm v7s"; - return armv7s; -} - -} // namespace - -namespace google_breakpad { - -const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name) { +std::optional<ArchInfo> GetArchInfoFromName(const char* arch_name) { // TODO: Remove this when the OS knows about arm64. if (!strcmp("arm64", arch_name)) - return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM64, - CPU_SUBTYPE_ARM64_ALL); + return ArchInfo{CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_ALL}; if (!strcmp("arm64e", arch_name)) - return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM64, - CPU_SUBTYPE_ARM64_E); - + return ArchInfo{CPU_TYPE_ARM64, CPU_SUBTYPE_ARM64_E}; // TODO: Remove this when the OS knows about armv7s. if (!strcmp("armv7s", arch_name)) - return BreakpadGetArchInfoFromCpuType(CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S); + return ArchInfo{CPU_TYPE_ARM, CPU_SUBTYPE_ARM_V7S}; - return NXGetArchInfoFromName(arch_name); + const NXArchInfo* info = NXGetArchInfoFromName(arch_name); + if (info) + return ArchInfo{info->cputype, info->cpusubtype}; + return std::nullopt; } -const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type, - cpu_subtype_t cpu_subtype) { +const char* GetNameFromCPUType(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { // TODO: Remove this when the OS knows about arm64. if (cpu_type == CPU_TYPE_ARM64 && cpu_subtype == CPU_SUBTYPE_ARM64_ALL) { - static const NXArchInfo* arm64 = ArchInfo_arm64(cpu_subtype); - return arm64; + return "arm64"; } if (cpu_type == CPU_TYPE_ARM64 && cpu_subtype == CPU_SUBTYPE_ARM64_E) { - static const NXArchInfo* arm64e = ArchInfo_arm64(cpu_subtype); - return arm64e; + return "arm64e"; } // TODO: Remove this when the OS knows about armv7s. if (cpu_type == CPU_TYPE_ARM && cpu_subtype == CPU_SUBTYPE_ARM_V7S) { - static const NXArchInfo* armv7s = ArchInfo_armv7s(); - return armv7s; + return "armv7s"; } - return NXGetArchInfoFromCpuType(cpu_type, cpu_subtype); + const NXArchInfo* info = NXGetArchInfoFromCpuType(cpu_type, cpu_subtype); + if (info) + return info->name; + return kUnknownArchName; } -} // namespace google_breakpad - // TODO(crbug.com/1242776): The "#ifndef __APPLE__" should be here, but the // system version of NXGetLocalArchInfo returns incorrect information on // x86_64 machines (treating them as just x86), so use the Breakpad version @@ -207,7 +165,7 @@ const NXArchInfo kKnownArchitectures[] = { } // namespace -const NXArchInfo *NXGetLocalArchInfo(void) { +ArchInfo GetLocalArchInfo(void) { Architecture arch; #if defined(__i386__) arch = kArch_i386; @@ -222,7 +180,8 @@ const NXArchInfo *NXGetLocalArchInfo(void) { #else #error "Unsupported CPU architecture" #endif - return &kKnownArchitectures[arch]; + NXArchInfo info = kKnownArchitectures[arch]; + return {info.cputype, info.cpusubtype}; } #ifndef __APPLE__ @@ -251,22 +210,4 @@ const NXArchInfo *NXGetArchInfoFromCpuType(cpu_type_t cputype, } return candidate; } - -struct fat_arch *NXFindBestFatArch(cpu_type_t cputype, - cpu_subtype_t cpusubtype, - struct fat_arch *fat_archs, - uint32_t nfat_archs) { - struct fat_arch *candidate = NULL; - for (uint32_t f = 0; f < nfat_archs; ++f) { - if (fat_archs[f].cputype == cputype) { - if (fat_archs[f].cpusubtype == cpusubtype) { - return &fat_archs[f]; - } - if (!candidate) { - candidate = &fat_archs[f]; - } - } - } - return candidate; -} #endif // !__APPLE__ diff --git a/src/common/mac/arch_utilities.h b/src/common/mac/arch_utilities.h index d267c43b..3b036738 100644 --- a/src/common/mac/arch_utilities.h +++ b/src/common/mac/arch_utilities.h @@ -31,16 +31,26 @@ #ifndef COMMON_MAC_ARCH_UTILITIES_H__ #define COMMON_MAC_ARCH_UTILITIES_H__ -#include <mach-o/arch.h> +#include <mach/machine.h> -namespace google_breakpad { +#include <optional> -// Custom implementation of |NXGetArchInfoFromName| and -// |NXGetArchInfoFromCpuType| that handle newer CPU on older OSes. -const NXArchInfo* BreakpadGetArchInfoFromName(const char* arch_name); -const NXArchInfo* BreakpadGetArchInfoFromCpuType(cpu_type_t cpu_type, - cpu_subtype_t cpu_subtype); +static constexpr const char* kUnknownArchName = "<Unknown architecture>"; -} // namespace google_breakpad +struct ArchInfo { + cpu_type_t cputype; + cpu_subtype_t cpusubtype; +}; + +// Returns architecture info if `arch_name` corresponds to a valid, known +// architecture, and std::nullopt otherwise. +std::optional<ArchInfo> GetArchInfoFromName(const char* arch_name); + +// Returns the name of the architecture specified by `cpu_type` and +// `cpu_subtype`, or `kUnknownArchName` if it's unknown or invalid. +const char* GetNameFromCPUType(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype); + +// Returns the architecture of the machine this code is running on. +ArchInfo GetLocalArchInfo(); #endif // COMMON_MAC_ARCH_UTILITIES_H__ diff --git a/src/common/mac/dump_syms.cc b/src/common/mac/dump_syms.cc index e1025f79..dd91196a 100644 --- a/src/common/mac/dump_syms.cc +++ b/src/common/mac/dump_syms.cc @@ -221,11 +221,10 @@ bool DumpSymbols::ReadData(uint8_t* contents, size_t size, return true; } -bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, - cpu_subtype_t cpu_subtype) { +bool DumpSymbols::SetArchitecture(const ArchInfo& info) { // Find the best match for the architecture the user requested. - const SuperFatArch* best_match = FindBestMatchForArchitecture( - cpu_type, cpu_subtype); + const SuperFatArch* best_match = + FindBestMatchForArchitecture(info.cputype, info.cpusubtype); if (!best_match) return false; // Record the selected object file. @@ -233,70 +232,37 @@ bool DumpSymbols::SetArchitecture(cpu_type_t cpu_type, return true; } -bool DumpSymbols::SetArchitecture(const std::string& arch_name) { - bool arch_set = false; - const NXArchInfo* arch_info = - google_breakpad::BreakpadGetArchInfoFromName(arch_name.c_str()); - if (arch_info) { - arch_set = SetArchitecture(arch_info->cputype, arch_info->cpusubtype); - } - return arch_set; -} SuperFatArch* DumpSymbols::FindBestMatchForArchitecture( - cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { - // Check if all the object files can be converted to struct fat_arch. - bool can_convert_to_fat_arch = true; - vector<struct fat_arch> fat_arch_vector; - for (vector<SuperFatArch>::const_iterator it = object_files_.begin(); - it != object_files_.end(); - ++it) { - struct fat_arch arch; - bool success = it->ConvertToFatArch(&arch); - if (!success) { - can_convert_to_fat_arch = false; - break; - } - fat_arch_vector.push_back(arch); - } - - // If all the object files can be converted to struct fat_arch, use - // NXFindBestFatArch. - if (can_convert_to_fat_arch) { - const struct fat_arch* best_match - = NXFindBestFatArch(cpu_type, cpu_subtype, &fat_arch_vector[0], - static_cast<uint32_t>(fat_arch_vector.size())); - - for (size_t i = 0; i < fat_arch_vector.size(); ++i) { - if (best_match == &fat_arch_vector[i]) - return &object_files_[i]; + cpu_type_t cpu_type, + cpu_subtype_t cpu_subtype) { + SuperFatArch* closest_match = nullptr; + for (auto& object_file : object_files_) { + if (static_cast<cpu_type_t>(object_file.cputype) == cpu_type) { + // If there's an exact match, return it directly. + if ((static_cast<cpu_subtype_t>(object_file.cpusubtype) & + ~CPU_SUBTYPE_MASK) == (cpu_subtype & ~CPU_SUBTYPE_MASK)) { + return &object_file; + } + // Otherwise, hold on to this as the closest match since at least the CPU + // type matches. + if (!closest_match) { + closest_match = &object_file; + } } - assert(best_match == NULL); - // Fall through since NXFindBestFatArch can't find arm slices on x86_64 - // macOS 13. See FB11955188. - } - - // Check for an exact match with cpu_type and cpu_subtype. - for (vector<SuperFatArch>::iterator it = object_files_.begin(); - it != object_files_.end(); - ++it) { - if (static_cast<cpu_type_t>(it->cputype) == cpu_type && - (static_cast<cpu_subtype_t>(it->cpusubtype) & ~CPU_SUBTYPE_MASK) == - (cpu_subtype & ~CPU_SUBTYPE_MASK)) - return &*it; } - // No exact match found. - // TODO(erikchen): If it becomes necessary, we can copy the implementation of - // NXFindBestFatArch, located at - // http://web.mit.edu/darwin/src/modules/cctools/libmacho/arch.c. - fprintf(stderr, "Failed to find an exact match for an object file with cpu " - "type: %d and cpu subtype: %d.\n", cpu_type, cpu_subtype); - if (!can_convert_to_fat_arch) { - fprintf(stderr, "Furthermore, at least one object file is larger " - "than 2**32.\n"); + fprintf(stderr, + "Failed to find an exact match for an object file with cpu " + "type: %d and cpu subtype: %d.\n", + cpu_type, cpu_subtype); + if (closest_match) { + fprintf(stderr, "Using %s as the closest match.\n", + GetNameFromCPUType(closest_match->cputype, + closest_match->cpusubtype)); + return closest_match; } - return NULL; + return nullptr; } string DumpSymbols::Identifier() { @@ -402,8 +368,8 @@ bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) { selected_object_file_ = &object_files_[0]; else { // Look for an object file whose architecture matches our own. - const NXArchInfo* local_arch = NXGetLocalArchInfo(); - if (!SetArchitecture(local_arch->cputype, local_arch->cpusubtype)) { + ArchInfo local_arch = GetLocalArchInfo(); + if (!SetArchitecture(local_arch)) { fprintf(stderr, "%s: object file contains more than one" " architecture, none of which match the current" " architecture; specify an architecture explicitly" @@ -418,18 +384,16 @@ bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) { // Find the name of the selected file's architecture, to appear in // the MODULE record and in error messages. - const NXArchInfo* selected_arch_info = - google_breakpad::BreakpadGetArchInfoFromCpuType( - selected_object_file_->cputype, selected_object_file_->cpusubtype); + const char* selected_arch_name = GetNameFromCPUType( + selected_object_file_->cputype, selected_object_file_->cpusubtype); // In certain cases, it is possible that architecture info can't be reliably // determined, e.g. new architectures that breakpad is unware of. In that // case, avoid crashing and return false instead. - if (selected_arch_info == NULL) { + if (selected_arch_name == kUnknownArchName) { return false; } - const char* selected_arch_name = selected_arch_info->name; if (strcmp(selected_arch_name, "i386") == 0) selected_arch_name = "x86"; @@ -442,7 +406,12 @@ bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) { } // Compute a module name, to appear in the MODULE record. - string module_name = google_breakpad::BaseName(object_filename_); + string module_name; + if (!module_name_.empty()) { + module_name = module_name_; + } else { + module_name = google_breakpad::BaseName(object_filename_); + } // Choose an identifier string, to appear in the MODULE record. string identifier = Identifier(); @@ -451,7 +420,7 @@ bool DumpSymbols::CreateEmptyModule(scoped_ptr<Module>& module) { // Create a module to hold the debugging information. module.reset(new Module(module_name, "mac", selected_arch_name, identifier, - "", enable_multiple_)); + "", enable_multiple_, prefer_extern_name_)); return true; } @@ -540,16 +509,14 @@ bool DumpSymbols::ReadCFI(google_breakpad::Module* module, register_names = DwarfCFIToModule::RegisterNames::ARM64(); break; default: { - const NXArchInfo* arch = google_breakpad::BreakpadGetArchInfoFromCpuType( - macho_reader.cpu_type(), macho_reader.cpu_subtype()); - fprintf(stderr, "%s: cannot convert DWARF call frame information for ", - selected_object_name_.c_str()); - if (arch) - fprintf(stderr, "architecture '%s'", arch->name); - else - fprintf(stderr, "architecture %d,%d", - macho_reader.cpu_type(), macho_reader.cpu_subtype()); - fprintf(stderr, " to Breakpad symbol file: no register name table\n"); + const char* arch_name = GetNameFromCPUType(macho_reader.cpu_type(), + macho_reader.cpu_subtype()); + fprintf( + stderr, + "%s: cannot convert DWARF call frame information for architecture " + "'%s' (%d, %d) to Breakpad symbol file: no register name table\n", + selected_object_name_.c_str(), arch_name, macho_reader.cpu_type(), + macho_reader.cpu_subtype()); return false; } } diff --git a/src/common/mac/dump_syms.h b/src/common/mac/dump_syms.h index c2e1b40b..5bcb0b58 100644 --- a/src/common/mac/dump_syms.h +++ b/src/common/mac/dump_syms.h @@ -43,6 +43,7 @@ #include <vector> #include "common/byte_cursor.h" +#include "common/mac/arch_utilities.h" #include "common/mac/macho_reader.h" #include "common/mac/super_fat_arch.h" #include "common/module.h" @@ -55,7 +56,9 @@ class DumpSymbols { public: DumpSymbols(SymbolData symbol_data, bool handle_inter_cu_refs, - bool enable_multiple = false) + bool enable_multiple = false, + const std::string& module_name = "", + bool prefer_extern_name = false) : symbol_data_(symbol_data), handle_inter_cu_refs_(handle_inter_cu_refs), object_filename_(), @@ -65,12 +68,19 @@ class DumpSymbols { object_files_(), selected_object_file_(), selected_object_name_(), - enable_multiple_(enable_multiple) {} + enable_multiple_(enable_multiple), + module_name_(module_name), + prefer_extern_name_(prefer_extern_name) {} ~DumpSymbols() = default; // Prepare to read debugging information from |filename|. |filename| may be // the name of a fat file, a Mach-O file, or a dSYM bundle containing either - // of the above. On success, return true; if there is a problem reading + // of the above. + // + // If |module_name_| is empty, uses the basename of |filename| as the module + // name. Otherwise, uses |module_name_| as the module name. + // + // On success, return true; if there is a problem reading // |filename|, report it and return false. bool Read(const std::string& filename); @@ -82,26 +92,15 @@ class DumpSymbols { // problem reading |contents|, report it and return false. bool ReadData(uint8_t* contents, size_t size, const std::string& filename); - // If this dumper's file includes an object file for |cpu_type| and - // |cpu_subtype|, then select that object file for dumping, and return - // true. Otherwise, return false, and leave this dumper's selected - // architecture unchanged. + // If this dumper's file includes an object file for `info`, then select that + // object file for dumping, and return true. Otherwise, return false, and + // leave this dumper's selected architecture unchanged. // // By default, if this dumper's file contains only one object file, then // the dumper will dump those symbols; and if it contains more than one // object file, then the dumper will dump the object file whose // architecture matches that of this dumper program. - bool SetArchitecture(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype); - - // If this dumper's file includes an object file for |arch_name|, then select - // that object file for dumping, and return true. Otherwise, return false, - // and leave this dumper's selected architecture unchanged. - // - // By default, if this dumper's file contains only one object file, then - // the dumper will dump those symbols; and if it contains more than one - // object file, then the dumper will dump the object file whose - // architecture matches that of this dumper program. - bool SetArchitecture(const std::string& arch_name); + bool SetArchitecture(const ArchInfo& info); // Return a pointer to an array of SuperFatArch structures describing the // object files contained in this dumper's file. Set *|count| to the number @@ -204,6 +203,19 @@ class DumpSymbols { // See: https://crbug.com/google-breakpad/751 and docs at // docs/symbol_files.md#records-3 bool enable_multiple_; + + // If non-empty, used as the module name. Otherwise, the basename of + // |object_filename_| is used as the module name. + const std::string module_name_; + + // If a Function and an Extern share the same address but have a different + // name, prefer the name of the Extern. + // + // Use this when dumping Mach-O .dSYMs built with -gmlt (Minimum Line Tables), + // as the Function's fully-qualified name will only be present in the STABS + // (which are placed in the Extern), not in the DWARF symbols (which are + // placed in the Function). + bool prefer_extern_name_; }; } // namespace google_breakpad diff --git a/src/common/mac/macho_walker.cc b/src/common/mac/macho_walker.cc index f9cd9327..4b9f56c2 100644 --- a/src/common/mac/macho_walker.cc +++ b/src/common/mac/macho_walker.cc @@ -38,15 +38,15 @@ #include <assert.h> #include <fcntl.h> -#include <mach-o/arch.h> #include <mach-o/fat.h> #include <mach-o/loader.h> #include <string.h> #include <unistd.h> +#include "common/mac/arch_utilities.h" #include "common/mac/byteswap.h" -#include "common/mac/macho_walker.h" #include "common/mac/macho_utilities.h" +#include "common/mac/macho_walker.h" namespace MacFileUtilities { @@ -85,9 +85,8 @@ bool MachoWalker::WalkHeader(cpu_type_t cpu_type, cpu_subtype_t cpu_subtype) { cpu_subtype_t valid_cpu_subtype = cpu_subtype; // if |cpu_type| is 0, use the native cpu type. if (cpu_type == 0) { - const NXArchInfo* arch = NXGetLocalArchInfo(); - assert(arch); - valid_cpu_type = arch->cputype; + ArchInfo arch = GetLocalArchInfo(); + valid_cpu_type = arch.cputype; valid_cpu_subtype = CPU_SUBTYPE_MULTIPLE; } off_t offset; diff --git a/src/common/module.cc b/src/common/module.cc index a5c1b6ad..e61c3b7a 100644 --- a/src/common/module.cc +++ b/src/common/module.cc @@ -105,14 +105,16 @@ Module::Module(const string& name, const string& architecture, const string& id, const string& code_id /* = "" */, - bool enable_multiple_field /* = false*/) + bool enable_multiple_field /* = false*/, + bool prefer_extern_name /* = false*/) : name_(name), os_(os), architecture_(architecture), id_(id), code_id_(code_id), load_address_(0), - enable_multiple_field_(enable_multiple_field) {} + enable_multiple_field_(enable_multiple_field), + prefer_extern_name_(prefer_extern_name) {} Module::~Module() { for (FileByNameMap::iterator it = files_.begin(); it != files_.end(); ++it) @@ -152,11 +154,14 @@ bool Module::AddFunction(Function* function) { it_ext = externs_.find(&arm_thumb_ext); } if (it_ext != externs_.end()) { + Extern* found_ext = it_ext->get(); + bool name_mismatch = found_ext->name != function->name; if (enable_multiple_field_) { - Extern* found_ext = it_ext->get(); // If the PUBLIC is for the same symbol as the FUNC, don't mark multiple. - function->is_multiple |= - found_ext->name != function->name || found_ext->is_multiple; + function->is_multiple |= name_mismatch || found_ext->is_multiple; + } + if (name_mismatch && prefer_extern_name_) { + function->name = AddStringToPool(it_ext->get()->name); } externs_.erase(it_ext); } diff --git a/src/common/module.h b/src/common/module.h index c1fd9f59..d736701f 100644 --- a/src/common/module.h +++ b/src/common/module.h @@ -131,6 +131,10 @@ class Module { // If this symbol has been folded with other symbols in the linked binary. bool is_multiple = false; + + // If the function's name should be filled out from a matching Extern, + // should they not match. + bool prefer_extern_name = false; }; struct InlineOrigin { @@ -317,7 +321,8 @@ class Module { const string& architecture, const string& id, const string& code_id = "", - bool enable_multiple_field = false); + bool enable_multiple_field = false, + bool prefer_extern_name = false); ~Module(); // Set the module's load address to LOAD_ADDRESS; addresses given @@ -502,6 +507,15 @@ class Module { // at // https://chromium.googlesource.com/breakpad/breakpad/+/master/docs/symbol_files.md#records-3 bool enable_multiple_field_; + + // If a Function and an Extern share the same address but have a different + // name, prefer the name of the Extern. + // + // Use this when dumping Mach-O .dSYMs built with -gmlt (Minimum Line Tables), + // as the Function's fully-qualified name will only be present in the STABS + // (which are placed in the Extern), not in the DWARF symbols (which are + // placed in the Function). + bool prefer_extern_name_; }; } // namespace google_breakpad diff --git a/src/common/module_unittest.cc b/src/common/module_unittest.cc index 213b3154..6a6762e5 100644 --- a/src/common/module_unittest.cc +++ b/src/common/module_unittest.cc @@ -641,6 +641,37 @@ TEST(Module, ConstructFunctionsAndExternsWithSameAddress) { } // If there exists an extern and a function at the same address, only write +// out the FUNC entry. +TEST(Module, ConstructFunctionsAndExternsWithSameAddressPreferExternName) { + stringstream s; + Module m(MODULE_NAME, MODULE_OS, MODULE_ARCH, MODULE_ID, "", false, true); + + // Two externs. + auto extern1 = std::make_unique<Module::Extern>(0xabc0); + extern1->name = "extern1"; + auto extern2 = std::make_unique<Module::Extern>(0xfff0); + extern2->name = "extern2"; + + m.AddExtern(std::move(extern1)); + m.AddExtern(std::move(extern2)); + + Module::Function* function = new Module::Function("function2", 0xfff0); + Module::Range range(0xfff0, 0x10); + function->ranges.push_back(range); + function->parameter_size = 0; + m.AddFunction(function); + + m.Write(s, ALL_SYMBOL_DATA); + string contents = s.str(); + + EXPECT_STREQ("MODULE " MODULE_OS " " MODULE_ARCH " " MODULE_ID " " MODULE_NAME + "\n" + "FUNC fff0 10 0 extern2\n" + "PUBLIC abc0 0 extern1\n", + contents.c_str()); +} + +// If there exists an extern and a function at the same address, only write // out the FUNC entry, and mark it with `m` if the multiple field is enabled. TEST(Module, ConstructFunctionsAndExternsWithSameAddressMultiple) { stringstream s; diff --git a/src/processor/minidump.cc b/src/processor/minidump.cc index 0f08271d..84a7f8c6 100644 --- a/src/processor/minidump.cc +++ b/src/processor/minidump.cc @@ -76,6 +76,11 @@ using std::vector; namespace { +// Limit arrived at by adding up possible states in Intel Ch. 13.5 X-SAVE +// MANAGED STATE +// (~ 3680 bytes) plus some extra for the future. +const uint32_t kMaxXSaveAreaSize = 16384; + // Returns true iff |context_size| matches exactly one of the sizes of the // various MDRawContext* types. // TODO(blundell): This function can be removed once @@ -507,6 +512,10 @@ bool MinidumpContext::Read(uint32_t expected_size) { // sizeof(MDRawContextAMD64). For now we skip this extended data. if (expected_size > sizeof(MDRawContextAMD64)) { size_t bytes_left = expected_size - sizeof(MDRawContextAMD64); + if (bytes_left > kMaxXSaveAreaSize) { + BPLOG(ERROR) << "MinidumpContext oversized xstate area"; + return false; + } std::vector<uint8_t> xstate(bytes_left); if (!minidump_->ReadBytes(xstate.data(), bytes_left)) { diff --git a/src/processor/minidump_processor.cc b/src/processor/minidump_processor.cc index d56a7d63..5ba6ff4f 100644 --- a/src/processor/minidump_processor.cc +++ b/src/processor/minidump_processor.cc @@ -633,6 +633,16 @@ bool MinidumpProcessor::GetCPUInfo(Minidump* dump, SystemInfo* info) { break; } + case MD_CPU_ARCHITECTURE_RISCV: { + info->cpu = "riscv"; + break; + } + + case MD_CPU_ARCHITECTURE_RISCV64: { + info->cpu = "riscv64"; + break; + } + default: { // Assign the numeric architecture ID into the CPU string. char cpu_string[7]; diff --git a/src/processor/minidump_stackwalk.cc b/src/processor/minidump_stackwalk.cc index 4c7a94f4..d44ddf84 100644 --- a/src/processor/minidump_stackwalk.cc +++ b/src/processor/minidump_stackwalk.cc @@ -66,6 +66,7 @@ struct Options { bool machine_readable; bool output_stack_contents; bool output_requesting_thread_only; + bool brief; string minidump_file; std::vector<string> symbol_paths; @@ -119,6 +120,8 @@ bool PrintMinidumpProcess(const Options& options) { if (options.machine_readable) { PrintProcessStateMachineReadable(process_state); + } else if (options.brief) { + PrintRequestingThreadBrief(process_state); } else { PrintProcessState(process_state, options.output_stack_contents, options.output_requesting_thread_only, &resolver); @@ -139,7 +142,8 @@ static void Usage(int argc, const char *argv[], bool error) { "\n" " -m Output in machine-readable format\n" " -s Output stack contents\n" - " -c Output thread that causes crash or dump only\n", + " -c Output thread that causes crash or dump only\n" + " -b Brief of the thread that causes crash or dump\n", google_breakpad::BaseName(argv[0]).c_str()); } @@ -149,14 +153,18 @@ static void SetupOptions(int argc, const char *argv[], Options* options) { options->machine_readable = false; options->output_stack_contents = false; options->output_requesting_thread_only = false; + options->brief = false; - while ((ch = getopt(argc, (char * const*)argv, "chms")) != -1) { + while ((ch = getopt(argc, (char* const*)argv, "bchms")) != -1) { switch (ch) { case 'h': Usage(argc, argv, false); exit(0); break; + case 'b': + options->brief = true; + break; case 'c': options->output_requesting_thread_only = true; break; diff --git a/src/processor/stackwalk_common.cc b/src/processor/stackwalk_common.cc index 253960f5..644ede79 100644 --- a/src/processor/stackwalk_common.cc +++ b/src/processor/stackwalk_common.cc @@ -281,7 +281,33 @@ static void PrintStackContents(const string& indent, } -// PrintStack prints the call stack in |stack| to PrintStream, in a reasonably +static void PrintFrameHeader(const StackFrame* frame, int frame_index) { + fprintf(PrintStream, "%2d ", frame_index); + + uint64_t instruction_address = frame->ReturnAddress(); + + if (frame->module) { + fprintf(PrintStream, "%s", PathnameStripper::File(frame->module->code_file()).c_str()); + if (!frame->function_name.empty()) { + fprintf(PrintStream, "!%s", frame->function_name.c_str()); + if (!frame->source_file_name.empty()) { + string source_file = PathnameStripper::File(frame->source_file_name); + fprintf(PrintStream, " [%s : %d + 0x%" PRIx64 "]", source_file.c_str(), + frame->source_line, + instruction_address - frame->source_line_base); + } else { + fprintf(PrintStream, " + 0x%" PRIx64, instruction_address - frame->function_base); + } + } else { + fprintf(PrintStream, " + 0x%" PRIx64, + instruction_address - frame->module->base_address()); + } + } else { + fprintf(PrintStream, "0x%" PRIx64, instruction_address); + } +} + +// PrintStack prints the call stack in |stack| to stdout, in a reasonably // useful form. Module, function, and source file names are displayed if // they are available. The code offset to the base code address of the // source line, function, or module is printed, preferring them in that @@ -302,30 +328,8 @@ static void PrintStack(const CallStack* stack, } for (int frame_index = 0; frame_index < frame_count; ++frame_index) { const StackFrame* frame = stack->frames()->at(frame_index); - fprintf(PrintStream, "%2d ", frame_index); - - uint64_t instruction_address = frame->ReturnAddress(); - - if (frame->module) { - fprintf(PrintStream, "%s", PathnameStripper::File(frame->module->code_file()).c_str()); - if (!frame->function_name.empty()) { - fprintf(PrintStream, "!%s", frame->function_name.c_str()); - if (!frame->source_file_name.empty()) { - string source_file = PathnameStripper::File(frame->source_file_name); - fprintf(PrintStream, " [%s : %d + 0x%" PRIx64 "]", - source_file.c_str(), - frame->source_line, - instruction_address - frame->source_line_base); - } else { - fprintf(PrintStream, " + 0x%" PRIx64, instruction_address - frame->function_base); - } - } else { - fprintf(PrintStream, " + 0x%" PRIx64, - instruction_address - frame->module->base_address()); - } - } else { - fprintf(PrintStream, "0x%" PRIx64, instruction_address); - } + PrintFrameHeader(frame, frame_index); + fprintf(PrintStream, "\n "); fprintf(PrintStream, "\n "); // Inlined frames don't have registers info. @@ -996,7 +1000,7 @@ static void PrintStackMachineReadable(int thread_num, const CallStack* stack) { instruction_address - frame->module->base_address()); } } else { - // the printf before this prints a trailing separator for module name + // the fprintf before this prints a trailing separator for module name fprintf(PrintStream, "%c%c%c%c0x%" PRIx64, kOutputSeparator, // empty function name kOutputSeparator, // empty source file @@ -1287,4 +1291,21 @@ void PrintProcessStateMachineReadable(const ProcessState& process_state) { } } +void PrintRequestingThreadBrief(const ProcessState& process_state) { + int requesting_thread = process_state.requesting_thread(); + if (requesting_thread == -1) { + fprintf(PrintStream, " <no crashing or requesting dump thread identified>\n"); + return; + } + + fprintf(PrintStream, "Thread %d (%s)\n", requesting_thread, + process_state.crashed() ? "crashed" : "requested dump, did not crash"); + const CallStack* stack = process_state.threads()->at(requesting_thread); + int frame_count = stack->frames()->size(); + for (int frame_index = 0; frame_index < frame_count; ++frame_index) { + PrintFrameHeader(stack->frames()->at(frame_index), frame_index); + fprintf(PrintStream, "\n"); + } +} + } // namespace google_breakpad diff --git a/src/processor/stackwalk_common.h b/src/processor/stackwalk_common.h index 5a861bbd..b2266e4d 100644 --- a/src/processor/stackwalk_common.h +++ b/src/processor/stackwalk_common.h @@ -45,6 +45,7 @@ void PrintProcessState(const ProcessState& process_state, bool output_stack_contents, bool output_requesting_thread_only, SourceLineResolverInterface* resolver); +void PrintRequestingThreadBrief(const ProcessState& process_state); } // namespace google_breakpad diff --git a/src/tools/mac/dump_syms/dump_syms_tool.cc b/src/tools/mac/dump_syms/dump_syms_tool.cc index 2f2815c5..9fb8d13f 100644 --- a/src/tools/mac/dump_syms/dump_syms_tool.cc +++ b/src/tools/mac/dump_syms/dump_syms_tool.cc @@ -63,16 +63,20 @@ struct Options { cfi(true), handle_inter_cu_refs(true), handle_inlines(false), - enable_multiple(false) {} + enable_multiple(false), + module_name(), + prefer_extern_name(false) {} string srcPath; string dsymPath; - const NXArchInfo *arch; + std::optional<ArchInfo> arch; bool header_only; bool cfi; bool handle_inter_cu_refs; bool handle_inlines; bool enable_multiple; + string module_name; + bool prefer_extern_name; }; static bool StackFrameEntryComparator(const Module::StackFrameEntry* a, @@ -121,11 +125,12 @@ static void CopyCFIDataBetweenModules(Module* to_module, } static bool SetArchitecture(DumpSymbols& dump_symbols, - const NXArchInfo* arch, + const ArchInfo& arch, const std::string& filename) { - if (!dump_symbols.SetArchitecture(arch->cputype, arch->cpusubtype)) { + if (!dump_symbols.SetArchitecture(arch)) { fprintf(stderr, "%s: no architecture '%s' is present in file.\n", - filename.c_str(), arch->name); + filename.c_str(), + GetNameFromCPUType(arch.cputype, arch.cpusubtype)); size_t available_size; const SuperFatArch* available = dump_symbols.AvailableArchitectures(&available_size); @@ -135,14 +140,8 @@ static bool SetArchitecture(DumpSymbols& dump_symbols, fprintf(stderr, "architectures present in the file are:\n"); for (size_t i = 0; i < available_size; i++) { const SuperFatArch* arch = &available[i]; - const NXArchInfo* arch_info = - google_breakpad::BreakpadGetArchInfoFromCpuType(arch->cputype, - arch->cpusubtype); - if (arch_info) - fprintf(stderr, "%s (%s)\n", arch_info->name, arch_info->description); - else - fprintf(stderr, "unrecognized cpu type 0x%x, subtype 0x%x\n", - arch->cputype, arch->cpusubtype); + fprintf(stderr, "%s\n", + GetNameFromCPUType(arch->cputype, arch->cpusubtype)); } return false; } @@ -154,7 +153,8 @@ static bool Start(const Options& options) { (options.handle_inlines ? INLINES : NO_DATA) | (options.cfi ? CFI : NO_DATA) | SYMBOLS_AND_FILES; DumpSymbols dump_symbols(symbol_data, options.handle_inter_cu_refs, - options.enable_multiple); + options.enable_multiple, options.module_name, + options.prefer_extern_name); // For x86_64 binaries, the CFI data is in the __TEXT,__eh_frame of the // Mach-O file, which is not copied into the dSYM. Whereas in i386, the CFI @@ -173,7 +173,7 @@ static bool Start(const Options& options) { return false; if (options.arch && - !SetArchitecture(dump_symbols, options.arch, primary_file)) { + !SetArchitecture(dump_symbols, *options.arch, primary_file)) { return false; } @@ -193,7 +193,7 @@ static bool Start(const Options& options) { return false; if (options.arch && - !SetArchitecture(dump_symbols, options.arch, options.srcPath)) { + !SetArchitecture(dump_symbols, *options.arch, options.srcPath)) { return false; } Module* cfi_module = NULL; @@ -201,13 +201,38 @@ static bool Start(const Options& options) { return false; scoped_ptr<Module> scoped_cfi_module(cfi_module); + bool name_matches; + if (!options.module_name.empty()) { + // Ignore the basename of the dSYM and binary and use the passed-in module + // name. + name_matches = true; + } else { + name_matches = cfi_module->name() == module->name(); + } + // Ensure that the modules are for the same debug code file. - if (cfi_module->name() != module->name() || - cfi_module->os() != module->os() || + if (!name_matches || cfi_module->os() != module->os() || cfi_module->architecture() != module->architecture() || cfi_module->identifier() != module->identifier()) { fprintf(stderr, "Cannot generate a symbol file from split sources that do" " not match.\n"); + if (!name_matches) { + fprintf(stderr, "Name mismatch: binary=[%s], dSYM=[%s]\n", + cfi_module->name().c_str(), module->name().c_str()); + } + if (cfi_module->os() != module->os()) { + fprintf(stderr, "OS mismatch: binary=[%s], dSYM=[%s]\n", + cfi_module->os().c_str(), module->os().c_str()); + } + if (cfi_module->architecture() != module->architecture()) { + fprintf(stderr, "Architecture mismatch: binary=[%s], dSYM=[%s]\n", + cfi_module->architecture().c_str(), + module->architecture().c_str()); + } + if (cfi_module->identifier() != module->identifier()) { + fprintf(stderr, "Identifier mismatch: binary=[%s], dSYM=[%s]\n", + cfi_module->identifier().c_str(), module->identifier().c_str()); + } return false; } @@ -220,8 +245,10 @@ static bool Start(const Options& options) { //============================================================================= static void Usage(int argc, const char *argv[]) { fprintf(stderr, "Output a Breakpad symbol file from a Mach-o file.\n"); - fprintf(stderr, "Usage: %s [-a ARCHITECTURE] [-c] [-g dSYM path] " - "<Mach-o file>\n", argv[0]); + fprintf(stderr, + "Usage: %s [-a ARCHITECTURE] [-c] [-g dSYM path] " + "[-n MODULE] [-x] <Mach-o file>\n", + argv[0]); fprintf(stderr, "\t-i: Output module header information only.\n"); fprintf(stderr, "\t-a: Architecture type [default: native, or whatever is\n"); fprintf(stderr, "\t in the file, if it contains only one architecture]\n"); @@ -233,6 +260,12 @@ static void Usage(int argc, const char *argv[]) { fprintf(stderr, "\t-m: Enable writing the optional 'm' field on FUNC " "and PUBLIC, denoting multiple symbols for the address.\n"); + fprintf(stderr, + "\t-n: Use MODULE as the name of the module rather than \n" + "the basename of the Mach-O file/dSYM.\n"); + fprintf(stderr, + "\t-x: Prefer the PUBLIC (extern) name over the FUNC if\n" + "they do not match.\n"); fprintf(stderr, "\t-h: Usage\n"); fprintf(stderr, "\t-?: Usage\n"); } @@ -242,14 +275,13 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { extern int optind; signed char ch; - while ((ch = getopt(argc, (char* const*)argv, "ia:g:crdm?h")) != -1) { + while ((ch = getopt(argc, (char* const*)argv, "ia:g:crdm?hn:x")) != -1) { switch (ch) { case 'i': options->header_only = true; break; case 'a': { - const NXArchInfo *arch_info = - google_breakpad::BreakpadGetArchInfoFromName(optarg); + std::optional<ArchInfo> arch_info = GetArchInfoFromName(optarg); if (!arch_info) { fprintf(stderr, "%s: Invalid architecture: %s\n", argv[0], optarg); Usage(argc, argv); @@ -273,6 +305,12 @@ static void SetupOptions(int argc, const char *argv[], Options *options) { case 'm': options->enable_multiple = true; break; + case 'n': + options->module_name = optarg; + break; + case 'x': + options->prefer_extern_name = true; + break; case '?': case 'h': Usage(argc, argv); diff --git a/src/tools/mac/dump_syms/macho_dump.cc b/src/tools/mac/dump_syms/macho_dump.cc deleted file mode 100644 index 2610025c..00000000 --- a/src/tools/mac/dump_syms/macho_dump.cc +++ /dev/null @@ -1,206 +0,0 @@ -// Copyright 2010 Google LLC -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// 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 LLC nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Original author: Jim Blandy <jimb@mozilla.com> <jimb@red-bean.com> - -// macho_dump.cc: Dump the contents of a Mach-O file. This is mostly -// a test program for the Mach_O::FatReader and Mach_O::Reader classes. - -#ifdef HAVE_CONFIG_H -#include <config.h> // Must come first -#endif - -#include <errno.h> -#include <fcntl.h> -#include <mach-o/arch.h> -#include <sys/mman.h> -#include <stdint.h> -#include <string.h> -#include <sys/stat.h> -#include <unistd.h> - -#include <sstream> -#include <string> -#include <vector> - -#include "common/byte_cursor.h" -#include "common/mac/arch_utilities.h" -#include "common/mac/macho_reader.h" -#include "common/path_helper.h" - -using google_breakpad::ByteBuffer; -using std::ostringstream; -using std::string; -using std::vector; - -namespace { -namespace mach_o = google_breakpad::mach_o; - -string program_name; - -int check_syscall(int result, const char* operation, const char* filename) { - if (result < 0) { - fprintf(stderr, "%s: %s '%s': %s\n", - program_name.c_str(), operation, - filename, strerror(errno)); - exit(1); - } - return result; -} - -class DumpSection: public mach_o::Reader::SectionHandler { - public: - DumpSection() : index_(0) { } - bool HandleSection(const mach_o::Section& section) { - printf(" section %d '%s' in segment '%s'\n" - " address: 0x%llx\n" - " alignment: 1 << %d B\n" - " flags: %d\n" - " size: %ld\n", - index_++, section.section_name.c_str(), section.segment_name.c_str(), - section.address, section.align, - mach_o::SectionFlags(section.flags), - section.contents.Size()); - return true; - } - - private: - int index_; -}; - -class DumpCommand: public mach_o::Reader::LoadCommandHandler { - public: - DumpCommand(mach_o::Reader* reader) : reader_(reader), index_(0) { } - bool UnknownCommand(mach_o::LoadCommandType type, - const ByteBuffer& contents) { - printf(" load command %d: %d", index_++, type); - return true; - } - bool SegmentCommand(const mach_o::Segment& segment) { - printf(" load command %d: %s-bit segment '%s'\n" - " address: 0x%llx\n" - " memory size: 0x%llx\n" - " maximum protection: 0x%x\n" - " initial protection: 0x%x\n" - " flags: %d\n" - " section_list size: %ld B\n", - index_++, (segment.bits_64 ? "64" : "32"), segment.name.c_str(), - segment.vmaddr, segment.vmsize, segment.maxprot, - segment.initprot, mach_o::SegmentFlags(segment.flags), - segment.section_list.Size()); - - DumpSection dump_section; - return reader_->WalkSegmentSections(segment, &dump_section); - } - private: - mach_o::Reader* reader_; - int index_; -}; - -void DumpFile(const char* filename) { - int fd = check_syscall(open(filename, O_RDONLY), "opening", filename); - struct stat attributes; - check_syscall(fstat(fd, &attributes), - "getting file attributes for", filename); - void* mapping = mmap(NULL, attributes.st_size, PROT_READ, - MAP_PRIVATE, fd, 0); - close(fd); - check_syscall(mapping == (void*)-1 ? -1 : 0, - "mapping contents of", filename); - - mach_o::FatReader::Reporter fat_reporter(filename); - mach_o::FatReader fat_reader(&fat_reporter); - if (!fat_reader.Read(reinterpret_cast<uint8_t*>(mapping), - attributes.st_size)) { - exit(1); - } - printf("filename: %s\n", filename); - size_t object_files_size; - const SuperFatArch* super_fat_object_files = - fat_reader.object_files(&object_files_size); - struct fat_arch* object_files; - if (!super_fat_object_files->ConvertToFatArch(object_files)) { - exit(1); - } - printf(" object file count: %ld\n", object_files_size); - for (size_t i = 0; i < object_files_size; i++) { - const struct fat_arch& file = object_files[i]; - const NXArchInfo* fat_arch_info = - google_breakpad::BreakpadGetArchInfoFromCpuType( - file.cputype, file.cpusubtype); - printf("\n object file %ld:\n" - " fat header:\n:" - " CPU type: %s (%s)\n" - " size: %d B\n" - " alignment: 1<<%d B\n", - i, fat_arch_info->name, fat_arch_info->description, - file.size, file.align); - - ostringstream name; - name << filename; - if (object_files_size > 1) - name << ", object file #" << i; - ByteBuffer file_contents(reinterpret_cast<uint8_t*>(mapping) - + file.offset, file.size); - mach_o::Reader::Reporter reporter(name.str()); - mach_o::Reader reader(&reporter); - if (!reader.Read(file_contents, file.cputype, file.cpusubtype)) { - exit(1); - } - - const NXArchInfo* macho_arch_info = - NXGetArchInfoFromCpuType(reader.cpu_type(), - reader.cpu_subtype()); - printf(" Mach-O header:\n" - " word size: %s\n" - " CPU type: %s (%s)\n" - " File type: %d\n" - " flags: %x\n", - (reader.bits_64() ? "64 bits" : "32 bits"), - macho_arch_info->name, macho_arch_info->description, - reader.file_type(), reader.flags()); - - DumpCommand dump_command(&reader); - reader.WalkLoadCommands(&dump_command); - } - munmap(mapping, attributes.st_size); -} - -} // namespace - -int main(int argc, char** argv) { - program_name = google_breakpad::BaseName(argv[0]); - if (argc == 1) { - fprintf(stderr, "Usage: %s FILE ...\n" - "Dump the contents of the Mach-O or fat binary files " - "'FILE ...'.\n", program_name.c_str()); - } - for (int i = 1; i < argc; i++) { - DumpFile(argv[i]); - } -} |