refactor elf32 assembler, add support for multiple isa's in cmdline

This commit is contained in:
Freya Murphy 2024-10-09 12:07:59 -04:00
parent 55137aee81
commit b663f82705
Signed by: freya
GPG key ID: 744AB800E383AE52
6 changed files with 337 additions and 165 deletions

View file

@ -1,99 +0,0 @@
/* Copyright (c) 2024 Freya Murphy */
#ifndef __ASM_H__
#define __ASM_H__
#include <elf.h>
#include "gen.h"
///
/// ELF string table
///
struct elf_str_table {
// size of the ptr in bytes
size_t size;
// pointer that contains
// the strings
char *ptr;
};
/* initalize a string table */
int strtab_init(struct elf_str_table *strtab);
/* free a string table */
void strtab_free(struct elf_str_table *strtab);
/* get a string form the string table */
int strtab_get_str(struct elf_str_table *strtab, const char *str, size_t *res);
/* get or append a string into the string table */
int strtab_write_str(struct elf_str_table *strtab, const char *str, size_t *res);
///
/// elf section
///
/* holds a section of the asm file (i.e. .text, .bss, .data) */
struct elf_section {
// section data *weak* pointer
struct section *data;
// index of the section in
// the ELF shdr
size_t shdr_idx;
// relocation table
size_t reltab_shidx;
uint32_t reltab_len;
Elf32_Rel *reltab;
};
///
/// assembler
///
struct assembler {
// the code generator
struct generator gen;
/// symbol table
size_t symtab_shidx;
size_t symtab_len;
Elf32_Sym *symbols;
// sh string table
size_t strtab_shidx;
struct elf_str_table strtab;
// string table
size_t shstrtab_shidx;
struct elf_str_table shstrtab;
/// sections
uint32_t section_len;
struct elf_section *sections;
/// section header
Elf32_Shdr *shdr;
uint32_t shdr_len;
};
/* defines arguments to the assembler */
struct assembler_arguments {
char *in_file;
char *out_file;
};
/* initalize the assembler */
int assembler_init(struct assembler *assembler, const char *path);
/* free the assembler */
void assembler_free(struct assembler *assembler);
/* assemble a file */
int assemble_file(struct assembler_arguments args);
#endif /* __ASM_H__ */

View file

@ -1,15 +1,11 @@
#include <merror.h> #include <merror.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <elf.h> #include <elf.h>
#include <string.h>
#include <stddef.h>
#include <melf.h> #include <melf.h>
#include "asm.h" #include "../tab.h"
#include "gen.h" #include "../masm.h"
#include "tab.h" #include "elf32.h"
extern char *current_file; extern char *current_file;
@ -83,10 +79,10 @@ static void elf_section_free(struct elf_section *sec)
free(sec->reltab); free(sec->reltab);
} }
static int asm_init_sections(struct assembler *assembler) static int asm_init_sections(struct elf_assembler *assembler)
{ {
struct section *sections = assembler->gen.sections; struct section *sections = assembler->gen->sections;
uint32_t len = assembler->gen.sections_len; uint32_t len = assembler->gen->sections_len;
struct elf_section *elftab = malloc(sizeof(struct elf_section) * len); struct elf_section *elftab = malloc(sizeof(struct elf_section) * len);
if (elftab == NULL) { if (elftab == NULL) {
@ -121,8 +117,8 @@ static int elf_sym_bind(enum symbol_type ty) {
return STB_GLOBAL; return STB_GLOBAL;
} }
static int asm_init_symtab(struct assembler *assembler) { static int asm_init_symtab(struct elf_assembler *assembler) {
struct symbol_table *symtab = &assembler->gen.symtab; struct symbol_table *symtab = &assembler->gen->symtab;
size_t len = symtab->len + 1; size_t len = symtab->len + 1;
Elf32_Sym *elftab = malloc(sizeof(Elf32_Sym) * len); Elf32_Sym *elftab = malloc(sizeof(Elf32_Sym) * len);
if (elftab == NULL) { if (elftab == NULL) {
@ -146,6 +142,17 @@ static int asm_init_symtab(struct assembler *assembler) {
return M_ERROR; return M_ERROR;
} }
// check if symbol is undefined
if (sym->secidx == SYM_SEC_STUB) {
if (sym->type == SYM_LOCAL &&
assembler->args->extern_undefined == false) {
ERROR("undefined symbol %s", sym->name.str);
return M_ERROR;
}
sym->secidx = 0;
bind = STB_GLOBAL;
}
elftab[i+1] = (Elf32_Sym) { elftab[i+1] = (Elf32_Sym) {
.st_name = B32(str_off), .st_name = B32(str_off),
.st_info = ELF32_ST_INFO(bind, type), .st_info = ELF32_ST_INFO(bind, type),
@ -162,18 +169,7 @@ static int asm_init_symtab(struct assembler *assembler) {
return M_SUCCESS; return M_SUCCESS;
} }
static int parse_file(struct assembler *assembler) static int assemble_shdr(struct elf_assembler *assembler, Elf32_Shdr **res,
{
if (generate_mips32r6(&assembler->gen))
return M_ERROR;
if (asm_init_sections(assembler))
return M_ERROR;
if (asm_init_symtab(assembler))
return M_ERROR;
return M_SUCCESS;
}
static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
uint32_t *res2) uint32_t *res2)
{ {
uint32_t max_entries = 0; uint32_t max_entries = 0;
@ -337,7 +333,7 @@ static int assemble_shdr(struct assembler *assembler, Elf32_Shdr **res,
return M_SUCCESS; return M_SUCCESS;
} }
static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr) static void update_offsets(struct elf_assembler *assembler, Elf32_Ehdr *ehdr)
{ {
Elf32_Shdr *shdr = (Elf32_Shdr *) assembler->shdr; Elf32_Shdr *shdr = (Elf32_Shdr *) assembler->shdr;
uint32_t ptr = 0; uint32_t ptr = 0;
@ -402,7 +398,7 @@ static void update_offsets(struct assembler *assembler, Elf32_Ehdr *ehdr)
ehdr->e_shoff = B32(ptr); ehdr->e_shoff = B32(ptr);
} }
static int write_file(struct assembler *assembler, Elf32_Ehdr *ehdr, static int write_file(struct elf_assembler *assembler, Elf32_Ehdr *ehdr,
const char *path) const char *path)
{ {
FILE *out = fopen(path, "w"); FILE *out = fopen(path, "w");
@ -453,11 +449,11 @@ static int write_file(struct assembler *assembler, Elf32_Ehdr *ehdr,
return M_SUCCESS; return M_SUCCESS;
} }
static void update_sym_shndx(struct assembler *assembler) static void update_sym_shndx(struct elf_assembler *assembler)
{ {
for (uint32_t i = 1; i < assembler->symtab_len; i++) { for (uint32_t i = 1; i < assembler->symtab_len; i++) {
Elf32_Sym *esym = &assembler->symbols[i]; Elf32_Sym *esym = &assembler->symbols[i];
struct symbol *sym = &assembler->gen.symtab.symbols[i - 1]; struct symbol *sym = &assembler->gen->symtab.symbols[i - 1];
// get shindx // get shindx
int shindx = 0; int shindx = 0;
@ -470,14 +466,42 @@ static void update_sym_shndx(struct assembler *assembler)
} }
} }
static int assemble_elf(struct assembler *assembler, const char *out) static int assemble_elf(struct elf_assembler *assembler, const char *out)
{ {
if (asm_init_sections(assembler))
return M_ERROR;
if (asm_init_symtab(assembler))
return M_ERROR;
if (assemble_shdr(assembler, &assembler->shdr, &assembler->shdr_len)) if (assemble_shdr(assembler, &assembler->shdr, &assembler->shdr_len))
return M_ERROR; return M_ERROR;
// get ehdr flags
uint32_t flags = EF_MIPS_NAN2008;
switch (assembler->args->isa) {
case ISA_MIPS1:
flags |= EF_MIPS_ARCH_1;
break;
case ISA_MIPS32R2:
flags |= EF_MIPS_ARCH_32R2;
break;
case ISA_MIPS32R6:
flags |= EF_MIPS_ARCH_32R6;
break;
}
switch (assembler->args->abi) {
case ABI_O32:
flags |= EF_MIPS_ABI_O32;
break;
case ABI_NONE:
break;
}
Elf32_Ehdr ehdr = MIPS_ELF_EHDR; Elf32_Ehdr ehdr = MIPS_ELF_EHDR;
ehdr.e_shnum = B16(assembler->shdr_len); ehdr.e_shnum = B16(assembler->shdr_len);
ehdr.e_shstrndx = B16(assembler->shstrtab_shidx); ehdr.e_shstrndx = B16(assembler->shstrtab_shidx);
ehdr.e_flags = B32(flags);
update_offsets(assembler, &ehdr); update_offsets(assembler, &ehdr);
update_sym_shndx(assembler); update_sym_shndx(assembler);
@ -487,41 +511,20 @@ static int assemble_elf(struct assembler *assembler, const char *out)
return M_SUCCESS; return M_SUCCESS;
} }
int assemble_file(struct assembler_arguments args) static int assembler_init(struct elf_assembler *assembler,
struct generator *gen,
struct arguments *args)
{ {
struct assembler assembler; assembler->args = args;
int res = M_SUCCESS; assembler->gen = gen;
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)
{
assembler->shdr = NULL; assembler->shdr = NULL;
assembler->symbols = NULL; assembler->symbols = NULL;
assembler->sections = NULL; assembler->sections = NULL;
assembler->strtab.ptr = NULL; assembler->strtab.ptr = NULL;
assembler->shstrtab.ptr = NULL; assembler->shstrtab.ptr = NULL;
assembler->gen.sections = NULL;
assembler->gen.symtab.symbols = NULL;
assembler->section_len = 0; assembler->section_len = 0;
if (generator_init(path, &assembler->gen))
return M_ERROR;
if (strtab_init(&assembler->shstrtab)) if (strtab_init(&assembler->shstrtab))
return M_ERROR; return M_ERROR;
@ -531,7 +534,7 @@ int assembler_init(struct assembler *assembler, const char *path)
return M_SUCCESS; return M_SUCCESS;
} }
void assembler_free(struct assembler *assembler) static void assembler_free(struct elf_assembler *assembler)
{ {
if (assembler->shdr) if (assembler->shdr)
free(assembler->shdr); free(assembler->shdr);
@ -545,5 +548,20 @@ void assembler_free(struct assembler *assembler)
strtab_free(&assembler->strtab); strtab_free(&assembler->strtab);
strtab_free(&assembler->shstrtab); strtab_free(&assembler->shstrtab);
generator_free(&assembler->gen);
} }
int assemble_elf32(struct generator *gen, struct arguments *args)
{
struct elf_assembler assembler;
int res = M_SUCCESS;
current_file = args->in_file;
if (assembler_init(&assembler, gen, args))
return M_ERROR;
res = assemble_elf(&assembler, args->out_file);
assembler_free(&assembler);
return res;
}

90
masm/asm/elf32.h Normal file
View file

@ -0,0 +1,90 @@
/* Copyright (c) 2024 Freya Murphy */
#ifndef __ELF32_H__
#define __ELF32_H__
#include <elf.h>
#include "../gen.h"
#include "../masm.h"
///
/// ELF string table
///
struct elf_str_table {
// size of the ptr in bytes
size_t size;
// pointer that contains
// the strings
char *ptr;
};
/* initalize a string table */
int strtab_init(struct elf_str_table *strtab);
/* free a string table */
void strtab_free(struct elf_str_table *strtab);
/* get a string form the string table */
int strtab_get_str(struct elf_str_table *strtab, const char *str, size_t *res);
/* get or append a string into the string table */
int strtab_write_str(struct elf_str_table *strtab, const char *str, size_t *res);
///
/// elf section
///
/* holds a section of the asm file (i.e. .text, .bss, .data) */
struct elf_section {
// section data *weak* pointer
struct section *data;
// index of the section in
// the ELF shdr
size_t shdr_idx;
// relocation table
size_t reltab_shidx;
uint32_t reltab_len;
Elf32_Rel *reltab;
};
///
/// assembler
///
struct elf_assembler {
// arguments passed in
struct arguments *args;
// the code generator
struct generator *gen;
/// symbol table
size_t symtab_shidx;
size_t symtab_len;
Elf32_Sym *symbols;
// sh string table
size_t strtab_shidx;
struct elf_str_table strtab;
// string table
size_t shstrtab_shidx;
struct elf_str_table shstrtab;
/// sections
uint32_t section_len;
struct elf_section *sections;
/// section header
Elf32_Shdr *shdr;
uint32_t shdr_len;
};
int assemble_elf32(struct generator *gen, struct arguments *args);
#endif /* __ELF32_H__ */

View file

@ -2,7 +2,7 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include "asm.h" #include "elf32.h"
int strtab_get_str(struct elf_str_table *strtab, const char *str, size_t *res) int strtab_get_str(struct elf_str_table *strtab, const char *str, size_t *res)
{ {

View file

@ -1,33 +1,151 @@
#include <unistd.h> #include <unistd.h>
#include <merror.h> #include <merror.h>
#include <string.h> #include <string.h>
#include <stdio.h>
#include "asm.h" #include "gen.h"
#include "masm.h"
#include "asm/elf32.h"
void help(void) { void help(void) {
printf("usage: masm [options] source.asm\n\n"); printf(
printf("options:\n"); "usage: masm [options] source.asm\n"
printf("\t-h\t\tprints this help message\n"); "\n"
printf("\t-o <output>\tselect a output file destination\n"); "options: \n"
" -h print the help message \n"
" -g assume undefined symbols are external\n"
" -o <output> specify the object file output name \n"
" -a <abi> specify mips abi used [none, o32] \n"
" default: o32 \n"
" -i <isa> mips machine isa to assemble for [mips1, mips32r2, mips32r6] \n"
" default: mips32r6\n"
" -f <format> specify the object file format [elf32] \n"
" defualt: elf32\n"
);
}
static int read_isa(enum isa *isa, const char *str)
{
#define __ISA_CHK(name) \
if (strcasecmp(#name, str) == 0) { \
*isa = ISA_ ##name; \
return M_SUCCESS; \
} \
__ISA_CHK(MIPS1);
__ISA_CHK(MIPS32R2);
__ISA_CHK(MIPS32R6);
ERROR("invalid isa '%s'", str);
return M_ERROR;
}
static int read_abi(enum abi *abi, const char *str)
{
#define __ABI_CHK(name) \
if (strcasecmp(#name, str) == 0) { \
*abi = ABI_ ##name; \
return M_SUCCESS; \
} \
__ABI_CHK(O32);
__ABI_CHK(NONE);
ERROR("invalid abi '%s'", str);
return M_ERROR;
}
static int read_format(enum format *format, const char *str)
{
#define __FORMAT_CHK(name) \
if (strcasecmp(#name, str) == 0) { \
*format = FORMAT_ ##name; \
return M_SUCCESS; \
} \
__FORMAT_CHK(ELF32);
ERROR("invalid format '%s'", str);
return M_ERROR;
}
static int generate(struct generator *gen, struct arguments *args)
{
if (generator_init(args->in_file, gen))
return M_ERROR;
switch (args->isa) {
case ISA_MIPS1:
return generate_mips1(gen);
case ISA_MIPS32R2:
return generate_mips32r2(gen);
case ISA_MIPS32R6:
return generate_mips32r6(gen);
}
return M_ERROR;
}
static int assemble(struct arguments *args)
{
struct generator gen;
int res = M_SUCCESS;
if (generate(&gen, args))
return M_ERROR;
switch (args->format) {
case FORMAT_ELF32:
res = assemble_elf32(&gen, args);
break;
default:
res = M_ERROR;
break;
}
generator_free(&gen);
return res;
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
struct assembler_arguments args = { struct arguments args = {
.in_file = NULL, .in_file = NULL,
.out_file = "out.o", .out_file = "out.o",
.extern_undefined = false,
.isa = ISA_MIPS32R6,
.abi = ABI_O32,
.format = FORMAT_ELF32
}; };
int c; int c;
while ((c = getopt(argc, argv, "ho:")) != 1) { while ((c = getopt(argc, argv, "hgo:a:i:f:")) != 1) {
switch(c) { switch(c) {
case 'h': case 'h':
help(); help();
return M_SUCCESS; return M_SUCCESS;
case 'g':
args.extern_undefined = true;
break;
case 'o': case 'o':
args.out_file = optarg; args.out_file = optarg;
break; break;
case 'a':
if (read_abi(&args.abi, optarg))
return M_ERROR;
break;
case 'i':
if (read_isa(&args.isa, optarg))
return M_ERROR;
break;
case 'f':
if (read_format(&args.format, optarg))
return M_ERROR;
break;
case '?': case '?':
return M_ERROR; return M_ERROR;
default: default:
@ -48,5 +166,5 @@ next:
args.in_file = argv[optind]; args.in_file = argv[optind];
return assemble_file(args); return assemble(&args);
} }

45
masm/masm.h Normal file
View file

@ -0,0 +1,45 @@
/* Copyright (c) 2024 Freya Murphy */
#ifndef __MASM_H__
#define __MASM_H__
// isa to asemble for
enum isa {
ISA_MIPS1, // a.k.a mipsR2000
ISA_MIPS32R2,
ISA_MIPS32R6,
};
// abi to mark output object
enum abi {
ABI_O32, // mips o32 abi
ABI_NONE, // no flag output
};
// format for the object file
enum format {
FORMAT_ELF32,
};
// defines arguments
struct arguments {
// files to read from and
// write to
char *in_file;
char *out_file;
// if undefined symbols should
// be treated as extern
bool extern_undefined;
// isa to assemble for
enum isa isa;
// abi to mark object
enum abi abi;
// format to output
enum format format;
};
#endif /* __ASM_H__ */