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 <vapier@chromium.org>
This commit is contained in:
parent
db1cda2653
commit
3e56ef9d4e
4 changed files with 78 additions and 44 deletions
|
@ -439,49 +439,6 @@ bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping,
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
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<const ElfW(Dyn)*>(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<const char*>(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
|
// Find the shared object name (SONAME) by examining the ELF information
|
||||||
// for |mapping|. If the SONAME is found copy it into the passed buffer
|
// 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|.
|
// |soname| and return true. The size of the buffer is |soname_size|.
|
||||||
|
|
|
@ -38,6 +38,7 @@
|
||||||
#include <elf.h>
|
#include <elf.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <link.h>
|
#include <link.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -936,7 +937,14 @@ bool InitModuleForElfClass(const typename ElfClass::Ehdr* elf_header,
|
||||||
return false;
|
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";
|
string os = "Linux";
|
||||||
// Add an extra "0" at the end. PDB files on Windows have an 'age'
|
// 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
|
// number appended to the end of the file identifier; this isn't
|
||||||
|
|
|
@ -173,4 +173,65 @@ bool FindElfSegments(const void* elf_mapped_base,
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename ElfClass>
|
||||||
|
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<const Dyn*>(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<const char*>(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<ElfClass32>(
|
||||||
|
segment_start, segment_size, dynstr_start,
|
||||||
|
dynstr_size, soname, soname_size)
|
||||||
|
: FindElfSoNameFromDynamicSection<ElfClass64>(
|
||||||
|
segment_start, segment_size, dynstr_start,
|
||||||
|
dynstr_size, soname, soname_size);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace google_breakpad
|
} // namespace google_breakpad
|
||||||
|
|
|
@ -45,6 +45,7 @@ namespace google_breakpad {
|
||||||
// with specific ELF bits.
|
// with specific ELF bits.
|
||||||
struct ElfClass32 {
|
struct ElfClass32 {
|
||||||
typedef Elf32_Addr Addr;
|
typedef Elf32_Addr Addr;
|
||||||
|
typedef Elf32_Dyn Dyn;
|
||||||
typedef Elf32_Ehdr Ehdr;
|
typedef Elf32_Ehdr Ehdr;
|
||||||
typedef Elf32_Nhdr Nhdr;
|
typedef Elf32_Nhdr Nhdr;
|
||||||
typedef Elf32_Phdr Phdr;
|
typedef Elf32_Phdr Phdr;
|
||||||
|
@ -62,6 +63,7 @@ struct ElfClass32 {
|
||||||
|
|
||||||
struct ElfClass64 {
|
struct ElfClass64 {
|
||||||
typedef Elf64_Addr Addr;
|
typedef Elf64_Addr Addr;
|
||||||
|
typedef Elf64_Dyn Dyn;
|
||||||
typedef Elf64_Ehdr Ehdr;
|
typedef Elf64_Ehdr Ehdr;
|
||||||
typedef Elf64_Nhdr Nhdr;
|
typedef Elf64_Nhdr Nhdr;
|
||||||
typedef Elf64_Phdr Phdr;
|
typedef Elf64_Phdr Phdr;
|
||||||
|
@ -122,6 +124,12 @@ const T*
|
||||||
GetOffset(const typename ElfClass::Ehdr* elf_header,
|
GetOffset(const typename ElfClass::Ehdr* elf_header,
|
||||||
typename ElfClass::Off offset);
|
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
|
} // namespace google_breakpad
|
||||||
|
|
||||||
#endif // COMMON_LINUX_ELFUTILS_H_
|
#endif // COMMON_LINUX_ELFUTILS_H_
|
||||||
|
|
Loading…
Reference in a new issue