Skip to content

Commit

Permalink
initrd-put: Split code for greater maintainability
Browse files Browse the repository at this point in the history
Signed-off-by: Alexey Gladkov <[email protected]>
  • Loading branch information
legionus committed Jul 12, 2024
1 parent e2b8a7d commit 1513d9e
Show file tree
Hide file tree
Showing 12 changed files with 593 additions and 396 deletions.
9 changes: 8 additions & 1 deletion utils/initrd-put/Makefile.mk
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@

ifeq ($(HAVE_LIBELF),yes)
initrd_put_DEST = $(dest_bindir)/initrd-put
initrd_put_SRCS = $(utils_srcdir)/initrd-put/initrd-put.c
initrd_put_SRCS = \
$(utils_srcdir)/initrd-put/memory.c \
$(utils_srcdir)/initrd-put/queue.c \
$(utils_srcdir)/initrd-put/tree.c \
$(utils_srcdir)/initrd-put/enqueue-library.c \
$(utils_srcdir)/initrd-put/enqueue-shebang.c \
$(utils_srcdir)/initrd-put/initrd-put.c

initrd_put_LIBS = $(HAVE_LIBELF_LIBS) $(HAVE_LIBJSON_C_LIBS)
initrd_put_CFLAGS = $(HAVE_LIBELF_CFLAGS) $(HAVE_LIBJSON_C_CFLAGS) \
-I$(utils_srcdir)/initrd-put \
Expand Down
5 changes: 2 additions & 3 deletions utils/initrd-put/elf_dlopen.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <json-c/json_visit.h>

#include "config.h"
#include "memory.h"
#include "elf_dlopen.h"

extern int verbose;
Expand Down Expand Up @@ -217,9 +218,7 @@ static int visit_userfunc(json_object *jso, int flags, json_object *,
in_envvar("IGNORE_PUT_DLOPEN_PRIORITY", elf_metadata.priority))
return JSON_C_VISIT_RETURN_SKIP;

soname = calloc(array_len, sizeof(char *));
if (!soname)
err(EXIT_FAILURE, "calloc");
soname = xcalloc(array_len, sizeof(char *));

for (i = 0; i < array_len; i++) {
json_object *child = json_object_array_get_idx(value, i);
Expand Down
216 changes: 216 additions & 0 deletions utils/initrd-put/enqueue-library.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#define _GNU_SOURCE

#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <limits.h>
#include <ctype.h>
#include <err.h>

#include <gelf.h>

#include "queue.h"
#include "tree.h"
#include "enqueue.h"
#include "elf_dlopen.h"

#define _FDO_ELF_METADATA 0x407c0c0a

extern int verbose;

static bool is_dynamic_elf_file(const char *filename, int fd) __attribute__((__nonnull__ (1)));
static int enqueue_elf_dlopen(const char *filename, int fd) __attribute__((__nonnull__ (1)));
static int enqueue_shared_libraries(const char *filename) __attribute__((__nonnull__ (1)));


bool is_dynamic_elf_file(const char *filename, int fd)
{
bool is_dynamic = false;
Elf *e;
Elf_Scn *scn;
size_t shstrndx;

if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
warnx("%s: elf_begin: %s", filename, elf_errmsg(-1));
goto err;
}

switch (elf_kind(e)) {
case ELF_K_NONE:
case ELF_K_AR:
case ELF_K_COFF:
case ELF_K_ELF:
break;
default:
goto end;
}

if (elf_getshdrstrndx(e, &shstrndx) != 0) {
warnx("%s: elf_getshdrstrndx: %s", filename, elf_errmsg(-1));
goto end;
}

for (scn = NULL; (scn = elf_nextscn(e, scn)) != NULL;) {
GElf_Shdr shdr;

if (gelf_getshdr(scn, &shdr) != &shdr) {
warnx("%s: gelf_getshdr: %s", filename, elf_errmsg(-1));
goto end;
}

if (shdr.sh_type == SHT_DYNAMIC) {
is_dynamic = true;
break;
}
}
end:
elf_end(e);
err:
return is_dynamic;
}

int enqueue_elf_dlopen(const char *filename, int fd)
{
int ret = 0;
char library[PATH_MAX + 1];
Elf *e;
Elf_Scn *scn;

if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
warnx("%s: elf_begin: %s", filename, elf_errmsg(-1));
goto err;
}

switch (elf_kind(e)) {
case ELF_K_NONE:
case ELF_K_AR:
case ELF_K_COFF:
case ELF_K_ELF:
break;
default:
goto end;
}

