| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "components/metrics/leak_detector/gnu_build_id_reader.h" |
| 6 |
| 7 #include <elf.h> |
| 8 |
| 9 #include <algorithm> |
| 10 |
| 11 #if defined(OS_CHROMEOS) |
| 12 #include <link.h> // for dl_iterate_phdr |
| 13 #else |
| 14 #error "Getting binary mapping info is not supported on this platform." |
| 15 #endif // defined(OS_CHROMEOS) |
| 16 |
| 17 namespace metrics { |
| 18 namespace leak_detector { |
| 19 namespace gnu_build_id_reader { |
| 20 |
| 21 namespace { |
| 22 |
| 23 // Contains data passed to dl_iterate_phdr() for reading build ID. |
| 24 struct ReadBuildIDData { |
| 25 // Points to a vector for storing the build ID. |
| 26 std::vector<uint8_t>* build_id; |
| 27 // Indicates whether build ID was read successfully. |
| 28 bool success; |
| 29 }; |
| 30 |
| 31 // Given a pointer and an offset, add the offset to the pointer and round it up |
| 32 // to the next uint32_t. |
| 33 const void* AlignPtrAndOffsetToUint32(const void* ptr, intptr_t offset) { |
| 34 uintptr_t addr = reinterpret_cast<uintptr_t>(ptr) + offset; |
| 35 uintptr_t aligned_addr = |
| 36 (addr + sizeof(uint32_t) - 1) & ~(sizeof(uint32_t) - 1); |
| 37 return reinterpret_cast<const void*>(aligned_addr); |
| 38 } |
| 39 |
| 40 // Searches for the ELF note containing the build ID within the data range |
| 41 // specified by [start, end). Returns the build ID in |*result|. If the build ID |
| 42 // is not found, |*result| will be unchanged. Returns true if the build ID was |
| 43 // successfully read or false otherwise. |
| 44 bool GetBuildIdFromNotes(const void* start, |
| 45 const void* end, |
| 46 std::vector<uint8_t>* result) { |
| 47 using NoteHeaderPtr = const ElfW(Nhdr)*; |
| 48 NoteHeaderPtr note = reinterpret_cast<NoteHeaderPtr>(start); |
| 49 |
| 50 while (note < end) { |
| 51 const char* name_ptr = reinterpret_cast<const char*>(note + 1); |
| 52 if (name_ptr >= end) { |
| 53 break; |
| 54 } |
| 55 // |desc_ptr| points the to the actual build ID data. |
| 56 const uint8_t* desc_ptr = reinterpret_cast<const uint8_t*>( |
| 57 AlignPtrAndOffsetToUint32(name_ptr, note->n_namesz)); |
| 58 if (note->n_type == NT_GNU_BUILD_ID && |
| 59 note->n_namesz == sizeof(ELF_NOTE_GNU) && |
| 60 std::equal(name_ptr, name_ptr + sizeof(ELF_NOTE_GNU), ELF_NOTE_GNU)) { |
| 61 result->assign(desc_ptr, desc_ptr + note->n_descsz); |
| 62 return true; |
| 63 } |
| 64 NoteHeaderPtr next_ptr = reinterpret_cast<NoteHeaderPtr>( |
| 65 AlignPtrAndOffsetToUint32(desc_ptr, note->n_descsz)); |
| 66 note = next_ptr; |
| 67 } |
| 68 return false; |
| 69 } |
| 70 |
| 71 // Callback for dl_iterate_phdr(). Finds the notes section and looks for the |
| 72 // build ID in there. |data| points to a ReadBuildIDData struct whose |build_id| |
| 73 // field should point to a valid std::vector<uint8_t>. Returns the build ID in |
| 74 // that field, and sets |ReadBuildIDData::success| based on whether the build ID |
| 75 // was successfully read. |
| 76 // |
| 77 // This function always returns 1 to signal the end of dl_iterate_phdr()'s |
| 78 // iteration, because it is only interested in the first binary iterated by |
| 79 // dl_iterate_phdr(), which is the current binary. |
| 80 int FindNotesAndGetBuildID(struct dl_phdr_info* info, |
| 81 size_t /* size */, |
| 82 void* data) { |
| 83 uintptr_t mapping_addr = reinterpret_cast<uintptr_t>(info->dlpi_addr); |
| 84 const ElfW(Ehdr)* file_header = |
| 85 reinterpret_cast<const ElfW(Ehdr)*>(mapping_addr); |
| 86 |
| 87 // Make sure that a valid |mapping_addr| was read. |
| 88 if (!file_header || file_header->e_phentsize != sizeof(ElfW(Phdr))) { |
| 89 return 1; |
| 90 } |
| 91 |
| 92 // Find the ELF segment header for the NOTES section. |
| 93 for (int i = 0; i < info->dlpi_phnum; ++i) { |
| 94 const ElfW(Phdr)& segment_header = info->dlpi_phdr[i]; |
| 95 if (segment_header.p_type == PT_NOTE) { |
| 96 const void* note = reinterpret_cast<const void*>( |
| 97 mapping_addr + segment_header.p_vaddr); |
| 98 const void* note_end = reinterpret_cast<const void*>( |
| 99 mapping_addr + segment_header.p_vaddr + segment_header.p_memsz); |
| 100 ReadBuildIDData* read_data = reinterpret_cast<ReadBuildIDData*>(data); |
| 101 read_data->success = |
| 102 GetBuildIdFromNotes(note, note_end, read_data->build_id); |
| 103 } |
| 104 } |
| 105 return 1; |
| 106 } |
| 107 |
| 108 } // namespace |
| 109 |
| 110 bool ReadBuildID(std::vector<uint8_t>* build_id) { |
| 111 ReadBuildIDData data; |
| 112 data.build_id = build_id; |
| 113 data.success = false; |
| 114 |
| 115 #if defined(OS_CHROMEOS) |
| 116 dl_iterate_phdr(FindNotesAndGetBuildID, &data); |
| 117 #endif // defined(OS_CHROMEOS) |
| 118 |
| 119 return data.success; |
| 120 } |
| 121 |
| 122 } // namespace gnu_build_id_reader |
| 123 } // namespace leak_detector |
| 124 } // namespace metrics |
| OLD | NEW |