Last active
March 23, 2022 10:37
-
-
Save Miouyouyou/c2567dba3e959251a9597d8e5680044f to your computer and use it in GitHub Desktop.
Quick and very dirty ARM64 ( AARCH64 ) ELF linker
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdint.h> | |
#include <elf.h> | |
#include <sections/data.h> | |
#include <string.h> | |
#include <assert.h> | |
#define INSTRUCTION_SIZE 9*4 | |
#define DATA_SIZE 16 | |
#define CODE_BASE_ADDR 0x10000 | |
#define DATA_BASE_ADDR 0x20000 | |
enum program_elements { | |
element_elf_header, | |
element_text_phdr, | |
element_data_phdr, | |
element_text_data, | |
element_data_data, | |
element_empty_shdr, | |
element_text_shdr, | |
element_data_shdr, | |
element_shstrtab_shdr, | |
element_shstrtab_data, | |
n_elements, | |
element_start_phdrs = element_text_phdr, | |
element_start_text = element_text_data, | |
element_end_text = element_data_data, | |
element_start_shdrs = element_empty_shdr | |
}; | |
Elf64_Ehdr program_header = { | |
.e_ident = {ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, 2, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, | |
.e_type = ET_EXEC, | |
.e_machine = EM_AARCH64, | |
.e_version = 1, | |
.e_entry = 0, | |
.e_phoff = sizeof(Elf64_Ehdr), | |
.e_shoff = 0, | |
.e_flags = 0, | |
.e_ehsize = sizeof(Elf64_Ehdr), | |
.e_phentsize = sizeof(Elf64_Phdr), | |
.e_phnum = 2, // .data and .text | |
.e_shentsize = sizeof(Elf64_Shdr), | |
.e_shnum = 4, // none, .data, .text, .symtab | |
.e_shstrndx = 3, // [0], [1], [2], [3] | |
}; | |
Elf64_Phdr text_header = { | |
.p_type = PT_LOAD, | |
.p_offset = 0, | |
.p_vaddr = 0, | |
.p_paddr = 0, | |
.p_filesz = 0, | |
.p_memsz = 0, | |
.p_flags = PF_X | PF_R, | |
.p_align = 0x1000 | |
}; | |
Elf64_Phdr data_header = { | |
.p_type = PT_LOAD, | |
.p_offset = 0, | |
.p_vaddr = 0, | |
.p_paddr = 0, | |
.p_filesz = 0, | |
.p_memsz = 0, | |
.p_flags = PF_W | PF_R, | |
.p_align = 0x1000 | |
}; | |
uint32_t dot_text[] = { | |
// Will be located at CODE_BASE_ADDR + sizeof(Elf64_Ehdr) + sizeof(Elf64_Phdr*2) -> 100b0 | |
0x90000081, // adrp x1, PC+(0b10000 * 4096) -> 0x10b0 + 0x1000 -> 0x20b0 >> 12 << 12 -> 0x2000 | |
0xd2800482, // mov x2, #0x24 // FIXME Bogus size | |
0xd2800020, // mov x0, #0x1 | |
0x91035021, // add x1, x1, #0xd4 -> 0x2000 + 0xd4 (offset of dot_tex + sizeof(dot_text)) -> 0x20d4 (Our data address) | |
0xd2800808, // mov x8, #0x40 | |
0xd4000001, // svc #0x0 | |
0xd2800000, // mov x0, #0x0 | |
0xd2800ba8, // mov x8, #0x5d | |
0xd4000001, // svc #0x0 | |
}; | |
uint8_t dot_data[] = "Meow!"; | |
Elf64_Shdr empty_section = {0}; | |
Elf64_Shdr text_shdr = { | |
.sh_name = 1, | |
.sh_type = SHT_PROGBITS, | |
.sh_flags = SHF_ALLOC | SHF_EXECINSTR, | |
.sh_addr = 0, | |
.sh_offset = 0, | |
.sh_size = 0, | |
.sh_info = 0, | |
.sh_addralign = 4, | |
.sh_entsize = 0 | |
}; | |
Elf64_Shdr data_shdr = { | |
.sh_name = 7, // \0.text\0.data\0 -> [7] -> .data\0 | |
.sh_type = SHT_PROGBITS, | |
.sh_flags = SHF_ALLOC | SHF_WRITE, | |
.sh_addr = 0, | |
.sh_offset = 0, | |
.sh_size = 0, | |
.sh_info = 0, | |
.sh_addralign = 1, | |
.sh_entsize = 0 | |
}; | |
Elf64_Shdr shstrtab_section = { | |
.sh_name = 13, // \0.text\0.data\0.shrstrtab\0 -> [13] -> .shrstrtab | |
.sh_type = SHT_STRTAB, | |
.sh_addralign = 1, | |
}; | |
static uint8_t scratch_space[10000]; | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <unistd.h> | |
#include <stdio.h> | |
#define N_TEST_INSTS 10 | |
uint32_t test_machine_code[50] = {0}; | |
uint8_t test_data[1000] = {0}; | |
static struct data_symbol test_symbols[10] = {0}; | |
uint8_t section_names[] = { "\0.text\0.data\0.shstrtab\0" }; | |
uint32_t write_data | |
(uint8_t * __restrict const storage, | |
uint32_t storage_offset, | |
void const * __restrict const data, | |
uint32_t data_size) | |
{ | |
memcpy(storage+storage_offset, data, data_size); | |
return storage_offset + data_size; | |
} | |
uint8_t test_data_string[] = | |
"My hamster is rich and can do kung-fu !\n"; | |
typedef uint32_t offset; | |
offset glbl_offsets[n_elements] = {0}; | |
uint32_t add_binary_data | |
(enum program_elements element, | |
uint32_t storage_offset, | |
void const * __restrict const data, | |
uint32_t data_size) | |
{ | |
glbl_offsets[element] = storage_offset; | |
return write_data( | |
scratch_space, storage_offset, data, data_size | |
); | |
} | |
static void setup_text_sections( | |
uint8_t * __restrict const elf_binary_data, | |
offset const * __restrict const offsets, | |
uint32_t const text_base_addr) | |
{ | |
/* It seems conventional that the size defined in the Text Program | |
* Header, defines where the .text section data stop in the binary | |
* file, instead of the amount of bytes these data really takes in | |
* the binary file. | |
* Also, the offset stays at 0 when this convention applies. | |
* | |
* Program Headers defining data sections do not seem to follow such | |
* convention. | |
* | |
* I have no idea if ignoring this convention could break the program | |
* execution, so I'll follow such convention for now. | |
*/ | |
unsigned int text_size = sizeof(dot_text); | |
offset const physical_text_offset = offsets[element_text_data]; | |
offset const virtual_text_offset = | |
text_base_addr + physical_text_offset; | |
offset const text_end_offset = physical_text_offset+text_size; | |
Elf64_Phdr * text_phdr = | |
(Elf64_Phdr *) (elf_binary_data+offsets[element_text_phdr]); | |
text_phdr->p_paddr = text_base_addr; | |
text_phdr->p_vaddr = text_base_addr; | |
text_phdr->p_memsz = text_end_offset; | |
text_phdr->p_filesz = text_end_offset; | |
Elf64_Shdr * text_shdr = | |
(Elf64_Shdr *) (elf_binary_data+offsets[element_text_shdr]); | |
text_shdr->sh_addr = virtual_text_offset; | |
text_shdr->sh_offset = physical_text_offset; | |
text_shdr->sh_size = text_size; | |
} | |
static void setup_data_sections | |
(uint8_t * __restrict const elf_binary_data, | |
offset const * __restrict const offsets, | |
uint32_t const data_base_addr) | |
{ | |
offset const physical_data_offset = offsets[element_data_data]; | |
offset const virtual_data_offset = | |
data_base_addr+physical_data_offset; | |
uint32_t data_size = sizeof(dot_data); | |
Elf64_Phdr * dh = | |
(Elf64_Phdr *) (elf_binary_data+offsets[element_data_phdr]); | |
dh->p_offset = physical_data_offset; | |
dh->p_vaddr = virtual_data_offset; | |
dh->p_paddr = virtual_data_offset; | |
dh->p_memsz = data_size; | |
dh->p_filesz = data_size; | |
Elf64_Shdr * dsh = | |
(Elf64_Shdr *) (elf_binary_data+offsets[element_data_shdr]); | |
dsh->sh_offset = physical_data_offset; | |
dsh->sh_addr = virtual_data_offset; | |
dsh->sh_size = data_size; | |
} | |
void build_program(void) | |
{ | |
memset(&empty_section, 0, sizeof(Elf64_Shdr)); | |
uint32_t bytes_written = 0; | |
bytes_written = add_binary_data( | |
element_elf_header, bytes_written, &program_header, | |
sizeof(program_header) | |
); | |
bytes_written = add_binary_data( | |
element_text_phdr, bytes_written, | |
&text_header, sizeof(text_header) | |
); | |
bytes_written = add_binary_data( | |
element_data_phdr, bytes_written, | |
&data_header, sizeof(data_header) | |
); | |
bytes_written = add_binary_data( | |
element_text_data, bytes_written, | |
dot_text, sizeof(dot_text) | |
); | |
bytes_written = add_binary_data( | |
element_data_data, bytes_written, | |
dot_data, sizeof(dot_data) | |
); | |
bytes_written = add_binary_data( | |
element_empty_shdr, bytes_written, | |
&empty_section, sizeof(empty_section) | |
); | |
bytes_written = add_binary_data( | |
element_text_shdr, bytes_written, | |
&text_shdr, sizeof(text_shdr) | |
); | |
bytes_written = add_binary_data( | |
element_data_shdr, bytes_written, | |
&data_shdr, sizeof(data_shdr) | |
); | |
bytes_written = add_binary_data( | |
element_shstrtab_shdr, bytes_written, | |
&shstrtab_section, sizeof(shstrtab_section) | |
); | |
bytes_written = add_binary_data( | |
element_shstrtab_data, bytes_written, | |
§ion_names, sizeof(section_names) | |
); | |
Elf64_Ehdr * header = (Elf64_Ehdr *) scratch_space; | |
header->e_shoff = glbl_offsets[element_empty_shdr]; | |
header->e_entry = CODE_BASE_ADDR+glbl_offsets[element_text_data]; | |
setup_text_sections( | |
scratch_space, glbl_offsets, | |
CODE_BASE_ADDR | |
); | |
setup_data_sections( | |
scratch_space, glbl_offsets, DATA_BASE_ADDR | |
); | |
Elf64_Shdr * shstrtab_shdr = | |
(Elf64_Shdr *) (scratch_space+glbl_offsets[element_shstrtab_shdr]); | |
shstrtab_shdr->sh_size = sizeof(section_names); | |
shstrtab_shdr->sh_offset = glbl_offsets[element_shstrtab_data]; | |
int fd = open("executable", O_WRONLY|O_CREAT|O_TRUNC, 00755); | |
if (fd != -1) { | |
write(fd, scratch_space, bytes_written); | |
close(fd); | |
} | |
} | |
int main() { | |
build_program(); | |
for (enum program_elements element = element_elf_header; | |
element < n_elements; element++) { | |
printf("%x\n", glbl_offsets[element]); | |
} | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef MYY_DATA_SECTION_H | |
#define MYY_DATA_SECTION_H 1 | |
#include <stdint.h> | |
struct data_section_status { | |
unsigned int allocated; | |
void * address; | |
}; | |
struct data_symbol { | |
uint32_t id; | |
uint32_t align; | |
uint32_t size; | |
uint8_t * name; | |
uint8_t * data; | |
}; | |
struct data_section { | |
struct data_symbol * symbols; | |
uint32_t stored; | |
uint32_t next_id; | |
uint32_t base_address; | |
uint32_t max_symbols_before_realloc; | |
}; | |
struct data_section_symbol_added { | |
unsigned int added; | |
unsigned int id; | |
}; | |
struct data_section * generate_data_section(); | |
unsigned int expand_data_symbols_storage_in | |
(struct data_section * __restrict const data_section); | |
struct data_section_symbol_added data_section_add | |
(struct data_section * __restrict const data_section, | |
unsigned int const alignment, | |
unsigned int const size, | |
uint8_t const * __restrict const name, | |
uint8_t const * __restrict const data); | |
struct uint32_result { | |
unsigned int found; | |
uint32_t value; | |
}; | |
struct symbol_found { | |
unsigned int found; | |
struct data_symbol * address; | |
}; | |
struct symbol_found get_data_symbol_infos | |
(struct data_section const * __restrict const data_infos, | |
uint32_t id); | |
void exchange_symbols_order | |
(struct data_section * __restrict const data_section, | |
unsigned int const id1, unsigned int const id2); | |
uint32_t write_data_section_content | |
(struct data_section const * __restrict const symbols, | |
uint8_t * __restrict const dest); | |
void delete_data_symbol | |
(struct data_section * __restrict const data_section, | |
uint32_t id); | |
uint32_t data_section_size | |
(struct data_section const * __restrict const data_section); | |
#define data_address_func_sig struct data_section const * __restrict const data_section,\ | |
uint32_t const data_id | |
uint32_t data_address(data_address_func_sig); | |
uint32_t data_address_upper16(data_address_func_sig); | |
uint32_t data_address_lower16(data_address_func_sig); | |
uint32_t data_size(data_address_func_sig); | |
void update_data_symbol | |
(struct data_section * __restrict const data_section, | |
uint32_t const id, | |
uint32_t const align, | |
uint32_t const data_size, | |
uint8_t const * __restrict const name, | |
uint8_t const * __restrict const data); | |
void data_section_set_base_address | |
(struct data_section * __restrict const data_section, | |
uint32_t const base_address); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment