Don't use debug info from ELF file if it doesn't correspond to the running binary.

This commit is contained in:
Alexey Milovidov 2020-06-14 06:10:35 +03:00
parent f53da4d36b
commit 4a052f60c7
3 changed files with 90 additions and 0 deletions

View File

@ -54,6 +54,18 @@ Elf::Elf(const std::string & path)
throw Exception("The ELF is truncated (section names string table points after end of file)", ErrorCodes::CANNOT_PARSE_ELF);
section_names = reinterpret_cast<const char *>(mapped + section_names_offset);
/// Get program headers
ElfOff program_header_offset = header->e_phoff;
uint16_t program_header_num_entries = header->e_phnum;
if (!program_header_offset
|| !program_header_num_entries
|| program_header_offset + program_header_num_entries * sizeof(ElfPhdr) > elf_size)
throw Exception("The ELF is truncated (program header points after end of file)", ErrorCodes::CANNOT_PARSE_ELF);
program_headers = reinterpret_cast<const ElfPhdr *>(mapped + program_header_offset);
}
@ -104,6 +116,40 @@ std::optional<Elf::Section> Elf::findSectionByName(const char * name) const
}
String Elf::getBuildID() const
{
for (size_t idx = 0; idx < header->e_phnum; ++idx)
{
const ElfPhdr & phdr = program_headers[idx];
if (phdr.p_type == PT_NOTE)
return getBuildID(mapped + phdr.p_offset, phdr.p_filesz);
}
return {};
}
String Elf::getBuildID(const char * nhdr_pos, size_t size)
{
const char * nhdr_end = nhdr_pos + size;
while (nhdr_pos < nhdr_end)
{
const ElfNhdr & nhdr = *reinterpret_cast<const ElfNhdr *>(nhdr_pos);
nhdr_pos += sizeof(ElfNhdr) + nhdr.n_namesz;
if (nhdr.n_type == NT_GNU_BUILD_ID)
{
const char * build_id = nhdr_pos;
return {build_id, nhdr.n_descsz};
}
nhdr_pos += nhdr.n_descsz;
}
return {};
}
const char * Elf::Section::name() const
{
if (!elf.section_names)

View File

@ -17,6 +17,7 @@ using ElfEhdr = ElfW(Ehdr);
using ElfOff = ElfW(Off);
using ElfPhdr = ElfW(Phdr);
using ElfShdr = ElfW(Shdr);
using ElfNhdr = ElfW(Nhdr);
using ElfSym = ElfW(Sym);
@ -53,12 +54,18 @@ public:
const char * end() const { return mapped + elf_size; }
size_t size() const { return elf_size; }
/// Obtain build id from PT_NOTES section of program headers. Return empty string if does not exist.
/// The string is returned in binary. Note that "readelf -n ./clickhouse-server" prints it in hex.
String getBuildID() const;
static String getBuildID(const char * nhdr_pos, size_t size);
private:
MMapReadBufferFromFile in;
size_t elf_size;
const char * mapped;
const ElfEhdr * header;
const ElfShdr * section_headers;
const ElfPhdr * program_headers;
const char * section_names = nullptr;
};

View File

@ -196,6 +196,20 @@ void collectSymbolsFromProgramHeaders(dl_phdr_info * info,
}
String getBuildIDFromProgramHeaders(dl_phdr_info * info)
{
for (size_t header_index = 0; header_index < info->dlpi_phnum; ++header_index)
{
const ElfPhdr & phdr = info->dlpi_phdr[header_index];
if (phdr.p_type != PT_NOTE)
continue;
return Elf::getBuildID(reinterpret_cast<const char *>(info->dlpi_addr + phdr.p_vaddr), phdr.p_memsz);
}
return {};
}
void collectSymbolsFromELFSymbolTable(
dl_phdr_info * info,
const Elf & elf,
@ -283,8 +297,31 @@ void collectSymbolsFromELF(dl_phdr_info * info,
object_name = std::filesystem::exists(debug_info_path) ? debug_info_path : canonical_path;
/// But we have to compare Build ID to check that debug info corresponds to the same executable.
String our_build_id = getBuildIDFromProgramHeaders(info);
SymbolIndex::Object object;
object.elf = std::make_unique<Elf>(object_name);
String file_build_id = object.elf->getBuildID();
if (our_build_id != file_build_id)
{
/// If debug info doesn't correspond to our binary, fallback to the info in our binary.
if (object_name != canonical_path)
{
object_name = canonical_path;
object.elf = std::make_unique<Elf>(object_name);
/// But it can still be outdated, for example, if executable file was deleted from filesystem and replaced by another file.
file_build_id = object.elf->getBuildID();
if (our_build_id != file_build_id)
return;
}
else
return;
}
object.address_begin = reinterpret_cast<const void *>(info->dlpi_addr);
object.address_end = reinterpret_cast<const void *>(info->dlpi_addr + object.elf->size());
object.name = object_name;