2022-06-10 08:22:31 +00:00
|
|
|
#if defined(__ELF__) && !defined(OS_FREEBSD)
|
2019-08-21 00:48:34 +00:00
|
|
|
|
2019-07-29 17:14:53 +00:00
|
|
|
#include <Common/Elf.h>
|
|
|
|
#include <Common/Exception.h>
|
2021-10-02 07:13:14 +00:00
|
|
|
#include <base/unaligned.h>
|
2019-07-29 17:14:53 +00:00
|
|
|
|
2022-05-08 17:01:47 +00:00
|
|
|
#include <cstring>
|
2019-07-29 17:14:53 +00:00
|
|
|
|
|
|
|
|
|
|
|
namespace DB
|
|
|
|
{
|
|
|
|
|
|
|
|
namespace ErrorCodes
|
|
|
|
{
|
|
|
|
extern const int CANNOT_PARSE_ELF;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Elf::Elf(const std::string & path)
|
|
|
|
: in(path, 0)
|
|
|
|
{
|
|
|
|
/// Check if it's an elf.
|
2019-07-29 18:38:04 +00:00
|
|
|
elf_size = in.buffer().size();
|
|
|
|
if (elf_size < sizeof(ElfEhdr))
|
2022-09-02 07:03:39 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_ELF, "The size of supposedly ELF file '{}' is too small", path);
|
2019-07-29 17:14:53 +00:00
|
|
|
|
|
|
|
mapped = in.buffer().begin();
|
|
|
|
header = reinterpret_cast<const ElfEhdr *>(mapped);
|
|
|
|
|
|
|
|
if (memcmp(header->e_ident, "\x7F""ELF", 4) != 0)
|
2022-09-02 07:03:39 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_ELF, "The file '{}' is not ELF according to magic", path);
|
2019-07-29 17:14:53 +00:00
|
|
|
|
|
|
|
/// Get section header.
|
|
|
|
ElfOff section_header_offset = header->e_shoff;
|
|
|
|
uint16_t section_header_num_entries = header->e_shnum;
|
|
|
|
|
|
|
|
if (!section_header_offset
|
|
|
|
|| !section_header_num_entries
|
2019-07-29 18:38:04 +00:00
|
|
|
|| section_header_offset + section_header_num_entries * sizeof(ElfShdr) > elf_size)
|
2022-09-02 07:03:39 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_ELF, "The ELF '{}' is truncated (section header points after end of file)", path);
|
2019-07-29 17:14:53 +00:00
|
|
|
|
|
|
|
section_headers = reinterpret_cast<const ElfShdr *>(mapped + section_header_offset);
|
|
|
|
|
|
|
|
/// The string table with section names.
|
|
|
|
auto section_names_strtab = findSection([&](const Section & section, size_t idx)
|
|
|
|
{
|
|
|
|
return section.header.sh_type == SHT_STRTAB && header->e_shstrndx == idx;
|
|
|
|
});
|
|
|
|
|
|
|
|
if (!section_names_strtab)
|
2022-09-02 07:03:39 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_ELF, "The ELF '{}' doesn't have string table with section names", path);
|
2019-07-29 17:14:53 +00:00
|
|
|
|
|
|
|
ElfOff section_names_offset = section_names_strtab->header.sh_offset;
|
2019-07-29 18:38:04 +00:00
|
|
|
if (section_names_offset >= elf_size)
|
2022-09-02 07:03:39 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_ELF, "The ELF '{}' is truncated (section names string table points after end of file)", path);
|
2019-07-29 17:14:53 +00:00
|
|
|
|
|
|
|
section_names = reinterpret_cast<const char *>(mapped + section_names_offset);
|
2020-06-14 03:10:35 +00:00
|
|
|
|
|
|
|
/// 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)
|
2022-09-02 07:03:39 +00:00
|
|
|
throw Exception(ErrorCodes::CANNOT_PARSE_ELF, "The ELF '{}' is truncated (program header points after end of file)", path);
|
2020-06-14 03:10:35 +00:00
|
|
|
|
|
|
|
program_headers = reinterpret_cast<const ElfPhdr *>(mapped + program_header_offset);
|
2019-07-29 17:14:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-08-03 11:02:40 +00:00
|
|
|
Elf::Section::Section(const ElfShdr & header_, const Elf & elf_)
|
|
|
|
: header(header_), elf(elf_)
|
2019-07-29 17:14:53 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool Elf::iterateSections(std::function<bool(const Section & section, size_t idx)> && pred) const
|
|
|
|
{
|
|
|
|
for (size_t idx = 0; idx < header->e_shnum; ++idx)
|
|
|
|
{
|
|
|
|
Section section(section_headers[idx], *this);
|
|
|
|
|
|
|
|
/// Sections spans after end of file.
|
2019-07-29 18:38:04 +00:00
|
|
|
if (section.header.sh_offset + section.header.sh_size > elf_size)
|
2019-07-29 17:14:53 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
if (pred(section, idx))
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
std::optional<Elf::Section> Elf::findSection(std::function<bool(const Section & section, size_t idx)> && pred) const
|
|
|
|
{
|
|
|
|
std::optional<Elf::Section> result;
|
|
|
|
|
|
|
|
iterateSections([&](const Section & section, size_t idx)
|
|
|
|
{
|
|
|
|
if (pred(section, idx))
|
|
|
|
{
|
|
|
|
result.emplace(section);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
});
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-29 18:06:39 +00:00
|
|
|
std::optional<Elf::Section> Elf::findSectionByName(const char * name) const
|
|
|
|
{
|
|
|
|
return findSection([&](const Section & section, size_t) { return 0 == strcmp(name, section.name()); });
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-06-14 03:10:35 +00:00
|
|
|
String Elf::getBuildID() const
|
|
|
|
{
|
2021-11-20 14:13:46 +00:00
|
|
|
/// Section headers are the first choice for a debuginfo file
|
2021-11-20 12:52:00 +00:00
|
|
|
if (String build_id; iterateSections([&build_id](const Section & section, size_t)
|
|
|
|
{
|
|
|
|
if (section.header.sh_type == SHT_NOTE)
|
|
|
|
{
|
|
|
|
build_id = Elf::getBuildID(section.begin(), section.size());
|
|
|
|
if (!build_id.empty())
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}))
|
|
|
|
{
|
|
|
|
return build_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// fallback to PHDR
|
2020-06-14 03:10:35 +00:00
|
|
|
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);
|
|
|
|
}
|
2021-11-20 12:52:00 +00:00
|
|
|
|
2020-06-14 03:10:35 +00:00
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2021-04-28 23:32:41 +00:00
|
|
|
#if defined(OS_SUNOS)
|
|
|
|
String Elf::getBuildID(const char * nhdr_pos, size_t size)
|
|
|
|
{
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
#else
|
2020-06-14 03:10:35 +00:00
|
|
|
String Elf::getBuildID(const char * nhdr_pos, size_t size)
|
|
|
|
{
|
|
|
|
const char * nhdr_end = nhdr_pos + size;
|
|
|
|
|
|
|
|
while (nhdr_pos < nhdr_end)
|
|
|
|
{
|
2020-06-21 16:10:38 +00:00
|
|
|
ElfNhdr nhdr = unalignedLoad<ElfNhdr>(nhdr_pos);
|
2020-06-14 03:10:35 +00:00
|
|
|
|
|
|
|
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 {};
|
|
|
|
}
|
2021-04-28 23:32:41 +00:00
|
|
|
#endif // OS_SUNOS
|
2020-06-14 03:10:35 +00:00
|
|
|
|
|
|
|
|
2022-06-14 08:50:53 +00:00
|
|
|
String Elf::getStoredBinaryHash() const
|
2021-01-07 02:56:57 +00:00
|
|
|
{
|
2022-06-14 08:50:53 +00:00
|
|
|
if (auto section = findSectionByName(".clickhouse.hash"))
|
2021-01-07 02:56:57 +00:00
|
|
|
return {section->begin(), section->end()};
|
|
|
|
else
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-07-29 17:14:53 +00:00
|
|
|
const char * Elf::Section::name() const
|
|
|
|
{
|
|
|
|
if (!elf.section_names)
|
|
|
|
throw Exception("Section names are not initialized", ErrorCodes::CANNOT_PARSE_ELF);
|
|
|
|
|
|
|
|
/// TODO buffer overflow is possible, we may need to check strlen.
|
|
|
|
return elf.section_names + header.sh_name;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const char * Elf::Section::begin() const
|
|
|
|
{
|
|
|
|
return elf.mapped + header.sh_offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char * Elf::Section::end() const
|
|
|
|
{
|
2019-07-29 18:06:39 +00:00
|
|
|
return begin() + size();
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t Elf::Section::size() const
|
|
|
|
{
|
|
|
|
return header.sh_size;
|
2019-07-29 17:14:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2019-08-21 00:48:34 +00:00
|
|
|
|
|
|
|
#endif
|