mips/masm/asm.c

738 lines
16 KiB
C
Raw Normal View History

2024-09-10 00:48:08 +00:00
#include <merror.h>
2024-09-11 16:06:09 +00:00
#include <mips.h>
#include <stdio.h>
2024-09-10 00:48:08 +00:00
#include <stdlib.h>
2024-09-11 16:06:09 +00:00
#include <elf.h>
#include <string.h>
#include <stddef.h>
2024-09-10 00:48:08 +00:00
#include "asm.h"
2024-09-11 16:06:09 +00:00
#include "mlimits.h"
#include "parse.h"
2024-09-10 00:48:08 +00:00
2024-09-11 16:06:09 +00:00
extern char *current_file;
#define SYMSEC_STUB -1
#define SYMSEC_EXTERN -1
static int create_symbol(struct assembler *assembler,
const char name[MAX_LEX_LENGTH],
ssize_t section_idx,
size_t section_offset,
unsigned char bind)
2024-09-10 00:48:08 +00:00
{
2024-09-11 16:06:09 +00:00
size_t str_off;
if (strtab_write_str(&assembler->strtab, name, &str_off))
return M_ERROR;
Elf32_Sym symbol = {
.st_name = str_off,
.st_value = section_offset,
.st_size = 0,
.st_info = ELF32_ST_INFO(bind, STT_NOTYPE),
.st_other = ELF32_ST_VISIBILITY(STV_DEFAULT),
.st_shndx = section_idx,
};
// dont put magic flag values inside symbol, only real indexes
if (section_idx < 0)
symbol.st_shndx = 0;
if (symtab_push(&assembler->symtab, symbol, section_idx))
return M_ERROR;
return M_SUCCESS;
}
static int find_symbol_or_stub(struct assembler *assembler,
const char name[MAX_LEX_LENGTH],
Elf32_Sym **res,
size_t *res2)
{
if (symtab_find(&assembler->symtab, res, res2, name) == M_SUCCESS)
return M_SUCCESS;
if (create_symbol(assembler, name, SYMSEC_STUB, 0, STB_LOCAL))
return M_ERROR;
size_t idx = assembler->symtab.len - 1;
if (res != NULL)
*res = &assembler->symtab.symbols[idx];
if (res2 != NULL)
*res2 = idx;
return M_SUCCESS;
}
static int handle_directive(struct assembler *assembler,
struct mips_directive *directive)
{
switch (directive->type) {
case MIPS_DIRECTIVE_SECTION: {
struct section_table *sec_tbl = &assembler->sectab;
struct section *sec;
if (sectab_get(sec_tbl, &sec, directive->name)
== M_SUCCESS) {
sec_tbl->current = sec;
break;
}
if (sectab_alloc(sec_tbl, &sec, directive->name))
return M_ERROR;
sec_tbl->current = sec;
break;
}
case MIPS_DIRECTIVE_ALIGN: {
assembler->sectab.current->alignment =
1 << directive->align;
break;
}
case MIPS_DIRECTIVE_SPACE: {
struct section_entry entry;
entry.type = ENT_NO_DATA;
entry.size = directive->space;
if (sec_push(assembler->sectab.current, entry))
return M_ERROR;
break;
}
case MIPS_DIRECTIVE_WORD: {
for (uint32_t i = 0; i < directive->len; i++) {
struct section_entry entry;
entry.type = ENT_WORD;
entry.word = directive->words[i];
if (sec_push(assembler->sectab.current,
entry))
return M_ERROR;
}
break;
}
case MIPS_DIRECTIVE_HALF: {
for (uint32_t i = 0; i < directive->len; i++) {
struct section_entry entry;
entry.type = ENT_HALF;
entry.half = directive->halfs[i];
if (sec_push(assembler->sectab.current,
entry))
return M_ERROR;
}
break;
}
case MIPS_DIRECTIVE_BYTE: {
for (uint32_t i = 0; i < directive->len; i++) {
struct section_entry entry;
entry.type = ENT_BYTE;
entry.byte = directive->bytes[i];
if (sec_push(assembler->sectab.current,
entry))
return M_ERROR;
}
break;
}
case MIPS_DIRECTIVE_EXTERN: {
if (symtab_find(&assembler->symtab, NULL, NULL,
directive->name) == M_SUCCESS) {
ERROR("cannot extern local symbol '%s'",
directive->name);
return M_ERROR;
}
if (create_symbol(assembler, directive->name, SYMSEC_EXTERN, 0,
STB_GLOBAL))
return M_ERROR;
break;
}
case MIPS_DIRECTIVE_GLOBL: {
Elf32_Sym *sym;
if (symtab_find(&assembler->symtab, &sym, NULL,
directive->name) == M_SUCCESS) {
sym->st_info = ELF32_ST_INFO(STB_GLOBAL, STT_NOTYPE);
break;
}
if (create_symbol(assembler, directive->name, SYMSEC_STUB, 0,
STB_GLOBAL))
return M_ERROR;
break;
}
case MIPS_DIRECTIVE_ASCII: {
struct section_entry entry;
entry.type = ENT_STR;
entry.size = strlen(directive->name);
memcpy(entry.str, directive->name, entry.size);
if (sec_push(assembler->sectab.current, entry))
return M_ERROR;
break;
}
case MIPS_DIRECTIVE_ASCIIZ: {
struct section_entry entry;
entry.type = ENT_STR;
entry.size = strlen(directive->name) + 1;
memcpy(entry.str, directive->name, entry.size);
if (sec_push(assembler->sectab.current, entry))
return M_ERROR;
break;
}
2024-09-11 16:06:09 +00:00
}
return M_SUCCESS;
}
static int handle_label(struct assembler *assembler,
const char name[MAX_LEX_LENGTH])
{
struct section *cur = assembler->sectab.current;
Elf32_Sym *ref;
size_t symidx;
if (symtab_find(&assembler->symtab, &ref, &symidx, name) == M_SUCCESS) {
ssize_t *sec = &assembler->symtab.sections[symidx];
// check if the symbol is acutally jus a stub, if so
// we need to update it
if (*sec == SYMSEC_STUB) {
*sec = cur->index;
ref->st_value = sec_size(cur);
return M_SUCCESS;
}
ERROR("redefined symbol '%s'", name);
2024-09-10 00:48:08 +00:00
return M_ERROR;
2024-09-11 16:06:09 +00:00
}
2024-09-10 00:48:08 +00:00
2024-09-11 16:06:09 +00:00
if (create_symbol(assembler, name, cur->index, sec_size(cur),
STB_LOCAL))
2024-09-10 00:48:08 +00:00
return M_ERROR;
2024-09-11 16:06:09 +00:00
return M_SUCCESS;
}
static int handle_ins(struct assembler *assembler,
struct ins_expr *expr)
{
struct section *sec = assembler->sectab.current;
size_t secidx = sec->len;
for (size_t i = 0; i < expr->ins_len; i++) {
struct mips_instruction *ins =
&expr->ins[i];
struct reference *ref =
&expr->ref[i];
struct section_entry entry;
entry.type = ENT_INS;
entry.size = sizeof(struct mips_instruction);
entry.ins = *ins;
if (sec_push(sec, entry))
return M_ERROR;
unsigned char type = 0;
switch (ref->type) {
case REF_NONE:
continue;
case REF_OFFESET:
type = R_MIPS_PC16;
break;
case REF_TARGET:
type = R_MIPS_26;
break;
}
size_t symidx;
if (find_symbol_or_stub(assembler, ref->name, NULL, &symidx))
return M_ERROR;
Elf32_Rela rel = {
.r_info = ELF32_R_INFO(symidx, type),
.r_addend = ref->addend,
.r_offset = sec_index(sec, secidx + i),
};
if (reltab_push(&sec->reltab, rel))
return M_ERROR;
break;
}
return M_SUCCESS;
}
static int parse_file(struct assembler *assembler)
{
struct parser *parser = &assembler->parser;
while (1) {
struct expr expr;
int res = parser_next(parser, &expr);
if (res == M_ERROR)
return M_ERROR;
if (res == M_EOF)
return M_SUCCESS;
switch (expr.type) {
case EXPR_INS:
if (handle_ins(assembler, &expr.ins))
return M_ERROR;
break;
case EXPR_DIRECTIVE:
if (handle_directive(assembler,
&expr.directive))
return M_ERROR;
break;
case EXPR_LABEL:
if (handle_label(assembler, expr.label))
return M_ERROR;
break;
case EXPR_CONSTANT:
break;
}
}
return M_SUCCESS;
}
static int assemble_phdr(struct assembler *assembler, Elf32_Phdr **res,
uint32_t *res2)
{
Elf32_Phdr *phdr = malloc(sizeof(Elf32_Phdr) *
assembler->sectab.len);
if (phdr == NULL) {
ERROR("cannot alloc");
return M_ERROR;;
}
for (uint32_t i = 0; i < assembler->sectab.len; i++) {
Elf32_Phdr *hdr = &phdr[i];
struct section *sec = &assembler->sectab.sections[i];
size_t size = sec_size(sec);
hdr->p_type = PT_LOAD;
hdr->p_flags = (sec->execute << 0) |
(sec->write << 1) |
(sec->read << 2);
hdr->p_offset = 0;
hdr->p_vaddr = 0;
hdr->p_paddr = 0;
hdr->p_filesz = size;
hdr->p_memsz = size;
hdr->p_align = sec->alignment;
}
*res = phdr;
*res2 = assembler->sectab.len;
return M_SUCCESS;
}
static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
uint32_t *res2)
{
uint32_t max_entries = 4; // symtab, strtab, shstrtab
max_entries += assembler->sectab.len; // sections
max_entries += assembler->sectab.len; // reltabs per section
Elf32_Shdr *shdr = malloc(sizeof(Elf32_Shdr) * max_entries);
size_t str_off;
uint32_t count = 0;
// eeltables
for (uint32_t i = 0; i < assembler->sectab.len; i++) {
struct section *sec = &assembler->sectab.sections[i];
const char *prefix = ".reltab.";
char reltab_name[MAX_LEX_LENGTH + 8];
if (sec->reltab.len == 0)
continue;
strcpy(reltab_name, prefix);
strcat(reltab_name, sec->name);
if (strtab_write_str(&assembler->shstrtab,
reltab_name, &str_off)) {
free(shdr);
return M_ERROR;
}
sec->reltab_shidx = count;
shdr[count++] = (Elf32_Shdr) {
.sh_name = str_off,
.sh_type = SHT_RELA,
.sh_flags = 0,
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = 1,
.sh_entsize = sizeof(Elf32_Rela),
};
2024-09-10 00:48:08 +00:00
}
2024-09-11 16:06:09 +00:00
// for each section
for (uint32_t i = 0; i < assembler->sectab.len; i++) {
struct section *sec = &assembler->sectab.sections[i];
char name[MAX_LEX_LENGTH+1] = ".";
strcat(name, sec->name);
if (strtab_write_str(&assembler->shstrtab, name, &str_off)) {
free(shdr);
return M_ERROR;
}
sec->shdr_idx = count;
if (sec->reltab.len != 0)
shdr[sec->reltab_shidx].sh_info = count;
shdr[count++] = (Elf32_Shdr){
.sh_name = str_off,
.sh_type = SHT_PROGBITS,
.sh_flags = (sec->write << 0) | (sec->execute << 2) |
SHF_ALLOC,
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = sec->alignment,
.sh_entsize = sizeof(struct mips_instruction),
};
}
// symbol table
if (strtab_write_str(&assembler->shstrtab, ".symtab", &str_off)) {
free(shdr);
2024-09-10 00:48:08 +00:00
return M_ERROR;
}
2024-09-11 16:06:09 +00:00
assembler->symtab_shidx = count;
shdr[count++] = (Elf32_Shdr) {
.sh_name = str_off,
.sh_type = SHT_SYMTAB,
.sh_flags = 0,
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 1,
.sh_info = 0,
.sh_addralign = 1,
.sh_entsize = sizeof(Elf32_Sym),
};
// string table
if (strtab_write_str(&assembler->shstrtab, ".strtab", &str_off)) {
free(shdr);
2024-09-10 00:48:08 +00:00
return M_ERROR;
}
2024-09-11 16:06:09 +00:00
assembler->strtab_shidx = count;
shdr[count++] = (Elf32_Shdr) {
.sh_name = str_off,
.sh_type = SHT_STRTAB,
.sh_flags = SHF_STRINGS,
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = 1,
.sh_entsize = 0,
};
// sh string table
if (strtab_write_str(&assembler->shstrtab, ".shstrtab", &str_off)) {
free(shdr);
2024-09-10 22:23:46 +00:00
return M_ERROR;
}
2024-09-11 16:06:09 +00:00
assembler->shstrtab_shidx = count;
shdr[count++] = (Elf32_Shdr) {
.sh_name = str_off,
.sh_type = SHT_STRTAB,
.sh_flags = SHF_STRINGS,
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = 1,
.sh_entsize = 0,
};
for (uint32_t i = 0; i < assembler->sectab.len; i++) {
struct section *sec = &assembler->sectab.sections[i];
if (sec->reltab.len == 0)
continue;
shdr[sec->reltab_shidx].sh_link =
assembler->symtab_shidx;
}
*res = shdr;
*res2 = count;
2024-09-10 00:48:08 +00:00
return M_SUCCESS;
}
2024-09-11 16:06:09 +00:00
static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr)
{
Elf32_Shdr *shdr = (Elf32_Shdr *) assembler->shdr;
Elf32_Phdr *phdr = (Elf32_Phdr *) assembler->phdr;
uint32_t ptr = 0;
// we must now correct offets and sizes inside the ehdr, phdr,
// and shdr
ptr += sizeof(Elf32_Ehdr);
// phdr
ehdr->e_phoff = ptr;
ptr += assembler->phdr_len * sizeof(Elf32_Phdr);
// reltbls
for (uint32_t i = 0; i < assembler->sectab.len; i++) {
struct section *sec = &assembler->sectab.sections[i];
if (sec->reltab.len == 0)
continue;
int idx = sec->reltab_shidx;
int len = sec->reltab.len;
shdr[idx].sh_offset = ptr;
shdr[idx].sh_size = len * sizeof(Elf32_Rela);
ptr += len * sizeof(Elf32_Rela);
}
// sections
for (uint32_t i = 0; i < assembler->sectab.len; i++) {
struct section *sec = &assembler->sectab.sections[i];
int idx = sec->shdr_idx;
phdr[i].p_offset = ptr;
phdr[i].p_vaddr = ptr;
phdr[i].p_paddr = ptr;
shdr[idx].sh_offset = ptr;
shdr[idx].sh_size = phdr[i].p_filesz;
shdr[idx].sh_addr = phdr[i].p_vaddr;
shdr[idx].sh_addralign = phdr[i].p_align;
ptr += phdr[i].p_filesz;
}
// symtab
shdr[assembler->symtab_shidx].sh_offset = ptr;
shdr[assembler->symtab_shidx].sh_link = assembler->strtab_shidx;
shdr[assembler->symtab_shidx].sh_size =
assembler->symtab.len * sizeof(Elf32_Sym);
ptr += assembler->symtab.len * sizeof(Elf32_Sym);
// strtab
shdr[assembler->strtab_shidx].sh_offset = ptr;
shdr[assembler->strtab_shidx].sh_size = assembler->strtab.size;
ptr += assembler->strtab.size;
// shstrtab
shdr[assembler->shstrtab_shidx].sh_offset = ptr;
shdr[assembler->shstrtab_shidx].sh_size = assembler->shstrtab.size;
ptr += assembler->shstrtab.size;
// shdr
ehdr->e_shoff = ptr;
}
static void update_sym_shindx(struct assembler *assembler)
{
for (size_t i = 0; i < assembler->symtab.len; i++)
{
Elf32_Sym *sym = &assembler->symtab.symbols[i];
ssize_t sec = assembler->symtab.sections[i];
if (sec >= 0) {
sym->st_shndx = assembler->
sectab.sections[sec].shdr_idx;
}
}
}
static int write_file(struct assembler *assembler, Elf32_Ehdr *ehdr,
const char *path)
2024-09-10 00:48:08 +00:00
{
2024-09-11 16:06:09 +00:00
FILE *out = fopen(path, "w");
if (out == NULL) {
ERROR("cannot write '%s'", path);
return M_ERROR;
}
// ehdr
fwrite(ehdr, sizeof(Elf32_Ehdr), 1, out);
// phdr
fwrite(assembler->phdr, sizeof(Elf32_Phdr), assembler->phdr_len, out);
// reltbls
for (uint32_t i = 0; i < assembler->sectab.len; i++) {
struct section *sec = &assembler->sectab.sections[i];
if (sec->reltab.len == 0)
continue;
void *ptr = sec->reltab.data;
int len = sec->reltab.len;
fwrite(ptr, sizeof(Elf32_Rela), len, out);
}
// sections
for (uint32_t i = 0; i < assembler->sectab.len; i++) {
struct section *sec = &assembler->sectab.sections[i];
for (uint32_t j = 0; j < sec->len; j++) {
struct section_entry *entry = &sec->entries[j];
size_t size = entry->size;
fwrite(&entry->data, size, 1, out);
while(size % sec->alignment) {
uint8_t zero = 0;
fwrite(&zero, 1, 1, out);
size++;
2024-09-10 22:23:46 +00:00
}
}
}
2024-09-11 16:06:09 +00:00
// sym tbl
fwrite(assembler->symtab.symbols, sizeof(Elf32_Sym),
assembler->symtab.len, out);
// str tbl
fwrite(assembler->strtab.ptr, assembler->strtab.size, 1, out);
// shstr tbl
fwrite(assembler->shstrtab.ptr, assembler->shstrtab.size, 1, out);
// shdr
fwrite(assembler->shdr, sizeof(Elf32_Shdr), assembler->shdr_len, out);
fclose(out);
return M_SUCCESS;
}
static int assemble_elf(struct assembler *assembler, const char *out)
{
if (assemble_phdr(assembler, (Elf32_Phdr **) &assembler->phdr,
&assembler->phdr_len)) {
return M_ERROR;
}
if (assemble_shdr(assembler, (Elf32_Shdr **) &assembler->shdr,
&assembler->shdr_len)) {
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] = ELFDATA2LSB,
[EI_VERSION] = EV_CURRENT,
[EI_OSABI] = ELFOSABI_NONE,
[EI_ABIVERSION] = 0x00,
[EI_PAD] = 0x00,
},
.e_type = ET_REL,
.e_machine = EM_MIPS,
.e_version = EV_CURRENT,
.e_entry = 0x00,
.e_phoff = 0x00,
.e_shoff = 0x00,
.e_flags = EF_MIPS_ARCH_32R6,
.e_ehsize = sizeof(Elf32_Ehdr),
.e_phentsize = sizeof(Elf32_Phdr),
.e_phnum = assembler->phdr_len,
.e_shentsize = sizeof(Elf32_Shdr),
.e_shnum = assembler->shdr_len,
.e_shstrndx = assembler->shstrtab_shidx,
};
update_offsets(assembler, &ehdr);
update_sym_shindx(assembler);
if (write_file(assembler, &ehdr, out))
return M_ERROR;
return M_SUCCESS;
}
int assemble_file(struct assembler_arguments args)
{
struct assembler assembler;
int res = M_SUCCESS;
current_file = args.in_file;
if (assembler_init(&assembler, args.in_file))
return M_ERROR;
if (res == M_SUCCESS)
res = parse_file(&assembler);
if (res == M_SUCCESS)
res = assemble_elf(&assembler, args.out_file);
assembler_free(&assembler);
return res;
}
int assembler_init(struct assembler *assembler, const char *path)
{
if (lexer_init(path, &assembler->lexer))
return M_ERROR;
if (parser_init(&assembler->lexer, &assembler->parser))
return M_ERROR;
if (strtab_init(&assembler->shstrtab))
return M_ERROR;
if (strtab_init(&assembler->strtab))
return M_ERROR;
if (symtab_init(&assembler->symtab))
return M_ERROR;
if (sectab_init(&assembler->sectab))
return M_ERROR;
assembler->symtab.strtab = &assembler->strtab;
assembler->phdr = NULL;
assembler->shdr = NULL;
return M_SUCCESS;
}
void assembler_free(struct assembler *assembler)
{
2024-09-10 22:23:46 +00:00
if (assembler->phdr)
free(assembler->phdr);
if (assembler->shdr)
free(assembler->shdr);
2024-09-11 16:06:09 +00:00
sectab_free(&assembler->sectab);
symtab_free(&assembler->symtab);
strtab_free(&assembler->strtab);
strtab_free(&assembler->shstrtab);
2024-09-10 22:23:46 +00:00
parser_free(&assembler->parser);
lexer_free(&assembler->lexer);
}