diff --git a/src/common/linux/elfutils.cc b/src/common/linux/elfutils.cc index ee2f4ac0..1fd504d9 100644 --- a/src/common/linux/elfutils.cc +++ b/src/common/linux/elfutils.cc @@ -75,6 +75,35 @@ void FindElfClassSection(const char *elf_base, } } +template +void FindElfClassSegment(const char *elf_base, + typename ElfClass::Word segment_type, + const void **segment_start, + int *segment_size) { + typedef typename ElfClass::Ehdr Ehdr; + typedef typename ElfClass::Phdr Phdr; + + assert(elf_base); + assert(segment_start); + assert(segment_size); + + assert(my_strncmp(elf_base, ELFMAG, SELFMAG) == 0); + + const Ehdr* elf_header = reinterpret_cast(elf_base); + assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass); + + const Phdr* phdrs = + GetOffset(elf_header, elf_header->e_phoff); + + for (int i = 0; i < elf_header->e_phnum; ++i) { + if (phdrs[i].p_type == segment_type) { + *segment_start = elf_base + phdrs[i].p_offset; + *segment_size = phdrs[i].p_filesz; + return; + } + } +} + } // namespace bool IsValidElf(const void* elf_base) { @@ -126,4 +155,40 @@ bool FindElfSection(const void *elf_mapped_base, return false; } +bool FindElfSegment(const void *elf_mapped_base, + uint32_t segment_type, + const void **segment_start, + int *segment_size, + int *elfclass) { + assert(elf_mapped_base); + assert(segment_start); + assert(segment_size); + + *segment_start = NULL; + *segment_size = 0; + + if (!IsValidElf(elf_mapped_base)) + return false; + + int cls = ElfClass(elf_mapped_base); + if (elfclass) { + *elfclass = cls; + } + + const char* elf_base = + static_cast(elf_mapped_base); + + if (cls == ELFCLASS32) { + FindElfClassSegment(elf_base, segment_type, + segment_start, segment_size); + return *segment_start != NULL; + } else if (cls == ELFCLASS64) { + FindElfClassSegment(elf_base, segment_type, + segment_start, segment_size); + return *segment_start != NULL; + } + + return false; +} + } // namespace google_breakpad diff --git a/src/common/linux/elfutils.h b/src/common/linux/elfutils.h index 748da988..fe12e251 100644 --- a/src/common/linux/elfutils.h +++ b/src/common/linux/elfutils.h @@ -93,6 +93,17 @@ FindElfSectionByName(const char* name, const char* names_end, int nsection); +// Attempt to find the first segment of type |segment_type| in the ELF +// binary data at |elf_mapped_base|. On success, returns true and sets +// |*segment_start| to point to the start of the segment data, and +// and |*segment_size| to the size of the segment's data. If |elfclass| +// is not NULL, set |*elfclass| to the ELF file class. +bool FindElfSegment(const void *elf_mapped_base, + uint32_t segment_type, + const void **segment_start, + int *segment_size, + int *elfclass); + // Convert an offset from an Elf header into a pointer to the mapped // address in the current process. Takes an extra template parameter // to specify the return type to avoid having to dynamic_cast the diff --git a/src/common/linux/file_id.cc b/src/common/linux/file_id.cc index 4e380b06..ca5a3e1f 100644 --- a/src/common/linux/file_id.cc +++ b/src/common/linux/file_id.cc @@ -55,22 +55,34 @@ FileID::FileID(const char* path) { strncpy(path_, path, sizeof(path_)); } -// These six functions are also used inside the crashed process, so be safe +// ELF note name and desc are 32-bits word padded. +#define NOTE_PADDING(a) ((a + 3) & ~3) + +// These functions are also used inside the crashed process, so be safe // and use the syscall/libc wrappers instead of direct syscalls or libc. template -static bool ElfClassBuildIDNoteIdentifier(const void *section, +static bool ElfClassBuildIDNoteIdentifier(const void *section, int length, uint8_t identifier[kMDGUIDSize]) { typedef typename ElfClass::Nhdr Nhdr; + const void* section_end = reinterpret_cast(section) + length; const Nhdr* note_header = reinterpret_cast(section); - if (note_header->n_type != NT_GNU_BUILD_ID || + while (reinterpret_cast(note_header) < section_end) { + if (note_header->n_type == NT_GNU_BUILD_ID) + break; + note_header = reinterpret_cast( + reinterpret_cast(note_header) + sizeof(Nhdr) + + NOTE_PADDING(note_header->n_namesz) + + NOTE_PADDING(note_header->n_descsz)); + } + if (reinterpret_cast(note_header) >= section_end || note_header->n_descsz == 0) { return false; } - const char* build_id = reinterpret_cast(section) + - sizeof(Nhdr) + note_header->n_namesz; + const char* build_id = reinterpret_cast(note_header) + + sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz); // Copy as many bits of the build ID as will fit // into the GUID space. my_memset(identifier, 0, kMDGUIDSize); @@ -86,16 +98,21 @@ 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) { + if ((!FindElfSegment(elf_mapped_base, PT_NOTE, + (const void**)¬e_section, ¬e_size, &elfclass) || + note_size == 0) && + (!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(note_section, identifier); + return ElfClassBuildIDNoteIdentifier(note_section, note_size, + identifier); } else if (elfclass == ELFCLASS64) { - return ElfClassBuildIDNoteIdentifier(note_section, identifier); + return ElfClassBuildIDNoteIdentifier(note_section, note_size, + identifier); } return false; diff --git a/src/common/linux/file_id_unittest.cc b/src/common/linux/file_id_unittest.cc index e2e13747..bcd030dc 100644 --- a/src/common/linux/file_id_unittest.cc +++ b/src/common/linux/file_id_unittest.cc @@ -175,6 +175,41 @@ TYPED_TEST(FileIDTest, BuildID) { EXPECT_STREQ(expected_identifier_string, identifier_string); } +TYPED_TEST(FileIDTest, BuildIDPH) { + const uint8_t kExpectedIdentifier[sizeof(MDGUID)] = + {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F}; + char expected_identifier_string[] = + "00000000-0000-0000-0000-000000000000"; + FileID::ConvertIdentifierToString(kExpectedIdentifier, + expected_identifier_string, + sizeof(expected_identifier_string)); + + uint8_t identifier[sizeof(MDGUID)]; + char identifier_string[sizeof(expected_identifier_string)]; + + ELF elf(EM_386, TypeParam::kClass, kLittleEndian); + Section text(kLittleEndian); + text.Append(4096, 0); + elf.AddSection(".text", text, SHT_PROGBITS); + Notes notes(kLittleEndian); + notes.AddNote(0, "Linux", + reinterpret_cast("\0x42\0x02\0\0"), 4); + notes.AddNote(NT_GNU_BUILD_ID, "GNU", kExpectedIdentifier, + sizeof(kExpectedIdentifier)); + int note_idx = elf.AddSection(".note", notes, SHT_NOTE); + elf.AddSegment(note_idx, note_idx, PT_NOTE); + elf.Finish(); + this->GetElfContents(elf); + + EXPECT_TRUE(FileID::ElfFileIdentifierFromMappedFile(this->elfdata, + identifier)); + + FileID::ConvertIdentifierToString(identifier, identifier_string, + sizeof(identifier_string)); + EXPECT_STREQ(expected_identifier_string, identifier_string); +} + // Test to make sure two files with different text sections produce // different hashes when not using a build id. TYPED_TEST(FileIDTest, UniqueHashes) {