aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2013-04-04 16:24:52 +0000
committerted.mielczarek@gmail.com <ted.mielczarek@gmail.com@4c0a9323-5329-0410-9bdc-e9ce6186880e>2013-04-04 16:24:52 +0000
commitb6e66b294fe5d851ffbb57c1527acccf2f53a4b5 (patch)
treed9393dd09c9dff808df5053667fd6a7f20d69226
parentSupport generic Elf notes, with unit tests (diff)
downloadbreakpad-b6e66b294fe5d851ffbb57c1527acccf2f53a4b5.tar.xz
Try to find a build-id through PT_NOTE program headers
A=Mike Hommey <mh@glandium.org> R=ted at https://breakpad.appspot.com/544003/ git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1143 4c0a9323-5329-0410-9bdc-e9ce6186880e
-rw-r--r--src/common/linux/elfutils.cc65
-rw-r--r--src/common/linux/elfutils.h11
-rw-r--r--src/common/linux/file_id.cc37
-rw-r--r--src/common/linux/file_id_unittest.cc35
4 files changed, 138 insertions, 10 deletions
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<typename ElfClass>
+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<const Ehdr*>(elf_base);
+ assert(elf_header->e_ident[EI_CLASS] == ElfClass::kClass);
+
+ const Phdr* phdrs =
+ GetOffset<ElfClass,Phdr>(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<const char*>(elf_mapped_base);
+
+ if (cls == ELFCLASS32) {
+ FindElfClassSegment<ElfClass32>(elf_base, segment_type,
+ segment_start, segment_size);
+ return *segment_start != NULL;
+ } else if (cls == ELFCLASS64) {
+ FindElfClassSegment<ElfClass64>(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<typename ElfClass>
-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<const char*>(section) + length;
const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section);
- if (note_header->n_type != NT_GNU_BUILD_ID ||
+ while (reinterpret_cast<const void *>(note_header) < section_end) {
+ if (note_header->n_type == NT_GNU_BUILD_ID)
+ break;
+ note_header = reinterpret_cast<const Nhdr*>(
+ reinterpret_cast<const char*>(note_header) + sizeof(Nhdr) +
+ NOTE_PADDING(note_header->n_namesz) +
+ NOTE_PADDING(note_header->n_descsz));
+ }
+ if (reinterpret_cast<const void *>(note_header) >= section_end ||
note_header->n_descsz == 0) {
return false;
}
- const char* build_id = reinterpret_cast<const char*>(section) +
- sizeof(Nhdr) + note_header->n_namesz;
+ const char* build_id = reinterpret_cast<const char*>(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**)&note_section, &note_size, &elfclass) ||
- note_size == 0) {
+ if ((!FindElfSegment(elf_mapped_base, PT_NOTE,
+ (const void**)&note_section, &note_size, &elfclass) ||
+ note_size == 0) &&
+ (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE,
+ (const void**)&note_section, &note_size, &elfclass) ||
+ note_size == 0)) {
return false;
}
if (elfclass == ELFCLASS32) {
- return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, identifier);
+ return ElfClassBuildIDNoteIdentifier<ElfClass32>(note_section, note_size,
+ identifier);
} else if (elfclass == ELFCLASS64) {
- return ElfClassBuildIDNoteIdentifier<ElfClass64>(note_section, identifier);
+ return ElfClassBuildIDNoteIdentifier<ElfClass64>(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<const uint8_t *>("\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) {