Commit 372bfc86 by Benjamin Barenblat Committed by Copybara-Service

Fix symbolization on PowerPC ELF v1

The big-endian PowerPC ELF ABI (ppc64 in Debian) relies on function
descriptors mapped in a non-executable segment. Make sure that segment
is scanned during symbolization. Also correct bounds computation for
that segment.

PiperOrigin-RevId: 544440302
Change-Id: Ic05532aa35ae9efa127028318640ee7cdeeecc5f
parent 4eaff9e6
...@@ -658,8 +658,10 @@ static bool ShouldPickFirstSymbol(const ElfW(Sym) & symbol1, ...@@ -658,8 +658,10 @@ static bool ShouldPickFirstSymbol(const ElfW(Sym) & symbol1,
} }
// Return true if an address is inside a section. // Return true if an address is inside a section.
static bool InSection(const void *address, const ElfW(Shdr) * section) { static bool InSection(const void *address, ptrdiff_t relocation,
const char *start = reinterpret_cast<const char *>(section->sh_addr); const ElfW(Shdr) * section) {
const char *start = reinterpret_cast<const char *>(
section->sh_addr + static_cast<ElfW(Addr)>(relocation));
size_t size = static_cast<size_t>(section->sh_size); size_t size = static_cast<size_t>(section->sh_size);
return start <= address && address < (start + size); return start <= address && address < (start + size);
} }
...@@ -699,8 +701,8 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( ...@@ -699,8 +701,8 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
// starting address. However, we do not always want to use the real // starting address. However, we do not always want to use the real
// starting address because we sometimes want to symbolize a function // starting address because we sometimes want to symbolize a function
// pointer into the .opd section, e.g. FindSymbol(&foo,...). // pointer into the .opd section, e.g. FindSymbol(&foo,...).
const bool pc_in_opd = const bool pc_in_opd = kPlatformUsesOPDSections && opd != nullptr &&
kPlatformUsesOPDSections && opd != nullptr && InSection(pc, opd); InSection(pc, relocation, opd);
const bool deref_function_descriptor_pointer = const bool deref_function_descriptor_pointer =
kPlatformUsesOPDSections && opd != nullptr && !pc_in_opd; kPlatformUsesOPDSections && opd != nullptr && !pc_in_opd;
...@@ -740,7 +742,7 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol( ...@@ -740,7 +742,7 @@ static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
#endif #endif
if (deref_function_descriptor_pointer && if (deref_function_descriptor_pointer &&
InSection(original_start_address, opd)) { InSection(original_start_address, /*relocation=*/0, opd)) {
// The opd section is mapped into memory. Just dereference // The opd section is mapped into memory. Just dereference
// start_address to get the first double word, which points to the // start_address to get the first double word, which points to the
// function entry. // function entry.
...@@ -1336,7 +1338,7 @@ static bool MaybeInitializeObjFile(ObjFile *obj) { ...@@ -1336,7 +1338,7 @@ static bool MaybeInitializeObjFile(ObjFile *obj) {
const int phnum = obj->elf_header.e_phnum; const int phnum = obj->elf_header.e_phnum;
const int phentsize = obj->elf_header.e_phentsize; const int phentsize = obj->elf_header.e_phentsize;
auto phoff = static_cast<off_t>(obj->elf_header.e_phoff); auto phoff = static_cast<off_t>(obj->elf_header.e_phoff);
size_t num_executable_load_segments = 0; size_t num_interesting_load_segments = 0;
for (int j = 0; j < phnum; j++) { for (int j = 0; j < phnum; j++) {
ElfW(Phdr) phdr; ElfW(Phdr) phdr;
if (!ReadFromOffsetExact(obj->fd, &phdr, sizeof(phdr), phoff)) { if (!ReadFromOffsetExact(obj->fd, &phdr, sizeof(phdr), phoff)) {
...@@ -1345,23 +1347,35 @@ static bool MaybeInitializeObjFile(ObjFile *obj) { ...@@ -1345,23 +1347,35 @@ static bool MaybeInitializeObjFile(ObjFile *obj) {
return false; return false;
} }
phoff += phentsize; phoff += phentsize;
constexpr int rx = PF_X | PF_R;
if (phdr.p_type != PT_LOAD || (phdr.p_flags & rx) != rx) { #if defined(__powerpc__) && !(_CALL_ELF > 1)
// Not a LOAD segment, or not executable code. // On the PowerPC ELF v1 ABI, function pointers actually point to function
// descriptors. These descriptors are stored in an .opd section, which is
// mapped read-only. We thus need to look at all readable segments, not
// just the executable ones.
constexpr int interesting = PF_R;
#else
constexpr int interesting = PF_X | PF_R;
#endif
if (phdr.p_type != PT_LOAD
|| (phdr.p_flags & interesting) != interesting) {
// Not a LOAD segment, not executable code, and not a function
// descriptor.
continue; continue;
} }
if (num_executable_load_segments < obj->phdr.size()) { if (num_interesting_load_segments < obj->phdr.size()) {
memcpy(&obj->phdr[num_executable_load_segments++], &phdr, sizeof(phdr)); memcpy(&obj->phdr[num_interesting_load_segments++], &phdr, sizeof(phdr));
} else { } else {
ABSL_RAW_LOG( ABSL_RAW_LOG(
WARNING, "%s: too many executable LOAD segments: %zu >= %zu", WARNING, "%s: too many interesting LOAD segments: %zu >= %zu",
obj->filename, num_executable_load_segments, obj->phdr.size()); obj->filename, num_interesting_load_segments, obj->phdr.size());
break; break;
} }
} }
if (num_executable_load_segments == 0) { if (num_interesting_load_segments == 0) {
// This object has no "r-x" LOAD segments. That's unexpected. // This object has no interesting LOAD segments. That's unexpected.
ABSL_RAW_LOG(WARNING, "%s: no executable LOAD segments", obj->filename); ABSL_RAW_LOG(WARNING, "%s: no interesting LOAD segments", obj->filename);
return false; return false;
} }
} }
...@@ -1389,8 +1403,8 @@ const char *Symbolizer::GetUncachedSymbol(const void *pc) { ...@@ -1389,8 +1403,8 @@ const char *Symbolizer::GetUncachedSymbol(const void *pc) {
// X in the file will have a start address of [true relocation]+X. // X in the file will have a start address of [true relocation]+X.
relocation = static_cast<ptrdiff_t>(start_addr - obj->offset); relocation = static_cast<ptrdiff_t>(start_addr - obj->offset);
// Note: some binaries have multiple "rx" LOAD segments. We must // Note: some binaries have multiple LOAD segments that can contain
// find the right one. // function pointers. We must find the right one.
ElfW(Phdr) *phdr = nullptr; ElfW(Phdr) *phdr = nullptr;
for (size_t j = 0; j < obj->phdr.size(); j++) { for (size_t j = 0; j < obj->phdr.size(); j++) {
ElfW(Phdr) &p = obj->phdr[j]; ElfW(Phdr) &p = obj->phdr[j];
...@@ -1400,7 +1414,7 @@ const char *Symbolizer::GetUncachedSymbol(const void *pc) { ...@@ -1400,7 +1414,7 @@ const char *Symbolizer::GetUncachedSymbol(const void *pc) {
ABSL_RAW_CHECK(p.p_type == PT_NULL, "unexpected p_type"); ABSL_RAW_CHECK(p.p_type == PT_NULL, "unexpected p_type");
break; break;
} }
if (pc < reinterpret_cast<void *>(start_addr + p.p_memsz)) { if (pc < reinterpret_cast<void *>(start_addr + p.p_vaddr + p.p_memsz)) {
phdr = &p; phdr = &p;
break; break;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment