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 += -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
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__
|
#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__ */
|
||||||
|
|
10
lib/error.c
10
lib/error.c
|
@ -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');
|
||||||
|
|
|
@ -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 $@ $<
|
||||||
|
|
142
masm/asm.c
142
masm/asm.c
|
@ -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);
|
||||||
|
|
|
@ -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
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