summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/runtime.asm29
-rw-r--r--mld/Makefile2
-rw-r--r--mld/link.c57
-rw-r--r--mld/link.h8
-rw-r--r--mld/main.c7
-rw-r--r--mld/seg.c9
-rw-r--r--mld/segtab.c2
7 files changed, 86 insertions, 28 deletions
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;
}
- 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");
+ // 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;
}
- uint32_t sym_vaddr = B32(sym->st_value) + ent->vaddr;
+ /// get the segment that the symbol is in
+ 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 <output>\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;
}