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 +#include + +#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 + /* 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 #include -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 #include #include -#include +#include #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 \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 +#include +#include +#include + +#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 +#include +#include +#include +#include +#include +#include + + +// 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 +#include +#include +#include + +#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 \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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include +#include +#include + +#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 Binary files /dev/null and b/test/mld/test.o differ