/* Copyright (c) 2022, 2024, Oracle and/or its affiliates. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License, version 2.0, as published by the Free Software Foundation. This program is designed to work with certain software (including but not limited to OpenSSL) that is licensed under separate terms, as designated in a particular file or component or in included license documentation. The authors of MySQL hereby grant you an additional permission to link the program and your derivative works with the separately licensed software that they have either included with the program or referenced in the documentation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License, version 2.0, for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include #include #include #include #include #include /* Assuming we link with: -Wl,--build-id=sha1 we will get a build-id which is: "a 160-bit SHA1 hash on the normative parts of the output contents." This can be used to uniquely identify the executable. */ using elf_word = ElfW(Word); // Align to 4 bytes: constexpr elf_word NOTE_ALIGN(elf_word sz) { return (((sz) + 3) & ~3); } struct callback_data { Elf64_Word size{0}; const uint8_t *build_id{nullptr}; }; /* typedef struct { Elf32_Word n_namesz; Length of the note's name. Elf32_Word n_descsz; Length of the note's descriptor. Elf32_Word n_type; Type of the note. } Elf32_Nhdr; NT_GNU_BUILD_ID == 3 So we are looking for: {{4, 20, 3}, "GNU", <20 bytes build-id>} */ using elf_note_struct = ElfW(Nhdr); struct elf_note { elf_note_struct nhdr; char name[4]; }; // Callback for dl_iterate_phdr(). // A nonzero return here will terminate iteration. static int build_id_callback(dl_phdr_info *info, size_t, void *data_) { /* The first object visited by the callback is the main program. For the main program, the dlpi_name field will be an empty string. */ if (info->dlpi_name == nullptr || strcmp(info->dlpi_name, "") != 0) { assert(false); return 0; } callback_data *data = reinterpret_cast(data_); for (unsigned i = 0; i < info->dlpi_phnum; i++) { if (info->dlpi_phdr[i].p_type != PT_NOTE) continue; elf_note *note = reinterpret_cast( reinterpret_cast(info->dlpi_addr + info->dlpi_phdr[i].p_vaddr)); ptrdiff_t segment_size = info->dlpi_phdr[i].p_filesz; while (segment_size > 0 && segment_size >= static_cast(sizeof(elf_note))) { if (note->nhdr.n_type == NT_GNU_BUILD_ID && note->nhdr.n_descsz != 0 && note->nhdr.n_namesz == 4 && memcmp(note->name, "GNU", 4) == 0) { // build_id is right after the name. data->build_id = reinterpret_cast(note) + sizeof(elf_note); data->size = note->nhdr.n_descsz; return 1; } // Skip to the next note: size_t offset = sizeof(elf_note_struct) + NOTE_ALIGN(note->nhdr.n_namesz) + NOTE_ALIGN(note->nhdr.n_descsz); note = reinterpret_cast( reinterpret_cast(note) + offset); segment_size -= offset; } } return 0; } bool my_find_build_id(char *dst) { callback_data data; if (!dl_iterate_phdr(build_id_callback, &data)) return true; assert(data.size == 20); for (ElfW(Word) i = 0; i < data.size; i++) { sprintf(&dst[2 * i], "%02x", data.build_id[i]); } return false; }