mirror of
https://github.com/zeldaret/oot.git
synced 2024-12-05 01:06:37 +00:00
236 lines
6.3 KiB
C
236 lines
6.3 KiB
C
|
/* SPDX-FileCopyrightText: Copyright (C) 2024 ZeldaRET */
|
||
|
/* SPDX-License-Identifier: CC0-1.0 */
|
||
|
#ifndef ELF32_H_
|
||
|
#define ELF32_H_
|
||
|
|
||
|
#include <stdint.h>
|
||
|
|
||
|
#include "util.h"
|
||
|
|
||
|
#define elf32_read16(x) be16toh(x)
|
||
|
#define elf32_write16(x) htobe16(x)
|
||
|
#define elf32_read32(x) be32toh(x)
|
||
|
#define elf32_write32(x) htobe32(x)
|
||
|
|
||
|
#ifndef ELF32_QUALIFIERS
|
||
|
#define ELF32_QUALIFIERS static UNUSED ALWAYS_INLINE
|
||
|
#endif
|
||
|
|
||
|
#define GET_PTR(data, offset) ((void *)&((uint8_t *)(data))[(offset)])
|
||
|
|
||
|
#define EI_NIDENT 16
|
||
|
#define EI_MAG0 0x00
|
||
|
#define EI_MAG1 0x01
|
||
|
#define EI_MAG2 0x02
|
||
|
#define EI_MAG3 0x03
|
||
|
#define EI_CLASS 0x04
|
||
|
#define EI_DATA 0x05
|
||
|
#define EI_VERSION 0x06
|
||
|
#define EI_OSABI 0x07
|
||
|
#define EI_ABIVERSION 0x08
|
||
|
#define EI_PAD 0x09
|
||
|
|
||
|
typedef struct {
|
||
|
uint8_t e_ident[EI_NIDENT];
|
||
|
uint16_t e_type;
|
||
|
uint16_t e_machine;
|
||
|
uint32_t e_version;
|
||
|
uint32_t e_entry;
|
||
|
uint32_t e_phoff;
|
||
|
uint32_t e_shoff;
|
||
|
uint32_t e_flags;
|
||
|
uint16_t e_ehsize;
|
||
|
uint16_t e_phentsize;
|
||
|
uint16_t e_phnum;
|
||
|
uint16_t e_shentsize;
|
||
|
uint16_t e_shnum;
|
||
|
uint16_t e_shstrndx;
|
||
|
} Elf32_Ehdr;
|
||
|
|
||
|
#define ELF32_HAS_MAGIC(ehdr) \
|
||
|
((ehdr)->e_ident[EI_MAG0] == '\x7F' && (ehdr)->e_ident[EI_MAG1] == 'E' && (ehdr)->e_ident[EI_MAG2] == 'L' && \
|
||
|
(ehdr)->e_ident[EI_MAG3] == 'F')
|
||
|
|
||
|
#define ELF32_IS_32(ehdr) ((ehdr)->e_ident[EI_CLASS] == 1 /*EI_CLASS_32*/)
|
||
|
|
||
|
#define ELF32_IS_BE(ehdr) ((ehdr)->e_ident[EI_DATA] == 2 /*EI_DATA_BE*/)
|
||
|
|
||
|
typedef struct {
|
||
|
uint32_t sh_name;
|
||
|
uint32_t sh_type;
|
||
|
uint32_t sh_flags;
|
||
|
uint32_t sh_addr;
|
||
|
uint32_t sh_offset;
|
||
|
uint32_t sh_size;
|
||
|
uint32_t sh_link;
|
||
|
uint32_t sh_info;
|
||
|
uint32_t sh_addralign;
|
||
|
uint32_t sh_entsize;
|
||
|
} Elf32_Shdr;
|
||
|
|
||
|
typedef struct {
|
||
|
uint32_t st_name;
|
||
|
uint32_t st_value;
|
||
|
uint32_t st_size;
|
||
|
uint8_t st_info;
|
||
|
uint8_t st_other;
|
||
|
uint16_t st_shndx;
|
||
|
} Elf32_Sym;
|
||
|
|
||
|
// sh_type
|
||
|
|
||
|
#define SHT_NULL 0x00000000
|
||
|
#define SHT_PROGBITS 0x00000001
|
||
|
#define SHT_SYMTAB 0x00000002
|
||
|
#define SHT_STRTAB 0x00000003
|
||
|
#define SHT_RELA 0x00000004
|
||
|
#define SHT_HASH 0x00000005
|
||
|
#define SHT_DYNAMIC 0x00000006
|
||
|
#define SHT_NOTE 0x00000007
|
||
|
#define SHT_NOBITS 0x00000008
|
||
|
#define SHT_REL 0x00000009
|
||
|
#define SHT_SHLIB 0x0000000A
|
||
|
#define SHT_DYNSYM 0x0000000B
|
||
|
#define SHT_INIT_ARRAY 0x0000000E
|
||
|
#define SHT_FINI_ARRAY 0x0000000F
|
||
|
#define SHT_PREINIT_ARRAY 0x00000010
|
||
|
#define SHT_GROUP 0x00000011
|
||
|
#define SHT_SYMTAB_SHNDX 0x00000012
|
||
|
#define SHT_NUM 0x00000013
|
||
|
#define SHT_LOOS 0x60000000
|
||
|
// MIPS specific
|
||
|
#define SHT_MIPS_DEBUG 0x70000005
|
||
|
#define SHT_MIPS_REGINFO 0x70000006
|
||
|
#define SHT_MIPS_OPTIONS 0x7000000D
|
||
|
|
||
|
// st_shndx
|
||
|
|
||
|
#define SHN_UND 0x0000
|
||
|
#define SHN_ABS 0xFFF1
|
||
|
#define SHN_COMMON 0xFFF2
|
||
|
#define SHN_LORESERVE 0xFF00
|
||
|
#define SHN_XINDEX 0xFFFF
|
||
|
|
||
|
// st_info [3:0]
|
||
|
|
||
|
#define ST_NOTYPE 0
|
||
|
#define ST_OBJECT 1
|
||
|
#define ST_FUNC 2
|
||
|
#define ST_SECTION 3
|
||
|
#define ST_FILE 4
|
||
|
|
||
|
// st_info [7:4]
|
||
|
|
||
|
#define SB_LOCAL 0
|
||
|
#define SB_GLOBAL 1
|
||
|
#define SB_WEAK 2
|
||
|
|
||
|
#define ELF32_ERR_PREFIX "[ELF32] "
|
||
|
|
||
|
ELF32_QUALIFIERS void
|
||
|
validate_read(size_t offset, size_t size, size_t data_size)
|
||
|
{
|
||
|
if (offset + size > data_size)
|
||
|
error(ELF32_ERR_PREFIX "Could not read %ld bytes at %08lX", size, offset);
|
||
|
}
|
||
|
|
||
|
ELF32_QUALIFIERS void *
|
||
|
elf32_read(const char *path, size_t *data_size_out)
|
||
|
{
|
||
|
size_t data_size;
|
||
|
void *data = util_read_whole_file(path, &data_size);
|
||
|
if (data == NULL)
|
||
|
error(ELF32_ERR_PREFIX "File is empty?");
|
||
|
|
||
|
validate_read(0, sizeof(Elf32_Ehdr), data_size);
|
||
|
|
||
|
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||
|
|
||
|
if (!ELF32_HAS_MAGIC(ehdr))
|
||
|
error(ELF32_ERR_PREFIX "Not an ELF file?");
|
||
|
if (!ELF32_IS_32(ehdr))
|
||
|
error(ELF32_ERR_PREFIX "Not ELF32?");
|
||
|
if (!ELF32_IS_BE(ehdr))
|
||
|
error(ELF32_ERR_PREFIX "Not big-endian?");
|
||
|
|
||
|
*data_size_out = data_size;
|
||
|
return data;
|
||
|
}
|
||
|
|
||
|
ELF32_QUALIFIERS Elf32_Shdr *
|
||
|
elf32_get_symtab(void *data, size_t data_size)
|
||
|
{
|
||
|
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||
|
uint32_t e_shoff = elf32_read32(ehdr->e_shoff);
|
||
|
uint16_t e_shnum = elf32_read16(ehdr->e_shnum);
|
||
|
|
||
|
Elf32_Shdr *shdr = GET_PTR(data, e_shoff);
|
||
|
for (size_t i = 0; i < e_shnum; i++, shdr++) {
|
||
|
validate_read(e_shoff + i * sizeof(Elf32_Shdr), sizeof(Elf32_Shdr), data_size);
|
||
|
|
||
|
if (elf32_read32(shdr->sh_type) == SHT_SYMTAB) {
|
||
|
// there should be only one section of this type
|
||
|
return shdr;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ELF32_QUALIFIERS Elf32_Shdr *
|
||
|
ef32_section_foridx(size_t idx, void *data, size_t data_size)
|
||
|
{
|
||
|
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||
|
uint32_t e_shoff = elf32_read32(ehdr->e_shoff);
|
||
|
uint16_t e_shnum = elf32_read16(ehdr->e_shnum);
|
||
|
Elf32_Shdr *shdr = GET_PTR(data, e_shoff);
|
||
|
|
||
|
if (idx >= e_shnum)
|
||
|
return NULL;
|
||
|
|
||
|
validate_read(e_shoff + idx * sizeof(Elf32_Shdr), sizeof(Elf32_Shdr), data_size);
|
||
|
return &shdr[idx];
|
||
|
}
|
||
|
|
||
|
ELF32_QUALIFIERS Elf32_Shdr *
|
||
|
elf32_get_shstrtab(void *data, size_t data_size)
|
||
|
{
|
||
|
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||
|
return ef32_section_foridx(elf32_read16(ehdr->e_shstrndx), data, data_size);
|
||
|
}
|
||
|
|
||
|
ELF32_QUALIFIERS const char *
|
||
|
elf32_get_string(size_t offset, Elf32_Shdr *strtab, void *data, size_t data_size)
|
||
|
{
|
||
|
uint32_t sh_offset = elf32_read32(strtab->sh_offset);
|
||
|
|
||
|
validate_read(sh_offset + offset, 1, data_size);
|
||
|
return (const char *)GET_PTR(data, sh_offset + offset);
|
||
|
}
|
||
|
|
||
|
ELF32_QUALIFIERS Elf32_Shdr *
|
||
|
elf32_section_forname(const char *name, Elf32_Shdr *shstrtab, void *data, size_t data_size)
|
||
|
{
|
||
|
Elf32_Ehdr *ehdr = GET_PTR(data, 0);
|
||
|
uint32_t e_shoff = elf32_read32(ehdr->e_shoff);
|
||
|
uint16_t e_shnum = elf32_read16(ehdr->e_shnum);
|
||
|
|
||
|
Elf32_Shdr *shdr = GET_PTR(data, e_shoff);
|
||
|
for (size_t i = 0; i < e_shnum; i++, shdr++) {
|
||
|
validate_read(e_shoff + i * sizeof(Elf32_Shdr), sizeof(Elf32_Shdr), data_size);
|
||
|
|
||
|
const char *s_name = elf32_get_string(elf32_read32(shdr->sh_name), shstrtab, data, data_size);
|
||
|
if (strequ(s_name, name)) {
|
||
|
return shdr;
|
||
|
}
|
||
|
}
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
ELF32_QUALIFIERS Elf32_Shdr *
|
||
|
elf32_get_strtab(void *data, size_t data_size)
|
||
|
{
|
||
|
return elf32_section_forname(".strtab", elf32_get_shstrtab(data, data_size), data, data_size);
|
||
|
}
|
||
|
|
||
|
#endif
|