efi-boot-shim/buildid.c
2021-03-23 23:49:46 +00:00

198 lines
3.6 KiB
C

// SPDX-License-Identifier: BSD-2-Clause-Patent
/*
* Walk a list of input files, printing the name and buildid of any file
* that has one.
*/
#include <err.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <libelf.h>
#include <gelf.h>
static Elf_Scn *get_scn_named(Elf * elf, char *goal, GElf_Shdr * shdrp_out)
{
int rc;
size_t shstrndx = -1;
int scn_no = 0;
Elf_Scn *scn = NULL;
GElf_Shdr shdr_data, *shdrp;
shdrp = shdrp_out ? shdrp_out : &shdr_data;
rc = elf_getshdrstrndx(elf, &shstrndx);
if (rc < 0)
return NULL;
do {
GElf_Shdr *shdr;
char *name;
scn = elf_getscn(elf, ++scn_no);
if (!scn)
break;
shdr = gelf_getshdr(scn, shdrp);
if (!shdr)
/*
* the binary is malformed, but hey, maybe the next
* one is fine, why not...
*/
continue;
name = elf_strptr(elf, shstrndx, shdr->sh_name);
if (name && !strcmp(name, goal))
return scn;
} while (scn != NULL);
return NULL;
}
static void *get_buildid(Elf * elf, size_t * sz)
{
Elf_Scn *scn;
size_t notesz;
size_t offset = 0;
Elf_Data *data;
GElf_Shdr shdr;
scn = get_scn_named(elf, ".note.gnu.build-id", &shdr);
if (!scn)
return NULL;
data = elf_getdata(scn, NULL);
if (!data)
return NULL;
do {
size_t nameoff;
size_t descoff;
GElf_Nhdr nhdr;
char *name;
notesz = gelf_getnote(data, offset, &nhdr, &nameoff, &descoff);
if (!notesz)
break;
offset += notesz;
if (nhdr.n_type != NT_GNU_BUILD_ID)
continue;
name = data->d_buf + nameoff;
if (!name || strcmp(name, ELF_NOTE_GNU))
continue;
*sz = nhdr.n_descsz;
return data->d_buf + descoff;
} while (notesz);
return NULL;
}
static void data2hex(uint8_t * data, size_t ds, char *str)
{
const char hex[] = "0123456789abcdef";
int s;
unsigned int d;
for (d = 0, s = 0; d < ds; d += 1, s += 2) {
str[s + 0] = hex[(data[d] >> 4) & 0x0f];
str[s + 1] = hex[(data[d] >> 0) & 0x0f];
}
str[s] = '\0';
}
static void handle_one(char *f)
{
int fd;
Elf *elf;
char *b = NULL;
size_t sz;
uint8_t *data;
ssize_t written;
if (!strcmp(f, "-")) {
fd = STDIN_FILENO;
if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
errx(1, "Couldn't read ELF data from \"%s\"", f);
} else {
if ((fd = open(f, O_RDONLY)) < 0)
err(1, "Couldn't open \"%s\"", f);
if ((elf = elf_begin(fd, ELF_C_READ_MMAP, NULL)) == NULL)
errx(1, "Couldn't read ELF data from \"%s\"", f);
}
data = get_buildid(elf, &sz);
if (data) {
b = alloca(sz * 2 + 1);
data2hex(data, sz, b);
if (b) {
written = write(1, f, strlen(f));
if (written < 0)
errx(1, "Error writing build id");
written = write(1, " ", 1);
written = write(1, b, strlen(b));
if (written < 0)
errx(1, "Error writing build id");
written = write(1, "\n", 1);
}
}
elf_end(elf);
close(fd);
}
static void
__attribute__ ((__noreturn__))
usage(int status)
{
FILE *out = status ? stderr : stdout;
fprintf(out, "Usage: buildid [ flags | file0 [file1 [.. fileN]]]\n");
fprintf(out, "Flags:\n");
fprintf(out, " -h Print this help text and exit\n");
exit(status);
}
int main(int argc, char **argv)
{
int i;
struct option options[] = {
{.name = "help",
.val = '?',
},
{.name = "usage",
.val = '?',
},
{.name = ""}
};
int longindex = -1;
while ((i = getopt_long(argc, argv, "h", options, &longindex)) != -1) {
switch (i) {
case 'h':
case '?':
usage(longindex == -1 ? 1 : 0);
break;
}
}
elf_version(EV_CURRENT);
if (optind == argc)
usage(1);
for (i = optind; i < argc; i++)
handle_one(argv[i]);
return 0;
}
// vim:fenc=utf-8:tw=75