From 3e56ef9d4e328142c07605a0bde1dfd2ed122611 Mon Sep 17 00:00:00 2001 From: Joshua Peraza Date: Thu, 24 Oct 2019 11:41:44 -0700 Subject: linux, dump_syms: set module name from DT_SONAME The Breakpad and Crashpad clients will use an object's DT_SONAME as the name for a module if it exists. Previously, linux dump_syms would assume the basename of an input elf file matches that value, causing symbol lookups to fail if they were mismatched. This patch updates dump_syms to use DT_SONAME as the module name, if present. Bug: 1016924 Change-Id: I5eff0cf06c703841df3fb552cb5a8e1e50a20c64 Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/1876763 Reviewed-by: Mike Frysinger --- src/client/linux/minidump_writer/linux_dumper.cc | 43 ----------------- src/common/linux/dump_symbols.cc | 10 +++- src/common/linux/elfutils.cc | 61 ++++++++++++++++++++++++ src/common/linux/elfutils.h | 8 ++++ 4 files changed, 78 insertions(+), 44 deletions(-) (limited to 'src') diff --git a/src/client/linux/minidump_writer/linux_dumper.cc b/src/client/linux/minidump_writer/linux_dumper.cc index dbedecd5..5653133e 100644 --- a/src/client/linux/minidump_writer/linux_dumper.cc +++ b/src/client/linux/minidump_writer/linux_dumper.cc @@ -439,49 +439,6 @@ bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping, } namespace { -bool ElfFileSoNameFromMappedFile( - const void* elf_base, char* soname, size_t soname_size) { - if (!IsValidElf(elf_base)) { - // Not ELF - return false; - } - - const void* segment_start; - size_t segment_size; - if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC, &segment_start, - &segment_size)) { - // No dynamic section - return false; - } - - const void* dynstr_start; - size_t dynstr_size; - if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB, &dynstr_start, - &dynstr_size)) { - // No dynstr section - return false; - } - - const ElfW(Dyn)* dynamic = static_cast(segment_start); - size_t dcount = segment_size / sizeof(ElfW(Dyn)); - for (const ElfW(Dyn)* dyn = dynamic; dyn < dynamic + dcount; ++dyn) { - if (dyn->d_tag == DT_SONAME) { - const char* dynstr = static_cast(dynstr_start); - if (dyn->d_un.d_val >= dynstr_size) { - // Beyond the end of the dynstr section - return false; - } - const char* str = dynstr + dyn->d_un.d_val; - const size_t maxsize = dynstr_size - dyn->d_un.d_val; - my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size); - return true; - } - } - - // Did not find SONAME - return false; -} - // Find the shared object name (SONAME) by examining the ELF information // for |mapping|. If the SONAME is found copy it into the passed buffer // |soname| and return true. The size of the buffer is |soname_size|. diff --git a/src/common/linux/dump_symbols.cc b/src/common/linux/dump_symbols.cc index 1110cb9d..40bb3ff2 100644 --- a/src/common/linux/dump_symbols.cc +++ b/src/common/linux/dump_symbols.cc @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -936,7 +937,14 @@ bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header, return false; } - string name = google_breakpad::BaseName(obj_filename); + string name; + char name_buf[NAME_MAX]; + memset(name_buf, 0, sizeof(name_buf)); + name = google_breakpad::ElfFileSoNameFromMappedFile(elf_header, name_buf, + sizeof(name_buf)) + ? name_buf + : google_breakpad::BaseName(obj_filename); + string os = "Linux"; // Add an extra "0" at the end. PDB files on Windows have an 'age' // number appended to the end of the file identifier; this isn't diff --git a/src/common/linux/elfutils.cc b/src/common/linux/elfutils.cc index 3bb8ff12..9532d5ad 100644 --- a/src/common/linux/elfutils.cc +++ b/src/common/linux/elfutils.cc @@ -173,4 +173,65 @@ bool FindElfSegments(const void* elf_mapped_base, return false; } +template +bool FindElfSoNameFromDynamicSection(const void* section_start, + size_t section_size, + const void* dynstr_start, + size_t dynstr_size, + char* soname, + size_t soname_size) { + typedef typename ElfClass::Dyn Dyn; + + auto* dynamic = static_cast(section_start); + size_t dcount = section_size / sizeof(Dyn); + for (const Dyn* dyn = dynamic; dyn < dynamic + dcount; ++dyn) { + if (dyn->d_tag == DT_SONAME) { + const char* dynstr = static_cast(dynstr_start); + if (dyn->d_un.d_val >= dynstr_size) { + // Beyond the end of the dynstr section + return false; + } + const char* str = dynstr + dyn->d_un.d_val; + const size_t maxsize = dynstr_size - dyn->d_un.d_val; + my_strlcpy(soname, str, maxsize < soname_size ? maxsize : soname_size); + return true; + } + } + + return false; +} + +bool ElfFileSoNameFromMappedFile(const void* elf_base, + char* soname, + size_t soname_size) { + if (!IsValidElf(elf_base)) { + // Not ELF + return false; + } + + const void* segment_start; + size_t segment_size; + if (!FindElfSection(elf_base, ".dynamic", SHT_DYNAMIC, &segment_start, + &segment_size)) { + // No dynamic section + return false; + } + + const void* dynstr_start; + size_t dynstr_size; + if (!FindElfSection(elf_base, ".dynstr", SHT_STRTAB, &dynstr_start, + &dynstr_size)) { + // No dynstr section + return false; + } + + int cls = ElfClass(elf_base); + return cls == ELFCLASS32 ? FindElfSoNameFromDynamicSection( + segment_start, segment_size, dynstr_start, + dynstr_size, soname, soname_size) + : FindElfSoNameFromDynamicSection( + segment_start, segment_size, dynstr_start, + dynstr_size, soname, soname_size); +} + } // namespace google_breakpad diff --git a/src/common/linux/elfutils.h b/src/common/linux/elfutils.h index 8fcb6a96..aefb6cf5 100644 --- a/src/common/linux/elfutils.h +++ b/src/common/linux/elfutils.h @@ -45,6 +45,7 @@ namespace google_breakpad { // with specific ELF bits. struct ElfClass32 { typedef Elf32_Addr Addr; + typedef Elf32_Dyn Dyn; typedef Elf32_Ehdr Ehdr; typedef Elf32_Nhdr Nhdr; typedef Elf32_Phdr Phdr; @@ -62,6 +63,7 @@ struct ElfClass32 { struct ElfClass64 { typedef Elf64_Addr Addr; + typedef Elf64_Dyn Dyn; typedef Elf64_Ehdr Ehdr; typedef Elf64_Nhdr Nhdr; typedef Elf64_Phdr Phdr; @@ -122,6 +124,12 @@ const T* GetOffset(const typename ElfClass::Ehdr* elf_header, typename ElfClass::Off offset); +// Read the value of DT_SONAME from the elf file mapped at |elf_base|. Returns +// true and fills |soname| with the result if found. +bool ElfFileSoNameFromMappedFile(const void* elf_base, + char* soname, + size_t soname_size); + } // namespace google_breakpad #endif // COMMON_LINUX_ELFUTILS_H_ -- cgit v1.2.1