michael@0: // Copyright (c) 2006, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: // michael@0: // file_id.cc: Return a unique identifier for a file michael@0: // michael@0: // See file_id.h for documentation michael@0: // michael@0: michael@0: #include "common/linux/file_id.h" michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: michael@0: #include "common/linux/elfutils.h" michael@0: #include "common/linux/linux_libc_support.h" michael@0: #include "common/linux/memory_mapped_file.h" michael@0: #include "third_party/lss/linux_syscall_support.h" michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: #ifndef NT_GNU_BUILD_ID michael@0: #define NT_GNU_BUILD_ID 3 michael@0: #endif michael@0: michael@0: FileID::FileID(const char* path) { michael@0: strncpy(path_, path, sizeof(path_)); michael@0: } michael@0: michael@0: // ELF note name and desc are 32-bits word padded. michael@0: #define NOTE_PADDING(a) ((a + 3) & ~3) michael@0: michael@0: // These functions are also used inside the crashed process, so be safe michael@0: // and use the syscall/libc wrappers instead of direct syscalls or libc. michael@0: michael@0: template michael@0: static bool ElfClassBuildIDNoteIdentifier(const void *section, int length, michael@0: uint8_t identifier[kMDGUIDSize]) { michael@0: typedef typename ElfClass::Nhdr Nhdr; michael@0: michael@0: const void* section_end = reinterpret_cast(section) + length; michael@0: const Nhdr* note_header = reinterpret_cast(section); michael@0: while (reinterpret_cast(note_header) < section_end) { michael@0: if (note_header->n_type == NT_GNU_BUILD_ID) michael@0: break; michael@0: note_header = reinterpret_cast( michael@0: reinterpret_cast(note_header) + sizeof(Nhdr) + michael@0: NOTE_PADDING(note_header->n_namesz) + michael@0: NOTE_PADDING(note_header->n_descsz)); michael@0: } michael@0: if (reinterpret_cast(note_header) >= section_end || michael@0: note_header->n_descsz == 0) { michael@0: return false; michael@0: } michael@0: michael@0: const char* build_id = reinterpret_cast(note_header) + michael@0: sizeof(Nhdr) + NOTE_PADDING(note_header->n_namesz); michael@0: // Copy as many bits of the build ID as will fit michael@0: // into the GUID space. michael@0: my_memset(identifier, 0, kMDGUIDSize); michael@0: memcpy(identifier, build_id, michael@0: std::min(kMDGUIDSize, (size_t)note_header->n_descsz)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Attempt to locate a .note.gnu.build-id section in an ELF binary michael@0: // and copy as many bytes of it as will fit into |identifier|. michael@0: static bool FindElfBuildIDNote(const void *elf_mapped_base, michael@0: uint8_t identifier[kMDGUIDSize]) { michael@0: void* note_section; michael@0: int note_size, elfclass; michael@0: if ((!FindElfSegment(elf_mapped_base, PT_NOTE, michael@0: (const void**)¬e_section, ¬e_size, &elfclass) || michael@0: note_size == 0) && michael@0: (!FindElfSection(elf_mapped_base, ".note.gnu.build-id", SHT_NOTE, michael@0: (const void**)¬e_section, ¬e_size, &elfclass) || michael@0: note_size == 0)) { michael@0: return false; michael@0: } michael@0: michael@0: if (elfclass == ELFCLASS32) { michael@0: return ElfClassBuildIDNoteIdentifier(note_section, note_size, michael@0: identifier); michael@0: } else if (elfclass == ELFCLASS64) { michael@0: return ElfClassBuildIDNoteIdentifier(note_section, note_size, michael@0: identifier); michael@0: } michael@0: michael@0: return false; michael@0: } michael@0: michael@0: // Attempt to locate the .text section of an ELF binary and generate michael@0: // a simple hash by XORing the first page worth of bytes into |identifier|. michael@0: static bool HashElfTextSection(const void *elf_mapped_base, michael@0: uint8_t identifier[kMDGUIDSize]) { michael@0: void* text_section; michael@0: int text_size; michael@0: if (!FindElfSection(elf_mapped_base, ".text", SHT_PROGBITS, michael@0: (const void**)&text_section, &text_size, NULL) || michael@0: text_size == 0) { michael@0: return false; michael@0: } michael@0: michael@0: my_memset(identifier, 0, kMDGUIDSize); michael@0: const uint8_t* ptr = reinterpret_cast(text_section); michael@0: const uint8_t* ptr_end = ptr + std::min(text_size, 4096); michael@0: while (ptr < ptr_end) { michael@0: for (unsigned i = 0; i < kMDGUIDSize; i++) michael@0: identifier[i] ^= ptr[i]; michael@0: ptr += kMDGUIDSize; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: // static michael@0: bool FileID::ElfFileIdentifierFromMappedFile(const void* base, michael@0: uint8_t identifier[kMDGUIDSize]) { michael@0: // Look for a build id note first. michael@0: if (FindElfBuildIDNote(base, identifier)) michael@0: return true; michael@0: michael@0: // Fall back on hashing the first page of the text section. michael@0: return HashElfTextSection(base, identifier); michael@0: } michael@0: michael@0: bool FileID::ElfFileIdentifier(uint8_t identifier[kMDGUIDSize]) { michael@0: MemoryMappedFile mapped_file(path_); michael@0: if (!mapped_file.data()) // Should probably check if size >= ElfW(Ehdr)? michael@0: return false; michael@0: michael@0: return ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); michael@0: } michael@0: michael@0: // static michael@0: void FileID::ConvertIdentifierToString(const uint8_t identifier[kMDGUIDSize], michael@0: char* buffer, int buffer_length) { michael@0: uint8_t identifier_swapped[kMDGUIDSize]; michael@0: michael@0: // Endian-ness swap to match dump processor expectation. michael@0: memcpy(identifier_swapped, identifier, kMDGUIDSize); michael@0: uint32_t* data1 = reinterpret_cast(identifier_swapped); michael@0: *data1 = htonl(*data1); michael@0: uint16_t* data2 = reinterpret_cast(identifier_swapped + 4); michael@0: *data2 = htons(*data2); michael@0: uint16_t* data3 = reinterpret_cast(identifier_swapped + 6); michael@0: *data3 = htons(*data3); michael@0: michael@0: int buffer_idx = 0; michael@0: for (unsigned int idx = 0; michael@0: (buffer_idx < buffer_length) && (idx < kMDGUIDSize); michael@0: ++idx) { michael@0: int hi = (identifier_swapped[idx] >> 4) & 0x0F; michael@0: int lo = (identifier_swapped[idx]) & 0x0F; michael@0: michael@0: if (idx == 4 || idx == 6 || idx == 8 || idx == 10) michael@0: buffer[buffer_idx++] = '-'; michael@0: michael@0: buffer[buffer_idx++] = (hi >= 10) ? 'A' + hi - 10 : '0' + hi; michael@0: buffer[buffer_idx++] = (lo >= 10) ? 'A' + lo - 10 : '0' + lo; michael@0: } michael@0: michael@0: // NULL terminate michael@0: buffer[(buffer_idx < buffer_length) ? buffer_idx : buffer_idx - 1] = 0; michael@0: } michael@0: michael@0: } // namespace google_breakpad