diff options
Diffstat (limited to 'src/common/linux/file_id.cc')
-rw-r--r-- | src/common/linux/file_id.cc | 160 |
1 files changed, 122 insertions, 38 deletions
diff --git a/src/common/linux/file_id.cc b/src/common/linux/file_id.cc index 2227c83b..acd448a9 100644 --- a/src/common/linux/file_id.cc +++ b/src/common/linux/file_id.cc @@ -56,39 +56,46 @@ namespace google_breakpad { +#ifndef NT_GNU_BUILD_ID +#define NT_GNU_BUILD_ID 3 +#endif + FileID::FileID(const char* path) { strncpy(path_, path, sizeof(path_)); } struct ElfClass32 { typedef Elf32_Ehdr Ehdr; + typedef Elf32_Nhdr Nhdr; typedef Elf32_Shdr Shdr; static const int kClass = ELFCLASS32; }; struct ElfClass64 { typedef Elf64_Ehdr Ehdr; + typedef Elf64_Nhdr Nhdr; typedef Elf64_Shdr Shdr; static const int kClass = ELFCLASS64; }; -// These three functions are also used inside the crashed process, so be safe +// These six functions are also used inside the crashed process, so be safe // and use the syscall/libc wrappers instead of direct syscalls or libc. template<typename ElfClass> -static void FindElfClassTextSection(const char *elf_base, - const void **text_start, - int *text_size) { +static void FindElfClassSection(const char *elf_base, + const char *section_name, + uint32_t section_type, + const void **section_start, + int *section_size) { typedef typename ElfClass::Ehdr Ehdr; typedef typename ElfClass::Shdr Shdr; assert(elf_base); - assert(text_start); - assert(text_size); + assert(section_start); + assert(section_size); assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0); - const char* text_section_name = ".text"; - int name_len = my_strlen(text_section_name); + int name_len = my_strlen(section_name); const Ehdr* elf_header = reinterpret_cast<const Ehdr*>(elf_base); assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); @@ -97,30 +104,41 @@ static void FindElfClassTextSection(const char *elf_base, reinterpret_cast<const Shdr*>(elf_base + elf_header->e_shoff); const Shdr* string_section = sections + elf_header->e_shstrndx; - const Shdr* text_section = NULL; + const Shdr* section = NULL; for (int i = 0; i < elf_header->e_shnum; ++i) { - if (sections[i].sh_type == SHT_PROGBITS) { + if (sections[i].sh_type == section_type) { const char* section_name = (char*)(elf_base + string_section->sh_offset + sections[i].sh_name); - if (!my_strncmp(section_name, text_section_name, name_len)) { - text_section = §ions[i]; + if (!my_strncmp(section_name, section_name, name_len)) { + section = §ions[i]; break; } } } - if (text_section != NULL && text_section->sh_size > 0) { - *text_start = elf_base + text_section->sh_offset; - *text_size = text_section->sh_size; + if (section != NULL && section->sh_size > 0) { + *section_start = elf_base + section->sh_offset; + *section_size = section->sh_size; } } -static bool FindElfTextSection(const void *elf_mapped_base, - const void **text_start, - int *text_size) { +// Attempt to find a section named |section_name| of type |section_type| +// in the ELF binary data at |elf_mapped_base|. On success, returns true +// and sets |*section_start| to point to the start of the section data, +// and |*section_size| to the size of the section's data. If |elfclass| +// is not NULL, set |*elfclass| to the ELF file class. +static bool FindElfSection(const void *elf_mapped_base, + const char *section_name, + uint32_t section_type, + const void **section_start, + int *section_size, + int *elfclass) { assert(elf_mapped_base); - assert(text_start); - assert(text_size); + assert(section_start); + assert(section_size); + + *section_start = NULL; + *section_size = 0; const char* elf_base = static_cast<const char*>(elf_mapped_base); @@ -129,14 +147,89 @@ static bool FindElfTextSection(const void *elf_mapped_base, if (my_strncmp(elf_base, ELFMAG, SELFMAG) != 0) return false; + if (elfclass) { + *elfclass = elf_header->e_ident[EI_CLASS]; + } + if (elf_header->e_ident[EI_CLASS] == ELFCLASS32) { - FindElfClassTextSection<ElfClass32>(elf_base, text_start, text_size); + FindElfClassSection<ElfClass32>(elf_base, section_name, section_type, + section_start, section_size); + return *section_start != NULL; } else if (elf_header->e_ident[EI_CLASS] == ELFCLASS64) { - FindElfClassTextSection<ElfClass64>(elf_base, text_start, text_size); - } else { + FindElfClassSection<ElfClass64>(elf_base, section_name, section_type, + section_start, section_size); + return *section_start != NULL; + } + + return false; +} + +template<typename ElfClass> +static bool ElfClassBuildIDNoteIdentifier(const void *section, + uint8_t identifier[kMDGUIDSize]) +{ + typedef typename ElfClass::Nhdr Nhdr; + + const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section); + if (note_header->n_type != NT_GNU_BUILD_ID || + note_header->n_descsz == 0) { return false; } + const char* build_id = reinterpret_cast<const char*>(section) + + sizeof(Nhdr) + note_header->n_namesz; + // Copy as many bits of the build ID as will fit + // into the GUID space. + my_memset(identifier, 0, kMDGUIDSize); + memcpy(identifier, build_id, + std::min(kMDGUIDSize, (size_t)note_header->n_descsz)); + + return true; +} + +// Attempt to locate a .note.gnu.build-id section in an ELF binary +// and copy as many bytes of it as will fit into |identifier|. +static bool FindElfBuildIDNote(const void *elf_mapped_base, + uint8_t identifier[kMDGUIDSize]) +{ + void* note_section; + int note_size, elfclass; + if (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE, + (const void**)¬e_section, ¬e_size, &elfclass) || + note_size == 0) { + return false; + } + + if (elfclass == ELFCLASS32) { + return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, identifier); + } else if (elfclass == ELFCLASS64) { + return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, identifier); + } + + return false; +} + +// Attempt to locate the .text section of an ELF binary and generate +// a simple hash by XORing the first page worth of bytes into |identifier|. +static bool HashElfTextSection(const void *elf_mapped_base, + uint8_t identifier[kMDGUIDSize]) { + + void* text_section; + int text_size; + if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, + (const void**)&text_section, &text_size, NULL) || + text_size == 0) { + return false; + } + + my_memset(identifier, 0, kMDGUIDSize); + const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section); + const uint8_t* ptr_end = ptr + std::min(text_size, 4096); + while (ptr < ptr_end) { + for (unsigned i = 0; i < kMDGUIDSize; i++) + identifier[i] ^= ptr[i]; + ptr += kMDGUIDSize; + } return true; } @@ -144,21 +237,12 @@ static bool FindElfTextSection(const void *elf_mapped_base, bool FileID::ElfFileIdentifierFromMappedFile(void* base, uint8_t identifier[kMDGUIDSize]) { - const void* text_section = NULL; - int text_size = 0; - bool success = false; - if (FindElfTextSection(base, &text_section, &text_size) && (text_size > 0)) { - my_memset(identifier, 0, kMDGUIDSize); - const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section); - const uint8_t* ptr_end = ptr + std::min(text_size, 4096); - while (ptr < ptr_end) { - for (unsigned i = 0; i < kMDGUIDSize; i++) - identifier[i] ^= ptr[i]; - ptr += kMDGUIDSize; - } - success = true; - } - return success; + // Look for a build id note first. + if (FindElfBuildIDNote(base, identifier)) + return true; + + // Fall back on hashing the first page of the text section. + return HashElfTextSection(base, identifier); } bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) { |