| // Copyright (c) 2006, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| // |
| // file_id.cc: Return a unique identifier for a file |
| // |
| // See file_id.h for documentation |
| // |
| |
| #include "common/linux/file_id.h" |
| |
| #include <arpa/inet.h> |
| #include <assert.h> |
| #include <string.h> |
| |
| #include <algorithm> |
| #include <string> |
| |
| #include "common/linux/elf_gnu_compat.h" |
| #include "common/linux/elfutils.h" |
| #include "common/linux/linux_libc_support.h" |
| #include "common/linux/memory_mapped_file.h" |
| #include "common/using_std_string.h" |
| #include "third_party/lss/linux_syscall_support.h" |
| |
| namespace google_breakpad { |
| |
| // Used in a few places for backwards-compatibility. |
| const size_t kMDGUIDSize = sizeof(MDGUID); |
| |
| FileID::FileID(const char* path) : path_(path) {} |
| |
| // 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. |
| |
| static bool ElfClassBuildIDNoteIdentifier(const void *section, size_t length, |
| wasteful_vector<uint8_t>& identifier) { |
| static_assert(sizeof(ElfClass32::Nhdr) == sizeof(ElfClass64::Nhdr), |
| "Elf32_Nhdr and Elf64_Nhdr should be the same"); |
| typedef typename ElfClass32::Nhdr Nhdr; |
| |
| const void* section_end = reinterpret_cast<const char*>(section) + length; |
| const Nhdr* note_header = reinterpret_cast<const Nhdr*>(section); |
| 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 uint8_t* build_id = reinterpret_cast<const uint8_t*>(note_header) + |
| sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz); |
| identifier.insert(identifier.end(), |
| build_id, |
| build_id + note_header->n_descsz); |
| |
| return true; |
| } |
| |
| // Attempt to locate a .note.gnu.build-id section in an ELF binary |
| // and copy it into |identifier|. |
| static bool FindElfBuildIDNote(const void* elf_mapped_base, |
| wasteful_vector<uint8_t>& identifier) { |
| PageAllocator allocator; |
| // lld normally creates 2 PT_NOTEs, gold normally creates 1. |
| auto_wasteful_vector<ElfSegment, 2> segs(&allocator); |
| if (FindElfSegments(elf_mapped_base, PT_NOTE, &segs)) { |
| for (ElfSegment& seg : segs) { |
| if (ElfClassBuildIDNoteIdentifier(seg.start, seg.size, identifier)) { |
| return true; |
| } |
| } |
| } |
| |
| void* note_section; |
| size_t note_size; |
| if (FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE, |
| (const void**)¬e_section, ¬e_size)) { |
| return ElfClassBuildIDNoteIdentifier(note_section, note_size, 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, |
| wasteful_vector<uint8_t>& identifier) { |
| identifier.resize(kMDGUIDSize); |
| |
| void* text_section; |
| size_t text_size; |
| if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, |
| (const void**)&text_section, &text_size) || |
| text_size == 0) { |
| return false; |
| } |
| |
| // Only provide |kMDGUIDSize| bytes to keep identifiers produced by this |
| // function backwards-compatible. |
| my_memset(&identifier[0], 0, kMDGUIDSize); |
| const uint8_t* ptr = reinterpret_cast<const uint8_t*>(text_section); |
| const uint8_t* ptr_end = ptr + std::min(text_size, static_cast<size_t>(4096)); |
| while (ptr < ptr_end) { |
| for (unsigned i = 0; i < kMDGUIDSize; i++) |
| identifier[i] ^= ptr[i]; |
| ptr += kMDGUIDSize; |
| } |
| return true; |
| } |
| |
| // static |
| bool FileID::ElfFileIdentifierFromMappedFile(const void* base, |
| wasteful_vector<uint8_t>& identifier) { |
| // 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(wasteful_vector<uint8_t>& identifier) { |
| MemoryMappedFile mapped_file(path_.c_str(), 0); |
| if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)? |
| return false; |
| |
| return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); |
| } |
| |
| // These three functions are not ever called in an unsafe context, so it's OK |
| // to allocate memory and use libc. |
| static string bytes_to_hex_string(const uint8_t* bytes, size_t count) { |
| string result; |
| for (unsigned int idx = 0; idx < count; ++idx) { |
| char buf[3]; |
| snprintf(buf, sizeof(buf), "%02X", bytes[idx]); |
| result.append(buf); |
| } |
| return result; |
| } |
| |
| // static |
| string FileID::ConvertIdentifierToUUIDString( |
| const wasteful_vector<uint8_t>& identifier) { |
| uint8_t identifier_swapped[kMDGUIDSize] = { 0 }; |
| |
| // Endian-ness swap to match dump processor expectation. |
| memcpy(identifier_swapped, &identifier[0], |
| std::min(kMDGUIDSize, identifier.size())); |
| uint32_t* data1 = reinterpret_cast<uint32_t*>(identifier_swapped); |
| *data1 = htonl(*data1); |
| uint16_t* data2 = reinterpret_cast<uint16_t*>(identifier_swapped + 4); |
| *data2 = htons(*data2); |
| uint16_t* data3 = reinterpret_cast<uint16_t*>(identifier_swapped + 6); |
| *data3 = htons(*data3); |
| |
| return bytes_to_hex_string(identifier_swapped, kMDGUIDSize); |
| } |
| |
| // static |
| string FileID::ConvertIdentifierToString( |
| const wasteful_vector<uint8_t>& identifier) { |
| return bytes_to_hex_string(&identifier[0], identifier.size()); |
| } |
| |
| } // namespace google_breakpad |