mips/masm/asm/elf32.c

568 lines
12 KiB
C
Raw Permalink Normal View History

2024-09-10 00:48:08 +00:00
#include <merror.h>
#include <stdlib.h>
2024-09-11 16:06:09 +00:00
#include <elf.h>
#include <melf.h>
2024-09-10 00:48:08 +00:00
#include "../tab.h"
#include "../masm.h"
#include "elf32.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
2024-09-12 13:42:04 +00:00
#define SEC_ALIGN 0x1000
2024-10-04 23:41:10 +00:00
static int elf_rel_type(enum reference_type ty) {
switch (ty) {
case REF_NONE:
return R_MIPS_NONE;
case REF_MIPS_16:
return R_MIPS_16;
case REF_MIPS_26:
return R_MIPS_26;
case REF_MIPS_PC16:
return R_MIPS_PC16;
case REF_MIPS_LO16:
return R_MIPS_LO16;
case REF_MIPS_HI16:
return R_MIPS_HI16;
}
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
return R_MIPS_NONE;
}
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
static int elf_section_init_reltab(struct section *sec,
struct elf_section *elf_sec)
{
Elf32_Rel *reltab = malloc(sizeof(Elf32_Rel) *
sec->reftab.len);
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
if (reltab == NULL) {
PERROR("cannot alloc");
2024-09-11 16:06:09 +00:00
return M_ERROR;
2024-10-04 23:41:10 +00:00
}
for (uint32_t i = 0; i < sec->reftab.len; i++) {
Elf32_Rel *rel = &reltab[i];
struct reference *ref = &sec->reftab.references[i];
rel->r_offset = B32(ref->offset);
int sym = ref->symbol->tabidx + 1;
int type = elf_rel_type(ref->type);
rel->r_info = B32(ELF32_R_INFO(sym, type));
}
elf_sec->reltab_len = sec->reftab.len;
elf_sec->reltab = reltab;
2024-09-11 16:06:09 +00:00
return M_SUCCESS;
}
2024-10-04 23:41:10 +00:00
static int elf_section_init(struct section *sec, struct elf_section *elf_sec)
2024-09-11 16:06:09 +00:00
{
2024-10-04 23:41:10 +00:00
elf_sec->data = sec;
elf_sec->shdr_idx = 0; // dont know yet
elf_sec->reltab_shidx = 0; // dont know yet
elf_sec->reltab_len = sec->reftab.len;
elf_sec->reltab = NULL;
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
if (sec->reftab.len && elf_section_init_reltab(sec, elf_sec))
2024-09-11 16:06:09 +00:00
return M_ERROR;
return M_SUCCESS;
}
2024-10-04 23:41:10 +00:00
/* free an elf section */
static void elf_section_free(struct elf_section *sec)
2024-09-11 16:06:09 +00:00
{
2024-10-04 23:41:10 +00:00
if (sec->reltab != NULL)
free(sec->reltab);
}
2024-09-11 16:06:09 +00:00
static int asm_init_sections(struct elf_assembler *assembler)
2024-10-04 23:41:10 +00:00
{
struct section *sections = assembler->gen->sections;
uint32_t len = assembler->gen->sections_len;
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
struct elf_section *elftab = malloc(sizeof(struct elf_section) * len);
if (elftab == NULL) {
PERROR("cannot alloc");
return M_ERROR;
2024-09-11 16:06:09 +00:00
}
2024-10-04 23:41:10 +00:00
for (uint32_t i = 0; i < len; i++) {
struct elf_section *elfsec = &elftab[i];
elfsec->data = &sections[i];
if (elf_section_init(&sections[i], elfsec)) {
free(elftab);
2024-09-11 16:06:09 +00:00
return M_ERROR;
}
}
2024-10-04 23:41:10 +00:00
assembler->sections = elftab;
assembler->section_len = len;
return M_SUCCESS;
2024-09-11 16:06:09 +00:00
}
2024-10-04 23:41:10 +00:00
static int elf_sym_bind(enum symbol_type ty) {
switch (ty) {
case SYM_LOCAL:
return STB_LOCAL;
case SYM_GLOBAL:
return STB_GLOBAL;
case SYM_EXTERN:
return STB_GLOBAL;
2024-09-11 16:06:09 +00:00
}
2024-09-10 00:48:08 +00:00
2024-10-04 23:41:10 +00:00
return STB_GLOBAL;
2024-09-11 16:06:09 +00:00
}
static int asm_init_symtab(struct elf_assembler *assembler) {
struct symbol_table *symtab = &assembler->gen->symtab;
2024-10-04 23:41:10 +00:00
size_t len = symtab->len + 1;
Elf32_Sym *elftab = malloc(sizeof(Elf32_Sym) * len);
if (elftab == NULL) {
PERROR("cannot alloc");
}
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
// add null entry
elftab[0] = (Elf32_Sym) {0};
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
// add rest of the entries
for (uint32_t i = 0; i < symtab->len; i++) {
struct symbol *sym = &symtab->symbols[i];
int bind = elf_sym_bind(sym->type);
int type = STT_NOTYPE;
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
// get name
size_t str_off;
if (strtab_write_str(&assembler->strtab, sym->name.str,
&str_off)) {
free(elftab);
2024-09-11 16:06:09 +00:00
return M_ERROR;
2024-10-04 23:41:10 +00:00
}
2024-09-11 16:06:09 +00:00
// 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;
}
2024-10-04 23:41:10 +00:00
elftab[i+1] = (Elf32_Sym) {
.st_name = B32(str_off),
.st_info = ELF32_ST_INFO(bind, type),
.st_size = 0,
.st_other = 0,
.st_value = B32(sym->offset),
.st_shndx = 0,
2024-09-11 16:06:09 +00:00
};
}
2024-10-04 23:41:10 +00:00
assembler->symbols = elftab;
assembler->symtab_len = len;
2024-09-11 16:06:09 +00:00
return M_SUCCESS;
}
static int assemble_shdr(struct elf_assembler *assembler, Elf32_Shdr **res,
2024-09-11 16:06:09 +00:00
uint32_t *res2)
{
2024-09-12 12:37:46 +00:00
uint32_t max_entries = 0;
max_entries += 1; // null
max_entries += 1; // symtab
max_entries += 1; // strtab
max_entries += 1; // shtrtab
2024-10-04 23:41:10 +00:00
max_entries += assembler->section_len; // sections
max_entries += assembler->section_len; // reltabs per section
2024-09-11 16:06:09 +00:00
Elf32_Shdr *shdr = malloc(sizeof(Elf32_Shdr) * max_entries);
2024-09-22 20:02:42 +00:00
if (shdr == NULL) {
PERROR("cannot alloc");
return M_ERROR;
}
2024-09-11 16:06:09 +00:00
size_t str_off;
uint32_t count = 0;
2024-09-12 12:37:46 +00:00
// null
shdr[count++] = (Elf32_Shdr) {0};
// reltables
2024-10-04 23:41:10 +00:00
for (uint32_t i = 0; i < assembler->section_len; i++) {
struct elf_section *sec = &assembler->sections[i];
const char *prefix = ".reltab";
2024-10-05 14:16:45 +00:00
char reltab_name[MAX_EXT_LENGTH + strlen(prefix)];
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
if (sec->reltab_len == 0)
2024-09-11 16:06:09 +00:00
continue;
strcpy(reltab_name, prefix);
2024-10-04 23:41:10 +00:00
strncat(reltab_name, sec->data->name.str,
2024-10-05 14:16:45 +00:00
MAX_EXT_LENGTH - strlen(prefix));
2024-09-11 16:06:09 +00:00
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 = B32(str_off),
2024-10-04 23:41:10 +00:00
.sh_type = B32(SHT_REL),
2024-09-11 16:06:09 +00:00
.sh_flags = 0,
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = B32(1),
2024-10-04 23:41:10 +00:00
.sh_entsize = B32(sizeof(Elf32_Rel)),
2024-09-11 16:06:09 +00:00
};
2024-09-10 00:48:08 +00:00
}
2024-09-11 16:06:09 +00:00
// for each section
2024-10-04 23:41:10 +00:00
for (uint32_t i = 0; i < assembler->section_len; i++) {
struct elf_section *sec = &assembler->sections[i];
const char *name = sec->data->name.str;
2024-09-11 16:06:09 +00:00
if (strtab_write_str(&assembler->shstrtab, name, &str_off)) {
free(shdr);
return M_ERROR;
}
sec->shdr_idx = count;
2024-10-04 23:41:10 +00:00
if (sec->reltab_len != 0)
shdr[sec->reltab_shidx].sh_info = B32(count);
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
shdr[count++] = (Elf32_Shdr){
.sh_name = B32(str_off),
.sh_type = B32(sec->data->execute ?
SHT_PROGBITS : SHT_NOBITS),
.sh_flags = B32(
(sec->data->write << 0) |
(sec->data->execute << 2) |
2024-09-12 12:37:46 +00:00
SHF_ALLOC),
2024-10-04 23:41:10 +00:00
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = B32(SEC_ALIGN),
.sh_entsize = 0,
};
}
2024-09-11 16:06:09 +00:00
// 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 = B32(str_off),
.sh_type = B32(SHT_SYMTAB),
2024-09-11 16:06:09 +00:00
.sh_flags = 0,
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 1,
.sh_info = 0,
.sh_addralign = B32(1),
.sh_entsize = B32(sizeof(Elf32_Sym)),
2024-09-11 16:06:09 +00:00
};
// 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 = B32(str_off),
.sh_type = B32(SHT_STRTAB),
.sh_flags = B32(SHF_STRINGS),
2024-09-11 16:06:09 +00:00
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = B32(1),
2024-09-11 16:06:09 +00:00
.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 = B32(str_off),
.sh_type = B32(SHT_STRTAB),
.sh_flags = B32(SHF_STRINGS),
2024-09-11 16:06:09 +00:00
.sh_addr = 0,
.sh_offset = 0,
.sh_size = 0,
.sh_link = 0,
.sh_info = 0,
.sh_addralign = B32(1),
2024-09-11 16:06:09 +00:00
.sh_entsize = 0,
};
2024-10-04 23:41:10 +00:00
for (uint32_t i = 0; i < assembler->section_len; i++) {
struct elf_section *sec = &assembler->sections[i];
if (sec->reltab_len == 0)
2024-09-11 16:06:09 +00:00
continue;
shdr[sec->reltab_shidx].sh_link =
B32(assembler->symtab_shidx);
2024-09-11 16:06:09 +00:00
}
*res = shdr;
*res2 = count;
2024-09-10 00:48:08 +00:00
return M_SUCCESS;
}
static void update_offsets(struct elf_assembler *assembler, Elf32_Ehdr *ehdr)
2024-09-11 16:06:09 +00:00
{
Elf32_Shdr *shdr = (Elf32_Shdr *) assembler->shdr;
uint32_t ptr = 0;
// we must now correct offets and sizes inside the ehdr, phdr,
// and shdr
ptr += sizeof(Elf32_Ehdr);
// reltbls
2024-10-04 23:41:10 +00:00
for (uint32_t i = 0; i < assembler->section_len; i++) {
struct elf_section *sec = &assembler->sections[i];
if (sec->reltab_len == 0)
2024-09-11 16:06:09 +00:00
continue;
int idx = sec->reltab_shidx;
2024-10-04 23:41:10 +00:00
int len = sec->reltab_len;
shdr[idx].sh_offset = B32(ptr);
2024-10-04 23:41:10 +00:00
shdr[idx].sh_size = B32(len * sizeof(Elf32_Rel));
ptr += len * sizeof(Elf32_Rel);
2024-09-12 13:42:04 +00:00
}
2024-09-11 16:06:09 +00:00
// sections
2024-09-13 15:11:18 +00:00
size_t v_addr = 0;
2024-10-04 23:41:10 +00:00
for (uint32_t i = 0; i < assembler->section_len; i++) {
size_t pad = v_addr % SEC_ALIGN;
if (pad)
pad = SEC_ALIGN - pad;
v_addr += pad;
struct elf_section *sec = &assembler->sections[i];
2024-09-12 13:42:04 +00:00
uint32_t idx = sec->shdr_idx;
2024-10-04 23:41:10 +00:00
uint32_t size = sec->data->len;
shdr[idx].sh_offset = B32(ptr);
2024-10-04 23:41:10 +00:00
shdr[idx].sh_size = B32(size);
shdr[idx].sh_addr = B32(v_addr);
2024-09-12 13:42:04 +00:00
v_addr += size;
ptr += size;
2024-09-11 16:06:09 +00:00
}
// symtab
2024-10-04 23:41:10 +00:00
{
uint32_t len = assembler->symtab_len;
uint32_t size = len * sizeof(Elf32_Sym);
shdr[assembler->symtab_shidx].sh_offset = B32(ptr);
shdr[assembler->symtab_shidx].sh_link =
B32(assembler->strtab_shidx);
shdr[assembler->symtab_shidx].sh_size = B32(size);
ptr += size;
}
2024-09-11 16:06:09 +00:00
// strtab
shdr[assembler->strtab_shidx].sh_offset = B32(ptr);
shdr[assembler->strtab_shidx].sh_size = B32(assembler->strtab.size);
2024-09-11 16:06:09 +00:00
ptr += assembler->strtab.size;
// shstrtab
shdr[assembler->shstrtab_shidx].sh_offset = B32(ptr);
2024-09-12 12:37:46 +00:00
shdr[assembler->shstrtab_shidx].sh_size =
B32(assembler->shstrtab.size);
2024-09-11 16:06:09 +00:00
ptr += assembler->shstrtab.size;
// shdr
ehdr->e_shoff = B32(ptr);
2024-09-11 16:06:09 +00:00
}
static int write_file(struct elf_assembler *assembler, Elf32_Ehdr *ehdr,
2024-09-11 16:06:09 +00:00
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)
{
2024-09-22 20:02:42 +00:00
PERROR("cannot write '%s'", path);
2024-09-11 16:06:09 +00:00
return M_ERROR;
}
// ehdr
fwrite(ehdr, sizeof(Elf32_Ehdr), 1, out);
// reltbls
2024-10-04 23:41:10 +00:00
for (uint32_t i = 0; i < assembler->section_len; i++) {
struct elf_section *sec = &assembler->sections[i];
void *ptr = sec->reltab;
int len = sec->reltab_len;
if (len < 1)
2024-09-11 16:06:09 +00:00
continue;
2024-10-04 23:41:10 +00:00
fwrite(ptr, sizeof(Elf32_Rel), len, out);
2024-09-12 13:42:04 +00:00
}
2024-09-11 16:06:09 +00:00
// sections
2024-10-04 23:41:10 +00:00
for (uint32_t i = 0; i < assembler->section_len; i++) {
struct elf_section *sec = &assembler->sections[i];
void *ptr = sec->data->data;
size_t size = sec->data->len;
fwrite(ptr, 1, size, out);
2024-09-10 22:23:46 +00:00
}
2024-09-11 16:06:09 +00:00
// sym tbl
2024-10-04 23:41:10 +00:00
fwrite(assembler->symbols, sizeof(Elf32_Sym), assembler->symtab_len,
out);
2024-09-11 16:06:09 +00:00
// str tbl
2024-10-04 23:41:10 +00:00
fwrite(assembler->strtab.ptr, 1, assembler->strtab.size, out);
2024-09-11 16:06:09 +00:00
// shstr tbl
2024-10-04 23:41:10 +00:00
fwrite(assembler->shstrtab.ptr, 1, assembler->shstrtab.size, out);
2024-09-11 16:06:09 +00:00
// shdr
fwrite(assembler->shdr, sizeof(Elf32_Shdr), assembler->shdr_len, out);
2024-10-04 23:41:10 +00:00
// close
2024-09-11 16:06:09 +00:00
fclose(out);
return M_SUCCESS;
}
static void update_sym_shndx(struct elf_assembler *assembler)
2024-09-11 16:06:09 +00:00
{
2024-10-04 23:41:10 +00:00
for (uint32_t i = 1; i < assembler->symtab_len; i++) {
Elf32_Sym *esym = &assembler->symbols[i];
struct symbol *sym = &assembler->gen->symtab.symbols[i - 1];
2024-10-04 23:41:10 +00:00
// get shindx
int shindx = 0;
if (sym->secidx != SYM_SEC_STUB)
shindx = assembler->sections[sym->secidx].shdr_idx;
else if (sym->type == SYM_EXTERN)
shindx = 0;
esym->st_shndx = B16(shindx);
2024-09-11 16:06:09 +00:00
}
2024-10-04 23:41:10 +00:00
}
2024-09-11 16:06:09 +00:00
static int assemble_elf(struct elf_assembler *assembler, const char *out)
2024-10-04 23:41:10 +00:00
{
if (asm_init_sections(assembler))
return M_ERROR;
if (asm_init_symtab(assembler))
return M_ERROR;
2024-10-04 23:41:10 +00:00
if (assemble_shdr(assembler, &assembler->shdr, &assembler->shdr_len))
2024-09-11 16:06:09 +00:00
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;
ehdr.e_shnum = B16(assembler->shdr_len);
ehdr.e_shstrndx = B16(assembler->shstrtab_shidx);
ehdr.e_flags = B32(flags);
2024-09-11 16:06:09 +00:00
update_offsets(assembler, &ehdr);
2024-10-04 23:41:10 +00:00
update_sym_shndx(assembler);
2024-09-11 16:06:09 +00:00
if (write_file(assembler, &ehdr, out))
return M_ERROR;
return M_SUCCESS;
}
static int assembler_init(struct elf_assembler *assembler,
struct generator *gen,
struct arguments *args)
2024-09-11 16:06:09 +00:00
{
assembler->args = args;
assembler->gen = gen;
2024-09-11 16:06:09 +00:00
2024-10-04 23:41:10 +00:00
assembler->shdr = NULL;
assembler->symbols = NULL;
assembler->sections = NULL;
assembler->strtab.ptr = NULL;
assembler->shstrtab.ptr = NULL;
assembler->section_len = 0;
2024-09-11 16:06:09 +00:00
if (strtab_init(&assembler->shstrtab))
return M_ERROR;
if (strtab_init(&assembler->strtab))
return M_ERROR;
return M_SUCCESS;
}
static void assembler_free(struct elf_assembler *assembler)
2024-09-11 16:06:09 +00:00
{
2024-09-10 22:23:46 +00:00
if (assembler->shdr)
free(assembler->shdr);
2024-10-04 23:41:10 +00:00
if (assembler->symbols)
free(assembler->symbols);
if (assembler->sections) {
for (uint32_t i = 0; i < assembler->section_len; i++)
elf_section_free(&assembler->sections[i]);
free(assembler->sections);
}
2024-09-10 22:23:46 +00:00
2024-09-11 16:06:09 +00:00
strtab_free(&assembler->strtab);
strtab_free(&assembler->shstrtab);
2024-09-10 22:23:46 +00:00
}
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;
}