122 lines
3.9 KiB
C++
122 lines
3.9 KiB
C++
/* 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 <assert.h>
|
|
#include <elf.h>
|
|
#include <link.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
/*
|
|
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<callback_data *>(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<elf_note *>(
|
|
reinterpret_cast<void *>(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<ptrdiff_t>(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<unsigned char *>(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<elf_note *>(
|
|
reinterpret_cast<unsigned char *>(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;
|
|
}
|