summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--.gitignore3
-rw-r--r--config.mk1
-rw-r--r--include/melf.h41
-rw-r--r--include/merror.h5
-rw-r--r--lib/error.c14
-rw-r--r--makefile.mk5
-rw-r--r--masm/asm.c142
-rw-r--r--masm/main.c8
-rw-r--r--mld/Makefile7
-rw-r--r--mld/link.c62
-rw-r--r--mld/link.h151
-rw-r--r--mld/main.c50
-rw-r--r--mld/obj.c335
-rw-r--r--mld/seg.c135
-rw-r--r--test/Makefile20
-rw-r--r--test/masm/test.asm244
-rw-r--r--test/mld/test.obin0 -> 5442 bytes
17 files changed, 1132 insertions, 91 deletions
diff --git a/.gitignore b/.gitignore
index ba077a4..dc410b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
-bin
+/bin
+/fuzz
diff --git a/config.mk b/config.mk
index 3f23d71..4586fb9 100644
--- a/config.mk
+++ b/config.mk
@@ -5,6 +5,7 @@ LD=cc
CFLAGS += -pipe
CFLAGS += -Wall -Wextra -pedantic
+CFLAGS += -Wno-initializer-overrides
CFLAGS += -O0 -g
# ======================== CONFIG OPTIONS ==
diff --git a/include/melf.h b/include/melf.h
new file mode 100644
index 0000000..095518b
--- /dev/null
+++ b/include/melf.h
@@ -0,0 +1,41 @@
+/* Copyright (c) 2024 Freya Murphy */
+
+#ifndef __MELF_H__
+#define __MELF_H__
+
+#include <elf.h>
+#include <arpa/inet.h>
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define B32(n) (__bswap_constant_32(n))
+#define B16(n) (__bswap_constant_16(n))
+#else
+#define B32(n) (n)
+#define B16(n) (n)
+#endif
+
+static const Elf32_Ehdr MIPS_ELF_EHDR =
+{
+ .e_ident = {
+ [EI_MAG0] = ELFMAG0,
+ [EI_MAG1] = ELFMAG1,
+ [EI_MAG2] = ELFMAG2,
+ [EI_MAG3] = ELFMAG3,
+ [EI_CLASS] = ELFCLASS32,
+ [EI_DATA] = ELFDATA2MSB,
+ [EI_VERSION] = EV_CURRENT,
+ [EI_OSABI] = ELFOSABI_NONE,
+ [EI_ABIVERSION] = 0x00,
+ [EI_PAD] = 0x00,
+ },
+ .e_type = B16(ET_REL),
+ .e_machine = B16(EM_MIPS),
+ .e_version = B32(EV_CURRENT),
+ .e_entry = 0x00,
+ .e_flags = B32(EF_MIPS_ARCH_32R6),
+ .e_ehsize = B16(sizeof(Elf32_Ehdr)),
+ .e_phentsize = B16(sizeof(Elf32_Phdr)),
+ .e_shentsize = B16(sizeof(Elf32_Shdr)),
+};
+
+#endif /* __MELF_H__ */
diff --git a/include/merror.h b/include/merror.h
index 727111d..7be6fc7 100644
--- a/include/merror.h
+++ b/include/merror.h
@@ -2,6 +2,8 @@
#ifndef __MERROR_H__
#define __MERROR_H__
+#include <errno.h>
+
/* Error codes
*/
#define M_SUCCESS 0
@@ -34,4 +36,7 @@ void __log_impl(int type, const char *format, ...);
#define ERROR_POS(pos, format, ...) \
__log_impl_pos(pos.y, pos.x, __ERROR, format, ##__VA_ARGS__)
+#define PERROR(format, ...) \
+ __log_impl(__ERROR, format ": %s", ##__VA_ARGS__, (strerror(errno)))
+
#endif /* __MERROR_H__ */
diff --git a/lib/error.c b/lib/error.c
index 352a60d..3d88234 100644
--- a/lib/error.c
+++ b/lib/error.c
@@ -2,8 +2,8 @@
#include <stdarg.h>
#include <stdio.h>
-char *current_file = "file.asm";
-int log_disabled = 1;
+char *current_file = NULL;
+int log_disabled = 0;
__attribute__((format(printf, 4, 5)))
void __log_impl_pos(int line, int column, int type, const char *format, ...)
@@ -27,7 +27,10 @@ void __log_impl_pos(int line, int column, int type, const char *format, ...)
break;
}
- printf("%s:%d:%d: %s ", current_file, line, column, t);
+ if (current_file != NULL)
+ printf("%s:%d:%d: %s ", current_file, line, column, t);
+ else
+ printf("%s ", t);
vprintf(format, list);
putchar('\n');
}
@@ -54,7 +57,10 @@ void __log_impl(int type, const char *format, ...)
break;
}
- printf("%s ", t);
+ if (current_file != NULL)
+ printf("%s: %s ", current_file, t);
+ else
+ printf("%s ", t);
vprintf(format, list);
putchar('\n');
}
diff --git a/makefile.mk b/makefile.mk
index a5321b8..5a82581 100644
--- a/makefile.mk
+++ b/makefile.mk
@@ -23,6 +23,11 @@ clean:
run: build
$(BIN)/$(OUT)
+test:
+ make -C ../test $(OUT)
+ mkdir -p ../fuzz
+ afl-fuzz -i ../test/$(OUT) -o ../fuzz -- $(BIN)/$(OUT) @@
+
$(C_OBJ): $(BIN)/%.o : %.c
@mkdir -p $(@D)
$(CC) -c $(CFLAGS) -o $@ $<
diff --git a/masm/asm.c b/masm/asm.c
index 0f07079..30628ec 100644
--- a/masm/asm.c
+++ b/masm/asm.c
@@ -6,7 +6,7 @@
#include <elf.h>
#include <string.h>
#include <stddef.h>
-#include <arpa/inet.h>
+#include <melf.h>
#include "asm.h"
#include "mlimits.h"
@@ -30,12 +30,12 @@ static int create_symbol(struct assembler *assembler,
return M_ERROR;
Elf32_Sym symbol = {
- .st_name = htonl(str_off),
- .st_value = htonl(section_offset),
+ .st_name = B32(str_off),
+ .st_value = B32(section_offset),
.st_size = 0,
.st_info = ELF32_ST_INFO(bind, STT_NOTYPE),
.st_other = ELF32_ST_VISIBILITY(STV_DEFAULT),
- .st_shndx = htons(section_idx),
+ .st_shndx = B16(section_idx),
};
// dont put magic flag values inside symbol, only real indexes
@@ -92,6 +92,10 @@ static int handle_directive(struct assembler *assembler,
case MIPS_DIRECTIVE_ALIGN: {
assembler->sectab.current->alignment =
1 << directive->align;
+ if (assembler->sectab.current->alignment == 0) {
+ ERROR("cannot align to zero");
+ return M_ERROR;
+ }
break;
}
@@ -212,7 +216,7 @@ static int handle_label(struct assembler *assembler,
// we need to update it
if (*sec == SYMSEC_STUB) {
*sec = cur->index;
- ref->st_value = htonl(sec_size(cur));
+ ref->st_value = B32(sec_size(cur));
return M_SUCCESS;
}
@@ -242,7 +246,7 @@ static int handle_ins(struct assembler *assembler,
entry.type = ENT_INS;
entry.size = sizeof(union mips_instruction_data);
- entry.ins = htonl(ins->raw);
+ entry.ins = B32(ins->raw);
if (sec_push(sec, entry))
return M_ERROR;
@@ -255,9 +259,9 @@ static int handle_ins(struct assembler *assembler,
return M_ERROR;
Elf32_Rela rel = {
- .r_info = htonl(ELF32_R_INFO(symidx, ref->type)),
- .r_addend = htonl(ref->addend),
- .r_offset = htonl(sec_index(sec, secidx + i)),
+ .r_info = B32(ELF32_R_INFO(symidx, ref->type)),
+ .r_addend = B32(ref->addend),
+ .r_offset = B32(sec_index(sec, secidx + i)),
};
if (reltab_push(&sec->reltab, rel))
@@ -319,17 +323,17 @@ static int assemble_phdr(struct assembler *assembler, Elf32_Phdr **res,
Elf32_Phdr *hdr = &phdr[i];
struct section *sec = &assembler->sectab.sections[i];
size_t size = sec_size(sec);
- hdr->p_type = htonl(PT_LOAD);
- hdr->p_flags = htonl(
+ hdr->p_type = B32(PT_LOAD);
+ hdr->p_flags = B32(
(sec->execute << 0) |
(sec->write << 1) |
(sec->read << 2));
hdr->p_offset = 0;
hdr->p_vaddr = 0;
hdr->p_paddr = 0;
- hdr->p_filesz = htonl(size);
- hdr->p_memsz = htonl(size);
- hdr->p_align = htonl(SEC_ALIGN);
+ hdr->p_filesz = B32(size);
+ hdr->p_memsz = B32(size);
+ hdr->p_align = B32(SEC_ALIGN);
}
*res = phdr;
@@ -376,16 +380,16 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
sec->reltab_shidx = count;
shdr[count++] = (Elf32_Shdr) {
- .sh_name = htonl(str_off),
- .sh_type = htonl(SHT_RELA),
+ .sh_name = B32(str_off),
+ .sh_type = B32(SHT_RELA),
.sh_flags = 0,
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
- .sh_addralign = htonl(1),
- .sh_entsize = htonl(sizeof(Elf32_Rela)),
+ .sh_addralign = B32(1),
+ .sh_entsize = B32(sizeof(Elf32_Rela)),
};
}
@@ -402,12 +406,12 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
sec->shdr_idx = count;
if (sec->reltab.len != 0)
- shdr[sec->reltab_shidx].sh_info = htonl(count);
+ shdr[sec->reltab_shidx].sh_info = B32(count);
shdr[count++] = (Elf32_Shdr){
- .sh_name = htonl(str_off),
- .sh_type = htonl(SHT_PROGBITS),
- .sh_flags = htonl(
+ .sh_name = B32(str_off),
+ .sh_type = B32(SHT_PROGBITS),
+ .sh_flags = B32(
(sec->write << 0) |
(sec->execute << 2) |
SHF_ALLOC),
@@ -416,7 +420,7 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
- .sh_addralign = htonl(sec->alignment),
+ .sh_addralign = B32(sec->alignment),
.sh_entsize = 0,
};
}
@@ -429,16 +433,16 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
assembler->symtab_shidx = count;
shdr[count++] = (Elf32_Shdr) {
- .sh_name = htonl(str_off),
- .sh_type = htonl(SHT_SYMTAB),
+ .sh_name = B32(str_off),
+ .sh_type = B32(SHT_SYMTAB),
.sh_flags = 0,
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 1,
.sh_info = 0,
- .sh_addralign = htonl(1),
- .sh_entsize = htonl(sizeof(Elf32_Sym)),
+ .sh_addralign = B32(1),
+ .sh_entsize = B32(sizeof(Elf32_Sym)),
};
// string table
@@ -449,15 +453,15 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
assembler->strtab_shidx = count;
shdr[count++] = (Elf32_Shdr) {
- .sh_name = htonl(str_off),
- .sh_type = htonl(SHT_STRTAB),
- .sh_flags = htonl(SHF_STRINGS),
+ .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 = htonl(1),
+ .sh_addralign = B32(1),
.sh_entsize = 0,
};
@@ -469,15 +473,15 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
assembler->shstrtab_shidx = count;
shdr[count++] = (Elf32_Shdr) {
- .sh_name = htonl(str_off),
- .sh_type = htonl(SHT_STRTAB),
- .sh_flags = htonl(SHF_STRINGS),
+ .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 = htonl(1),
+ .sh_addralign = B32(1),
.sh_entsize = 0,
};
@@ -486,7 +490,7 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
if (sec->reltab.len == 0)
continue;
shdr[sec->reltab_shidx].sh_link =
- htonl(assembler->symtab_shidx);
+ B32(assembler->symtab_shidx);
}
*res = shdr;
@@ -506,7 +510,7 @@ static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr)
ptr += sizeof(Elf32_Ehdr);
// phdr
- ehdr->e_phoff = htonl(ptr);
+ ehdr->e_phoff = B32(ptr);
ptr += assembler->phdr_len * sizeof(Elf32_Phdr);
// reltbls
@@ -516,8 +520,8 @@ static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr)
continue;
int idx = sec->reltab_shidx;
int len = sec->reltab.len;
- shdr[idx].sh_offset = htonl(ptr);
- shdr[idx].sh_size = htonl(len * sizeof(Elf32_Rela));
+ shdr[idx].sh_offset = B32(ptr);
+ shdr[idx].sh_size = B32(len * sizeof(Elf32_Rela));
ptr += len * sizeof(Elf32_Rela);
}
@@ -537,10 +541,10 @@ static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr)
struct section *sec = &assembler->sectab.sections[i];
uint32_t idx = sec->shdr_idx;
uint32_t size = ntohl(phdr[i].p_filesz);
- phdr[i].p_offset = htonl(ptr);
- phdr[i].p_vaddr = htonl(v_addr);
- phdr[i].p_paddr = htonl(v_addr);
- shdr[idx].sh_offset = htonl(ptr);
+ phdr[i].p_offset = B32(ptr);
+ phdr[i].p_vaddr = B32(v_addr);
+ phdr[i].p_paddr = B32(v_addr);
+ shdr[idx].sh_offset = B32(ptr);
shdr[idx].sh_size = phdr[i].p_filesz;
shdr[idx].sh_addr = phdr[i].p_vaddr;
v_addr += size;
@@ -548,25 +552,25 @@ static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr)
}
// symtab
- shdr[assembler->symtab_shidx].sh_offset = htonl(ptr);
- shdr[assembler->symtab_shidx].sh_link = htonl(assembler->strtab_shidx);
+ shdr[assembler->symtab_shidx].sh_offset = B32(ptr);
+ shdr[assembler->symtab_shidx].sh_link = B32(assembler->strtab_shidx);
shdr[assembler->symtab_shidx].sh_size =
- htonl(assembler->symtab.len * sizeof(Elf32_Sym));
+ B32(assembler->symtab.len * sizeof(Elf32_Sym));
ptr += assembler->symtab.len * sizeof(Elf32_Sym);
// strtab
- shdr[assembler->strtab_shidx].sh_offset = htonl(ptr);
- shdr[assembler->strtab_shidx].sh_size = htonl(assembler->strtab.size);
+ shdr[assembler->strtab_shidx].sh_offset = B32(ptr);
+ shdr[assembler->strtab_shidx].sh_size = B32(assembler->strtab.size);
ptr += assembler->strtab.size;
// shstrtab
- shdr[assembler->shstrtab_shidx].sh_offset = htonl(ptr);
+ shdr[assembler->shstrtab_shidx].sh_offset = B32(ptr);
shdr[assembler->shstrtab_shidx].sh_size =
- htonl(assembler->shstrtab.size);
+ B32(assembler->shstrtab.size);
ptr += assembler->shstrtab.size;
// shdr
- ehdr->e_shoff = htonl(ptr);
+ ehdr->e_shoff = B32(ptr);
}
static void update_sym_shindx(struct assembler *assembler)
@@ -576,7 +580,7 @@ static void update_sym_shindx(struct assembler *assembler)
ssize_t sec = assembler->symtab.sections[i];
if (sec >= 0) {
- sym->st_shndx = htons(assembler->
+ sym->st_shndx = B16(assembler->
sectab.sections[sec].shdr_idx);
}
}
@@ -587,7 +591,8 @@ static int write_file(struct assembler *assembler, Elf32_Ehdr *ehdr,
{
FILE *out = fopen(path, "w");
- if (out == NULL) {
+ if (out == NULL)
+ {
ERROR("cannot write '%s'", path);
return M_ERROR;
}
@@ -662,33 +667,10 @@ static int assemble_elf(struct assembler *assembler, const char *out)
return M_ERROR;
};
- Elf32_Ehdr ehdr = {
- .e_ident = {
- [EI_MAG0] = ELFMAG0,
- [EI_MAG1] = ELFMAG1,
- [EI_MAG2] = ELFMAG2,
- [EI_MAG3] = ELFMAG3,
- [EI_CLASS] = ELFCLASS32,
- [EI_DATA] = ELFDATA2MSB,
- [EI_VERSION] = EV_CURRENT,
- [EI_OSABI] = ELFOSABI_NONE,
- [EI_ABIVERSION] = 0x00,
- [EI_PAD] = 0x00,
- },
- .e_type = htons(ET_REL),
- .e_machine = htons(EM_MIPS),
- .e_version = htonl(EV_CURRENT),
- .e_entry = 0x00,
- .e_phoff = 0x00,
- .e_shoff = 0x00,
- .e_flags = htonl(EF_MIPS_ARCH_32R6),
- .e_ehsize = htons(sizeof(Elf32_Ehdr)),
- .e_phentsize = htons(sizeof(Elf32_Phdr)),
- .e_phnum = htons(assembler->phdr_len),
- .e_shentsize = htons(sizeof(Elf32_Shdr)),
- .e_shnum = htons(assembler->shdr_len),
- .e_shstrndx = htons(assembler->shstrtab_shidx),
- };
+ Elf32_Ehdr ehdr = MIPS_ELF_EHDR;
+ ehdr.e_phnum = B16(assembler->phdr_len);
+ ehdr.e_shnum = B16(assembler->shdr_len);
+ ehdr.e_shstrndx = B16(assembler->shstrtab_shidx);
update_offsets(assembler, &ehdr);
update_sym_shindx(assembler);
diff --git a/masm/main.c b/masm/main.c
index 760e4fa..caa8420 100644
--- a/masm/main.c
+++ b/masm/main.c
@@ -8,14 +8,14 @@ void help(void) {
printf("usage: masm [options] source.asm\n\n");
printf("options:\n");
printf("\t-h\t\tprints this help message\n");
- printf("\t-o output\tselect a output file destination\n");
+ printf("\t-o <output>\tselect a output file destination\n");
}
int main(int argc, char **argv) {
struct assembler_arguments args = {
.in_file = NULL,
- .out_file = NULL,
+ .out_file = "out.o",
};
int c;
@@ -48,9 +48,5 @@ next:
args.in_file = argv[optind];
- if (args.out_file == NULL) {
- args.out_file = "out.o";
- }
-
return assemble_file(args);
}
diff --git a/mld/Makefile b/mld/Makefile
new file mode 100644
index 0000000..fd907d9
--- /dev/null
+++ b/mld/Makefile
@@ -0,0 +1,7 @@
+include ../config.mk
+
+SRC=.
+BIN=../bin/mld
+OUT=mld
+
+include ../makefile.mk
diff --git a/mld/link.c b/mld/link.c
new file mode 100644
index 0000000..2593806
--- /dev/null
+++ b/mld/link.c
@@ -0,0 +1,62 @@
+#include <stdlib.h>
+#include <merror.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "link.h"
+
+static int load_objects(struct linker *linker)
+{
+ linker->objects = malloc(sizeof(struct object) *
+ linker->args->in_count);
+ linker->obj_len = 0;
+
+ if (linker->objects == NULL) {
+ ERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ for (int i = 0; i < linker->args->in_count; i++) {
+ char *path = linker->args->in_files[i];
+ struct object *obj = &linker->objects[linker->obj_len];
+
+ // check for duplicate
+ for (size_t j = 0; j < linker->obj_len; j++) {
+ const char *dupname = linker->objects[j].name;
+ struct stat st, st2;
+ if (stat(path, &st) || stat(dupname, &st2))
+ continue;
+ if (st.st_ino == st2.st_ino)
+ goto skip_obj;
+ }
+
+ // load obj file
+ linker->obj_len++;
+ if (object_load(obj, path))
+ return M_ERROR;
+skip_obj:
+ }
+
+ return M_SUCCESS;
+}
+
+static void linker_free(struct linker *linker)
+{
+ if (linker->objects != NULL) {
+ for (size_t i = 0; i < linker->obj_len; i++)
+ object_free(&linker->objects[i]);
+ free(linker->objects);
+ }
+}
+
+int link_files(struct linker_arguments args) {
+ struct linker linker;
+ linker.args = &args;
+ int res = M_SUCCESS;
+
+ if (res == M_SUCCESS)
+ res = load_objects(&linker);
+
+ linker_free(&linker);
+ return res;
+}
diff --git a/mld/link.h b/mld/link.h
new file mode 100644
index 0000000..ac97bbf
--- /dev/null
+++ b/mld/link.h
@@ -0,0 +1,151 @@
+/* Copyright (c) 2024 Freya Murphy */
+
+#ifndef __LINK_H__
+#define __LINK_H__
+
+#include <linux/limits.h>
+#include <mlimits.h>
+#include <mips.h>
+#include <merror.h>
+#include <stdint.h>
+#include <elf.h>
+#include <stdio.h>
+
+
+// when mapping porinters, we need to bounds check to
+// make sure its in the mapped object file
+//
+// this checks that
+// 1. the end is in the file
+// 2. the off and len doesnt integer overflow
+#define BOUND_CHK(obj, len, off) \
+ (off > UINT32_MAX - len || off + len > obj->mapped_size)
+
+
+// pre define
+struct linker;
+struct object;
+struct segment;
+
+///
+/// relocation table
+///
+
+struct relocation_table {
+ uint32_t type;
+ union {
+ void *raw;
+ Elf32_Rel *rel;
+ Elf32_Rela *rela;
+ };
+ size_t len;
+ struct symbol_table *symtab;
+};
+
+///
+/// string table
+///
+
+struct string_table {
+ char *data;
+ size_t len;
+};
+
+///
+/// symbol table
+///
+
+struct symbol_table {
+ struct string_table *strtab;
+ Elf32_Sym *syms;
+ size_t len;
+};
+
+///
+/// segment
+///
+
+/* a loadable program segment */
+struct segment {
+ // phdr
+ Elf32_Phdr *phdr;
+ uint32_t phdr_idx;
+
+ // shdr
+ Elf32_Shdr *shdr;
+ uint32_t shdr_idx;
+
+ // segment data
+ char *name;
+ unsigned char *bytes;
+
+ // relocation table
+ struct relocation_table reltab;
+};
+
+int segment_load(struct object *object, struct segment *segment, size_t index);
+
+///
+/// object file
+///
+
+struct object {
+ // file
+ int fd;
+ char *mapped;
+ size_t mapped_size;
+
+ // ehdr
+ Elf32_Ehdr *ehdr;
+
+ // section header table
+ Elf32_Shdr *shdr;
+ size_t shdr_len;
+
+ // program table
+ Elf32_Phdr *phdr;
+ size_t phdr_len;
+
+ // object meta
+ const char *name;
+ size_t index;
+
+ // segments
+ size_t segment_len;
+ struct segment *segments;
+
+ // section header strtab
+ struct string_table *shstrtab;
+
+ // strtabs
+ struct string_table *strtabs;
+ // symtabs
+ struct symbol_table *symtabs;
+};
+
+int object_load(struct object *object, char *path);
+
+void object_free(struct object *object);
+
+///
+/// linker
+///
+
+struct linker {
+ size_t obj_len;
+ struct object *objects;
+
+ struct linker_arguments *args;
+};
+
+/* defines arguments to the linker */
+struct linker_arguments {
+ char **in_files;
+ int in_count;
+ char *out_file;
+};
+
+/* link object files */
+int link_files(struct linker_arguments args);
+
+#endif /* __LINK_H__ */
diff --git a/mld/main.c b/mld/main.c
new file mode 100644
index 0000000..3dd5b5c
--- /dev/null
+++ b/mld/main.c
@@ -0,0 +1,50 @@
+#include <stdio.h>
+#include <unistd.h>
+#include <merror.h>
+#include <string.h>
+
+#include "link.h"
+
+void help(void) {
+ printf("usage: mld [options] objfile...\n\n");
+ printf("options:\n");
+ printf("\t-h\t\tprints this help message\n");
+ printf("\t-o <output>\tselect a output file destination\n");
+}
+
+int main(int argc, char **argv) {
+
+ struct linker_arguments args = {
+ .in_files = NULL,
+ .in_count = 0,
+ .out_file = "a.out",
+ };
+
+ int c;
+
+ while ((c = getopt(argc, argv, "ho:")) != 1) {
+ switch(c) {
+ case 'h':
+ help();
+ return M_SUCCESS;
+ case 'o':
+ args.out_file = optarg;
+ break;
+ case '?':
+ return M_ERROR;
+ default:
+ goto next;
+ }
+ }
+
+next:
+ if (optind >= argc) {
+ ERROR("no object files passed");
+ return M_ERROR;
+ }
+
+ args.in_files = &argv[optind];
+ args.in_count = argc - optind;
+
+ return link_files(args);
+}
diff --git a/mld/obj.c b/mld/obj.c
new file mode 100644
index 0000000..9a83d31
--- /dev/null
+++ b/mld/obj.c
@@ -0,0 +1,335 @@
+#include <elf.h>
+#include <merror.h>
+#include <melf.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "link.h"
+
+extern char *current_file;
+
+static int obj_assert_ehdr(const void *given, const void *assert, size_t size)
+{
+ if (memcmp(given, assert, size) == 0)
+ return M_SUCCESS;
+ ERROR("invalid elf file");
+ return M_ERROR;
+}
+
+/**
+ * Map the ehdr
+ */
+static int load_ehdr(struct object *object)
+{
+ uint32_t off = 0;
+ object->ehdr = (Elf32_Ehdr *) (object->mapped + off);
+
+ if (BOUND_CHK(object, sizeof(Elf32_Ehdr), off)) {
+ ERROR("cannot read ehdr");
+ return M_ERROR;
+ }
+
+ /**
+ * Compare each "static" value in the ehdr and make sure it
+ * is what it should be. If not throw and error and eventually
+ * return.
+ */
+
+ #define EHDR_ASSERT(name, size) \
+ if (res == M_SUCCESS) \
+ res |= obj_assert_ehdr(&MIPS_ELF_EHDR.e_##name, \
+ &object->ehdr->e_##name, size) \
+
+ int res = 0;
+ EHDR_ASSERT(ident, EI_NIDENT);
+ EHDR_ASSERT(type, sizeof(Elf32_Half));
+ EHDR_ASSERT(machine, sizeof(Elf32_Half));
+ EHDR_ASSERT(version, sizeof(Elf32_Word));
+ EHDR_ASSERT(flags, sizeof(Elf32_Word));
+ EHDR_ASSERT(ehsize, sizeof(Elf32_Half));
+ EHDR_ASSERT(phentsize, sizeof(Elf32_Half));
+ EHDR_ASSERT(shentsize, sizeof(Elf32_Half));
+
+ #undef EHDR_ASSERT
+
+ if (res)
+ return M_ERROR;
+
+ return M_SUCCESS;
+}
+
+/**
+ * Map the shdr
+ */
+static int load_shdr(struct object *object)
+{
+ size_t shdr_len = B16(object->ehdr->e_shentsize) *
+ B16(object->ehdr->e_shnum);
+ size_t shdr_off = B32(object->ehdr->e_shoff);
+ object->shdr = (Elf32_Shdr *) (object->mapped + shdr_off);
+ object->shdr_len = B16(object->ehdr->e_shnum);
+
+ if (BOUND_CHK(object, shdr_len, shdr_off)) {
+ ERROR("cannot read shdr");
+ return M_ERROR;
+ }
+
+ return M_SUCCESS;
+}
+
+/**
+ * Map the phdr
+ */
+static int load_phdr(struct object *object)
+{
+ size_t phdr_len = B16(object->ehdr->e_phentsize) *
+ B16(object->ehdr->e_phnum);
+ size_t phdr_off = B32(object->ehdr->e_phoff);
+ object->phdr = (Elf32_Phdr *) (object->mapped + phdr_off);
+ object->phdr_len = B16(object->ehdr->e_phnum);
+
+ if (BOUND_CHK(object, phdr_len, phdr_off)) {
+ ERROR("cannot read phdr");
+ return M_ERROR;
+ }
+
+ return M_SUCCESS;
+}
+
+/**
+ * Load the strtabs
+ */
+static int load_strtabs(struct object *object)
+{
+ uint32_t max_entries = object->shdr_len;
+ struct string_table *strtabs = malloc(max_entries *
+ sizeof(struct string_table));
+
+ if (strtabs == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ for (size_t i = 0; i < object->shdr_len; i++) {
+ Elf32_Shdr *hdr = &object->shdr[i];
+ struct string_table *strtab = &strtabs[i];
+
+ if (B32(hdr->sh_type) != SHT_STRTAB) {
+ strtab->len = 0;
+ continue;
+ }
+
+ uint32_t off = B32(hdr->sh_offset);
+ uint32_t len = B32(hdr->sh_size);
+
+ strtab->len = len;
+ strtab->data = object->mapped + off;
+
+ if (len < 1) {
+ ERROR("invalid or empty strtab [%d]", i);
+ return M_ERROR;
+ }
+
+ if (BOUND_CHK(object, len, off)) {
+ ERROR("cannot map strtab [%d]", i);
+ return M_ERROR;
+ }
+
+ if (strtab->data[0] != '\0' ||
+ strtab->data[len - 1] != '\0') {
+ ERROR("strtab [%d] doesn't not have bounding null "
+ "values", i);
+ return M_ERROR;
+ }
+ }
+
+ object->strtabs = strtabs;
+ return M_SUCCESS;
+}
+
+/**
+ * Load the symtabs
+ */
+static int load_symtabs(struct object *object)
+{
+ uint32_t max_entries = object->shdr_len;
+ struct symbol_table *symtabs = malloc(max_entries *
+ sizeof(struct symbol_table));
+
+ if (symtabs == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ for (size_t i = 0; i < object->shdr_len; i++) {
+ Elf32_Shdr *hdr = &object->shdr[i];
+ struct symbol_table *symtab = &symtabs[i];
+
+ if (B32(hdr->sh_type) != SHT_SYMTAB) {
+ symtab->len = 0;
+ continue;
+ }
+
+ uint32_t off = B32(hdr->sh_offset);
+ uint32_t len = B32(hdr->sh_size);
+ uint32_t stridx = B32(hdr->sh_link);
+
+ if (stridx >= max_entries) {
+ ERROR("strtab index [%d] out of bounds", stridx);
+ return M_ERROR;
+ }
+
+ struct string_table *strtab = &object->strtabs[stridx];
+ if (strtab->len < 1) {
+ ERROR("strtab index [%d] empty or invalid", stridx);
+ return M_ERROR;
+ }
+
+ symtab->strtab = strtab;
+ symtab->len = len;
+ symtab->syms = (Elf32_Sym *) (object->mapped + off);
+
+ if (BOUND_CHK(object, len, off)) {
+ ERROR("cannot map symtab index [%d]", i);
+ return M_ERROR;
+ }
+ }
+
+ object->symtabs = symtabs;
+ return M_SUCCESS;
+}
+
+/**
+ * Load the shstrtab
+ */
+static int load_shstrtab(struct object *object)
+{
+ uint16_t idx = B16(object->ehdr->e_shstrndx);
+ if (idx >= object->shdr_len) {
+ ERROR("shstrndx [%d] out of bounds", idx);
+ return M_ERROR;
+ }
+
+ struct string_table *strtab = &object->strtabs[idx];
+ if (strtab->len < 1) {
+ ERROR("shstrndx is invalid or empty");
+ return M_ERROR;
+ }
+
+ object->shstrtab = strtab;
+ return M_SUCCESS;
+}
+
+/**
+ * Load the program segments
+ */
+static int load_segments(struct object *object)
+{
+ object->segment_len = B16(object->ehdr->e_phnum);
+ object->segments = malloc(sizeof(struct segment) *
+ object->segment_len);
+
+ if (object->segments == NULL) {
+ PERROR("cannot alloc");
+ return M_ERROR;
+ }
+
+ for (size_t i = 0; i < object->segment_len; i++) {
+ struct segment *seg = &object->segments[i];
+ if (segment_load(object, seg, i)) {
+ return M_ERROR;
+ }
+ }
+
+ return M_SUCCESS;
+}
+
+static int map_file(struct object *obj, char *path)
+{
+ obj->fd = open(path, O_RDONLY);
+ if (obj->fd == -1) {
+ PERROR("cannot read");
+ return M_ERROR;
+ }
+
+ struct stat st;
+ if (stat(path, &st)) {
+ PERROR("cannot stat");
+ return M_ERROR;
+ }
+
+ obj->mapped_size = st.st_size;
+ obj->mapped = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED,
+ obj->fd, 0);
+
+ if (obj->mapped == MAP_FAILED) {
+ PERROR("cannot map");
+ return M_ERROR;
+ }
+
+ return M_SUCCESS;
+}
+
+int object_load(struct object *object, char *path)
+{
+ current_file = path;
+
+ object->fd = 0;
+ object->segments = NULL;
+ object->symtabs = NULL;
+ object->strtabs = NULL;
+ object->mapped = NULL;
+ object->name = path;
+
+ /** load the file */
+ if (map_file(object, path))
+ return M_ERROR;
+
+ /* ehdr */
+ if (load_ehdr(object))
+ return M_ERROR;
+
+ /* shdr */
+ if (load_shdr(object))
+ return M_ERROR;
+
+ /* phdr */
+ if (load_phdr(object))
+ return M_ERROR;
+
+ /* strtabs */
+ if (load_strtabs(object))
+ return M_ERROR;
+
+ /* symtabs */
+ if (load_symtabs(object))
+ return M_ERROR;
+
+ /* shstrtab */
+ if (load_shstrtab(object))
+ return M_ERROR;
+
+ /* segments */
+ if (load_segments(object))
+ return M_ERROR;
+
+ return M_SUCCESS;
+}
+
+void object_free(struct object *obj)
+{
+ if (obj->symtabs != NULL)
+ free(obj->symtabs);
+ if (obj->strtabs != NULL)
+ free(obj->strtabs);
+ if (obj->segments != NULL)
+ free(obj->segments);
+ if (obj->fd > 0)
+ close(obj->fd);
+ if (obj->mapped != NULL && obj->mapped != MAP_FAILED)
+ munmap(obj->mapped, obj->mapped_size);
+}
diff --git a/mld/seg.c b/mld/seg.c
new file mode 100644
index 0000000..a720720
--- /dev/null
+++ b/mld/seg.c
@@ -0,0 +1,135 @@
+#include <merror.h>
+#include <melf.h>
+#include <elf.h>
+#include <sys/mman.h>
+
+#include "link.h"
+
+/**
+ * Read the phdr for this segment
+ */
+static int load_phdr(struct object *obj, struct segment *seg, size_t index)
+{
+ if (index >= obj->phdr_len) {
+ ERROR("phdr index [%d] out of bounds", index);
+ return M_ERROR;
+ }
+
+ seg->phdr = &obj->phdr[index];
+ seg->phdr_idx = index;
+ return M_SUCCESS;
+}
+
+/**
+ * Read the shdr for this segment
+ */
+static int load_shdr(struct object *obj, struct segment *seg, size_t index)
+{
+ bool found = false;
+
+ for (size_t i = 0; i < obj->shdr_len; i++) {
+ Elf32_Shdr *hdr = &obj->shdr[i];
+
+ // find shdr that matches the offset in phdr
+ if (seg->phdr->p_offset != hdr->sh_offset)
+ continue;
+
+
+ // get name
+ uint32_t name = B32(hdr->sh_name);
+ if (name >= obj->shstrtab->len) {
+ ERROR("section name index [%d] out of bounds", name);
+ return M_ERROR;
+ }
+ seg->name = &obj->shstrtab->data[name];
+
+ // map bytes
+ uint32_t len = B32(hdr->sh_size);
+ uint32_t off = B32(hdr->sh_offset);
+ seg->bytes = (unsigned char *) (obj->mapped + off);
+
+ if (BOUND_CHK(obj, len, off)) {
+ ERROR("cannot map seg %s:%d", seg->name, index);
+ return M_ERROR;
+ }
+
+ found = true;
+ seg->shdr = hdr;
+ seg->shdr_idx = i;
+ break;
+ }
+
+ if (!found) {
+ ERROR("cannot find shdr for segment [%d]", index);
+ return M_ERROR;
+ }
+
+ return M_SUCCESS;
+}
+
+/**
+ * Read the relocation table for this segment (if one exists)
+ */
+static int load_reltab(struct object *obj, struct segment *seg)
+{
+ // default to none
+ seg->reltab.len = 0;
+
+ for (size_t i = 0; i < obj->shdr_len; i++) {
+ Elf32_Shdr *hdr = &obj->shdr[i];
+ uint32_t type = B32(hdr->sh_type);
+
+ if (type != SHT_REL && type != SHT_RELA)
+ continue;
+
+ if (B32(hdr->sh_info) != seg->shdr_idx)
+ continue;
+
+ uint32_t symtab_idx = B32(hdr->sh_link);
+ if (symtab_idx >= obj->shdr_len) {
+ ERROR("reltab [%d] symtab index [%d] out of bounds",
+ i, symtab_idx);
+ return M_ERROR;
+ }
+
+ struct symbol_table *symtab = &obj->symtabs[symtab_idx];
+ if (symtab->len < 1) {
+ ERROR("reltab [%d] symtab is empty or invalid", i);
+ return M_ERROR;
+ }
+
+ uint32_t len = B32(hdr->sh_size);
+ uint32_t off = B32(hdr->sh_offset);
+
+
+ seg->reltab.symtab = symtab;
+ seg->reltab.len = len;
+ seg->reltab.raw = obj->mapped + off;
+ seg->reltab.type = type;
+
+ if (BOUND_CHK(obj, len, off)) {
+ ERROR("cannot map reltab [%d] for %s",
+ i, seg->name);
+ return M_ERROR;
+ }
+
+ break;
+ }
+
+ return M_SUCCESS;
+}
+
+int segment_load(struct object *obj, struct segment *seg, size_t index)
+{
+
+ if (load_phdr(obj, seg, index))
+ return M_ERROR;
+
+ if (load_shdr(obj, seg, index))
+ return M_ERROR;
+
+ if (load_reltab(obj, seg))
+ return M_ERROR;
+
+ return M_SUCCESS;
+}
diff --git a/test/Makefile b/test/Makefile
new file mode 100644
index 0000000..1a97dd6
--- /dev/null
+++ b/test/Makefile
@@ -0,0 +1,20 @@
+CC=afl-cc
+LD=afl-cc
+
+BIN=../bin
+
+none:
+
+clean:
+ rm -fr $(BIN)
+
+masm: clean
+ make -C ../masm build CC=$(CC) LD=$(LD)
+ $(BIN)/masm/masm -o ./mld/test.o ./masm/test.asm
+
+mld: clean
+ make -C ../mld build CC=$(CC) LD=$(LD)
+ $(BIN)/mld/mld -o ./msim/test ./mld/test.o
+
+msim: clean
+ make -C ../msim build CC=$(CC) LD=$(LD)
diff --git a/test/masm/test.asm b/test/masm/test.asm
new file mode 100644
index 0000000..d70f3e7
--- /dev/null
+++ b/test/masm/test.asm
@@ -0,0 +1,244 @@
+# Copyright (c) 2024 Freya Murphy
+
+.data
+.align 2
+
+heap_start:
+ .space 4
+heap_len:
+ .space 4
+heap_free:
+ .space 4
+heap_ptr:
+ .space 4
+
+null:
+ .space 4
+
+.text
+.align 2
+.globl main
+.globl _start
+
+# init the heap
+heap_init:
+ # sbrk(0)
+ li $v0, 9
+ li $a0, 0
+ syscall
+ sw $v0, heap_start
+ sw $v1, heap_len
+
+ # heap is empty, ptr at start
+ sw $v0, heap_ptr
+ # heap is empty, size = len
+ sw $v1, heap_free
+
+ jr $ra
+
+
+
+# $a0 : amount of bytes to increase
+heap_increase:
+ # t0 : OLD heap len + 1
+ # t1 : heap ptr
+ # t2 : heap end
+ # t2 : heap free
+ # t3 : a0
+
+ # save current length
+ lw $t0, heap_len
+
+ # save a0
+ move $t3, $a0
+
+ # sbrk(amt)
+ li $v0, 9
+ syscall
+ # sbrk(0)
+ li $v0, 9
+ li $a0, 0
+ syscall
+ sw $v0, heap_start
+ sw $v1, heap_len
+
+ lw $t1, heap_ptr # heap ptr
+ add $t2, $v0, $v1 # heap end
+ sub $t3, $t2, $t1 # heap free
+ sw $t3, heap_free
+
+ # set return code to 1 if fail to allocate
+ # i.e. $v1 = $t0 or ($v1 < $t0 + 1)
+ # i.e. heap size hasen't changed
+ addi $t0, $t0, 1
+ slt $v0, $v1, $t0
+ jr $ra
+
+
+
+# $a0 : amount of bytes to allocate
+malloc:
+ # t0 : heap free + 1
+ # t1 : if enough mem is free
+ # t2 : alloc size [(a0 align 4) + 4096 + 4]
+ # t3 : temp for heap_ptr
+ # t7 : alloc amt (a0 align 4) + 4
+ # t8 : temp for modulo
+
+ # save $a0
+ move $t7, $a0
+ addi $t7, $t7, 4 # add 4 bytes to save INTERAL ptr size
+
+ # align $t7 by 4
+ # allows us to use lw and sw in realloc
+ li $t8, 4
+ div $t8, $t7, $t8
+ mfhi $t8
+ beq $t8, $zero, malloc_aligned
+ addi $t7, $t7, 4
+ sub $t7, $t7, $t8
+
+malloc_aligned:
+ # check if we have enough memory free
+ lw $t0, heap_free
+ addi $t0, $t0, 1
+ slt $t1, $t7, $t0
+ bne $t1, $zero, malloc_hasmem
+
+ # set needed mem to alloc
+ # page size + alloc request
+
+ # save $ra and $t7
+ addi $sp, $sp, -8
+ sw $ra, 0($sp)
+ sw $t7, 4($sp)
+
+ li $t2, 4096
+ add $t2, $t2, $t7
+ move $a0, $t2
+ jal heap_increase
+
+ # pop $ra and $t7
+ lw $ra, 0($sp)
+ lw $t7, 4($sp)
+ addi $sp, $sp, 8
+
+ # check heap_increase return code
+ beq $v0, $zero, malloc_hasmem
+
+malloc_error:
+ # failed to sbrk, return null
+ la $v0, null
+ jr $ra
+
+malloc_hasmem:
+ # set return value, and save ptr size in it
+ # add 4 to return ptr, since first 4 bytes are INTERNAL
+ addi $t3, $t7, -4
+ lw $v0, heap_ptr
+ sw $t3, ($v0)
+ addi $v0, $v0, 4
+
+ lw $t3, heap_ptr
+ add $t3, $t3, $t7 # increase ptr by alloc size
+ sw $t3, heap_ptr
+
+ jr $ra
+
+
+
+# $a0 : address of ptr
+free:
+ # haha hehe hoho
+ jr $ra
+
+
+
+# $a0 : new size
+# $a1 : old ptr
+realloc:
+ # t0 : old ptr size
+ # t2 : new ptr addr
+
+ # check if $a0 is zero, if so then just free
+ bne $a0, $zero, realloc_inner
+ move $a0, $a1
+ la $v0, null
+ j free
+
+ # save to stack
+ addi $sp, $sp, -12
+ sw $ra, 0($sp)
+ sw $a0, 4($sp)
+ sw $a1, 8($sp)
+
+ jal malloc
+
+ # pop to stack
+ lw $ra, 0($sp)
+ lw $a0, 4($sp)
+ lw $a1, 8($sp)
+ addi $sp, $sp, 12
+
+realloc_inner:
+ addi $a1, $a1, -4
+ lw $t0, ($a1)
+ addi $a1, $a1, 4
+
+realloc_loop:
+ # loop until $t0 or $t1 is zero
+ beq $t0, $zero, realloc_end
+ beq $a0, $zero, realloc_end
+
+ addi $a1, $a1, 4
+ addi $t0, $t0, -4
+ addi $a0, $a0, -4
+ #lw $t3,
+
+ j realloc_loop
+
+realloc_end:
+
+
+realloc_free:
+ jr $ra
+
+
+_start:
+main:
+ # push return address
+ addi $sp, $sp, -4
+ sw $ra, ($sp)
+
+ jal heap_init
+
+ li $a0, 24
+ jal malloc
+
+ move $a0, $v0
+ li $v0, 1
+ syscall
+
+ li $v0, 11
+ li $a0, 10
+ syscall
+
+ li $a0, 24
+ jal malloc
+
+ move $a0, $v0
+ li $v0, 1
+ syscall
+
+ li $v0, 11
+ li $a0, 10
+ syscall
+
+ # pop return address
+ lw $ra, ($sp)
+ addi $sp, $sp, 4
+
+exit:
+ # exit with code 0
+ li $v0, 0
+ jr $ra
diff --git a/test/mld/test.o b/test/mld/test.o
new file mode 100644
index 0000000..9281a13
--- /dev/null
+++ b/test/mld/test.o
Binary files differ