summaryrefslogtreecommitdiff
path: root/mld
diff options
context:
space:
mode:
Diffstat (limited to 'mld')
-rw-r--r--mld/link.c652
-rw-r--r--mld/link.h117
-rw-r--r--mld/obj.c8
-rw-r--r--mld/seg.c23
-rw-r--r--mld/segtab.c147
-rw-r--r--mld/strtab.c56
-rw-r--r--mld/symtab.c60
7 files changed, 1051 insertions, 12 deletions
diff --git a/mld/link.c b/mld/link.c
index 2593806..de703c2 100644
--- a/mld/link.c
+++ b/mld/link.c
@@ -1,9 +1,17 @@
+#include <elf.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
#include <stdlib.h>
#include <merror.h>
#include <string.h>
#include <sys/stat.h>
+#include <melf.h>
#include "link.h"
+#include "mips.h"
+
+#define SEC_ALIGN 0x1000
static int load_objects(struct linker *linker)
{
@@ -12,7 +20,7 @@ static int load_objects(struct linker *linker)
linker->obj_len = 0;
if (linker->objects == NULL) {
- ERROR("cannot alloc");
+ PERROR("cannot alloc");
return M_ERROR;
}
@@ -40,6 +48,627 @@ skip_obj:
return M_SUCCESS;
}
+/**
+ * Relocates all segments with the given name
+ * (since they need to be next to eachother)
+ */
+static int relocate_segment_name(struct linker *linker, const char *name)
+{
+ for (size_t i = 0; i < linker->obj_len; i++) {
+ struct object *obj = &linker->objects[i];
+ for (size_t j = 0; j < obj->segment_len; j++) {
+ struct segment *seg = &obj->segments[j];
+
+ // check if the segment has already been relocated
+ if (seg->new_vaddr != 0)
+ continue;
+
+ // make sure the segments name matches what
+ // we are looking for
+ if (strcmp(seg->name, name) != 0)
+ continue;
+
+ if (ADDR_CHK(linker->off, seg->size, UINT32_MAX)) {
+ ERROR("linker offset overflow");
+ return M_ERROR;
+ }
+
+ // is the segment a TEXT type or DATA type??
+ if (B32(seg->phdr->p_flags) & PF_X) {
+ // TEXT
+ if (ADDR_CHK(linker->text_vaddr, seg->size,
+ DATA_VADDR_MIN)) {
+ ERROR("linker text vaddr overflow");
+ return M_ERROR;
+ }
+
+ seg->new_off = linker->off;
+ seg->new_vaddr = linker->text_vaddr;
+ linker->off += seg->size;
+ linker->text_vaddr += seg->size;
+ } else {
+ // DATA
+ if (ADDR_CHK(linker->data_vaddr, seg->size,
+ UINT32_MAX)) {
+ ERROR("linker data vaddr overflow");
+ return M_ERROR;
+ }
+
+ seg->new_off = linker->off;
+ seg->new_vaddr = linker->data_vaddr;
+ linker->off += seg->size;
+ linker->data_vaddr += seg->size;
+ }
+
+ // if this is an existing segment, append this
+ // part
+ struct segment_table_entry *ent;
+ if (segtab_get(&linker->segments, &ent, name) ==
+ M_SUCCESS) {
+ if (segtab_ent_push(ent, seg))
+ return M_ERROR;
+ } else {
+ // else create a new segment
+ if (segtab_push(&linker->segments, NULL, seg))
+ return M_ERROR;
+ }
+ }
+ }
+
+ return M_SUCCESS;
+}
+
+static int relocate_segments(struct linker *linker)
+{
+ for (size_t i = 0; i < linker->obj_len; i++) {
+ struct object *obj = &linker->objects[i];
+ for (size_t j = 0; j < obj->segment_len; j++) {
+ struct segment *seg = &obj->segments[j];
+
+ // check if the segment has already been relocated
+ if (seg->new_vaddr != 0)
+ continue;
+ if(relocate_segment_name(linker, seg->name))
+ return M_ERROR;
+ }
+ }
+
+ return M_SUCCESS;
+}
+
+static int relocate_symbol(struct linker *linker, struct object *obj,
+ struct symbol_table *symtab, const Elf32_Sym *sym)
+{
+ size_t shndx = B16(sym->st_shndx);
+ if (shndx == 0)
+ return M_SUCCESS; // ignore this symbol
+
+ // find the given section
+ const char *name = symtab->strtab->data + B32(sym->st_name);
+
+ if (shndx >= obj->shdr_len) {
+ ERROR("shdr entry [%d] name out of bounds", shndx);
+ return M_ERROR;
+ }
+
+ if (B32(sym->st_name) >= symtab->strtab->len) {
+ ERROR("symbol name out of bounds");
+ return M_ERROR;
+ }
+
+ Elf32_Shdr const *shdr = &obj->shdr[shndx];
+ struct segment *sec = NULL;
+ for (size_t i = 0; i < obj->phdr_len; i++) {
+ Elf32_Phdr *temp = &obj->phdr[i];
+ if (shdr->sh_offset == temp->p_offset) {
+ sec = &obj->segments[i];
+ break;
+ }
+ }
+
+ if (sec == NULL) {
+ ERROR("could not locate segment for symbol '%s'", name);
+ return M_ERROR;
+ }
+
+ struct segment_table_entry *ent = NULL;
+ if (segtab_get(&linker->segments, &ent, sec->name)) {
+ ERROR("could not locate segment for symbol '%s'", name);
+ return M_ERROR;
+ }
+
+ // segments start at shindx 1
+ ptrdiff_t new_shndx = (ent - linker->segments.entries) + 1;
+
+ size_t str_off = 0;
+ if (strtab_push(linker->symtab.strtab, name, &str_off))
+ return M_ERROR;
+
+ int32_t off = sec->new_vaddr + B32(sym->st_value);
+ Elf32_Sym new = *sym;
+ new.st_name = B32(str_off);
+ new.st_value = B32(off);
+ new.st_shndx = B16(new_shndx);
+ new.st_size = 0;
+
+ if (symtab_get(&linker->symtab, NULL, name) == M_SUCCESS) {
+ ERROR("cannot link doubly defiend symbol '%s'", name);
+ return M_ERROR;
+ }
+
+ if (symtab_push(&linker->symtab, &new))
+ return M_ERROR;
+
+ return M_SUCCESS;
+}
+
+static int relocate_symbols(struct linker *linker)
+{
+ for (size_t i = 0; i < linker->obj_len; i++) {
+ struct object *obj = &linker->objects[i];
+ for (size_t j = 0; j < obj->shdr_len; j++) {
+ struct symbol_table *symtab = &obj->symtabs[j];
+ if (symtab->len < 1)
+ continue;
+
+ for (size_t k = 0; k < symtab->len; k++) {
+ const Elf32_Sym *sym = &symtab->syms[k];
+ if (relocate_symbol(linker, obj, symtab, sym))
+ return M_ERROR;
+ }
+ }
+ }
+ return M_SUCCESS;
+}
+
+static int assemble_phdr(struct linker *linker)
+{
+ Elf32_Phdr *phdr = malloc(sizeof(Elf32_Phdr) * linker->segments.len);
+
+ if (phdr == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ for (uint32_t i = 0; i < linker->segments.len; i++) {
+ Elf32_Phdr *hdr = &phdr[i];
+ struct segment_table_entry *ent = &linker->segments.entries[i];
+ size_t size = segtab_ent_size(ent);
+ hdr->p_type = B32(PT_LOAD);
+ hdr->p_flags = B32(
+ (ent->parts[0]->execute << 0) |
+ (ent->parts[0]->write << 1) |
+ (ent->parts[0]->read << 2));
+ hdr->p_offset = B32(ent->off);
+ hdr->p_vaddr = B32(ent->vaddr);
+ hdr->p_paddr = B32(ent->vaddr);
+ hdr->p_filesz = B32(size);
+ hdr->p_memsz = B32(size);
+ hdr->p_align = B32(SEC_ALIGN);
+ }
+
+ linker->phdr = phdr;
+ linker->phdr_len = linker->segments.len;
+ return M_SUCCESS;
+}
+
+static int assemble_shdr(struct linker *linker)
+{
+ uint32_t max_entries = 0;
+ max_entries += 1; // null
+ max_entries += 1; // symtab
+ max_entries += 1; // strtab
+ max_entries += 1; // shstrtab
+ max_entries += linker->segments.len; // segments
+
+ Elf32_Shdr *shdr = malloc(sizeof(Elf32_Shdr) * max_entries);
+ if (shdr == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+ linker->shdr = shdr;
+
+ size_t str_off;
+ uint32_t count = 0;
+
+ // null
+ shdr[count++] = (Elf32_Shdr) {0};
+
+ // segments
+ for (uint32_t i = 0; i < linker->segments.len; i++) {
+ struct segment_table_entry *ent = &linker->segments.entries[i];
+ if (strtab_push(&linker->shstrtab, ent->name, &str_off))
+ return M_ERROR;
+
+ shdr[count++] = (Elf32_Shdr) {
+ .sh_name = B32(str_off),
+ .sh_type = B32(SHT_PROGBITS),
+ .sh_flags = B32(
+ (ent->parts[0]->write << 0) |
+ (ent->parts[0]->execute << 2) |
+ SHF_ALLOC),
+ .sh_addr = B32(ent->vaddr),
+ .sh_offset = B32(ent->off),
+ .sh_size = B32(segtab_ent_size(ent)),
+ .sh_link = 0,
+ .sh_info = 0,
+ .sh_addralign = B32(ent->parts[0]->align),
+ .sh_entsize = 0,
+ };
+ }
+
+ // symbol table
+ if (strtab_push(&linker->shstrtab, ".symtab", &str_off))
+ return M_ERROR;
+
+ linker->symtab_shidx = count;
+ shdr[count++] = (Elf32_Shdr) {
+ .sh_name = B32(str_off),
+ .sh_type = B32(SHT_SYMTAB),
+ .sh_flags = 0,
+ .sh_addr = 0,
+ .sh_offset = 0,
+ .sh_size = 0,
+ .sh_link = 0,
+ .sh_info = 0,
+ .sh_addralign = B32(1),
+ .sh_entsize = B32(sizeof(Elf32_Sym)),
+ };
+
+ // string table
+ if (strtab_push(&linker->shstrtab, ".strtab", &str_off))
+ return M_ERROR;
+
+ linker->strtab_shidx = count;
+ shdr[count++] = (Elf32_Shdr) {
+ .sh_name = B32(str_off),
+ .sh_type = B32(SHT_STRTAB),
+ .sh_flags = B32(SHF_STRINGS),
+ .sh_addr = 0,
+ .sh_offset = 0,
+ .sh_size = 0,
+ .sh_link = 0,
+ .sh_info = 0,
+ .sh_addralign = B32(1),
+ .sh_entsize = 0,
+ };
+
+ // shstring table
+ if (strtab_push(&linker->shstrtab, ".shstrtab", &str_off))
+ return M_ERROR;
+
+ linker->shstrtab_shidx = count;
+ shdr[count++] = (Elf32_Shdr) {
+ .sh_name = B32(str_off),
+ .sh_type = B32(SHT_STRTAB),
+ .sh_flags = B32(SHF_STRINGS),
+ .sh_addr = 0,
+ .sh_offset = 0,
+ .sh_size = 0,
+ .sh_link = 0,
+ .sh_info = 0,
+ .sh_addralign = B32(1),
+ .sh_entsize = 0,
+ };
+
+ linker->shdr_len = count;
+ return M_SUCCESS;
+}
+
+static int relocate_instruction_rela(struct linker *linker,
+ struct segment *seg,
+ Elf32_Rela *rel)
+{
+ /// get start of the segment part in bytes and the byte offset
+ /// of this relocation in that segment part
+ uint32_t off = B32(rel->r_offset);
+ if (off > seg->size) {
+ ERROR("relocation in segment '%s' out of bounds", seg->name);
+ return M_ERROR;
+ }
+
+ if (B32(rel->r_info) == 0) {
+ WARNING("skiping empty relocation entry");
+ return M_SUCCESS;
+ }
+
+ /// read the relocation entry
+ uint8_t idx = B32(rel->r_info) >> 8;
+ uint8_t typ = B32(rel->r_info) & 0xFF;
+ int32_t add = B32(rel->r_addend);
+
+ /// read the symbol from the relocation
+ struct symbol_table *symtab = seg->reltab.symtab;
+ if (idx >= symtab->len) {
+ ERROR("relocation in segment '%s', symbol index [%d] out of "
+ "bounds", seg->name, idx);
+ return M_ERROR;
+ }
+ Elf32_Sym *sym = &symtab->syms[idx];
+ const char *symname = symtab->strtab->data + B32(sym->st_name);
+
+
+ /// get the section header that the symbol is related to
+ Elf32_Shdr *shdr = NULL;
+ if (B16(sym->st_shndx) >= seg->obj->shdr_len) {
+ ERROR("shndx index [%d] out of bounds", B16(sym->st_shndx));
+ return M_ERROR;
+ }
+ shdr = &seg->obj->shdr[B16(sym->st_shndx)];
+
+ /// get the segment that the symbol is in
+ struct segment_table_entry *ent;
+ const char *segname = seg->obj->shstrtab->data + B32(shdr->sh_name);
+ if (segtab_get(&linker->segments, &ent, segname)) {
+ ERROR("could not locate segment for relocation");
+ return M_ERROR;
+ }
+
+ uint32_t sym_vaddr = B32(sym->st_value) + ent->vaddr;
+ uint32_t *ins_raw = (uint32_t *) &seg->bytes[off];
+
+ union mips_instruction_data ins;
+ ins.raw = B32(*ins_raw);
+
+ uint32_t ins_vaddr = seg->new_vaddr + off;
+
+ uint32_t vaddr_abs = sym_vaddr + add;
+ int32_t vaddr_rel = (vaddr_abs - ins_vaddr - 4) >> 2;
+ bool warn = false;
+
+ switch (typ) {
+ case R_MIPS_16:
+ // 16bit absolute
+ if (vaddr_abs > UINT16_MAX)
+ warn = true;
+ ins.immd = (uint16_t)vaddr_abs;
+ break;
+ case R_MIPS_PC16:
+ // 16bit relative shifted
+ if (vaddr_rel > INT16_MAX || vaddr_rel < INT16_MIN)
+ warn = true;
+ ins.offset = vaddr_rel;
+ break;
+ case R_MIPS_26:
+ // 26bit absolute shifted
+ if (vaddr_abs >= (1 << 25))
+ warn = true;
+ ins.target = (vaddr_abs & 0x0FFFFFFF) >> 2;
+ break;
+ case R_MIPS_PC26_S2:
+ // 26bit relative shifted
+ if (vaddr_rel >= (1 << 24) || -vaddr_rel > (1 << 24))
+ warn = true;
+ ins.offs26 = vaddr_rel;
+ break;
+ case R_MIPS_LO16:
+ // lo 16bit absolute
+ ins.immd = (uint16_t)(vaddr_abs & 0xFFFF);
+ break;
+ case R_MIPS_HI16:
+ // hi 16bit absolute
+ ins.immd = (uint16_t)(vaddr_abs >> 16);
+ break;
+ default:
+ ERROR("do not know how do handle relocation type [%d]", typ);
+ return M_ERROR;
+ }
+
+ *ins_raw = B32(ins.raw);
+
+ if (warn)
+ WARNING("truncating relocation for symbol '%s'", symname);
+
+ return M_SUCCESS;
+
+}
+
+static int relocate_instruction_rel(struct linker *linker,
+ struct segment *seg,
+ Elf32_Rel *rel)
+{
+ Elf32_Rela temp;
+ temp.r_info = rel->r_info;
+ temp.r_offset = rel->r_offset;
+ temp.r_addend = 0;
+
+ return relocate_instruction_rela(linker, seg, &temp);
+}
+
+static int relocate_segment_instructions(struct linker *linker,
+ struct segment *seg)
+{
+ for (uint32_t i = 0; i < seg->reltab.len; i++) {
+ int res = M_SUCCESS;
+ if (seg->reltab.type == SHT_RELA)
+ res = relocate_instruction_rela(linker, seg,
+ &seg->reltab.rela[i]);
+ else if (seg->reltab.type == SHT_REL)
+ res = relocate_instruction_rel(linker, seg,
+ &seg->reltab.rel[i]);
+ else {
+ ERROR("unknown reltab type");
+ return M_ERROR;
+ }
+ if (res)
+ return M_ERROR;
+ }
+ return M_SUCCESS;
+}
+
+static int relocate_instructions(struct linker *linker)
+{
+ for (uint32_t i = 0; i < linker->segments.len; i++) {
+ struct segment_table_entry *ent = &linker->segments.entries[i];
+ for (uint32_t j = 0; j < ent->len; j++) {
+ struct segment *seg = ent->parts[j];
+ if (relocate_segment_instructions(linker, seg))
+ return M_ERROR;
+ }
+ }
+ return M_SUCCESS;
+}
+
+static void update_offsets(struct linker *linker)
+{
+ uint32_t ptr = 0;
+
+ // we must now correct offsets and sizes in side the ehdr, phdr,
+ // and shdr
+ ptr += sizeof(Elf32_Ehdr);
+
+ // phdr
+ linker->ehdr.e_phoff = B32(ptr);
+ ptr += linker->phdr_len * sizeof(Elf32_Phdr);
+
+ // section padding
+ {
+ uint32_t mod = ptr % SEC_ALIGN;
+ if (mod != 0)
+ linker->secalign = (SEC_ALIGN - mod);
+ else
+ linker->secalign = 0;
+ ptr += linker->secalign;
+ }
+
+ // sections
+ for (uint32_t i = 0; i < linker->segments.len; i++) {
+ struct segment_table_entry *ent = &linker->segments.entries[i];
+ uint32_t idx = i + 1;
+ uint32_t size = segtab_ent_size(ent);
+ linker->phdr[i].p_offset = B32(ptr);
+ linker->shdr[idx].sh_offset = linker->phdr[i].p_offset;
+ ptr += size;
+ }
+
+ // symtab
+ Elf32_Shdr *symtab = &linker->shdr[linker->symtab_shidx];
+ symtab->sh_offset = B32(ptr);
+ symtab->sh_link = B32(linker->strtab_shidx);
+ symtab->sh_size = B32(linker->symtab.len * sizeof(Elf32_Sym));
+ ptr += B32(symtab->sh_size);
+
+ // strtab
+ Elf32_Shdr *strtab = &linker->shdr[linker->strtab_shidx];
+ strtab->sh_offset = B32(ptr);
+ strtab->sh_size = B32(linker->strtab.len);
+ ptr += linker->strtab.len;
+
+ // shstrtab
+ Elf32_Shdr *shstrtab = &linker->shdr[linker->shstrtab_shidx];
+ shstrtab->sh_offset = B32(ptr);
+ shstrtab->sh_size = B32(linker->shstrtab.len);
+ ptr += linker->shstrtab.len;
+
+ // shdr
+ linker->ehdr.e_shoff = B32(ptr);
+}
+
+static int write_file(struct linker *linker)
+{
+ extern char *current_file;
+ current_file = linker->args->out_file;
+
+ FILE *out = fopen(linker->args->out_file, "w");
+ if (out == NULL) {
+ PERROR("cannot write");
+ return M_ERROR;
+ }
+
+ int res = 0;
+
+ // ehdr
+ res |= fwrite(&linker->ehdr, sizeof(Elf32_Ehdr), 1, out);
+
+ // phdr
+ res |= fwrite(linker->phdr, sizeof(Elf32_Phdr), linker->phdr_len, out);
+
+ // section padding
+ for (uint32_t i = 0; i < linker->secalign; i++) {
+ uint8_t zero = 0;
+ res |= fwrite(&zero, 1, 1, out);
+ }
+
+ // sections
+ for (uint32_t i = 0; i < linker->segments.len; i++) {
+ struct segment_table_entry *ent = &linker->segments.entries[i];
+ for (uint32_t j = 0; j < ent->len; j++) {
+ struct segment *seg = ent->parts[j];
+ res |= fwrite(seg->bytes, 1, seg->size, out);
+ }
+ }
+
+ // sym tbl
+ res |= fwrite(linker->symtab.syms, sizeof(Elf32_Sym), linker->symtab.len, out);
+
+ // str tbl
+ res |= fwrite(linker->strtab.data, 1, linker->strtab.len, out);
+
+ // shstr tbl
+ res |= fwrite(linker->shstrtab.data, 1, linker->shstrtab.len, out);
+
+ // shdr
+ res |= fwrite(linker->shdr, sizeof(Elf32_Shdr), linker->shdr_len, out);
+
+ if (res < 0) {
+ ERROR("cannot write data");
+ return M_ERROR;
+ }
+
+ fclose(out);
+
+ return M_SUCCESS;
+}
+
+static int link_executable(struct linker *linker)
+{
+ Elf32_Ehdr *ehdr = &linker->ehdr;
+ *ehdr = MIPS_ELF_EHDR;
+
+ if (assemble_phdr(linker))
+ return M_ERROR;
+
+ if (assemble_shdr(linker))
+ return M_ERROR;
+
+ ehdr->e_type = B16(ET_EXEC);
+ ehdr->e_phnum = B16(linker->phdr_len);
+ ehdr->e_shnum = B16(linker->shdr_len);
+ ehdr->e_shstrndx = B16(linker->shstrtab_shidx);
+
+ update_offsets(linker);
+
+ if (write_file(linker))
+ return M_ERROR;
+
+ return M_SUCCESS;
+}
+
+static int linker_init(struct linker *linker, struct linker_arguments *args)
+{
+ linker->args = args;
+ linker->off = 0;
+ linker->text_vaddr = TEXT_VADDR_MIN;
+ linker->data_vaddr = DATA_VADDR_MIN;
+ linker->objects = NULL;
+ linker->segments.size = 0;
+ linker->symtab.syms = NULL;
+ linker->shstrtab.data = NULL;
+ linker->strtab.data = NULL;
+ linker->shdr = NULL;
+ linker->phdr = NULL;
+ if (segtab_init(&linker->segments))
+ return M_ERROR;
+ if (strtab_init(&linker->shstrtab))
+ return M_ERROR;
+ if (strtab_init(&linker->strtab))
+ return M_ERROR;
+ if (symtab_init(&linker->symtab))
+ return M_ERROR;
+ linker->symtab.strtab = &linker->strtab;
+ return M_SUCCESS;
+}
+
static void linker_free(struct linker *linker)
{
if (linker->objects != NULL) {
@@ -47,15 +676,32 @@ static void linker_free(struct linker *linker)
object_free(&linker->objects[i]);
free(linker->objects);
}
+ if (linker->shdr != NULL)
+ free(linker->shdr);
+ if (linker->phdr != NULL)
+ free(linker->phdr);
+ segtab_free(&linker->segments);
+ strtab_free(&linker->shstrtab);
+ strtab_free(&linker->strtab);
+ symtab_free(&linker->symtab);
}
int link_files(struct linker_arguments args) {
struct linker linker;
- linker.args = &args;
- int res = M_SUCCESS;
+ int res = M_SUCCESS;
+ if (res == M_SUCCESS)
+ res = linker_init(&linker, &args);
if (res == M_SUCCESS)
res = load_objects(&linker);
+ if (res == M_SUCCESS)
+ res = relocate_segments(&linker);
+ if (res == M_SUCCESS)
+ res = relocate_symbols(&linker);
+ if (res == M_SUCCESS)
+ res = relocate_instructions(&linker);
+ if (res == M_SUCCESS)
+ res = link_executable(&linker);
linker_free(&linker);
return res;
diff --git a/mld/link.h b/mld/link.h
index ac97bbf..22e8c6b 100644
--- a/mld/link.h
+++ b/mld/link.h
@@ -9,7 +9,6 @@
#include <merror.h>
#include <stdint.h>
#include <elf.h>
-#include <stdio.h>
// when mapping porinters, we need to bounds check to
@@ -21,6 +20,15 @@
#define BOUND_CHK(obj, len, off) \
(off > UINT32_MAX - len || off + len > obj->mapped_size)
+// when relocating segments, we need to bounds check to
+// make sure it wont overflow the addresses past the 32bit
+// ELF file
+#define ADDR_CHK(lnk_f, seg_f, max) \
+ ((lnk_f) > max - (seg_f) || (lnk_f) + (seg_f) > max)
+
+// start addresses for each tyoe of segment
+#define TEXT_VADDR_MIN 0x00400000
+#define DATA_VADDR_MIN 0x10000000
// pre define
struct linker;
@@ -51,6 +59,12 @@ struct string_table {
size_t len;
};
+int strtab_init(struct string_table *strtab);
+void strtab_free(struct string_table *strtab);
+
+int strtab_push(struct string_table *strtab, const char *str, size_t *res);
+int strtab_get(struct string_table *strtab, const char *str, size_t *res);
+
///
/// symbol table
///
@@ -59,14 +73,42 @@ struct symbol_table {
struct string_table *strtab;
Elf32_Sym *syms;
size_t len;
+ size_t size;
};
+int symtab_init(struct symbol_table *symtab);
+void symtab_free(struct symbol_table *symtab);
+
+int symtab_push(struct symbol_table *symtab, const Elf32_Sym *sym);
+int symtab_get(struct symbol_table *symtab, Elf32_Sym **sym, const char *name);
+
///
/// segment
///
/* a loadable program segment */
struct segment {
+ // segment data
+ char *name;
+ unsigned char *bytes;
+
+ // current loc
+ uint32_t off;
+ uint32_t vaddr;
+
+ // new loc
+ uint32_t new_off;
+ uint32_t new_vaddr;
+
+ // meta
+ bool read;
+ bool write;
+ bool execute;
+ uint32_t align;
+
+ // size
+ uint32_t size;
+
// phdr
Elf32_Phdr *phdr;
uint32_t phdr_idx;
@@ -75,9 +117,8 @@ struct segment {
Elf32_Shdr *shdr;
uint32_t shdr_idx;
- // segment data
- char *name;
- unsigned char *bytes;
+ // object im related to
+ struct object *obj;
// relocation table
struct relocation_table reltab;
@@ -86,6 +127,46 @@ struct segment {
int segment_load(struct object *object, struct segment *segment, size_t index);
///
+/// segment table
+///
+
+struct segment_table_entry {
+ char *name;
+ uint32_t len;
+ uint32_t size;
+ uint32_t off;
+ uint32_t vaddr;
+ // weak segment pointers. we do not own these!!!
+ struct segment **parts;
+};
+
+int segtab_ent_init(struct segment_table_entry *ent);
+void segtab_ent_free(struct segment_table_entry *ent);
+
+int segtab_ent_push(struct segment_table_entry *ent, struct segment *seg);
+uint32_t segtab_ent_size(struct segment_table_entry *ent);
+
+// holds each segment by name
+// and all the segment parts from each of the
+// object files
+struct segment_table {
+ uint32_t len;
+ uint32_t size;
+ struct segment_table_entry *entries;
+};
+
+int segtab_init(struct segment_table *segtab);
+void segtab_free(struct segment_table *segtab);
+
+/* create a new entry with <seg> as its first segment part */
+int segtab_push(struct segment_table *segtab, struct segment_table_entry **ent,
+ struct segment *seg);
+
+/* find a segment table entry with a given name */
+int segtab_get(struct segment_table *segtab, struct segment_table_entry **ent,
+ const char *name);
+
+///
/// object file
///
@@ -136,6 +217,34 @@ struct linker {
struct object *objects;
struct linker_arguments *args;
+
+ // current pointers to relocate
+ // sections
+ uint32_t off;
+ uint32_t text_vaddr;
+ uint32_t data_vaddr;
+
+ // elf tables
+ struct string_table shstrtab;
+ struct string_table strtab;
+ struct symbol_table symtab;
+
+ // output elf
+ Elf32_Ehdr ehdr;
+ Elf32_Phdr *phdr;
+ uint32_t phdr_len;
+ Elf32_Shdr *shdr;
+ uint32_t shdr_len;
+
+ uint32_t symtab_shidx;
+ uint32_t strtab_shidx;
+ uint32_t shstrtab_shidx;
+
+ // section alignment after phdr bytes
+ uint32_t secalign;
+
+ // all segments
+ struct segment_table segments;
};
/* defines arguments to the linker */
diff --git a/mld/obj.c b/mld/obj.c
index 9a83d31..9706371 100644
--- a/mld/obj.c
+++ b/mld/obj.c
@@ -171,6 +171,7 @@ static int load_symtabs(struct object *object)
if (B32(hdr->sh_type) != SHT_SYMTAB) {
symtab->len = 0;
+ symtab->size = 0;
continue;
}
@@ -190,7 +191,8 @@ static int load_symtabs(struct object *object)
}
symtab->strtab = strtab;
- symtab->len = len;
+ symtab->len = len / sizeof(Elf32_Sym);
+ symtab->size = len / sizeof(Elf32_Sym);
symtab->syms = (Elf32_Sym *) (object->mapped + off);
if (BOUND_CHK(object, len, off)) {
@@ -263,8 +265,8 @@ static int map_file(struct object *obj, char *path)
}
obj->mapped_size = st.st_size;
- obj->mapped = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED,
- obj->fd, 0);
+ obj->mapped = mmap(NULL, st.st_size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE, obj->fd, 0);
if (obj->mapped == MAP_FAILED) {
PERROR("cannot map");
diff --git a/mld/seg.c b/mld/seg.c
index a720720..23cf062 100644
--- a/mld/seg.c
+++ b/mld/seg.c
@@ -82,6 +82,13 @@ static int load_reltab(struct object *obj, struct segment *seg)
if (type != SHT_REL && type != SHT_RELA)
continue;
+ if ((type == SHT_REL && B32(hdr->sh_entsize) !=
+ sizeof(Elf32_Rel)) || (type == SHT_RELA &&
+ B32(hdr->sh_entsize) != sizeof(Elf32_Rela))) {
+ ERROR("reltab [%d] has invalid entry size", i);
+ return M_ERROR;
+ }
+
if (B32(hdr->sh_info) != seg->shdr_idx)
continue;
@@ -101,9 +108,8 @@ static int load_reltab(struct object *obj, struct segment *seg)
uint32_t len = B32(hdr->sh_size);
uint32_t off = B32(hdr->sh_offset);
-
seg->reltab.symtab = symtab;
- seg->reltab.len = len;
+ seg->reltab.len = len / B32(hdr->sh_entsize);
seg->reltab.raw = obj->mapped + off;
seg->reltab.type = type;
@@ -131,5 +137,18 @@ int segment_load(struct object *obj, struct segment *seg, size_t index)
if (load_reltab(obj, seg))
return M_ERROR;
+ seg->off = B32(seg->phdr->p_offset);
+ seg->vaddr = B32(seg->phdr->p_vaddr);
+ seg->size = B32(seg->phdr->p_filesz);
+ seg->new_off = 0;
+ seg->new_vaddr = 0;
+
+ seg->read = B32(seg->phdr->p_flags) & PF_R;
+ seg->write = B32(seg->phdr->p_flags) & PF_W;
+ seg->execute = B32(seg->phdr->p_flags) & PF_X;
+ seg->align = B32(seg->phdr->p_align);
+
+ seg->obj = obj;
+
return M_SUCCESS;
}
diff --git a/mld/segtab.c b/mld/segtab.c
new file mode 100644
index 0000000..22356d5
--- /dev/null
+++ b/mld/segtab.c
@@ -0,0 +1,147 @@
+#include <stdlib.h>
+#include <merror.h>
+
+#include "link.h"
+
+#define SEGTAB_INIT_SIZE 8
+
+int segtab_init(struct segment_table *segtab)
+{
+ segtab->len = 0;
+ segtab->size = SEGTAB_INIT_SIZE;
+ segtab->entries = malloc(sizeof(struct segment_table_entry) *
+ SEGTAB_INIT_SIZE);
+
+ if (segtab->entries == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ return M_SUCCESS;
+}
+
+void segtab_free(struct segment_table *segtab)
+{
+ for (uint32_t i = 0; i < segtab->len; i++) {
+ segtab_ent_free(&segtab->entries[i]);
+ }
+ free(segtab->entries);
+}
+
+/* create a new entry with <seg> as its first segment part */
+int segtab_push(struct segment_table *segtab, struct segment_table_entry **res,
+ struct segment *seg)
+{
+ if (segtab->len >= segtab->size) {
+ uint32_t size = segtab->size * 2;
+ void *new = realloc(segtab->entries,
+ sizeof(struct segment_table_entry) * size);
+ if (new == NULL) {
+ PERROR("cannot relloc");
+ return M_ERROR;
+ }
+ segtab->size = size;
+ segtab->entries = new;
+ }
+
+ struct segment_table_entry ent;
+ if (segtab_ent_init(&ent))
+ return M_ERROR;
+ ent.name = seg->name;
+ ent.vaddr = seg->vaddr;
+ ent.off = seg->off;
+
+ if (segtab_ent_push(&ent, seg)) {
+ segtab_ent_free(&ent);
+ return M_ERROR;
+ }
+
+ segtab->entries[segtab->len] = ent;
+
+ if (res != NULL)
+ *res = &segtab->entries[segtab->len];
+
+ segtab->len++;
+
+ return M_SUCCESS;
+}
+
+/* find a segment table entry with a given name */
+int segtab_get(struct segment_table *segtab, struct segment_table_entry **ent,
+ const char *name)
+{
+ for (uint32_t i = 0; i < segtab->len; i++) {
+ const char *segname = segtab->entries[i].name;
+ if (strcmp(name, segname) != 0)
+ continue;
+
+ *ent = &segtab->entries[i];
+ return M_SUCCESS;
+ }
+
+ return M_ERROR;
+}
+
+int segtab_ent_init(struct segment_table_entry *ent)
+{
+ ent->len = 0;
+ ent->size = SEGTAB_INIT_SIZE;
+ ent->parts = malloc(sizeof(struct segment *) *
+ SEGTAB_INIT_SIZE);
+
+ if (ent->parts == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ return M_SUCCESS;
+}
+
+void segtab_ent_free(struct segment_table_entry *ent)
+{
+ free(ent->parts);
+}
+
+int segtab_ent_push(struct segment_table_entry *ent, struct segment *seg)
+{
+ if (ent->len >= ent->size) {
+ uint32_t size = ent->size * 2;
+ void *new = realloc(ent->parts,
+ sizeof(struct segment *) * size);
+ if (new == NULL) {
+ PERROR("cannot relloc");
+ return M_ERROR;
+ }
+ ent->size = size;
+ ent->parts = new;
+ }
+
+ if (ent->len > 0) {
+ struct segment *first = ent->parts[0];
+ if (first->align != seg->align) {
+ ERROR("segment '%s' doest not have matching alignment",
+ ent->name);
+ }
+ if (first->read != seg->read ||
+ first->write != seg->write ||
+ first->execute != seg->execute) {
+ ERROR("segment '%s' doest not have matching RWX",
+ ent->name);
+ }
+ } else {
+ ent->off = seg->new_off;
+ ent->vaddr = seg->new_vaddr;
+ }
+
+ ent->parts[ent->len++] = seg;
+ return M_SUCCESS;
+}
+
+uint32_t segtab_ent_size(struct segment_table_entry *ent)
+{
+ uint32_t size = 0;
+ for (uint32_t i = 0; i < ent->len; i++) {
+ size += ent->parts[i]->size;
+ }
+ return size;
+}
diff --git a/mld/strtab.c b/mld/strtab.c
new file mode 100644
index 0000000..c31889b
--- /dev/null
+++ b/mld/strtab.c
@@ -0,0 +1,56 @@
+#include <merror.h>
+#include <stdlib.h>
+
+#include "link.h"
+
+int strtab_init(struct string_table *strtab)
+{
+ strtab->len = 1;
+ strtab->data = malloc(1);
+
+ if (strtab->data == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ strtab->data[0] = '\0';
+ return M_SUCCESS;
+}
+
+void strtab_free(struct string_table *strtab)
+{
+ free(strtab->data);
+}
+
+int strtab_push(struct string_table *strtab, const char *str, size_t *res)
+{
+ if (strtab_get(strtab, str, res) == M_SUCCESS)
+ return M_SUCCESS;
+
+ size_t len = strlen(str);
+ char *new = realloc(strtab->data, strtab->len + len + 1);
+ if (new == NULL) {
+ PERROR("cannot realloc");
+ return M_ERROR;
+ }
+ strtab->data = new;
+ memcpy(strtab->data + strtab->len, str, len + 1);
+
+ if (res != NULL)
+ *res = strtab->len;
+ strtab->len += len + 1;
+
+ return M_SUCCESS;
+}
+
+int strtab_get(struct string_table *strtab, const char *str, size_t *res)
+{
+ for (size_t i = 0; i < strtab->len; i++) {
+ if (strcmp(strtab->data + i, str) == 0) {
+ if (res != NULL)
+ *res = i;
+ return M_SUCCESS;
+ }
+ }
+ return M_ERROR;
+}
diff --git a/mld/symtab.c b/mld/symtab.c
new file mode 100644
index 0000000..eca6dbe
--- /dev/null
+++ b/mld/symtab.c
@@ -0,0 +1,60 @@
+#include <elf.h>
+#include <merror.h>
+#include <stdlib.h>
+#include <melf.h>
+
+#include "link.h"
+
+#define SYMTAB_INIT_LEN 8
+
+int symtab_init(struct symbol_table *symtab)
+{
+ symtab->len = 1;
+ symtab->size = SYMTAB_INIT_LEN;
+ symtab->syms = malloc(sizeof(Elf32_Sym) * SYMTAB_INIT_LEN);
+
+ if (symtab->syms == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ symtab->syms[0] = (Elf32_Sym){0};
+
+ return M_SUCCESS;
+}
+
+void symtab_free(struct symbol_table *symtab)
+{
+ free(symtab->syms);
+}
+
+int symtab_push(struct symbol_table *symtab, const Elf32_Sym *sym)
+{
+ if (symtab->len >= symtab->size) {
+ size_t size = symtab->size *= 2;
+ void *new = realloc(symtab->syms, sizeof(Elf32_Sym) * size);
+ if (new == NULL) {
+ PERROR("cannot realloc");
+ return M_ERROR;
+ }
+ symtab->size = size;
+ symtab->syms = new;
+ }
+
+ symtab->syms[symtab->len++] = *sym;
+ return M_SUCCESS;
+}
+
+int symtab_get(struct symbol_table *symtab, Elf32_Sym **res, const char *name)
+{
+ for (size_t i = 0; i < symtab->len; i++) {
+ Elf32_Sym *sym = &symtab->syms[i];
+ const char *symname = symtab->strtab->data + B32(sym->st_name);
+ if (strcmp(name, symname) == 0) {
+ if (res != NULL)
+ *res = sym;
+ return M_SUCCESS;
+ }
+ }
+ return M_ERROR;
+}