start mld, add loading of object files, add fuzzing support
This commit is contained in:
parent
3d339caada
commit
3b0a87254f
17 changed files with 1132 additions and 91 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1 +1,2 @@
|
|||
bin
|
||||
/bin
|
||||
/fuzz
|
||||
|
|
|
@ -5,6 +5,7 @@ LD=cc
|
|||
|
||||
CFLAGS += -pipe
|
||||
CFLAGS += -Wall -Wextra -pedantic
|
||||
CFLAGS += -Wno-initializer-overrides
|
||||
CFLAGS += -O0 -g
|
||||
|
||||
# ======================== CONFIG OPTIONS ==
|
||||
|
|
41
include/melf.h
Normal file
41
include/melf.h
Normal 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__ */
|
|
@ -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__ */
|
||||
|
|
10
lib/error.c
10
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;
|
||||
}
|
||||
|
||||
if (current_file != NULL)
|
||||
printf("%s:%d:%d: %s ", current_file, line, column, t);
|
||||
else
|
||||
printf("%s ", t);
|
||||
vprintf(format, list);
|
||||
putchar('\n');
|
||||
}
|
||||
|
@ -54,6 +57,9 @@ void __log_impl(int type, const char *format, ...)
|
|||
break;
|
||||
}
|
||||
|
||||
if (current_file != NULL)
|
||||
printf("%s: %s ", current_file, t);
|
||||
else
|
||||
printf("%s ", t);
|
||||
vprintf(format, list);
|
||||
putchar('\n');
|
||||
|
|
|
@ -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 $@ $<
|
||||
|
|
142
masm/asm.c
142
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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
7
mld/Makefile
Normal file
7
mld/Makefile
Normal file
|
@ -0,0 +1,7 @@
|
|||
include ../config.mk
|
||||
|
||||
SRC=.
|
||||
BIN=../bin/mld
|
||||
OUT=mld
|
||||
|
||||
include ../makefile.mk
|
62
mld/link.c
Normal file
62
mld/link.c
Normal 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
151
mld/link.h
Normal 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
50
mld/main.c
Normal 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
335
mld/obj.c
Normal 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
135
mld/seg.c
Normal 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
20
test/Makefile
Normal 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
244
test/masm/test.asm
Normal 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
BIN
test/mld/test.o
Normal file
Binary file not shown.
Loading…
Reference in a new issue