#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include "base/debug/elf_reader.h"
#include <arpa/inet.h>
#include <elf.h>
#include <string.h>
#include <optional>
#include <string_view>
#include "base/bits.h"
#include "base/containers/span.h"
#include "base/hash/sha1.h"
#include "base/strings/safe_sprintf.h"
#include "build/build_config.h"
namespace base::debug {
namespace {
#if __SIZEOF_POINTER__ == 4
using Ehdr = Elf32_Ehdr;
using Dyn = Elf32_Dyn;
using Half = Elf32_Half;
using Nhdr = Elf32_Nhdr;
using Word = Elf32_Word;
using Xword = Elf32_Word;
#else
using Ehdr = Elf64_Ehdr;
using Dyn = Elf64_Dyn;
using Half = Elf64_Half;
using Nhdr = Elf64_Nhdr;
using Word = Elf64_Word;
using Xword = Elf64_Xword;
#endif
constexpr char kGnuNoteName[] = "GNU";
const Ehdr* GetElfHeader(const void* elf_mapped_base) {
if (strncmp(reinterpret_cast<const char*>(elf_mapped_base), ELFMAG,
SELFMAG) != 0) {
return nullptr;
}
return reinterpret_cast<const Ehdr*>(elf_mapped_base);
}
}
size_t ReadElfBuildId(const void* elf_mapped_base,
bool uppercase,
ElfBuildIdBuffer build_id) {
const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
if (!elf_header) {
return 0;
}
const size_t relocation_offset = GetRelocationOffset(elf_mapped_base);
for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) {
if (header.p_type != PT_NOTE) {
continue;
}
const char* current_section =
reinterpret_cast<const char*>(header.p_vaddr + relocation_offset);
const char* section_end = current_section + header.p_memsz;
const Nhdr* current_note = nullptr;
bool found = false;
while (current_section < section_end) {
current_note = reinterpret_cast<const Nhdr*>(current_section);
if (current_note->n_type == NT_GNU_BUILD_ID) {
std::string_view note_name(current_section + sizeof(Nhdr),
current_note->n_namesz);
if (note_name == std::string_view(kGnuNoteName, sizeof(kGnuNoteName))) {
found = true;
break;
}
}
size_t section_size =
bits::AlignUp(current_note->n_namesz, static_cast<Word>(4)) +
bits::AlignUp(current_note->n_descsz, static_cast<Word>(4)) +
sizeof(Nhdr);
if (section_size > static_cast<size_t>(section_end - current_section)) {
return 0;
}
current_section += section_size;
}
if (!found) {
continue;
}
size_t note_size = current_note->n_descsz;
if ((note_size * 2) > kMaxBuildIdStringLength) {
continue;
}
const uint8_t* build_id_raw =
reinterpret_cast<const uint8_t*>(current_note) + sizeof(Nhdr) +
bits::AlignUp(current_note->n_namesz, static_cast<Word>(4));
size_t i = 0;
for (i = 0; i < current_note->n_descsz; ++i) {
strings::SafeSNPrintf(&build_id[i * 2], 3, (uppercase ? "%02X" : "%02x"),
build_id_raw[i]);
}
build_id[i * 2] = '\0';
return i * 2;
}
return 0;
}
std::optional<std::string_view> ReadElfLibraryName(
const void* elf_mapped_base) {
const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
if (!elf_header) {
return {};
}
const size_t relocation_offset = GetRelocationOffset(elf_mapped_base);
for (const Phdr& header : GetElfProgramHeaders(elf_mapped_base)) {
if (header.p_type != PT_DYNAMIC) {
continue;
}
const Dyn* dynamic_start =
reinterpret_cast<const Dyn*>(header.p_vaddr + relocation_offset);
const Dyn* dynamic_end = reinterpret_cast<const Dyn*>(
header.p_vaddr + relocation_offset + header.p_memsz);
Xword soname_strtab_offset = 0;
const char* strtab_addr = nullptr;
for (const Dyn* dynamic_iter = dynamic_start; dynamic_iter < dynamic_end;
++dynamic_iter) {
if (dynamic_iter->d_tag == DT_STRTAB) {
#if BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_ANDROID) || \
(defined(ARCH_CPU_RISCV_FAMILY) && BUILDFLAG(IS_LINUX))
strtab_addr = static_cast<size_t>(dynamic_iter->d_un.d_ptr) +
reinterpret_cast<const char*>(relocation_offset);
#else
strtab_addr = reinterpret_cast<const char*>(dynamic_iter->d_un.d_ptr);
#endif
} else if (dynamic_iter->d_tag == DT_SONAME) {
soname_strtab_offset = static_cast<Xword>(dynamic_iter->d_un.d_val);
}
}
if (soname_strtab_offset && strtab_addr) {
return std::string_view(strtab_addr + soname_strtab_offset);
}
}
return std::nullopt;
}
span<const Phdr> GetElfProgramHeaders(const void* elf_mapped_base) {
const Ehdr* elf_header = GetElfHeader(elf_mapped_base);
if (!elf_header) {
return {};
}
const char* phdr_start =
reinterpret_cast<const char*>(elf_header) + elf_header->e_phoff;
return span<const Phdr>(reinterpret_cast<const Phdr*>(phdr_start),
elf_header->e_phnum);
}
size_t GetRelocationOffset(const void* elf_mapped_base) {
span<const Phdr> headers = GetElfProgramHeaders(elf_mapped_base);
for (const Phdr& header : headers) {
if (header.p_type == PT_LOAD) {
const char* const mapped_address =
reinterpret_cast<const char*>(elf_mapped_base) + header.p_offset;
return reinterpret_cast<uintptr_t>(mapped_address) - header.p_vaddr;
}
}
return static_cast<size_t>(reinterpret_cast<uintptr_t>(elf_mapped_base) -
reinterpret_cast<uintptr_t>(nullptr));
}
}