From 7882cf63e859a23f79bb34b5d37670501da4ade7 Mon Sep 17 00:00:00 2001 From: Pedro Falcato Date: Wed, 22 Nov 2023 18:30:18 +0000 Subject: [PATCH] Add static library support hold can now open archives (smartly!) and link against them. Also fix a few bugs found in the process. Signed-off-by: Pedro Falcato --- include/elf/ar.h | 32 ++++ include/elf/elf.h | 44 +++++- src/elf/archive.c | 305 ++++++++++++++++++++++++++++++++++++++ src/elf/link.c | 256 ++++++++++++++++++++++++-------- src/elf/meson.build | 3 +- src/elf/output_sections.c | 53 ++++--- src/elf/relocs.c | 11 +- src/elf/writer.c | 23 ++- src/link.c | 8 +- 9 files changed, 634 insertions(+), 101 deletions(-) create mode 100644 include/elf/ar.h create mode 100644 src/elf/archive.c diff --git a/include/elf/ar.h b/include/elf/ar.h new file mode 100644 index 0000000..b8af049 --- /dev/null +++ b/include/elf/ar.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2023 Pedro Falcato */ +#ifndef ELF_AR_H +#define ELF_AR_H + +#include + +#define AR_MAGIC "!\n" +#define ARFMAG "`\n" + +struct ar_hdr { + char ar_name[16]; + char ar_date[12]; + char ar_uid[6]; + char ar_gid[6]; + char ar_mode[8]; + char ar_size[10]; + char ar_fmag[2]; +}; + +static inline int is_ar_archive(const void *buf) +{ + return !memcmp(buf, AR_MAGIC, strlen(AR_MAGIC)); +} + +struct input_file; +struct symbol; + +int parse_ar(struct input_file *file, const void *mapping, unsigned long filesz); +int resolve_lazy(struct symbol *sym); + +#endif diff --git a/include/elf/elf.h b/include/elf/elf.h index b4aee34..aa567e5 100644 --- a/include/elf/elf.h +++ b/include/elf/elf.h @@ -37,7 +37,13 @@ struct symbol { * has not been an overridden symbol. */ u8 nofree_name : 1; - struct input_section *section; + + /* For lazy symbols, this holds the position in the ar archive */ + u32 ar_pos; + union { + struct input_section *section; + struct input_file *file; + }; struct symbol *next; }; @@ -81,9 +87,18 @@ struct input_section { muptr output_off; }; +struct ar_archive_data; + +struct input_file_ops { + int (*open)(struct input_file *file); + void (*read)(struct input_file *file, void *buf, uptr size, uptr offset); + void (*close)(struct input_file *file); +}; + struct input_file { const char *name; u16 emachine; + u32 free_name : 1; struct input_section *sections; u32 nsections; @@ -91,6 +106,21 @@ struct input_file { u32 nsyms; struct relocation *relocs; u32 nrelocs; + + struct ar_archive_data *ardata; + + struct { + u8 *long_file_names; + } archive; + + union { + int fd; + struct { + uptr archive_off; + }; + } openf; + + const struct input_file_ops *ops; }; typedef Elf64_Ehdr elf_ehdr; @@ -102,13 +132,13 @@ typedef Elf64_Phdr elf_phdr; int process_rela(struct input_file *file, elf_shdr *section, u8 *mapping); struct symbol *lookup_symtable(const char *name); -struct output_section *elf_merge_sections(struct input_file **files, u32 nfiles, +struct output_section **elf_merge_sections(struct input_file **files, u32 nfiles, u32 *p_noutput); struct program_header; struct elf_writer { - struct output_section *out_section; + struct output_section **out_section; u32 nr_output_secs; struct hold_options *options; struct input_file **files; @@ -126,4 +156,12 @@ void relocate_symbols(void); void elf_do_relocs(struct input_file *file, struct relocation *relocs, u32 nrelocs, u8 *mapping); +int add_to_symtable(struct symbol *sym); + +/* If set, we should free this filename (as it was malloc'd) */ +#define ELF_PROCESS_FILENAME_FREE (1 << 0) + +int elf_process_objfile(const char *filename, void *map, uptr fd_size, + const struct input_file_ops *ops, int flags); + #endif diff --git a/src/elf/archive.c b/src/elf/archive.c new file mode 100644 index 0000000..89bab78 --- /dev/null +++ b/src/elf/archive.c @@ -0,0 +1,305 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2023 Pedro Falcato */ +#include +#include +#include +#include + +#include + +#include +#include + +static +int parse_ar_symtab(struct input_file *file, const struct ar_hdr *hdr, u64 size) +{ + const char *string_data; + u32 *data, syms, i; + /* Look at the symbol table and add lazy symbols + * Note that the symbol table is always the first entry. + */ + + /* The symbol table is defined by: + * [Number of entries in the table (big endian)] + * [ N entries (big endian) for the position within the ar ] + * [ N zero-terminated strings (symbol names) in the same order as above ] + */ + data = (u32 *) (hdr + 1); + syms = be32toh(*data); + string_data = (const char *) (data + syms + 1); + verbose("%s: Found %u symbols\n", file->name, syms); + file->syms = calloc(syms, sizeof(struct symbol)); + if (!file->syms) + return -1; + file->nsyms = syms; + + for (i = 0; i < syms; i++) { + struct symbol *sym = &file->syms[i]; + const char *name; + u32 pos; + uptr namelen; + + pos = be32toh(data[i + 1]); + name = string_data; + namelen = strlen(name); + string_data += namelen + 1; + + sym->name = strdup(name); + if (!sym->name) + return -1; + sym->name_hash = fnv_hash(sym->name, namelen); + sym->symtype = SYM_TYPE_LAZY; + sym->ar_pos = pos; + sym->file = file; + + if (add_to_symtable(sym) < 0) + return -1; + } + + return 0; +} + +int parse_ar(struct input_file *file, const void *mapping, unsigned long filesz) +{ + const struct ar_hdr *hdr = mapping + strlen(AR_MAGIC); + const u8 *end = mapping + filesz; + int seen_symtab = 0, seen_lfn = 0; + + if (!!memcmp(hdr->ar_fmag, ARFMAG, 2)) { + warn("%s looked like an AR archive, but it isn't!", file->name); + return -1; + } + + while ((u8 *) hdr < end) { + u64 size; + + if (seen_symtab && seen_lfn) { + /* We're only here for the symtab (first entry) and + * the LFN entry (probably the second entry). Anything + * else is irrelevant to us atm. + */ + break; + } + sscanf(hdr->ar_size, "%10lu", &size); + + if (hdr->ar_name[0] == '/') { + /* Check what kind of special entry this is */ + switch(hdr->ar_name[1]) { + case ' ': { + /* symbol table */ + if (parse_ar_symtab(file, hdr, size) < 0) + return 1; + seen_symtab++; + break; + } + + case '/': { + /* Long filename entry */ + file->archive.long_file_names = (u8 *) (hdr + 1); + seen_lfn++; + break; + } + } + } + + if (size & 1) + size++; + hdr = (struct ar_hdr *) ((u8 *) (hdr + 1) + size); + } + + + file->ardata = (struct ar_archive_data *) mapping; + + return 0; +} + +static +int archive_obj_open(struct input_file *file) +{ + return 0; +} + +static +void archive_obj_read(struct input_file *file, void *buf, uptr size, uptr offset) +{ + u8 *mapping = (u8 *) file->ardata; + memcpy(buf, mapping + offset, size); +} + +static +void archive_obj_close(struct input_file *file) +{ + /* nop */ +} + +const struct input_file_ops ar_objfile_ops = +{ + .open = archive_obj_open, + .read = archive_obj_read, + .close = archive_obj_close +}; + +static +char *ar_get_name(struct input_file *file, struct ar_hdr *hdr) +{ + int i; + char buf[sizeof(hdr->ar_name) + 1]; + + if (hdr->ar_name[0] == '/') { + /* Long filename (this is 100% not going to be a symbol table + * or LFN entry). + */ + char *lfn_start, *lfn_end, *str; + uptr offset; + if (sscanf(hdr->ar_name + 1, "%15lu", &offset) != 1) { + warn("ar_get_name: sscanf"); + return NULL; + } + + lfn_start = (char *) file->archive.long_file_names + offset; + lfn_end = strchr(lfn_start, '/'); + str = calloc(lfn_end - lfn_start + 1, 1); + if (!str) + return NULL; + memcpy(str, lfn_start, lfn_end - lfn_start); + return str; + } + + for (i = 0; i < sizeof(hdr->ar_name); i++) { + char c; + + c = hdr->ar_name[i]; + + if (c == '/') { + buf[i] = 0; + break; + } + + buf[i] = c; + } + + return strdup(buf); +} + +struct deferred_sym { + struct symbol *s; + struct deferred_sym *next_defer; +}; + +static +struct deferred_sym *defer_head, *defer_tail; + +static int in_resolve_lazy = 0; + +static +int defer_sym(struct symbol *sym) +{ + /* Because resolve_lazy can pull in extra object files, we may get + * stuck in boundless stack recursion. To avoid that, defer symbols + * in a list that we'll look at after resolve_lazy is complete. + */ + struct deferred_sym *df; + + df = malloc(sizeof(*df)); + if (!df) + return -1; + df->s = sym; + df->next_defer = NULL; + + if (!defer_head) + defer_head = df; + else + defer_tail->next_defer = df; + defer_tail = df; + return 0; +} + +static +int resolve_lazy_internal(struct symbol *sym) +{ + u8 *mapping = (u8 *) sym->file->ardata; + struct ar_hdr *hdr = (struct ar_hdr *) (mapping + sym->ar_pos); + char buf[sizeof(hdr->ar_size) + 1]; + char *filename; + uptr size; + printf("%s\n", sym->file->name); + + memcpy(buf, hdr->ar_size, sizeof(hdr->ar_size)); + buf[sizeof(hdr->ar_size)] = 0; + + if (sscanf(buf, "%lu", &size) != 1) { + warn("sscanf"); + return -1; + } + + filename = ar_get_name(sym->file, hdr); + if (!filename) + return -1; + + int st = elf_process_objfile(filename, hdr + 1, size, &ar_objfile_ops, + ELF_PROCESS_FILENAME_FREE); + + if (st < 0) { + free(filename); + return -1; + } + + return st; +} + +static +int undefer_syms(void) +{ + while (defer_head) { + /* We need to be careful to avoid list corruption. Lets splice + * the list every time we check it is not empty. + */ + struct deferred_sym *head, *next; + + head = defer_head; + defer_head = defer_tail = NULL; + + while (head != NULL) { + struct symbol *sym = head->s; + next = head->next_defer; + free(head); + + if (sym->symtype != SYM_TYPE_LAZY) { + /* Something unlazied it in the meanwhile, just + * skip. + */ + head = next; + continue; + } + + if (resolve_lazy_internal(sym) < 0) { + in_resolve_lazy = 0; + return -1; + } + + head = next; + } + } + + in_resolve_lazy = 0; + return 0; +} + +int resolve_lazy(struct symbol *sym) +{ + int st; + assert(sym->symtype == SYM_TYPE_LAZY); + + if (in_resolve_lazy) + return defer_sym(sym); + + in_resolve_lazy = 1; + st = resolve_lazy_internal(sym); + + if (st < 0) { + in_resolve_lazy = 0; + return st; + } + + return undefer_syms(); +} diff --git a/src/elf/link.c b/src/elf/link.c index fb366b9..d9930b4 100644 --- a/src/elf/link.c +++ b/src/elf/link.c @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -38,6 +39,100 @@ const char *elf_get_str(u32 str, const char *strs) } static +void replace_sym(struct symbol *dst, struct symbol *src) +{ + src->next = dst->next; + free((void *) dst->name); + memcpy(dst, src, sizeof(*src)); + src->nofree_name = 1; + +} + +static +int resolve_sym_conflict(struct symbol *s, struct symbol *sym) +{ + /* Rules for symbol "overlaying": + * UNDEFINED -> DEFINED is allowed (we have just defined a symbol, yay!) + * DEFINED weak -> DEFINED strong is allowed + * DEFINED -> DEFINED is an error + * UNDEFINED -> UNDEFINED is a nop + * DEFINED -> UNDEFINED is a nop + * LAZY -> LAZY is a nop (second symbol is ignored, see below for details on static archives) + * DEFINED -> LAZY is a nop + * UNDEFINED -> LAZY and LAZY -> UNDEFINED prompt the archive + * file to be loaded + */ + switch (s->symtype) { + case SYM_TYPE_DEFINED: { + if (sym->symtype == SYM_TYPE_DEFINED) { + if (!(s->weak && !sym->weak) && !(!s->weak && sym->weak)) { + warnx("double definition error for symbol %s", sym->name); + return -1; + } + + if (!sym->weak) { + /* ok, previous was weak, new is strong, replace. */ + replace_sym(s, sym); + } + + /* fallthrough to return 0 */ + } + + return 0; + } + + case SYM_TYPE_UNDEFINED: { + if (sym->symtype == SYM_TYPE_DEFINED) { + replace_sym(s, sym); + return 0; + } + + if (sym->symtype == SYM_TYPE_LAZY) + return resolve_lazy(sym); + return 0; + } + + case SYM_TYPE_LAZY: { + /* Notes on static archive symbol resolution: + * Traditionally, static library symbol resolution is + * very simple. Imagining an invocation such as + * "ld a.o b.o libc.a c.o", libc.a will resolve a.o + * and b.o's undefined references, but not c.o's. + * When looking up a symbol, we only look at the first + * entry in that archive's symbol table; it doesn't + * matter whether the entry is weak or strong or whatnot. + * We take a slightly different strategy: lld does not + * do this, and "ld libc.a a.o b.o c.o" will resolve a, + * b and c's undefined references. This allows for + * greater parallelism when linking. + * + * Note: a common trick in static libraries such as + * libc.a is to stub out certain functions with weak + * binding, in order to avoid pulling in more object + * files. If those individual features are then pulled + * (through, say, a reference to a symbol), it will pull + * the entire object file, replacing the previous weak + * symbol with a strong one. + * + * It's always worth keeping in mind that a single + * symbol reference pulls an entire object file. + */ + if (sym->symtype == SYM_TYPE_LAZY) { + return 0; + } + + if (sym->symtype == SYM_TYPE_UNDEFINED) + return resolve_lazy(s); + + replace_sym(s, sym); + return 0; + } + + default: + abort(); + } +} + int add_to_symtable(struct symbol *sym) { /* Add @sym to the symbol table. If we overlay this symbol on top of another, @@ -57,55 +152,19 @@ int add_to_symtable(struct symbol *sym) struct symbol *s = *sp; /* Check for name collisions */ if (s->name_hash == sym->name_hash && !strcmp(s->name, sym->name)) { - /* Rules for symbol "overlaying": - * UNDEFINED -> DEFINED is allowed (we have just defined a symbol, yay!) - * DEFINED weak -> DEFINED strong is allowed - * DEFINED -> DEFINED is an error - * UNDEFINED -> UNDEFINED is a nop - * DEFINED -> UNDEFINED is a nop + if (resolve_sym_conflict(s, sym) < 0) + return -1; + /* We'll never append after having a conflict. Either + * resolve_sym_conflict overwrote it with memcpy, or + * we got an error. No other option. */ - switch (s->symtype) { - case SYM_TYPE_DEFINED: { - if (sym->symtype == SYM_TYPE_DEFINED) { - if (!(s->weak && !sym->weak) && !(!s->weak && sym->weak)) { - warnx("double definition error for symbol %s", sym->name); - return -1; - } - - if (!sym->weak) { - /* ok, previous was weak, new is strong, replace. */ - sym->next = s->next; - free((void *) s->name); - memcpy(s, sym, sizeof(*s)); - s->nofree_name = 1; - } - - goto out_noappend; - } - - goto out_noappend; - } - - case SYM_TYPE_UNDEFINED: { - if (sym->symtype == SYM_TYPE_DEFINED) { - sym->next = s->next; - free((void *) s->name); - memcpy(s, sym, sizeof(*s)); - s->nofree_name = 1; - goto out_noappend; - } - } - - default: - abort(); - } + return 0; } sp = &(s->next); } *sp = sym; -out_noappend: return 0; } @@ -130,8 +189,8 @@ void relocate_sym(struct symbol *s) struct input_section *inp; struct output_section *os; - /* Skip ABS */ - if (s->abs) + /* Skip ABS, lazy symbols and weak undefined */ + if (s->abs || s->symtype == SYM_TYPE_LAZY || (s->weak && s->symtype == SYM_TYPE_UNDEFINED)) return; inp = s->section; @@ -161,6 +220,7 @@ void relocate_symbols(void) for (i = 0; i < nfiles; i++) { struct input_file *inp = files[i]; + verbose("Relocating %s\n", inp->name); for (j = 0; j < inp->nsyms; j++) { struct symbol *s = &inp->syms[j]; @@ -181,8 +241,16 @@ int check_for_unresolved_syms(void) while (s) { if (s->symtype == SYM_TYPE_UNDEFINED) { - warnx("Undefined symbol %s", s->name); - st = -1; + if (s->weak) { + /* A weak undefined reference is + * allowed. In this case, we set value + * to 0. + */ + s->value = 0; + } else { + warnx("Undefined symbol %s", s->name); + st = -1; + } } s = s->next; @@ -275,6 +343,37 @@ uptr get_fd_size(int fd) return buf.st_size; } +static +int default_file_open(struct input_file *file) +{ + file->openf.fd = open(file->name, O_RDONLY); + if (file->openf.fd < 0) { + err(1, "%s", file->name); + } + + return 0; +} + +static +void default_file_read(struct input_file *file, void *buf, uptr size, uptr offset) +{ + if (pread(file->openf.fd, buf, size, offset) < 0) { + err(1, "default_file_read: pread"); + } +} + +static void default_file_close(struct input_file *file) +{ + close(file->openf.fd); +} + +const struct input_file_ops default_ops = +{ + .open = default_file_open, + .read = default_file_read, + .close = default_file_close +}; + static struct input_file *create_input_file(const char *filename) { @@ -290,6 +389,9 @@ struct input_file *create_input_file(const char *filename) return NULL; } + file->openf.fd = -1; + file->ops = &default_ops; + files[nfiles++] = file; return file; } @@ -368,10 +470,12 @@ int parse_elf_sections(struct input_file *f, uptr shoff, u32 shnum, u32 shentsiz } static -int parse_elf(struct input_file *f, u8 *mapping) +int parse_elf(struct input_file *f, u8 *mapping, uptr filesz) { elf_ehdr *header = (elf_ehdr *) mapping; if (!!memcmp(header->e_ident, "\x7f""ELF", 4)) { + if (is_ar_archive(mapping)) + return parse_ar(f, mapping, filesz); warnx("%s is not an ELF file!", f->name); return -1; } @@ -394,35 +498,64 @@ int parse_elf(struct input_file *f, u8 *mapping) return 0; } +int elf_process_objfile(const char *filename, void *map, uptr fd_size, + const struct input_file_ops *ops, int flags) +{ + struct input_file *f = create_input_file(filename); + if (!f) { + warn("%s: Failed to create input_file", filename); + return -1; + } + + f->ops = ops; + + if (ops != &default_ops) { + /* HACK! I hate this, but since we only have 3 types of + * input files atm (object/archive file and .o coming from a .a) + * this solution Just Works. + */ + f->ardata = map; + } + + if (flags & ELF_PROCESS_FILENAME_FREE) + f->free_name = 1; + + return parse_elf(f, map, fd_size); +} + static int elf_process_input(const char *filename) { + uptr fd_size; + int fd = open(filename, O_RDONLY); if (fd < 0) { warn("%s: Error opening", filename); return -1; } - void *map = mmap(NULL, get_fd_size(fd), PROT_READ, MAP_SHARED, fd, 0); + fd_size = get_fd_size(fd); + + void *map = mmap(NULL, fd_size, PROT_READ, MAP_SHARED, fd, 0); if (map == MAP_FAILED) { warn("%s: Error mmapping input file", filename); return -1; } - struct input_file *f = create_input_file(filename); - if (!f) { - warn("%s: Failed to create input_file", filename); - return -1; - } - - int st = parse_elf(f, map); + int st = elf_process_objfile(filename, map, fd_size, &default_ops, 0); + if (!is_ar_archive(map)) { + /* HACK! Since ar archives want permanent mappings, this works. + * But should eventually be refactored as something else. + */ #define DEBUG_MMAP 1 #ifdef DEBUG_MMAP - /* Catch erroneous accesses to the file mappings using PROT_NONE */ - mprotect(map, get_fd_size(fd), PROT_NONE); + /* Catch erroneous accesses to the file mappings using PROT_NONE */ + mprotect(map, fd_size, PROT_NONE); #else - munmap(map, get_fd_size(fd)); + munmap(map, get_fd_size(fd)); #endif + } + close(fd); return st; } @@ -432,7 +565,7 @@ static void destroy_symbol_table(void); int elf_do_link(struct hold_options *options) { - struct output_section *out_sec; + struct output_section **out_sec; u32 nr_output_sec; int i; @@ -491,7 +624,12 @@ void destroy_file(struct input_file *inp) if (!s->nofree_name) free((void *) s->name); } + free(inp->syms); + + if (inp->free_name) + free((void *) inp->name); + free(inp); } diff --git a/src/elf/meson.build b/src/elf/meson.build index 46926a3..468f9c5 100644 --- a/src/elf/meson.build +++ b/src/elf/meson.build @@ -3,7 +3,8 @@ elf_sources = [ 'elf/relocs.c', 'elf/output_sections.c', 'elf/writer.c', - 'elf/x86_64.c' + 'elf/x86_64.c', + 'elf/archive.c' ] # TODO: This is not working like I want it to. diff --git a/src/elf/output_sections.c b/src/elf/output_sections.c index fdabacc..c47bf0a 100644 --- a/src/elf/output_sections.c +++ b/src/elf/output_sections.c @@ -13,36 +13,44 @@ #include static -int try_merge(struct output_section *out, u32 nr_output, struct input_section *inp) +int try_merge(struct output_section **out, u32 nr_output, struct input_section *inp) { u32 i; + char *dot; + struct output_section *s; /* TODO: In reality, this is probably not so simple... */ - for (i = 0; i < nr_output; i++, out++) { - if (!strcmp(out->name, inp->name)) + for (i = 0; i < nr_output; i++) { + s = out[i]; + + if (!strcmp(s->name, inp->name)) + goto merge; + dot = strchr(inp->name + 1, '.'); + + if (!strncmp(s->name, inp->name, dot - inp->name)) goto merge; } return -1; merge: verbose("Merging input section %s(%s) into output section %s\n", inp->name, - inp->file->name, out->name); - out->max_alignment = out->max_alignment > inp->sh_addralign ? out->max_alignment : inp->sh_addralign; - assert(out->isection_head != NULL); - assert(out->isection_tail != NULL); + inp->file->name, s->name); + s->max_alignment = s->max_alignment > inp->sh_addralign ? s->max_alignment : inp->sh_addralign; + assert(s->isection_head != NULL); + assert(s->isection_tail != NULL); - struct input_section *tail = out->isection_tail; + struct input_section *tail = s->isection_tail; if (inp->sh_addralign > 1) { /* Align size so we can insert the new section at tail * Note that sh_addralign is always a power of 2. */ - out->size = (out->size + inp->sh_addralign - 1) & -inp->sh_addralign; + s->size = (s->size + inp->sh_addralign - 1) & -inp->sh_addralign; } - out->size += inp->sh_size; + s->size += inp->sh_size; tail->next_outputsec = inp; - out->isection_tail = inp; - inp->out = out; + s->isection_tail = inp; + inp->out = s; return 0; } @@ -63,10 +71,11 @@ int should_ignore_section(struct input_section *inp) } } -struct output_section *elf_merge_sections(struct input_file **files, u32 nfiles, +struct output_section **elf_merge_sections(struct input_file **files, u32 nfiles, u32 *p_noutput) { - struct output_section *out = NULL; + char *dotp; + struct output_section **out = NULL, *sec; struct input_section *inp = NULL; u32 nr_output = 0; u32 capacity = 0; @@ -100,21 +109,31 @@ struct output_section *elf_merge_sections(struct input_file **files, u32 nfiles, create_out: if (nr_output + 1 > capacity) { capacity = capacity == 0 ? 8 : capacity << 1; - out = reallocarray(out, capacity, sizeof(struct output_section)); + out = reallocarray(out, capacity, sizeof(struct output_section *)); if (!out) { warn("elf_merge_sections: reallocarray"); return NULL; } } - struct output_section *sec = &out[nr_output++]; - memset(sec, 0, sizeof(*sec)); + sec = calloc(1, sizeof(struct output_section)); + if (!sec) { + warn("elf_merge_sections: calloc"); + return NULL; + } + + out[nr_output++] = sec; + sec->name = strdup(inp->name); if (!sec->name) { warn("strdup"); return NULL; } + /* Stop at the first . */ + if ((dotp = strchr(sec->name + 1, '.'))) + *dotp = '\0'; + verbose("Creating output section %s with input section %s(%s)\n", sec->name, inp->name, inp->file->name); /* Replace the 1-as-0 encoding here, to avoid further pain */ diff --git a/src/elf/relocs.c b/src/elf/relocs.c index 22f95e8..b1a86ae 100644 --- a/src/elf/relocs.c +++ b/src/elf/relocs.c @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -15,16 +16,18 @@ int process_rela(struct input_file *file, elf_shdr *section, u8 *mapping) u32 target_section = section->sh_info - 1; elf_rela *rela = (elf_rela *) (mapping + section->sh_offset); uptr nr_relocs = section->sh_size / section->sh_entsize; - uptr i; + uptr i = file->nrelocs; - file->nrelocs = nr_relocs; - file->relocs = calloc(file->nrelocs, sizeof(struct relocation)); + file->nrelocs += nr_relocs; + file->relocs = reallocarray(file->relocs, file->nrelocs, sizeof(struct relocation)); if (!file->relocs) { warn("calloc"); return -1; } - for (i = 0; i < nr_relocs; i++, rela = (elf_rela *)((u8 *) rela + section->sh_entsize)) { + memset(file->relocs + i, 0, sizeof(struct relocation) * nr_relocs); + + for (; i < file->nrelocs; i++, rela = (elf_rela *)((u8 *) rela + section->sh_entsize)) { struct relocation *reloc = &file->relocs[i]; u32 target_sym_idx; diff --git a/src/elf/writer.c b/src/elf/writer.c index 2c59a10..80a7872 100644 --- a/src/elf/writer.c +++ b/src/elf/writer.c @@ -107,7 +107,7 @@ int gather_sections_for_phdr(struct program_header *phdr, int seen, struct elf_w return -1; for (i = 0; i < writer->nr_output_secs; i++) { - struct output_section *sec = &writer->out_section[i]; + struct output_section *sec = writer->out_section[i]; /* Infer flags and permissions from the first input section * associated with this output section. */ @@ -215,6 +215,7 @@ void assign_output_section_addr_off(struct program_header *phdr, u32 to_skip) assert(size >= total); size -= total; addr += total; + base_offset += total; assign_input_sections_off(os); @@ -264,7 +265,7 @@ int create_program_headers(struct elf_writer *writer) muptr base_address = DEFAULT_BASE_ADDRESS; for (i = 0; i < writer->nr_output_secs; i++) { - struct output_section *sec = &writer->out_section[i]; + struct output_section *sec = writer->out_section[i]; /* Infer flags and permissions from the first input section * associated with this output section. */ @@ -388,36 +389,31 @@ void write_phdrs(struct elf_writer *writer, u8 *mapping) } static -void write_section(int fd, struct input_section *inp, u8 *mapping) +void write_section(struct input_file *file, struct input_section *inp, u8 *mapping) { if (inp->sh_type == SHT_NOBITS) return; struct output_section *out = inp->out; verbose("Copying [%lu, %lu] to %lu\n", inp->sh_offset, inp->sh_offset + inp->sh_size - 1, out->offset + inp->output_off); - if (pread(fd, mapping + out->offset + inp->output_off, inp->sh_size, inp->sh_offset) < 0) { - err(1, "write_section: pread"); - } + file->ops->read(file, mapping + out->offset + inp->output_off, inp->sh_size, inp->sh_offset); } static void input_write_segs(struct input_file *file, u8 *mapping) { - int fd; u32 i; - fd = open(file->name, O_RDONLY); - if (fd < 0) - err(1, "%s", file->name); + file->ops->open(file); for (i = 0; i < file->nsections; i++) { struct input_section *inp = &file->sections[i]; /* TODO: This is kind of random */ if (inp->out && inp->out->offset) - write_section(fd, inp, mapping); + write_section(file, inp, mapping); } - close(fd); + file->ops->close(file); } static @@ -497,8 +493,9 @@ void elf_writer_destroy(struct elf_writer *writer) u32 i; for (i = 0; i < writer->nr_output_secs; i++) { - struct output_section *sec = &writer->out_section[i]; + struct output_section *sec = writer->out_section[i]; free((void *) sec->name); + free(sec); } free(writer->out_section); diff --git a/src/link.c b/src/link.c index 78ab66f..8e2426f 100644 --- a/src/link.c +++ b/src/link.c @@ -13,7 +13,7 @@ int elf_do_link(struct hold_options *opts); static -int is_not_elf(const char *filename) +int is_not_elfish(const char *filename) { int fd = open(filename, O_RDONLY); if (fd < 0) { @@ -21,7 +21,7 @@ int is_not_elf(const char *filename) return 2; } - char magic[4]; + char magic[8]; if (read(fd, magic, sizeof(magic)) < sizeof(magic)) { warn("%s: Failed to read magic", filename); close(fd); @@ -30,12 +30,12 @@ int is_not_elf(const char *filename) close(fd); - return !!memcmp(magic, "\x7f""ELF", 4); + return !!memcmp(magic, "\x7f""ELF", 4) && !!memcmp(magic, "!\n", 8); } int hold_do_link(struct hold_options *opts) { - if (is_not_elf(opts->input_files[0])) { + if (is_not_elfish(opts->input_files[0])) { warnx("%s: unsupported backend", opts->input_files[0]); return 1; }