in absl/debugging/symbolize_elf.inc [662:774]
static ABSL_ATTRIBUTE_NOINLINE FindSymbolResult FindSymbol(
const void *const pc, const int fd, char *out, int out_size,
ptrdiff_t relocation, const ElfW(Shdr) * strtab, const ElfW(Shdr) * symtab,
const ElfW(Shdr) * opd, char *tmp_buf, int tmp_buf_size) {
if (symtab == nullptr) {
return SYMBOL_NOT_FOUND;
}
// Read multiple symbols at once to save read() calls.
ElfW(Sym) *buf = reinterpret_cast<ElfW(Sym) *>(tmp_buf);
const int buf_entries = tmp_buf_size / sizeof(buf[0]);
const int num_symbols = symtab->sh_size / symtab->sh_entsize;
// On platforms using an .opd section (PowerPC & IA64), a function symbol
// has the address of a function descriptor, which contains the real
// starting address. However, we do not always want to use the real
// starting address because we sometimes want to symbolize a function
// pointer into the .opd section, e.g. FindSymbol(&foo,...).
const bool pc_in_opd =
kPlatformUsesOPDSections && opd != nullptr && InSection(pc, opd);
const bool deref_function_descriptor_pointer =
kPlatformUsesOPDSections && opd != nullptr && !pc_in_opd;
ElfW(Sym) best_match;
SafeMemZero(&best_match, sizeof(best_match));
bool found_match = false;
for (int i = 0; i < num_symbols;) {
off_t offset = symtab->sh_offset + i * symtab->sh_entsize;
const int num_remaining_symbols = num_symbols - i;
const int entries_in_chunk = std::min(num_remaining_symbols, buf_entries);
const int bytes_in_chunk = entries_in_chunk * sizeof(buf[0]);
const ssize_t len = ReadFromOffset(fd, buf, bytes_in_chunk, offset);
SAFE_ASSERT(len % sizeof(buf[0]) == 0);
const ssize_t num_symbols_in_buf = len / sizeof(buf[0]);
SAFE_ASSERT(num_symbols_in_buf <= entries_in_chunk);
for (int j = 0; j < num_symbols_in_buf; ++j) {
const ElfW(Sym) &symbol = buf[j];
// For a DSO, a symbol address is relocated by the loading address.
// We keep the original address for opd redirection below.
const char *const original_start_address =
reinterpret_cast<const char *>(symbol.st_value);
const char *start_address =
ComputeOffset(original_start_address, relocation);
#ifdef __arm__
// ARM functions are always aligned to multiples of two bytes; the
// lowest-order bit in start_address is ignored by the CPU and indicates
// whether the function contains ARM (0) or Thumb (1) code. We don't care
// about what encoding is being used; we just want the real start address
// of the function.
start_address = reinterpret_cast<const char *>(
reinterpret_cast<uintptr_t>(start_address) & ~1);
#endif
if (deref_function_descriptor_pointer &&
InSection(original_start_address, opd)) {
// The opd section is mapped into memory. Just dereference
// start_address to get the first double word, which points to the
// function entry.
start_address = *reinterpret_cast<const char *const *>(start_address);
}
// If pc is inside the .opd section, it points to a function descriptor.
const size_t size = pc_in_opd ? kFunctionDescriptorSize : symbol.st_size;
const void *const end_address = ComputeOffset(start_address, size);
if (symbol.st_value != 0 && // Skip null value symbols.
symbol.st_shndx != 0 && // Skip undefined symbols.
#ifdef STT_TLS
ELF_ST_TYPE(symbol.st_info) != STT_TLS && // Skip thread-local data.
#endif // STT_TLS
((start_address <= pc && pc < end_address) ||
(start_address == pc && pc == end_address))) {
if (!found_match || ShouldPickFirstSymbol(symbol, best_match)) {
found_match = true;
best_match = symbol;
}
}
}
i += num_symbols_in_buf;
}
if (found_match) {
const size_t off = strtab->sh_offset + best_match.st_name;
const ssize_t n_read = ReadFromOffset(fd, out, out_size, off);
if (n_read <= 0) {
// This should never happen.
ABSL_RAW_LOG(WARNING,
"Unable to read from fd %d at offset %zu: n_read = %zd", fd,
off, n_read);
return SYMBOL_NOT_FOUND;
}
ABSL_RAW_CHECK(n_read <= out_size, "ReadFromOffset read too much data.");
// strtab->sh_offset points into .strtab-like section that contains
// NUL-terminated strings: '\0foo\0barbaz\0...".
//
// sh_offset+st_name points to the start of symbol name, but we don't know
// how long the symbol is, so we try to read as much as we have space for,
// and usually over-read (i.e. there is a NUL somewhere before n_read).
if (memchr(out, '\0', n_read) == nullptr) {
// Either out_size was too small (n_read == out_size and no NUL), or
// we tried to read past the EOF (n_read < out_size) and .strtab is
// corrupt (missing terminating NUL; should never happen for valid ELF).
out[n_read - 1] = '\0';
return SYMBOL_TRUNCATED;
}
return SYMBOL_FOUND;
}
return SYMBOL_NOT_FOUND;
}