diff --git a/lib/runtime.asm b/lib/runtime.asm new file mode 100644 index 0000000..5cb4458 --- /dev/null +++ b/lib/runtime.asm @@ -0,0 +1,29 @@ +# +# MIPS32r6 ASSEMBLY RUNTIME +# - sets up the stack +# - calls main +# - exits +# + +.extern main + +.stack +.align 2 + +.space 4096 +__mips_stack: + +.text +.align 2 + +_start: + # setup stack + la $sp, __mips_stack + + # call main + jal main + + # exit + move $a0, $v0 + li $v0, 1 + syscall diff --git a/mld/Makefile b/mld/Makefile index fd907d9..8216c08 100644 --- a/mld/Makefile +++ b/mld/Makefile @@ -4,4 +4,6 @@ SRC=. BIN=../bin/mld OUT=mld +CFLAGS += -DPREFIX=$(PREFIX) + include ../makefile.mk diff --git a/mld/link.c b/mld/link.c index 5a8e151..64cffe5 100644 --- a/mld/link.c +++ b/mld/link.c @@ -16,8 +16,11 @@ static int load_objects(struct linker *linker) { + int max_entries = linker->args->in_count + 1; + // 1 needed for the runtime + linker->objects = malloc(sizeof(struct object) * - linker->args->in_count); + max_entries); linker->obj_len = 0; if (linker->objects == NULL) { @@ -46,6 +49,19 @@ static int load_objects(struct linker *linker) skip_obj: } + if (linker->args->freestanding == false) { + #define _STR(x) _STR2(x) + #define _STR2(x) #x + + char *path = _STR(PREFIX) "/lib/mips/runtime.o"; + struct object *obj = &linker->objects[linker->obj_len++]; + if (object_load(obj, path)) + return M_ERROR; + + #undef _STR + #undef _STR2 + } + return M_SUCCESS; } @@ -161,7 +177,7 @@ static int relocate_symbol(struct linker *linker, struct object *obj, 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) { + if (PHDR_SHDR_MATCH(temp, shdr)) { sec = &obj->segments[i]; break; } @@ -386,30 +402,22 @@ static int relocate_instruction_rela(struct linker *linker, 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)); + char const *sym_name = symtab->strtab->data + B32(sym->st_name); + if (B32(sym->st_name) >= symtab->strtab->len) { + ERROR("relocation symbol name out of bounds"); + return M_ERROR; + } + + // get the new sym for the new vaddr + Elf32_Sym *new_sym = NULL; + if (symtab_get(&linker->symtab, &new_sym, sym_name)) { + ERROR("relocation symbol not found"); 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 (B32(shdr->sh_name) >= seg->obj->shstrtab->len) { - ERROR("relocation segment name out of bounds"); - return M_ERROR; - } - 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 sym_vaddr = B32(new_sym->st_value); uint32_t *ins_raw = (uint32_t *) &seg->bytes[off]; union mips_instruction_data ins; @@ -462,7 +470,7 @@ static int relocate_instruction_rela(struct linker *linker, *ins_raw = B32(ins.raw); if (warn) - WARNING("truncating relocation for symbol '%s'", symname); + WARNING("truncating relocation for symbol '%s'", sym_name); return M_SUCCESS; @@ -647,6 +655,15 @@ static int link_executable(struct linker *linker) ehdr->e_shnum = B16(linker->shdr_len); ehdr->e_shstrndx = B16(linker->shstrtab_shidx); + Elf32_Sym *entry = NULL; + if (symtab_get(&linker->symtab, &entry, "_start")) { + ERROR("undefined symbol _start"); + return M_ERROR; + } + + // BE to BE, no endiness conversion needed + ehdr->e_entry = entry->st_value; + update_offsets(linker); if (write_file(linker)) diff --git a/mld/link.h b/mld/link.h index 22e8c6b..9096975 100644 --- a/mld/link.h +++ b/mld/link.h @@ -26,6 +26,11 @@ #define ADDR_CHK(lnk_f, seg_f, max) \ ((lnk_f) > max - (seg_f) || (lnk_f) + (seg_f) > max) +// checks if a phdr and shdr are matches +#define PHDR_SHDR_MATCH(phdr, shdr) ( \ + ((phdr)->p_offset == (shdr)->sh_offset) && \ + ((phdr)->p_filesz == (shdr)->sh_size)) \ + // start addresses for each tyoe of segment #define TEXT_VADDR_MIN 0x00400000 #define DATA_VADDR_MIN 0x10000000 @@ -119,6 +124,8 @@ struct segment { // object im related to struct object *obj; + // segment table entry im related to + struct segment_table_entry *ent; // relocation table struct relocation_table reltab; @@ -252,6 +259,7 @@ struct linker_arguments { char **in_files; int in_count; char *out_file; + bool freestanding; }; /* link object files */ diff --git a/mld/main.c b/mld/main.c index 3dd5b5c..675a056 100644 --- a/mld/main.c +++ b/mld/main.c @@ -10,6 +10,7 @@ void help(void) { printf("options:\n"); printf("\t-h\t\tprints this help message\n"); printf("\t-o \tselect a output file destination\n"); + printf("\t-f\t\tmake this binary freestanding (no runtime)\n"); } int main(int argc, char **argv) { @@ -18,11 +19,12 @@ int main(int argc, char **argv) { .in_files = NULL, .in_count = 0, .out_file = "a.out", + .freestanding = false, }; int c; - while ((c = getopt(argc, argv, "ho:")) != 1) { + while ((c = getopt(argc, argv, "ho:f")) != 1) { switch(c) { case 'h': help(); @@ -30,6 +32,9 @@ int main(int argc, char **argv) { case 'o': args.out_file = optarg; break; + case 'f': + args.freestanding = true; + break; case '?': return M_ERROR; default: diff --git a/mld/seg.c b/mld/seg.c index ba14cf9..61c5296 100644 --- a/mld/seg.c +++ b/mld/seg.c @@ -31,10 +31,9 @@ static int load_shdr(struct object *obj, struct segment *seg, size_t index) Elf32_Shdr *hdr = &obj->shdr[i]; // find shdr that matches the offset in phdr - if (seg->phdr->p_offset != hdr->sh_offset) + if (!PHDR_SHDR_MATCH(seg->phdr, hdr)) continue; - // get name uint32_t name = B32(hdr->sh_name); if (name >= obj->shstrtab->len) { @@ -43,11 +42,6 @@ static int load_shdr(struct object *obj, struct segment *seg, size_t index) } seg->name = &obj->shstrtab->data[name]; - if (seg->phdr->p_filesz != hdr->sh_size) { - ERROR("segment phdr and shdr file sizes to not match"); - return M_ERROR; - } - // map bytes uint32_t len = B32(hdr->sh_size); uint32_t off = B32(hdr->sh_offset); @@ -154,6 +148,7 @@ int segment_load(struct object *obj, struct segment *seg, size_t index) seg->align = B32(seg->phdr->p_align); seg->obj = obj; + seg->ent = NULL; return M_SUCCESS; } diff --git a/mld/segtab.c b/mld/segtab.c index d62b9a8..7456eba 100644 --- a/mld/segtab.c +++ b/mld/segtab.c @@ -136,6 +136,8 @@ int segtab_ent_push(struct segment_table_entry *ent, struct segment *seg) } ent->parts[ent->len++] = seg; + seg->ent = ent; + return M_SUCCESS; }