diff options
Diffstat (limited to '')
-rw-r--r-- | masm/asm.c | 718 |
1 files changed, 679 insertions, 39 deletions
@@ -1,77 +1,717 @@ #include <merror.h> +#include <mips.h> +#include <stdio.h> #include <stdlib.h> +#include <elf.h> +#include <string.h> +#include <stddef.h> #include "asm.h" +#include "mlimits.h" +#include "parse.h" -int assembler_init(struct assembler *assembler, const char *path) +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) { - if (lexer_init(path, &assembler->lexer)) + 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; + } + } + + 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); return M_ERROR; + } - if (parser_init(&assembler->lexer, &assembler->parser)) { - lexer_free(&assembler->lexer); + if (create_symbol(assembler, name, cur->index, sec_size(cur), + STB_LOCAL)) return M_ERROR; + + 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), + }; } - if (strtbl_init(&assembler->shstr_tbl)) { - parser_free(&assembler->parser); - lexer_free(&assembler->lexer); + // 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); return M_ERROR; } - if (strtbl_init(&assembler->str_tbl)) { - strtbl_free(&assembler->shstr_tbl); - parser_free(&assembler->parser); - lexer_free(&assembler->lexer); + 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); return M_ERROR; } - if (symtbl_init(&assembler->sym_tbl)) { - strtbl_free(&assembler->str_tbl); - strtbl_free(&assembler->shstr_tbl); - parser_free(&assembler->parser); - lexer_free(&assembler->lexer); + 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); return M_ERROR; } - assembler->meta = NULL; - assembler->phdr = NULL; - assembler->shdr = NULL; - assembler->symtab = NULL; + 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; return M_SUCCESS; } -void assembler_free(struct assembler *assembler) +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) { - if (assembler->meta) { - for (uint32_t i = 0; i < assembler->parser.sec_tbl.count; i++) { - if (assembler->meta[i].reltbl != NULL) { - free(assembler->meta[i].reltbl); + 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++; } } - free(assembler->meta); } + // 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) +{ if (assembler->phdr) free(assembler->phdr); if (assembler->shdr) free(assembler->shdr); - if (assembler->symtab) - free(assembler->symtab); - symtbl_free(&assembler->sym_tbl); - strtbl_free(&assembler->str_tbl); - strtbl_free(&assembler->shstr_tbl); + sectab_free(&assembler->sectab); + symtab_free(&assembler->symtab); + strtab_free(&assembler->strtab); + strtab_free(&assembler->shstrtab); parser_free(&assembler->parser); lexer_free(&assembler->lexer); } - -int assemble_file(struct assembler_arguments args) { - switch (args.isa) { - case ISA_MIPS32: - return assemble_file_mips32(args); - } - return M_ERROR; -} |