for (scn = NULL; (scn = elf_nextscn(e, scn)) != NULL;) {
GElf_Shdr shdr;
GElf_Nhdr nhdr;
size_t off, next, n_off, d_off;
Elf_Data *data;

if (gelf_getshdr(scn, &shdr) != &shdr) {
warnx("%s: gelf_getshdr: %s", filename, elf_errmsg(-1));
goto end;
}

if (shdr.sh_type != SHT_NOTE)
continue;

data = elf_getdata(scn, NULL);
if (data == NULL) {
warnx("%s: elf_getdata: %s", filename, elf_errmsg(-1));
goto end;
}

off = 0;
while ((next = gelf_getnote(data, off, &nhdr, &n_off, &d_off)) > 0) {
if (nhdr.n_type == _FDO_ELF_METADATA && nhdr.n_namesz == sizeof(ELF_NOTE_FDO) && !strcmp(data->d_buf + n_off, ELF_NOTE_FDO)) {
library[0] = '\0';

process_json_metadata(filename, (char *)data->d_buf + d_off, library);

if (library[0] == '/' && !is_path_added(library))
enqueue_item(library, -1);
}
off = next;
}
}
end:
elf_end(e);
err:
return ret;
}

int enqueue_shared_libraries(const char *filename)
{
FILE *pfd;
char *line = NULL;
size_t len = 0;
ssize_t n;
char command[PATH_MAX + 10];

snprintf(command, sizeof(command), "ldd %s 2>&1", filename);

pfd = popen(command, "r");
if (!pfd) {
warn("popen(ldd): %s", filename);
return -1;
}

while ((n = getline(&line, &len, pfd)) != -1) {
char *p;

if (line[n - 1] == '\n')
line[n - 1] = '\0';

p = strstr(line, "(0x");
if (!p)
continue;
*p-- = '\0';

while (line != p && isspace(*p))
*p-- = '\0';

p = strstr(line, " => ");
if (p)
p += 4;
else
p = line;

while (*p != '\0' && isspace(*p))
*p++ = '\0';

if (*p != '/')
continue;

if (verbose > 1)
warnx("shared object '%s' depends on '%s'", filename, p);

if (is_path_added(p))
continue;

enqueue_item(p, -1);
}

free(line);
pclose(pfd);

return 0;
}

void init_elf_library(void)
{
if (elf_version(EV_CURRENT) == EV_NONE)
errx(EXIT_FAILURE, "ELF library initialization failed: %s", elf_errmsg(-1));
}

bool is_elf_file(char buf[LINE_MAX])
{
return (buf[0] == ELFMAG[0] &&
buf[1] == ELFMAG[1] &&
buf[2] == ELFMAG[2] &&
buf[3] == ELFMAG[3]);
}

int enqueue_libraries(const char *filename, int fd)
{
int ret = 0;

if (is_dynamic_elf_file(filename, fd)) {
ret = enqueue_shared_libraries(filename);
if (!ret)
ret = enqueue_elf_dlopen(filename, fd);
}
return ret;
}
36 changes: 36 additions & 0 deletions utils/initrd-put/enqueue-shebang.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#define _GNU_SOURCE

#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
#include <err.h>

#include "tree.h"
#include "queue.h"
#include "enqueue.h"

extern int verbose;

bool is_shebang(char buf[LINE_MAX])
{
return (buf[0] == '#' &&
buf[1] == '!');
}

int enqueue_shebang(const char *filename, char buf[LINE_MAX])
{
char *p, *q;

for (p = &buf[2]; *p && isspace(*p); p++);
for (q = p; *q && (!isspace(*q)); q++);
*q = '\0';

if (verbose > 1)
warnx("shell script '%s' uses the '%s' interpreter", filename, p);

if (!is_path_added(p))
enqueue_item(p, -1);

return 0;
}
16 changes: 16 additions & 0 deletions utils/initrd-put/enqueue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: GPL-3.0-or-later
#ifndef __INITRD_PUT_ENQUEUE_H__
#define __INITRD_PUT_ENQUEUE_H__

#include <stdbool.h>
#include <limits.h>

bool is_shebang(char buf[LINE_MAX]);
int enqueue_shebang(const char *filename, char buf[LINE_MAX]);

void init_elf_library(void);
bool is_elf_file(char buf[LINE_MAX]);
int enqueue_libraries(const char *filename, int fd);

#endif // __INITRD_PUT_ENQUEUE_H__

Loading

0 comments on commit 1513d9e

Please sign in to comment.