#include #include #include #include #include #include #include #include #include "asm.h" #include "mlimits.h" #include "parse.h" #include "parse_mips32.h" extern char *current_file; static int handle_directive(struct assembler *assembler, struct mips32_directive *directive) { switch (directive->type) { case MIPS32_DIRECTIVE_SECTION: { struct section_table *sec_tbl = &assembler->parser.sec_tbl; struct section *sec; if (sectbl_get(sec_tbl, &sec, directive->name) == M_SUCCESS) { sec_tbl->current = sec; break; } if (sectbl_alloc(sec_tbl, &sec, directive->name)) return M_ERROR; sec_tbl->current = sec; break; } case MIPS32_DIRECTIVE_ALIGN: { assembler->parser.sec_tbl.current->alignment = 1 << directive->align; break; } case MIPS32_DIRECTIVE_SPACE: { struct section_entry entry; entry.type = ENT_NO_DATA; entry.size = directive->space; if (sec_push(assembler->parser.sec_tbl.current, entry)) return M_ERROR; break; } case MIPS32_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->parser.sec_tbl.current, entry)) return M_ERROR; } break; } case MIPS32_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->parser.sec_tbl.current, entry)) return M_ERROR; } break; } case MIPS32_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->parser.sec_tbl.current, entry)) return M_ERROR; } break; } case MIPS32_DIRECTIVE_EXTERN: { struct symbol symbol; if (symtbl_find(&assembler->sym_tbl, NULL, directive->name) == M_SUCCESS) { ERROR("cannot extern local symbol '%s'", directive->name); return M_ERROR; } symbol = (struct symbol) { .name = "", .sec = NULL, .index = 0, .flag = SYM_EXTERNAL, }; strcpy(symbol.name, directive->name); if (symtbl_push(&assembler->sym_tbl, symbol)) return M_ERROR; break; } case MIPS32_DIRECTIVE_GLOBL: { struct symbol symbol; if (symtbl_find(&assembler->sym_tbl, NULL, directive->name) == M_SUCCESS) { symbol.flag = SYM_GLOBAL; break; } symbol = (struct symbol) { .name = "", .sec = NULL, .index = 0, .flag = SYM_GLOBAL, }; strcpy(symbol.name, directive->name); if (symtbl_push(&assembler->sym_tbl, symbol)) return M_ERROR; break; } } return M_SUCCESS; } static int handle_label(struct assembler *assembler, const char name[MAX_LEX_LENGTH]) { struct symbol *ref; if (symtbl_find(&assembler->sym_tbl, &ref, name) == M_SUCCESS) { if (ref->flag == SYM_GLOBAL && ref->sec == NULL) { ref->sec = assembler->parser.sec_tbl.current; ref->index = assembler->parser.sec_tbl.current->count; return M_SUCCESS; } ERROR("redefined symbol '%s'", name); return M_ERROR; } struct symbol symbol; symbol = (struct symbol) { .name = "", .sec = assembler->parser.sec_tbl.current, .index = assembler->parser.sec_tbl.current->count, .flag = SYM_LOCAL, }; strcpy(symbol.name, name); if (symtbl_push(&assembler->sym_tbl, symbol)) return M_ERROR; return M_SUCCESS; } static int parse_file(struct assembler *assembler) { struct parser *parser = &assembler->parser; while (1) { struct expr expr; if (parser_next(parser, &expr)) return M_ERROR; switch (expr.type) { case EXPR_INS: struct section_entry entry; entry.type = ENT_INS; entry.size = sizeof(struct mips32_instruction); entry.ins = expr.ins; if (sec_push(parser->sec_tbl.current, entry)) return M_ERROR; break; case EXPR_DIRECTIVE: if (handle_directive(assembler, &expr.directive.mips32)) return M_ERROR; break; case EXPR_LABEL: if (handle_label(assembler, expr.text)) return M_ERROR; break; case EXPR_CONSTANT: break; } } struct section_meta *meta = malloc(sizeof(struct section_meta) * parser->sec_tbl.count); if (meta == NULL) { ERROR("cannot alloc"); return M_ERROR; } assembler->meta = meta; size_t ptr = 0; for (uint32_t i = 0; i < parser->sec_tbl.count; i++) { struct section *sec = &parser->sec_tbl.sections[i]; meta[i].v_addr = ptr; ptr += sec_size(sec); } return M_SUCCESS; } static int assemble_phdr(struct assembler *assembler, Elf32_Phdr **res, uint32_t *res2) { struct parser *parser = &assembler->parser; Elf32_Phdr *phdr = malloc(sizeof(Elf32_Phdr) * parser->sec_tbl.count); if (phdr == NULL) { ERROR("cannot alloc"); return M_ERROR;; } for (uint32_t i = 0; i < parser->sec_tbl.count; i++) { Elf32_Phdr *hdr = &phdr[i]; struct section *sec = &parser->sec_tbl.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 = parser->sec_tbl.count; return M_SUCCESS; } static int assemble_symtab(struct assembler *assembler, Elf32_Sym **res, uint32_t *res2) { Elf32_Sym *stbl = malloc(sizeof(Elf32_Sym) * assembler->sym_tbl .count); size_t size = 0; if (stbl == NULL) return M_ERROR; for (uint32_t i = 0; i < assembler->sym_tbl.count; i++) { struct symbol *sym = &assembler->sym_tbl.symbols[i]; size_t str_off; unsigned char bind; unsigned char type = STT_NOTYPE; if (strtbl_write_str(&assembler->str_tbl, sym->name, &str_off)) { free(stbl); return M_ERROR; } if (sym->flag == SYM_GLOBAL && sym->sec == NULL) { ERROR("never defined global symbol '%s'", sym->name); return M_ERROR; } if (sym->flag == SYM_LOCAL) bind = STB_LOCAL; else bind = STB_GLOBAL; stbl[i] = (Elf32_Sym) { .st_name = str_off, .st_value = sym->index, .st_size = 0, .st_info = ELF32_ST_INFO(bind, type), .st_other = ELF32_ST_VISIBILITY(STV_DEFAULT), .st_shndx = 0, }; size = i + 1; }; *res = stbl; *res2 = size; return M_SUCCESS; } static void assemble_symtab_shndx(struct assembler *assembler, Elf32_Sym *tbl) { for (uint32_t i = 0; i < assembler->sym_tbl.count; i++) { struct symbol *sym = &assembler->sym_tbl.symbols[i]; if (sym->sec != NULL) tbl[i].st_shndx = assembler->meta[sym->sec->index].shdr_idx; } } static int assemble_reltbl_sec(struct assembler *assembler, Elf32_Sym *symtab, uint32_t symtab_len, struct section *sec) { uint32_t len = 0; for (uint32_t i = 0; i < assembler->parser.ref_tbl.count; i++) { struct reference *ref = &assembler->parser.ref_tbl.references[i]; if (ref->section->index == sec->index) { len++; } } if (len == 0) { assembler->meta[sec->index].reltbl = NULL; return M_SUCCESS; } Elf32_Rela *reltbl = malloc(sizeof(Elf32_Rela) * len); if (reltbl == NULL) { ERROR("cannot alloc"); return M_ERROR; } for (uint32_t i = 0; i < assembler->parser.ref_tbl.count; i++) { struct reference *ref = &assembler->parser.ref_tbl.references[i]; struct mips32_instruction *ins = &ref->section-> entries[ref->index].ins.mips32; if (ref->section->index != sec->index) { continue; } int32_t addend = 0; unsigned char type = 0; switch (ref->type) { case REF_OFFESET: addend = ins->B_data.offset; type = R_MIPS_PC16; break; case REF_TARGET: addend = ins->J_data.target; type = R_MIPS_26; break; } int32_t symidx = -1; for (uint32_t i = 0; i < symtab_len; i++) { Elf32_Sym *sym = &symtab[i]; const char *str = &assembler->str_tbl.ptr[sym->st_name]; if (strcmp(ref->name, str) == 0) { symidx = i; break; } } if (symidx == -1) { ERROR("undefined symbol '%s'", ref->name); free(reltbl); return M_ERROR; } reltbl[i] = (Elf32_Rela) { .r_info = ELF32_R_INFO(symidx, type), .r_addend = addend, .r_offset = sec_index(ref->section, ref->index), }; }; assembler->meta[sec->index].reltbl_len = len; assembler->meta[sec->index].reltbl = reltbl; return M_SUCCESS; } static int assemble_reltbl(struct assembler *assembler, Elf32_Sym *symtab, uint32_t symtab_len) { for (uint32_t i = 0; i < assembler->parser.sec_tbl.count; i++) { struct section *sec = &assembler->parser.sec_tbl.sections[i]; if (assemble_reltbl_sec(assembler, symtab, symtab_len, sec)) return M_ERROR; } 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->parser.sec_tbl.count; // sections max_entries += assembler->parser.sec_tbl.count; // 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->parser.sec_tbl.count; i++) { if (assembler->meta[i].reltbl == NULL) continue; struct section *sec = &assembler->parser.sec_tbl.sections[i]; const char *prefix = ".reltab."; char reltab_name[MAX_LEX_LENGTH + 8]; strcpy(reltab_name, prefix); strcat(reltab_name, sec->name); if (strtbl_write_str(&assembler->shstr_tbl, reltab_name, &str_off)) { free(shdr); return M_ERROR; } assembler->meta[i].reltbl_idx = 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), }; } // for each section for (uint32_t i = 0; i < assembler->parser.sec_tbl.count; i++) { struct section *sec = &assembler->parser.sec_tbl.sections[i]; char name[MAX_LEX_LENGTH+1] = "."; strcat(name, sec->name); if (strtbl_write_str(&assembler->shstr_tbl, name, &str_off)) { free(shdr); return M_ERROR; } assembler->meta[i].shdr_idx = count; if (assembler->meta[i].reltbl != NULL) shdr[assembler->meta[i].reltbl_idx].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 mips32_instruction), }; } // symbol table if (strtbl_write_str(&assembler->shstr_tbl, ".symtab", &str_off)) { free(shdr); return M_ERROR; } assembler->symtab_idx = 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 (strtbl_write_str(&assembler->shstr_tbl, ".strtab", &str_off)) { free(shdr); return M_ERROR; } assembler->strtbl_idx = 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 (strtbl_write_str(&assembler->shstr_tbl, ".shstrtab", &str_off)) { free(shdr); return M_ERROR; } assembler->shstrtbl_idx = 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->parser.sec_tbl.count; i++) { if (assembler->meta[i].reltbl == NULL) continue; shdr[assembler->meta[i].reltbl_idx].sh_link = assembler->symtab_idx; } *res = shdr; *res2 = count; return M_SUCCESS; } 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->parser.sec_tbl.count; i++) { if (assembler->meta[i].reltbl == NULL) continue; int idx = assembler->meta[i].reltbl_idx; int len = assembler->meta[i].reltbl_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->parser.sec_tbl.count; i++) { int idx = assembler->meta[i].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_idx].sh_offset = ptr; shdr[assembler->symtab_idx].sh_link = assembler->strtbl_idx; shdr[assembler->symtab_idx].sh_size = assembler->symtab_len * sizeof(Elf32_Sym); ptr += assembler->symtab_len * sizeof(Elf32_Sym); // strtbl shdr[assembler->strtbl_idx].sh_offset = ptr; shdr[assembler->strtbl_idx].sh_size = assembler->str_tbl.size; ptr += assembler->str_tbl.size; // shstrtbl shdr[assembler->shstrtbl_idx].sh_offset = ptr; shdr[assembler->shstrtbl_idx].sh_size = assembler->shstr_tbl.size; ptr += assembler->shstr_tbl.size; // shdr ehdr->e_shoff = ptr; } static int write_file(struct assembler *assembler, Elf32_Ehdr *ehdr, const char *path) { 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->parser.sec_tbl.count; i++) { if (assembler->meta[i].reltbl == NULL) continue; void *ptr = assembler->meta[i].reltbl; int len = assembler->meta[i].reltbl_len; fwrite(ptr, sizeof(Elf32_Rela), len, out); } // sections for (uint32_t i = 0; i < assembler->parser.sec_tbl.count; i++) { struct section *sec = &assembler->parser.sec_tbl.sections[i]; for (uint32_t j = 0; j < sec->count; 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++; } } } // sym tbl fwrite(assembler->symtab, sizeof(Elf32_Sym), assembler->symtab_len, out); // str tbl fwrite(assembler->str_tbl.ptr, assembler->str_tbl.size, 1, out); // shstr tbl fwrite(assembler->shstr_tbl.ptr, assembler->shstr_tbl.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_symtab(assembler, (Elf32_Sym **) &assembler->symtab, &assembler->symtab_len)) return M_ERROR; if (assemble_reltbl(assembler, assembler->symtab, assembler->symtab_len)) { return M_ERROR; }; 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; }; // update the symbol tables with their given section assemble_symtab_shndx(assembler, assembler->symtab); 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->shstrtbl_idx, }; update_offsets(assembler, &ehdr); if (write_file(assembler, &ehdr, out)) return M_ERROR; return M_SUCCESS; } int assemble_file_mips32(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; mips32_parser_init(&assembler.parser); if (res == M_SUCCESS) res = parse_file(&assembler); if (res == M_SUCCESS) res = assemble_elf(&assembler, args.out_file); assembler_free(&assembler); return res; }