multitude of mld fixes, add entrypoint
This commit is contained in:
parent
fff0444f6c
commit
49af3bfc62
7 changed files with 88 additions and 30 deletions
29
lib/runtime.asm
Normal file
29
lib/runtime.asm
Normal file
|
@ -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
|
|
@ -4,4 +4,6 @@ SRC=.
|
|||
BIN=../bin/mld
|
||||
OUT=mld
|
||||
|
||||
CFLAGS += -DPREFIX=$(PREFIX)
|
||||
|
||||
include ../makefile.mk
|
||||
|
|
61
mld/link.c
61
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))
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue