start mld, add loading of object files, add fuzzing support

This commit is contained in:
Freya Murphy 2024-09-20 20:46:37 -04:00
parent 3d339caada
commit 3b0a87254f
Signed by: freya
GPG key ID: 744AB800E383AE52
17 changed files with 1132 additions and 91 deletions

3
.gitignore vendored
View file

@ -1 +1,2 @@
bin /bin
/fuzz

View file

@ -5,6 +5,7 @@ LD=cc
CFLAGS += -pipe CFLAGS += -pipe
CFLAGS += -Wall -Wextra -pedantic CFLAGS += -Wall -Wextra -pedantic
CFLAGS += -Wno-initializer-overrides
CFLAGS += -O0 -g CFLAGS += -O0 -g
# ======================== CONFIG OPTIONS == # ======================== CONFIG OPTIONS ==

41
include/melf.h Normal file
View file

@ -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__ */

View file

@ -2,6 +2,8 @@
#ifndef __MERROR_H__ #ifndef __MERROR_H__
#define __MERROR_H__ #define __MERROR_H__
#include <errno.h>
/* Error codes /* Error codes
*/ */
#define M_SUCCESS 0 #define M_SUCCESS 0
@ -34,4 +36,7 @@ void __log_impl(int type, const char *format, ...);
#define ERROR_POS(pos, format, ...) \ #define ERROR_POS(pos, format, ...) \
__log_impl_pos(pos.y, pos.x, __ERROR, format, ##__VA_ARGS__) __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__ */ #endif /* __MERROR_H__ */

View file

@ -2,8 +2,8 @@
#include <stdarg.h> #include <stdarg.h>
#include <stdio.h> #include <stdio.h>
char *current_file = "file.asm"; char *current_file = NULL;
int log_disabled = 1; int log_disabled = 0;
__attribute__((format(printf, 4, 5))) __attribute__((format(printf, 4, 5)))
void __log_impl_pos(int line, int column, int type, const char *format, ...) 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; break;
} }
if (current_file != NULL)
printf("%s:%d:%d: %s ", current_file, line, column, t); printf("%s:%d:%d: %s ", current_file, line, column, t);
else
printf("%s ", t);
vprintf(format, list); vprintf(format, list);
putchar('\n'); putchar('\n');
} }
@ -54,6 +57,9 @@ void __log_impl(int type, const char *format, ...)
break; break;
} }
if (current_file != NULL)
printf("%s: %s ", current_file, t);
else
printf("%s ", t); printf("%s ", t);
vprintf(format, list); vprintf(format, list);
putchar('\n'); putchar('\n');

View file

@ -23,6 +23,11 @@ clean:
run: build run: build
$(BIN)/$(OUT) $(BIN)/$(OUT)
test:
make -C ../test $(OUT)
mkdir -p ../fuzz
afl-fuzz -i ../test/$(OUT) -o ../fuzz -- $(BIN)/$(OUT) @@
$(C_OBJ): $(BIN)/%.o : %.c $(C_OBJ): $(BIN)/%.o : %.c
@mkdir -p $(@D) @mkdir -p $(@D)
$(CC) -c $(CFLAGS) -o $@ $< $(CC) -c $(CFLAGS) -o $@ $<

View file

@ -6,7 +6,7 @@
#include <elf.h> #include <elf.h>
#include <string.h> #include <string.h>
#include <stddef.h> #include <stddef.h>
#include <arpa/inet.h> #include <melf.h>
#include "asm.h" #include "asm.h"
#include "mlimits.h" #include "mlimits.h"
@ -30,12 +30,12 @@ static int create_symbol(struct assembler *assembler,
return M_ERROR; return M_ERROR;
Elf32_Sym symbol = { Elf32_Sym symbol = {
.st_name = htonl(str_off), .st_name = B32(str_off),
.st_value = htonl(section_offset), .st_value = B32(section_offset),
.st_size = 0, .st_size = 0,
.st_info = ELF32_ST_INFO(bind, STT_NOTYPE), .st_info = ELF32_ST_INFO(bind, STT_NOTYPE),
.st_other = ELF32_ST_VISIBILITY(STV_DEFAULT), .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 // 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: { case MIPS_DIRECTIVE_ALIGN: {
assembler->sectab.current->alignment = assembler->sectab.current->alignment =
1 << directive->align; 1 << directive->align;
if (assembler->sectab.current->alignment == 0) {
ERROR("cannot align to zero");
return M_ERROR;
}
break; break;
} }
@ -212,7 +216,7 @@ static int handle_label(struct assembler *assembler,
// we need to update it // we need to update it
if (*sec == SYMSEC_STUB) { if (*sec == SYMSEC_STUB) {
*sec = cur->index; *sec = cur->index;
ref->st_value = htonl(sec_size(cur)); ref->st_value = B32(sec_size(cur));
return M_SUCCESS; return M_SUCCESS;
} }
@ -242,7 +246,7 @@ static int handle_ins(struct assembler *assembler,
entry.type = ENT_INS; entry.type = ENT_INS;
entry.size = sizeof(union mips_instruction_data); entry.size = sizeof(union mips_instruction_data);
entry.ins = htonl(ins->raw); entry.ins = B32(ins->raw);
if (sec_push(sec, entry)) if (sec_push(sec, entry))
return M_ERROR; return M_ERROR;
@ -255,9 +259,9 @@ static int handle_ins(struct assembler *assembler,
return M_ERROR; return M_ERROR;
Elf32_Rela rel = { Elf32_Rela rel = {
.r_info = htonl(ELF32_R_INFO(symidx, ref->type)), .r_info = B32(ELF32_R_INFO(symidx, ref->type)),
.r_addend = htonl(ref->addend), .r_addend = B32(ref->addend),
.r_offset = htonl(sec_index(sec, secidx + i)), .r_offset = B32(sec_index(sec, secidx + i)),
}; };
if (reltab_push(&sec->reltab, rel)) 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]; Elf32_Phdr *hdr = &phdr[i];
struct section *sec = &assembler->sectab.sections[i]; struct section *sec = &assembler->sectab.sections[i];
size_t size = sec_size(sec); size_t size = sec_size(sec);
hdr->p_type = htonl(PT_LOAD); hdr->p_type = B32(PT_LOAD);
hdr->p_flags = htonl( hdr->p_flags = B32(
(sec->execute << 0) | (sec->execute << 0) |
(sec->write << 1) | (sec->write << 1) |
(sec->read << 2)); (sec->read << 2));
hdr->p_offset = 0; hdr->p_offset = 0;
hdr->p_vaddr = 0; hdr->p_vaddr = 0;
hdr->p_paddr = 0; hdr->p_paddr = 0;
hdr->p_filesz = htonl(size); hdr->p_filesz = B32(size);
hdr->p_memsz = htonl(size); hdr->p_memsz = B32(size);
hdr->p_align = htonl(SEC_ALIGN); hdr->p_align = B32(SEC_ALIGN);
} }
*res = phdr; *res = phdr;
@ -376,16 +380,16 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
sec->reltab_shidx = count; sec->reltab_shidx = count;
shdr[count++] = (Elf32_Shdr) { shdr[count++] = (Elf32_Shdr) {
.sh_name = htonl(str_off), .sh_name = B32(str_off),
.sh_type = htonl(SHT_RELA), .sh_type = B32(SHT_RELA),
.sh_flags = 0, .sh_flags = 0,
.sh_addr = 0, .sh_addr = 0,
.sh_offset = 0, .sh_offset = 0,
.sh_size = 0, .sh_size = 0,
.sh_link = 0, .sh_link = 0,
.sh_info = 0, .sh_info = 0,
.sh_addralign = htonl(1), .sh_addralign = B32(1),
.sh_entsize = htonl(sizeof(Elf32_Rela)), .sh_entsize = B32(sizeof(Elf32_Rela)),
}; };
} }
@ -402,12 +406,12 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
sec->shdr_idx = count; sec->shdr_idx = count;
if (sec->reltab.len != 0) 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){ shdr[count++] = (Elf32_Shdr){
.sh_name = htonl(str_off), .sh_name = B32(str_off),
.sh_type = htonl(SHT_PROGBITS), .sh_type = B32(SHT_PROGBITS),
.sh_flags = htonl( .sh_flags = B32(
(sec->write << 0) | (sec->write << 0) |
(sec->execute << 2) | (sec->execute << 2) |
SHF_ALLOC), SHF_ALLOC),
@ -416,7 +420,7 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
.sh_size = 0, .sh_size = 0,
.sh_link = 0, .sh_link = 0,
.sh_info = 0, .sh_info = 0,
.sh_addralign = htonl(sec->alignment), .sh_addralign = B32(sec->alignment),
.sh_entsize = 0, .sh_entsize = 0,
}; };
} }
@ -429,16 +433,16 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
assembler->symtab_shidx = count; assembler->symtab_shidx = count;
shdr[count++] = (Elf32_Shdr) { shdr[count++] = (Elf32_Shdr) {
.sh_name = htonl(str_off), .sh_name = B32(str_off),
.sh_type = htonl(SHT_SYMTAB), .sh_type = B32(SHT_SYMTAB),
.sh_flags = 0, .sh_flags = 0,
.sh_addr = 0, .sh_addr = 0,
.sh_offset = 0, .sh_offset = 0,
.sh_size = 0, .sh_size = 0,
.sh_link = 1, .sh_link = 1,
.sh_info = 0, .sh_info = 0,
.sh_addralign = htonl(1), .sh_addralign = B32(1),
.sh_entsize = htonl(sizeof(Elf32_Sym)), .sh_entsize = B32(sizeof(Elf32_Sym)),
}; };
// string table // string table
@ -449,15 +453,15 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
assembler->strtab_shidx = count; assembler->strtab_shidx = count;
shdr[count++] = (Elf32_Shdr) { shdr[count++] = (Elf32_Shdr) {
.sh_name = htonl(str_off), .sh_name = B32(str_off),
.sh_type = htonl(SHT_STRTAB), .sh_type = B32(SHT_STRTAB),
.sh_flags = htonl(SHF_STRINGS), .sh_flags = B32(SHF_STRINGS),
.sh_addr = 0, .sh_addr = 0,
.sh_offset = 0, .sh_offset = 0,
.sh_size = 0, .sh_size = 0,
.sh_link = 0, .sh_link = 0,
.sh_info = 0, .sh_info = 0,
.sh_addralign = htonl(1), .sh_addralign = B32(1),
.sh_entsize = 0, .sh_entsize = 0,
}; };
@ -469,15 +473,15 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
assembler->shstrtab_shidx = count; assembler->shstrtab_shidx = count;
shdr[count++] = (Elf32_Shdr) { shdr[count++] = (Elf32_Shdr) {
.sh_name = htonl(str_off), .sh_name = B32(str_off),
.sh_type = htonl(SHT_STRTAB), .sh_type = B32(SHT_STRTAB),
.sh_flags = htonl(SHF_STRINGS), .sh_flags = B32(SHF_STRINGS),
.sh_addr = 0, .sh_addr = 0,
.sh_offset = 0, .sh_offset = 0,
.sh_size = 0, .sh_size = 0,
.sh_link = 0, .sh_link = 0,
.sh_info = 0, .sh_info = 0,
.sh_addralign = htonl(1), .sh_addralign = B32(1),
.sh_entsize = 0, .sh_entsize = 0,
}; };
@ -486,7 +490,7 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
if (sec->reltab.len == 0) if (sec->reltab.len == 0)
continue; continue;
shdr[sec->reltab_shidx].sh_link = shdr[sec->reltab_shidx].sh_link =
htonl(assembler->symtab_shidx); B32(assembler->symtab_shidx);
} }
*res = shdr; *res = shdr;
@ -506,7 +510,7 @@ static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr)
ptr += sizeof(Elf32_Ehdr); ptr += sizeof(Elf32_Ehdr);
// phdr // phdr
ehdr->e_phoff = htonl(ptr); ehdr->e_phoff = B32(ptr);
ptr += assembler->phdr_len * sizeof(Elf32_Phdr); ptr += assembler->phdr_len * sizeof(Elf32_Phdr);
// reltbls // reltbls
@ -516,8 +520,8 @@ static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr)
continue; continue;
int idx = sec->reltab_shidx; int idx = sec->reltab_shidx;
int len = sec->reltab.len; int len = sec->reltab.len;
shdr[idx].sh_offset = htonl(ptr); shdr[idx].sh_offset = B32(ptr);
shdr[idx].sh_size = htonl(len * sizeof(Elf32_Rela)); shdr[idx].sh_size = B32(len * sizeof(Elf32_Rela));
ptr += 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]; struct section *sec = &assembler->sectab.sections[i];
uint32_t idx = sec->shdr_idx; uint32_t idx = sec->shdr_idx;
uint32_t size = ntohl(phdr[i].p_filesz); uint32_t size = ntohl(phdr[i].p_filesz);
phdr[i].p_offset = htonl(ptr); phdr[i].p_offset = B32(ptr);
phdr[i].p_vaddr = htonl(v_addr); phdr[i].p_vaddr = B32(v_addr);
phdr[i].p_paddr = htonl(v_addr); phdr[i].p_paddr = B32(v_addr);
shdr[idx].sh_offset = htonl(ptr); shdr[idx].sh_offset = B32(ptr);
shdr[idx].sh_size = phdr[i].p_filesz; shdr[idx].sh_size = phdr[i].p_filesz;
shdr[idx].sh_addr = phdr[i].p_vaddr; shdr[idx].sh_addr = phdr[i].p_vaddr;
v_addr += size; v_addr += size;
@ -548,25 +552,25 @@ static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr)
} }
// symtab // symtab
shdr[assembler->symtab_shidx].sh_offset = htonl(ptr); shdr[assembler->symtab_shidx].sh_offset = B32(ptr);
shdr[assembler->symtab_shidx].sh_link = htonl(assembler->strtab_shidx); shdr[assembler->symtab_shidx].sh_link = B32(assembler->strtab_shidx);
shdr[assembler->symtab_shidx].sh_size = 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); ptr += assembler->symtab.len * sizeof(Elf32_Sym);
// strtab // strtab
shdr[assembler->strtab_shidx].sh_offset = htonl(ptr); shdr[assembler->strtab_shidx].sh_offset = B32(ptr);
shdr[assembler->strtab_shidx].sh_size = htonl(assembler->strtab.size); shdr[assembler->strtab_shidx].sh_size = B32(assembler->strtab.size);
ptr += assembler->strtab.size; ptr += assembler->strtab.size;
// shstrtab // shstrtab
shdr[assembler->shstrtab_shidx].sh_offset = htonl(ptr); shdr[assembler->shstrtab_shidx].sh_offset = B32(ptr);
shdr[assembler->shstrtab_shidx].sh_size = shdr[assembler->shstrtab_shidx].sh_size =
htonl(assembler->shstrtab.size); B32(assembler->shstrtab.size);
ptr += assembler->shstrtab.size; ptr += assembler->shstrtab.size;
// shdr // shdr
ehdr->e_shoff = htonl(ptr); ehdr->e_shoff = B32(ptr);
} }
static void update_sym_shindx(struct assembler *assembler) 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]; ssize_t sec = assembler->symtab.sections[i];
if (sec >= 0) { if (sec >= 0) {
sym->st_shndx = htons(assembler-> sym->st_shndx = B16(assembler->
sectab.sections[sec].shdr_idx); sectab.sections[sec].shdr_idx);
} }
} }
@ -587,7 +591,8 @@ static int write_file(struct assembler *assembler, Elf32_Ehdr *ehdr,
{ {
FILE *out = fopen(path, "w"); FILE *out = fopen(path, "w");
if (out == NULL) { if (out == NULL)
{
ERROR("cannot write '%s'", path); ERROR("cannot write '%s'", path);
return M_ERROR; return M_ERROR;
} }
@ -662,33 +667,10 @@ static int assemble_elf(struct assembler *assembler, const char *out)
return M_ERROR; return M_ERROR;
}; };
Elf32_Ehdr ehdr = { Elf32_Ehdr ehdr = MIPS_ELF_EHDR;
.e_ident = { ehdr.e_phnum = B16(assembler->phdr_len);
[EI_MAG0] = ELFMAG0, ehdr.e_shnum = B16(assembler->shdr_len);
[EI_MAG1] = ELFMAG1, ehdr.e_shstrndx = B16(assembler->shstrtab_shidx);
[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),
};
update_offsets(assembler, &ehdr); update_offsets(assembler, &ehdr);
update_sym_shindx(assembler); update_sym_shindx(assembler);

View file

@ -8,14 +8,14 @@ void help(void) {
printf("usage: masm [options] source.asm\n\n"); printf("usage: masm [options] source.asm\n\n");
printf("options:\n"); printf("options:\n");
printf("\t-h\t\tprints this help message\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) { int main(int argc, char **argv) {
struct assembler_arguments args = { struct assembler_arguments args = {
.in_file = NULL, .in_file = NULL,
.out_file = NULL, .out_file = "out.o",
}; };
int c; int c;
@ -48,9 +48,5 @@ next:
args.in_file = argv[optind]; args.in_file = argv[optind];
if (args.out_file == NULL) {
args.out_file = "out.o";
}
return assemble_file(args); return assemble_file(args);
} }

7
mld/Makefile Normal file
View file

@ -0,0 +1,7 @@
include ../config.mk
SRC=.
BIN=../bin/mld
OUT=mld
include ../makefile.mk

62
mld/link.c Normal file
View file

@ -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;
}

151
mld/link.h Normal file
View file

@ -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__ */

50
mld/main.c Normal file
View file

@ -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);
}

335
mld/obj.c Normal file
View file

@ -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);
}

135
mld/seg.c Normal file
View file

@ -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;
}

20
test/Makefile Normal file
View file

@ -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)

244
test/masm/test.asm Normal file
View file

@ -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

BIN
test/mld/test.o Normal file

Binary file not shown.