From f61cc5a1bc46b3a2ea52d03d231c519c63353792 Mon Sep 17 00:00:00 2001 From: Yu-En Hsiao Date: Tue, 13 May 2025 21:14:46 +0800 Subject: [PATCH 1/2] Support dynamic linking --- Makefile | 17 +- lib/c.h | 47 ++++ mk/arm.mk | 6 + mk/common.mk | 3 + mk/riscv.mk | 8 + src/arm-codegen.c | 145 ++++++++--- src/arm.c | 10 + src/defs.h | 38 +++ src/elf.c | 585 +++++++++++++++++++++++++++++++++++--------- src/globals.c | 62 ++++- src/main.c | 14 +- src/parser.c | 8 +- src/reg-alloc.c | 8 + src/riscv-codegen.c | 4 - src/ssa.c | 9 +- 15 files changed, 792 insertions(+), 172 deletions(-) create mode 100644 lib/c.h diff --git a/Makefile b/Makefile index 98f28818..369175ab 100644 --- a/Makefile +++ b/Makefile @@ -36,6 +36,15 @@ STAGE0 := shecc STAGE1 := shecc-stage1.elf STAGE2 := shecc-stage2.elf +BUILTIN_LIBC ?= c.c +STAGE0_FLAGS ?= --dump-ir +STAGE1_FLAGS ?= +ifeq ($(DYNLINK),1) +BUILTIN_LIBC := c.h +STAGE0_FLAGS += --dynlink +STAGE1_FLAGS += --dynlink +endif + OUT ?= out ARCHS = arm riscv ARCH ?= $(firstword $(ARCHS)) @@ -122,9 +131,9 @@ $(OUT)/norm-lf: tools/norm-lf.c $(VECHO) " CC+LD\t$@\n" $(Q)$(CC) $(CFLAGS) -o $@ $^ -$(OUT)/libc.inc: $(OUT)/inliner $(OUT)/norm-lf $(LIBDIR)/c.c +$(OUT)/libc.inc: $(OUT)/inliner $(OUT)/norm-lf $(LIBDIR)/$(BUILTIN_LIBC) $(VECHO) " GEN\t$@\n" - $(Q)$(OUT)/norm-lf $(LIBDIR)/c.c $(OUT)/c.normalized.c + $(Q)$(OUT)/norm-lf $(LIBDIR)/$(BUILTIN_LIBC) $(OUT)/c.normalized.c $(Q)$(OUT)/inliner $(OUT)/c.normalized.c $@ $(Q)$(RM) $(OUT)/c.normalized.c @@ -143,12 +152,12 @@ $(OUT)/$(STAGE0)-sanitizer: $(OUT)/libc.inc $(OBJS) $(OUT)/$(STAGE1): $(OUT)/$(STAGE0) $(Q)$(STAGE1_CHECK_CMD) $(VECHO) " SHECC\t$@\n" - $(Q)$(OUT)/$(STAGE0) --dump-ir -o $@ $(SRCDIR)/main.c > $(OUT)/shecc-stage1.log + $(Q)$(OUT)/$(STAGE0) $(STAGE0_FLAGS) -o $@ $(SRCDIR)/main.c > $(OUT)/shecc-stage1.log $(Q)chmod a+x $@ $(OUT)/$(STAGE2): $(OUT)/$(STAGE1) $(VECHO) " SHECC\t$@\n" - $(Q)$(TARGET_EXEC) $(OUT)/$(STAGE1) -o $@ $(SRCDIR)/main.c + $(Q)$(TARGET_EXEC) $(OUT)/$(STAGE1) $(STAGE1_FLAGS) -o $@ $(SRCDIR)/main.c bootstrap: $(OUT)/$(STAGE2) $(Q)chmod 775 $(OUT)/$(STAGE2) diff --git a/lib/c.h b/lib/c.h new file mode 100644 index 00000000..1626dd09 --- /dev/null +++ b/lib/c.h @@ -0,0 +1,47 @@ +/* + * shecc - Self-Hosting and Educational C Compiler. + * + * shecc is freely redistributable under the BSD 2 clause license. See the + * file "LICENSE" for information on usage and redistribution of this file. + */ + +#pragma once +/* Declarations of C standard library functions */ + +#define NULL 0 + +#define bool _Bool +#define true 1 +#define false 0 + +/* File I/O */ +typedef int FILE; +FILE *fopen(char *filename, char *mode); +int fclose(FILE *stream); +int fgetc(FILE *stream); +char *fgets(char *str, int n, FILE *stream); +int fputc(int c, FILE *stream); + +/* string-related functions */ +int strlen(char *str); +int strcmp(char *s1, char *s2); +int strncmp(char *s1, char *s2, int len); +char *strcpy(char *dest, char *src); +char *strncpy(char *dest, char *src, int len); +char *memcpy(char *dest, char *src, int count); +int memcmp(void *s1, void *s2, int n); +void *memset(void *s, int c, int n); + +/* formatted output string */ +int printf(char *str, ...); +int sprintf(char *buffer, char *str, ...); +int snprintf(char *buffer, int n, char *str, ...); + +/* Terminating program */ +void exit(int exit_code); +void abort(void); + +/* Dynamic memory allocation/deallocation functions */ +void *malloc(int size); +void *calloc(int n, int size); +void free(void *ptr); diff --git a/mk/arm.mk b/mk/arm.mk index 57bbebd4..e6e1590f 100644 --- a/mk/arm.mk +++ b/mk/arm.mk @@ -6,4 +6,10 @@ ARCH_DEFS = \ \#define ARCH_PREDEFINED \"__arm__\" /* defined by GNU C and RealView */\n$\ \#define ELF_MACHINE 0x28 /* up to ARMv7/Aarch32 */\n$\ \#define ELF_FLAGS 0x5000200\n$\ + \#define DYN_LINKER \"/lib/ld-linux.so.3\"\n$\ + \#define LIBC_SO \"libc.so.6\"\n$\ + \#define PLT_FIXUP_SIZE 20\n$\ + \#define PLT_ENT_SIZE 12\n$\ + \#define R_ARCH_JUMP_SLOT 0x16\n$\ " +RUNNER_LD_PREFIX=-L /usr/arm-linux-gnueabi/ diff --git a/mk/common.mk b/mk/common.mk index 95380cba..079ea803 100644 --- a/mk/common.mk +++ b/mk/common.mk @@ -36,6 +36,9 @@ ifneq ($(HOST_ARCH),$(ARCH_NAME)) # Generate the path to the architecture-specific qemu TARGET_EXEC = $(shell which $(ARCH_RUNNER)) + ifeq ($(DYNLINK),1) + TARGET_EXEC += $(RUNNER_LD_PREFIX) + endif endif export TARGET_EXEC diff --git a/mk/riscv.mk b/mk/riscv.mk index 89e33341..fe3ce7e5 100644 --- a/mk/riscv.mk +++ b/mk/riscv.mk @@ -7,4 +7,12 @@ ARCH_DEFS = \ \#define ARCH_PREDEFINED \"__riscv\" /* Older versions of the GCC toolchain defined __riscv__ */\n$\ \#define ELF_MACHINE 0xf3\n$\ \#define ELF_FLAGS 0\n$\ + \#define DYN_LINKER \"/lib/ld-linux.so.3\"\n$\ + \#define LIBC_SO \"libc.so.6\"\n$\ + \#define PLT_FIXUP_SIZE 20\n$\ + \#define PLT_ENT_SIZE 12\n$\ + \#define R_ARCH_JUMP_SLOT 0x5\n$\ " + +# TODO: Set this variable for RISC-V architecture +RUNNER_LD_PREFIX= diff --git a/src/arm-codegen.c b/src/arm-codegen.c index 154e4616..0d67d225 100644 --- a/src/arm-codegen.c +++ b/src/arm-codegen.c @@ -136,10 +136,17 @@ void update_elf_offset(ph2_ir_t *ph2_ir) void cfg_flatten(void) { - func_t *func = find_func("__syscall"); - func->bbs->elf_offset = 48; /* offset of start + branch + exit in codegen */ + func_t *func; + + if (dynlink) + elf_offset = 80; /* offset of __libc_start_main + start in codegen */ + else { + func = find_func("__syscall"); + func->bbs->elf_offset = 48; /* offset of start + exit in codegen */ + elf_offset = + 84; /* offset of start + branch + exit + syscall in codegen */ + } - elf_offset = 84; /* offset of start + branch + exit + syscall in codegen */ GLOBAL_FUNC->bbs->elf_offset = elf_offset; for (ph2_ir_t *ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir; @@ -148,7 +155,10 @@ void cfg_flatten(void) } /* prepare 'argc' and 'argv', then proceed to 'main' function */ - elf_offset += 32; /* 6 insns for main call + 2 for exit */ + if (dynlink) + elf_offset += 20; + else + elf_offset += 32; /* 6 insns for main call + 2 for exit */ for (func = FUNC_LIST.head; func; func = func->next) { /* Skip function declarations without bodies */ @@ -287,7 +297,12 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir) return; case OP_call: func = find_func(ph2_ir->func_name); - emit(__bl(__AL, func->bbs->elf_offset - elf_code->size)); + if (func->bbs) + ofs = func->bbs->elf_offset - elf_code->size; + else + ofs = (elf_plt_start + func->plt_offset) - + (elf_code_start + elf_code->size); + emit(__bl(__AL, ofs)); return; case OP_load_data_address: emit(__movw(__AL, rd, ph2_ir->src0 + elf_data_start)); @@ -299,7 +314,10 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir) return; case OP_address_of_func: func = find_func(ph2_ir->func_name); - ofs = elf_code_start + func->bbs->elf_offset; + if (func->bbs) + ofs = elf_code_start + func->bbs->elf_offset; + else + ofs = elf_plt_start + func->plt_offset; emit(__movw(__AL, __r8, ofs)); emit(__movt(__AL, __r8, ofs)); emit(__sw(__AL, __r8, rn, 0)); @@ -456,39 +474,72 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir) } } +void plt_generate(void); void code_generate(void) { - elf_data_start = elf_code_start + elf_offset; - elf_rodata_start = elf_data_start + elf_data->size; - elf_bss_start = elf_rodata_start + elf_rodata->size; + if (dynlink) { + plt_generate(); + /* Call __libc_start_main() */ + emit(__mov_i(__AL, __r11, 0)); + emit(__mov_i(__AL, __lr, 0)); + emit(__pop_word(__AL, __r1)); + emit(__mov_r(__AL, __r2, __sp)); + emit(__push_reg(__AL, __r2)); + emit(__push_reg(__AL, __r0)); + emit(__mov_i(__AL, __r12, 0)); + emit(__push_reg(__AL, __r12)); + + int main_wrapper_offset = elf_code->size + 24; + emit(__movw(__AL, __r0, elf_code_start + main_wrapper_offset)); + emit(__movt(__AL, __r0, elf_code_start + main_wrapper_offset)); + emit(__mov_i(__AL, __r3, 0)); + emit(__bl(__AL, (elf_plt_start + PLT_FIXUP_SIZE) - + (elf_code_start + elf_code->size))); + /* Goto the 'exit' code snippet if __libc_start_main returns */ + emit(__mov_i(__AL, __r0, 127)); + emit(__bl(__AL, 28)); - /* start */ + /* If the compiled program is dynamic linking, the starting + * point of 'start' is located here. + * + * Preserve 'argc' and 'argv' for the 'main' function. + * */ + emit(__mov_r(__AL, __r9, __r0)); + emit(__mov_r(__AL, __r10, __r1)); + } + /* For both static and dynamic linking, we need to set up the stack + * and call the main function. + * */ emit(__movw(__AL, __r8, GLOBAL_FUNC->stack_size)); emit(__movt(__AL, __r8, GLOBAL_FUNC->stack_size)); emit(__sub_r(__AL, __sp, __sp, __r8)); emit(__mov_r(__AL, __r12, __sp)); - emit(__bl(__AL, GLOBAL_FUNC->bbs->elf_offset - elf_code->size)); - /* After global init, jump to main preparation */ - emit(__b(__AL, 56)); /* PC+8: skip exit (24) + syscall (36) + ret (4) - 8 */ - /* exit */ - emit(__movw(__AL, __r8, GLOBAL_FUNC->stack_size)); - emit(__movt(__AL, __r8, GLOBAL_FUNC->stack_size)); - emit(__add_r(__AL, __sp, __sp, __r8)); - emit(__mov_r(__AL, __r0, __r0)); - emit(__mov_i(__AL, __r7, 1)); - emit(__svc()); - - /* syscall */ - emit(__mov_r(__AL, __r7, __r0)); - emit(__mov_r(__AL, __r0, __r1)); - emit(__mov_r(__AL, __r1, __r2)); - emit(__mov_r(__AL, __r2, __r3)); - emit(__mov_r(__AL, __r3, __r4)); - emit(__mov_r(__AL, __r4, __r5)); - emit(__mov_r(__AL, __r5, __r6)); - emit(__svc()); - emit(__bx(__AL, __lr)); + if (!dynlink) { + emit(__bl(__AL, GLOBAL_FUNC->bbs->elf_offset - elf_code->size)); + /* After global init, jump to main preparation */ + emit(__b(__AL, + 56)); /* PC+8: skip exit (24) + syscall (36) + ret (4) - 8 */ + + /* exit - only for static linking */ + emit(__movw(__AL, __r8, GLOBAL_FUNC->stack_size)); + emit(__movt(__AL, __r8, GLOBAL_FUNC->stack_size)); + emit(__add_r(__AL, __sp, __sp, __r8)); + emit(__mov_r(__AL, __r0, __r0)); + emit(__mov_i(__AL, __r7, 1)); + emit(__svc()); + + /* syscall */ + emit(__mov_r(__AL, __r7, __r0)); + emit(__mov_r(__AL, __r0, __r1)); + emit(__mov_r(__AL, __r1, __r2)); + emit(__mov_r(__AL, __r2, __r3)); + emit(__mov_r(__AL, __r3, __r4)); + emit(__mov_r(__AL, __r4, __r5)); + emit(__mov_r(__AL, __r5, __r6)); + emit(__svc()); + emit(__bx(__AL, __lr)); + } ph2_ir_t *ph2_ir; for (ph2_ir = GLOBAL_FUNC->bbs->ph2_ir_list.head; ph2_ir; @@ -497,11 +548,16 @@ void code_generate(void) /* prepare 'argc' and 'argv', then proceed to 'main' function */ if (MAIN_BB) { - emit(__movw(__AL, __r8, GLOBAL_FUNC->stack_size)); - emit(__movt(__AL, __r8, GLOBAL_FUNC->stack_size)); - emit(__add_r(__AL, __r8, __r12, __r8)); - emit(__lw(__AL, __r0, __r8, 0)); - emit(__add_i(__AL, __r1, __r8, 4)); + if (dynlink) { + emit(__mov_r(__AL, __r0, __r9)); + emit(__mov_r(__AL, __r1, __r10)); + } else { + emit(__movw(__AL, __r8, GLOBAL_FUNC->stack_size)); + emit(__movt(__AL, __r8, GLOBAL_FUNC->stack_size)); + emit(__add_r(__AL, __r8, __r12, __r8)); + emit(__lw(__AL, __r0, __r8, 0)); + emit(__add_i(__AL, __r1, __r8, 4)); + } emit(__bl(__AL, MAIN_BB->elf_offset - elf_code->size)); /* exit with main's return value - r0 already has the return value */ @@ -514,3 +570,20 @@ void code_generate(void) emit_ph2_ir(ph2_ir); } } + +void plt_generate(void) +{ + int addr_of_got = elf_got_start + PTR_SIZE * 2; + int end = plt_size - PLT_FIXUP_SIZE; + elf_write_int(elf_plt, __push_reg(__AL, __lr)); + elf_write_int(elf_plt, __movw(__AL, __r10, addr_of_got)); + elf_write_int(elf_plt, __movt(__AL, __r10, addr_of_got)); + elf_write_int(elf_plt, __mov_r(__AL, __lr, __r10)); + elf_write_int(elf_plt, __lw(__AL, __pc, __lr, 0)); + for (int i = 0; i * PLT_ENT_SIZE < end; i++) { + addr_of_got = elf_got_start + PTR_SIZE * (i + 3); + elf_write_int(elf_plt, __movw(__AL, __r12, addr_of_got)); + elf_write_int(elf_plt, __movt(__AL, __r12, addr_of_got)); + elf_write_int(elf_plt, __lw(__AL, __pc, __r12, 0)); + } +} diff --git a/src/arm.c b/src/arm.c index 1ad08831..60d5b3b0 100644 --- a/src/arm.c +++ b/src/arm.c @@ -312,6 +312,16 @@ int __ldm(arm_cond_t cond, int w, arm_reg rn, int reg_list) return arm_encode(cond, arm_ldm + (0x2 << 6) + (w << 1), rn, 0, reg_list); } +int __push_reg(arm_cond_t cond, arm_reg rt) +{ + return arm_encode(cond, (0x5 << 4) | 0x2, 0xd, rt, 0x4); +} + +int __pop_word(arm_cond_t cond, arm_reg rt) +{ + return arm_encode(cond, (0x4 << 4) | 0x9, 0xd, rt, 0x4); +} + int __b(arm_cond_t cond, int ofs) { int o = (ofs - 8) >> 2; diff --git a/src/defs.h b/src/defs.h index 1f25e83f..5102d930 100644 --- a/src/defs.h +++ b/src/defs.h @@ -31,8 +31,18 @@ #define MAX_SYMTAB 65536 #define MAX_STRTAB 65536 #define MAX_HEADER 1024 +#define MAX_PROGRAM_HEADER 1024 #define MAX_SECTION 1024 #define MAX_ALIASES 128 +#define MAX_SECTION_HEADER 1024 +#define MAX_SHSTR 1024 +#define MAX_INTERP 1024 +#define MAX_DYNAMIC 1024 +#define MAX_DYNSYM 1024 +#define MAX_DYNSTR 1024 +#define MAX_RELPLT 1024 +#define MAX_PLT 1024 +#define MAX_GOTPLT 1024 #define MAX_CONSTANTS 1024 #define MAX_CASES 128 #define MAX_NESTING 128 @@ -581,6 +591,11 @@ struct func { int bb_cnt; int visited; + /* Information used for dynamic linking */ + bool is_used; + int plt_offset; + int got_offset; + struct func *next; }; @@ -643,3 +658,26 @@ typedef struct { int sh_addralign; int sh_entsize; } elf32_shdr_t; + +/* Structures for dynamic linked program */ +/* For .dynsym section. */ +typedef struct { + int st_name; + int st_value; + int st_size; + char st_info; + char st_other; + char st_shndx[2]; +} elf32_sym_t; + +/* For .rel.plt section */ +typedef struct { + int r_offset; + int r_info; +} elf32_rel_t; + +/* For .dynamic section */ +typedef struct { + int d_tag; + int d_un; +} elf32_dyn_t; diff --git a/src/elf.c b/src/elf.c index a0505fc1..9489dbd6 100644 --- a/src/elf.c +++ b/src/elf.c @@ -54,16 +54,34 @@ void elf_write_blk(strbuf_t *elf_array, void *blk, int sz) strbuf_putc(elf_array, ptr[i]); } +int ELF32_ST_INFO(int b, int t) +{ + return (b << 4) + (t & 0xf); +} + void elf_generate_header(void) { /* Check for null pointers to prevent crashes */ - if (!elf_code || !elf_data || !elf_symtab || !elf_strtab || !elf_header) { + if (!elf_header) { error("ELF buffers not initialized"); return; } elf32_hdr_t hdr; - /* The following table explains the meaning of each field in the + int phnum = 1, shnum = 8, shstrndx = 7; + int shoff = elf_header_len + elf_code->size + elf_data->size + + elf_rodata->size + elf_symtab->size + elf_strtab->size + + elf_shstr->size; + if (dynlink) { + phnum += 3; + shnum += 7; + shstrndx += 7; + shoff += elf_interp->size + elf_relplt->size + elf_plt->size + + elf_got->size + elf_dynstr->size + elf_dynsym->size + + elf_dynamic->size; + } + /* + * The following table explains the meaning of each field in the * ELF32 file header. * * Notice that the following values are hexadecimal. @@ -129,37 +147,30 @@ void elf_generate_header(void) hdr.e_type[1] = 0; hdr.e_machine[0] = ELF_MACHINE; /* Instruction Set Architecture */ hdr.e_machine[1] = 0; - hdr.e_version = 1; /* ELF version */ - hdr.e_entry = ELF_START + elf_header_len; /* entry point */ - hdr.e_phoff = 0x34; /* program header offset */ - /* Section header offset: The section headers come after symtab, strtab, and - * shstrtab which are all written as part of elf_section buffer. - * shstrtab size = 1 (null) + 10 (.shstrtab\0) + 6 (.text\0) + 6 (.data\0) + - * 8 (.rodata\0) + 5 (.bss\0) + 8 (.symtab\0) + 8 - * (.strtab\0) + 1 (padding) = 53 - */ - const int shstrtab_size = 53; /* section header string table with padding */ - hdr.e_shoff = elf_header_len + elf_code->size + elf_data->size + - elf_rodata->size + elf_symtab->size + elf_strtab->size + - shstrtab_size; - hdr.e_flags = ELF_FLAGS; /* flags */ - hdr.e_ehsize[0] = (char) 0x34; /* header size */ + hdr.e_version = 1; /* ELF version */ + hdr.e_entry = elf_code_start; /* entry point */ + hdr.e_phoff = sizeof(elf32_hdr_t); /* program header offset */ + hdr.e_shoff = shoff; /* section header offset */ + hdr.e_flags = ELF_FLAGS; /* flags */ + hdr.e_ehsize[0] = sizeof(elf32_hdr_t); /* header size */ hdr.e_ehsize[1] = 0; - hdr.e_phentsize[0] = (char) 0x20; /* program header size */ + hdr.e_phentsize[0] = sizeof(elf32_phdr_t); /* program header size */ hdr.e_phentsize[1] = 0; - hdr.e_phnum[0] = 1; /* number of program headers */ + hdr.e_phnum[0] = phnum; /* number of program headers */ hdr.e_phnum[1] = 0; - hdr.e_shentsize[0] = (char) 0x28; /* section header size */ + hdr.e_shentsize[0] = sizeof(elf32_shdr_t); /* section header size */ hdr.e_shentsize[1] = 0; - /* number of section headers: .rodata and .bss included */ - hdr.e_shnum[0] = 8; + hdr.e_shnum[0] = shnum; /* number of section headers */ hdr.e_shnum[1] = 0; - /* section index with names: updated for new sections */ - hdr.e_shstrndx[0] = 7; + hdr.e_shstrndx[0] = shstrndx; /* section index with names */ hdr.e_shstrndx[1] = 0; elf_write_blk(elf_header, &hdr, sizeof(elf32_hdr_t)); +} - /* Explain the meaning of each field in the ELF32 program header. +void elf_generate_program_headers(void) +{ + /* + * Explain the meaning of each field in the ELF32 program header. * * | Program | | * & | Header bytes | Explanation | @@ -182,64 +193,73 @@ void elf_generate_header(void) */ /* program header - code and data combined */ elf32_phdr_t phdr; - phdr.p_type = 1; /* PT_LOAD */ - phdr.p_offset = elf_header_len; /* offset of segment */ - phdr.p_vaddr = ELF_START + elf_header_len; /* virtual address */ - phdr.p_paddr = ELF_START + elf_header_len; /* physical address */ - phdr.p_filesz = elf_code->size + elf_data->size + - elf_rodata->size; /* size in file - includes .rodata */ + phdr.p_type = 1; /* PT_LOAD */ + phdr.p_offset = elf_header_len; /* offset of segment */ + phdr.p_vaddr = ELF_START + phdr.p_offset; /* virtual address */ + phdr.p_paddr = ELF_START + phdr.p_offset; /* physical address */ + phdr.p_filesz = + elf_code->size + elf_data->size + elf_rodata->size; /* size in file */ phdr.p_memsz = elf_code->size + elf_data->size + elf_rodata->size + - elf_bss_size; /* size in memory - includes .bss */ + elf_bss_size; /* size in memory */ phdr.p_flags = 7; /* flags */ phdr.p_align = 4; /* alignment */ - elf_write_blk(elf_header, &phdr, sizeof(elf32_phdr_t)); + elf_write_blk(elf_program_header, &phdr, sizeof(elf32_phdr_t)); + if (dynlink) { + /* program header - all dynamic sections combined */ + phdr.p_type = 1; /* PT_LOAD */ + phdr.p_offset = elf_header_len + elf_code->size + elf_data->size + + elf_rodata->size; /* offset of segment */ + phdr.p_vaddr = ELF_START + phdr.p_offset; /* virtual address */ + phdr.p_paddr = ELF_START + phdr.p_offset; /* physical address */ + phdr.p_filesz = elf_interp->size + elf_relplt->size + elf_plt->size + + elf_got->size + elf_dynstr->size + elf_dynsym->size + + elf_dynamic->size; /* size in file */ + phdr.p_memsz = elf_interp->size + elf_relplt->size + elf_plt->size + + elf_got->size + elf_dynstr->size + elf_dynsym->size + + elf_dynamic->size; /* size in memory */ + phdr.p_flags = 7; /* flags */ + phdr.p_align = 4; /* alignment */ + elf_write_blk(elf_program_header, &phdr, sizeof(elf32_phdr_t)); + + /* program header - program interpreter (.interp section) */ + phdr.p_type = 3; /* PT_INTERP */ + phdr.p_offset = elf_header_len + elf_code->size + elf_data->size + + elf_rodata->size; /* offset of segment */ + phdr.p_vaddr = ELF_START + phdr.p_offset; /* virtual address */ + phdr.p_paddr = ELF_START + phdr.p_offset; /* physical address */ + phdr.p_filesz = strlen(DYN_LINKER) + 1; /* size in file */ + phdr.p_memsz = strlen(DYN_LINKER) + 1; /* size in memory */ + phdr.p_flags = 4; /* flags */ + phdr.p_align = 1; /* alignment */ + elf_write_blk(elf_program_header, &phdr, sizeof(elf32_phdr_t)); + + /* program header - .dynamic section */ + phdr.p_type = 2; /* PT_DYNAMIC */ + phdr.p_offset = elf_header_len + elf_code->size + elf_data->size + + elf_rodata->size + elf_interp->size + elf_relplt->size + + elf_plt->size + elf_got->size + elf_dynstr->size + + elf_dynsym->size; /* offset of segment */ + phdr.p_vaddr = ELF_START + phdr.p_offset; /* virtual address */ + phdr.p_paddr = ELF_START + phdr.p_offset; /* physical address */ + phdr.p_filesz = elf_dynamic->size; /* size in file */ + phdr.p_memsz = elf_dynamic->size; /* size in memory */ + phdr.p_flags = 6; /* flags */ + phdr.p_align = 4; /* alignment */ + elf_write_blk(elf_program_header, &phdr, sizeof(elf32_phdr_t)); + } } -void elf_generate_sections(void) +void elf_generate_section_headers(void) { /* Check for null pointers to prevent crashes */ - if (!elf_symtab || !elf_strtab || !elf_section) { + if (!elf_section_header) { error("ELF section buffers not initialized"); return; } - int section_data_size = 0; - int shstrtab_start = 0; /* Track start of shstrtab */ - - /* symtab section */ - for (int b = 0; b < elf_symtab->size; b++) - elf_write_byte(elf_section, elf_symtab->elements[b]); - section_data_size += elf_symtab->size; - - /* strtab section */ - for (int b = 0; b < elf_strtab->size; b++) - elf_write_byte(elf_section, elf_strtab->elements[b]); - section_data_size += elf_strtab->size; - - /* shstr section - compute size dynamically */ - shstrtab_start = elf_section->size; - elf_write_byte(elf_section, 0); - elf_write_str(elf_section, ".shstrtab"); - elf_write_byte(elf_section, 0); - elf_write_str(elf_section, ".text"); - elf_write_byte(elf_section, 0); - elf_write_str(elf_section, ".data"); - elf_write_byte(elf_section, 0); - elf_write_str(elf_section, ".rodata"); - elf_write_byte(elf_section, 0); - elf_write_str(elf_section, ".bss"); - elf_write_byte(elf_section, 0); - elf_write_str(elf_section, ".symtab"); - elf_write_byte(elf_section, 0); - elf_write_str(elf_section, ".strtab"); - elf_write_byte(elf_section, 0); - /* Add padding byte for alignment - some tools expect this */ - elf_write_byte(elf_section, 0); - int shstrtab_size = elf_section->size - shstrtab_start; - /* section header table */ elf32_shdr_t shdr; - int ofs = elf_header_len; + int ofs = elf_header_len, sh_name = 0; /* * The following table uses the text section header as an example @@ -269,7 +289,7 @@ void elf_generate_sections(void) * | | | */ /* NULL section */ - shdr.sh_name = 0; + shdr.sh_name = sh_name; shdr.sh_type = 0; shdr.sh_flags = 0; shdr.sh_addr = 0; @@ -279,81 +299,194 @@ void elf_generate_sections(void) shdr.sh_info = 0; shdr.sh_addralign = 0; shdr.sh_entsize = 0; - elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + sh_name += 1; /* .text */ - shdr.sh_name = 0xb; + shdr.sh_name = sh_name; shdr.sh_type = 1; shdr.sh_flags = 7; - shdr.sh_addr = ELF_START + elf_header_len; + shdr.sh_addr = elf_code_start; shdr.sh_offset = ofs; shdr.sh_size = elf_code->size; shdr.sh_link = 0; shdr.sh_info = 0; shdr.sh_addralign = 4; shdr.sh_entsize = 0; - elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); ofs += elf_code->size; + sh_name += strlen(".text") + 1; /* .data */ - shdr.sh_name = 0x11; + shdr.sh_name = sh_name; shdr.sh_type = 1; shdr.sh_flags = 3; - shdr.sh_addr = elf_code_start + elf_code->size; + shdr.sh_addr = elf_data_start; shdr.sh_offset = ofs; shdr.sh_size = elf_data->size; shdr.sh_link = 0; shdr.sh_info = 0; shdr.sh_addralign = 4; shdr.sh_entsize = 0; - elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); ofs += elf_data->size; + sh_name += strlen(".data") + 1; /* .rodata */ - shdr.sh_name = 0x17; /* Offset in shstrtab for ".rodata" */ - shdr.sh_type = 1; /* SHT_PROGBITS */ - shdr.sh_flags = 2; /* SHF_ALLOC only (read-only) */ - shdr.sh_addr = elf_code_start + elf_code->size + elf_data->size; + shdr.sh_name = sh_name; /* Offset in shstrtab for ".rodata" */ + shdr.sh_type = 1; /* SHT_PROGBITS */ + shdr.sh_flags = 2; /* SHF_ALLOC only (read-only) */ + shdr.sh_addr = elf_rodata_start; shdr.sh_offset = ofs; shdr.sh_size = elf_rodata->size; shdr.sh_link = 0; shdr.sh_info = 0; shdr.sh_addralign = 4; shdr.sh_entsize = 0; - elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); ofs += elf_rodata->size; + sh_name += strlen(".rodata") + 1; /* .bss */ - shdr.sh_name = 0x1f; /* Offset in shstrtab for ".bss" */ - shdr.sh_type = 8; /* SHT_NOBITS */ - shdr.sh_flags = 3; /* SHF_ALLOC | SHF_WRITE */ - shdr.sh_addr = - elf_code_start + elf_code->size + elf_data->size + elf_rodata->size; + shdr.sh_name = sh_name; /* Offset in shstrtab for ".bss" */ + shdr.sh_type = 8; /* SHT_NOBITS */ + shdr.sh_flags = 3; /* SHF_ALLOC | SHF_WRITE */ + shdr.sh_addr = elf_bss_start; shdr.sh_offset = ofs; /* File offset (not actually used for NOBITS) */ shdr.sh_size = elf_bss_size; shdr.sh_link = 0; shdr.sh_info = 0; shdr.sh_addralign = 4; shdr.sh_entsize = 0; - elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + sh_name += strlen(".bss") + 1; /* Note: .bss is not written to file (SHT_NOBITS) */ + if (dynlink) { + /* .interp */ + shdr.sh_name = sh_name; + shdr.sh_type = 1; + shdr.sh_flags = 0x2; + shdr.sh_addr = elf_interp_start; + shdr.sh_offset = ofs; + shdr.sh_size = strlen(DYN_LINKER) + 1; + shdr.sh_link = 0; + shdr.sh_info = 0; + shdr.sh_addralign = 1; + shdr.sh_entsize = 0; + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + ofs += elf_interp->size; + sh_name += strlen(".interp") + 1; + + /* .rel.plt */ + shdr.sh_name = sh_name; + shdr.sh_type = 9; /* SHT_REL */ + shdr.sh_flags = 0x42; /* 0x40 | SHF_ALLOC */ + shdr.sh_addr = elf_relplt_start; + shdr.sh_offset = ofs; + shdr.sh_size = elf_relplt->size; + shdr.sh_link = 10; /* The section header index of .dynsym. */ + shdr.sh_info = 8; /* The section header index of .got. */ + shdr.sh_addralign = 4; + shdr.sh_entsize = sizeof(elf32_rel_t); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + ofs += elf_relplt->size; + sh_name += strlen(".rel.plt") + 1; + + /* .plt */ + shdr.sh_name = sh_name; + shdr.sh_type = 1; + shdr.sh_flags = 0x6; + shdr.sh_addr = elf_plt_start; + shdr.sh_offset = ofs; + shdr.sh_size = elf_plt->size; + shdr.sh_link = 0; + shdr.sh_info = 0; + shdr.sh_addralign = 4; + shdr.sh_entsize = PTR_SIZE; + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + ofs += elf_plt->size; + sh_name += strlen(".plt") + 1; + + /* .got */ + shdr.sh_name = sh_name; + shdr.sh_type = 1; + shdr.sh_flags = 0x3; + shdr.sh_addr = elf_got_start; + shdr.sh_offset = ofs; + shdr.sh_size = elf_got->size; + shdr.sh_link = 0; + shdr.sh_info = 0; + shdr.sh_addralign = 4; + shdr.sh_entsize = PTR_SIZE; + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + ofs += elf_got->size; + sh_name += strlen(".got") + 1; + + /* .dynstr */ + shdr.sh_name = sh_name; + shdr.sh_type = 3; + shdr.sh_flags = 0x2; + shdr.sh_addr = elf_got_start + elf_got->size; + shdr.sh_offset = ofs; + shdr.sh_size = elf_dynstr->size; + shdr.sh_link = 0; + shdr.sh_info = 0; + shdr.sh_addralign = 1; + shdr.sh_entsize = 0; + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + ofs += elf_dynstr->size; + sh_name += strlen(".dynstr") + 1; + + /* .dynsym */ + shdr.sh_name = sh_name; + shdr.sh_type = 11; + shdr.sh_flags = 0x2; + shdr.sh_addr = elf_got_start + elf_got->size + elf_dynstr->size; + shdr.sh_offset = ofs; + shdr.sh_size = elf_dynsym->size; + shdr.sh_link = 9; + shdr.sh_info = 1; + shdr.sh_addralign = 4; + shdr.sh_entsize = sizeof(elf32_sym_t); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + ofs += elf_dynsym->size; + sh_name += strlen(".dynsym") + 1; + + /* .dynamic */ + shdr.sh_name = sh_name; + shdr.sh_type = 6; + shdr.sh_flags = 0x3; + shdr.sh_addr = + elf_got_start + elf_got->size + elf_dynstr->size + elf_dynsym->size; + shdr.sh_offset = ofs; + shdr.sh_size = elf_dynamic->size; + shdr.sh_link = 9; /* The section header index of .dynstr. */ + shdr.sh_info = 0; + shdr.sh_addralign = 4; + shdr.sh_entsize = 0; + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + ofs += elf_dynamic->size; + sh_name += strlen(".dynamic") + 1; + } + /* .symtab */ - shdr.sh_name = 0x24; /* Updated offset for ".symtab" */ + shdr.sh_name = sh_name; shdr.sh_type = 2; shdr.sh_flags = 0; shdr.sh_addr = 0; shdr.sh_offset = ofs; shdr.sh_size = elf_symtab->size; - shdr.sh_link = 6; /* Link to .strtab (section 6) */ + shdr.sh_link = dynlink ? 13 : 6; /* Link to .strtab */ shdr.sh_info = elf_symbol_index; shdr.sh_addralign = 4; shdr.sh_entsize = 16; - elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); ofs += elf_symtab->size; + sh_name += strlen(".symtab") + 1; /* .strtab */ - shdr.sh_name = 0x2c; /* Updated offset for ".strtab" */ + shdr.sh_name = sh_name; shdr.sh_type = 3; shdr.sh_flags = 0; shdr.sh_addr = 0; @@ -363,42 +496,225 @@ void elf_generate_sections(void) shdr.sh_info = 0; shdr.sh_addralign = 1; shdr.sh_entsize = 0; - elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); ofs += elf_strtab->size; + sh_name += strlen(".strtab") + 1; /* .shstr */ - shdr.sh_name = 1; + shdr.sh_name = sh_name; shdr.sh_type = 3; shdr.sh_flags = 0; shdr.sh_addr = 0; shdr.sh_offset = ofs; - shdr.sh_size = shstrtab_size; /* Computed dynamically */ + shdr.sh_size = elf_shstr->size; shdr.sh_link = 0; shdr.sh_info = 0; shdr.sh_addralign = 1; shdr.sh_entsize = 0; - elf_write_blk(elf_section, &shdr, sizeof(elf32_shdr_t)); + elf_write_blk(elf_section_header, &shdr, sizeof(elf32_shdr_t)); + sh_name += strlen(".shstrtab") + 1; } -void elf_align(void) +void elf_align(strbuf_t *elf_array) { /* Check for null pointers to prevent crashes */ - if (!elf_data || !elf_rodata || !elf_symtab || !elf_strtab) { + if (!elf_array) { error("ELF buffers not initialized for alignment"); return; } - while (elf_data->size & 3) - elf_write_byte(elf_data, 0); - - while (elf_rodata->size & 3) - elf_write_byte(elf_rodata, 0); + while (elf_array->size & 3) + elf_write_byte(elf_array, 0); +} - while (elf_symtab->size & 3) - elf_write_byte(elf_symtab, 0); +void elf_generate_sections(void) +{ + if (dynlink) { + elf32_sym_t sym; + elf32_dyn_t dyn; + elf32_rel_t rel; + int dymsym_idx = 1, func_plt_ofs, func_got_ofs, st_name = 0; + relplt_size = 0; + plt_size = PLT_FIXUP_SIZE; + /* Add three elements to got: + * + * The first element will point to the .dynamic section later. + * The second and third elements are respectively reserved + * for link_map and resolver. + * */ + got_size = PTR_SIZE * 3; + memset(&sym, 0, sizeof(elf32_sym_t)); + memset(&dyn, 0, sizeof(elf32_dyn_t)); + + /* interp section */ + elf_write_str(elf_interp, DYN_LINKER); + elf_write_byte(elf_interp, 0); + elf_align(elf_interp); + + /* Precalculate the size of rel.plt, plt, got sections. */ + relplt_size += sizeof(elf32_rel_t); /* For __libc_start_main */ + plt_size += PLT_ENT_SIZE; + got_size += PTR_SIZE; + for (func_t *func = FUNC_LIST.head; func; func = func->next) { + if (func->is_used && !func->bbs) { + relplt_size += sizeof(elf32_rel_t); + plt_size += PLT_ENT_SIZE; + got_size += PTR_SIZE; + } + } + /* Add an entry, which is set to 0x0, to the end of got. */ + got_size += PTR_SIZE; + + /* Get the starting points of the sections. */ + elf_interp_start = elf_bss_start + elf_bss_size; + elf_relplt_start = elf_interp_start + elf_interp->size; + elf_plt_start = elf_relplt_start + relplt_size; + elf_got_start = elf_plt_start + plt_size; + + /* dynstr, dynsym and relplt sections */ + elf_write_byte(elf_dynstr, 0); + elf_write_blk(elf_dynsym, &sym, sizeof(elf32_sym_t)); + st_name += 1; + + elf_write_str(elf_dynstr, LIBC_SO); /* Add "libc.so.6" to .dynstr. */ + elf_write_byte(elf_dynstr, 0); + st_name += strlen(LIBC_SO) + 1; + + /* Add __libc_start_main explicitly. */ + elf_write_str(elf_dynstr, "__libc_start_main"); + elf_write_byte(elf_dynstr, 0); + sym.st_name = st_name; + sym.st_info = ELF32_ST_INFO(1, 2); /* STB_GLOBAL = 1, STT_FUNC = 2 */ + elf_write_blk(elf_dynsym, &sym, sizeof(elf32_sym_t)); + st_name += strlen("__libc_start_main") + 1; + rel.r_offset = elf_got_start + PTR_SIZE * 3; + rel.r_info = (dymsym_idx << 8) | R_ARCH_JUMP_SLOT; + elf_write_blk(elf_relplt, &rel, sizeof(elf32_rel_t)); + dymsym_idx += 1; + func_plt_ofs = PLT_FIXUP_SIZE + PLT_ENT_SIZE; + func_got_ofs = PTR_SIZE << 2; + for (func_t *func = FUNC_LIST.head; func; func = func->next) { + if (func->is_used && !func->bbs) { + /* Handle all functions that need relocation */ + elf_write_str(elf_dynstr, func->return_def.var_name); + elf_write_byte(elf_dynstr, 0); + sym.st_name = st_name; + sym.st_info = ELF32_ST_INFO(1, 2); + elf_write_blk(elf_dynsym, &sym, sizeof(elf32_sym_t)); + st_name += strlen(func->return_def.var_name) + 1; + rel.r_offset += PTR_SIZE; + rel.r_info = (dymsym_idx << 8) | R_ARCH_JUMP_SLOT; + elf_write_blk(elf_relplt, &rel, sizeof(elf32_rel_t)); + dymsym_idx += 1; + func->plt_offset = func_plt_ofs; + func->got_offset = func_got_ofs; + func_plt_ofs += PLT_ENT_SIZE; + func_got_ofs += PTR_SIZE; + } + } + elf_align(elf_dynstr); + + /* got section */ + elf_write_int( + elf_got, + elf_got_start + got_size + elf_dynstr->size + + elf_dynsym->size); /* The virtual address of .dynamic. */ + elf_write_int(elf_got, 0); /* Set got[1] to 0 for link_map. */ + elf_write_int(elf_got, 0); /* Set got[2] to 0 for resolver. */ + for (int i = PTR_SIZE * 3; i < got_size - PTR_SIZE; i += PTR_SIZE) + elf_write_int(elf_got, elf_plt_start); + elf_write_int(elf_got, 0); /* End with 0x0 */ + + /* dynamic section */ + dyn.d_tag = 0x5; /* STRTAB */ + dyn.d_un = + elf_got_start + got_size; /* The virtual address of .dynstr. */ + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0xa; /* STRSZ */ + dyn.d_un = elf_dynstr->size; + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x6; /* SYMTAB */ + dyn.d_un = elf_got_start + got_size + + elf_dynstr->size; /* The virtual address of .dynsym. */ + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0xb; /* SYMENT */ + dyn.d_un = sizeof(elf32_sym_t); /* Size of an entry. */ + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x11; /* REL */ + dyn.d_un = elf_relplt_start; /* The virtual address of .rel.plt. */ + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x12; /* RELSZ */ + dyn.d_un = relplt_size; + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x13; /* RELENT */ + dyn.d_un = sizeof(elf32_rel_t); + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x3; /* PLTGOT */ + dyn.d_un = elf_got_start; /* The virtual address of .got.*/ + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x2; /* PLTRELSZ */ + dyn.d_un = relplt_size; + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x14; /* PLTREL */ + dyn.d_un = 0x11; + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x17; /* JMPREL */ + dyn.d_un = elf_relplt_start; + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x1; /* NEEDED */ + dyn.d_un = 0x1; /* The index of "libc.so.6" in .dynstr. */ + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + + dyn.d_tag = 0x0; /* NULL */ + dyn.d_un = 0x0; + elf_write_blk(elf_dynamic, &dyn, sizeof(elf32_dyn_t)); + } - while (elf_strtab->size & 3) - elf_write_byte(elf_strtab, 0); + /* shstr section; len = 53 + * If using dynamic linking, len = 105. + */ + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".text"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".data"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".rodata"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".bss"); + elf_write_byte(elf_shstr, 0); + if (dynlink) { + elf_write_str(elf_shstr, ".interp"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".rel.plt"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".plt"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".got"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".dynstr"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".dynsym"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".dynamic"); + elf_write_byte(elf_shstr, 0); + } + elf_write_str(elf_shstr, ".symtab"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".strtab"); + elf_write_byte(elf_shstr, 0); + elf_write_str(elf_shstr, ".shstrtab"); + elf_write_byte(elf_shstr, 0); } void elf_add_symbol(const char *symbol, int pc) @@ -419,12 +735,31 @@ void elf_add_symbol(const char *symbol, int pc) elf_symbol_index++; } -void elf_generate(const char *outfile) +void elf_preprocess(void) { - elf_align(); - elf_generate_header(); + elf_header_len = sizeof(elf32_hdr_t) + sizeof(elf32_phdr_t); + if (dynlink) + elf_header_len += (sizeof(elf32_phdr_t) * 3); + elf_align(elf_data); + elf_align(elf_rodata); + elf_code_start = ELF_START + elf_header_len; + elf_data_start = elf_code_start + elf_offset; + elf_rodata_start = elf_data_start + elf_data->size; + elf_bss_start = elf_rodata_start + elf_rodata->size; elf_generate_sections(); + elf_align(elf_symtab); + elf_align(elf_strtab); +} +void elf_postprocess(void) +{ + elf_generate_header(); + elf_generate_program_headers(); + elf_generate_section_headers(); +} + +void elf_generate(const char *outfile) +{ if (!outfile) outfile = "a.out"; @@ -436,6 +771,8 @@ void elf_generate(const char *outfile) for (int i = 0; i < elf_header->size; i++) fputc(elf_header->elements[i], fp); + for (int i = 0; i < elf_program_header->size; i++) + fputc(elf_program_header->elements[i], fp); for (int i = 0; i < elf_code->size; i++) fputc(elf_code->elements[i], fp); for (int i = 0; i < elf_data->size; i++) @@ -443,7 +780,29 @@ void elf_generate(const char *outfile) for (int i = 0; i < elf_rodata->size; i++) fputc(elf_rodata->elements[i], fp); /* Note: .bss is not written to file (SHT_NOBITS) */ - for (int i = 0; i < elf_section->size; i++) - fputc(elf_section->elements[i], fp); + if (dynlink) { + for (int i = 0; i < elf_interp->size; i++) + fputc(elf_interp->elements[i], fp); + for (int i = 0; i < elf_relplt->size; i++) + fputc(elf_relplt->elements[i], fp); + for (int i = 0; i < elf_plt->size; i++) + fputc(elf_plt->elements[i], fp); + for (int i = 0; i < elf_got->size; i++) + fputc(elf_got->elements[i], fp); + for (int i = 0; i < elf_dynstr->size; i++) + fputc(elf_dynstr->elements[i], fp); + for (int i = 0; i < elf_dynsym->size; i++) + fputc(elf_dynsym->elements[i], fp); + for (int i = 0; i < elf_dynamic->size; i++) + fputc(elf_dynamic->elements[i], fp); + } + for (int i = 0; i < elf_symtab->size; i++) + fputc(elf_symtab->elements[i], fp); + for (int i = 0; i < elf_strtab->size; i++) + fputc(elf_strtab->elements[i], fp); + for (int i = 0; i < elf_shstr->size; i++) + fputc(elf_shstr->elements[i], fp); + for (int i = 0; i < elf_section_header->size; i++) + fputc(elf_section_header->elements[i], fp); fclose(fp); } diff --git a/src/globals.c b/src/globals.c index 037c8c55..0be66f4f 100644 --- a/src/globals.c +++ b/src/globals.c @@ -95,15 +95,34 @@ strbuf_t *elf_code; strbuf_t *elf_data; strbuf_t *elf_rodata; strbuf_t *elf_header; +strbuf_t *elf_program_header; strbuf_t *elf_symtab; strbuf_t *elf_strtab; -strbuf_t *elf_section; -int elf_header_len = 0x54; /* ELF fixed: 0x34 + 1 * 0x20 */ +strbuf_t *elf_section_header; +strbuf_t *elf_shstr; +strbuf_t *elf_interp; +strbuf_t *elf_dynamic; +strbuf_t *elf_dynsym; +strbuf_t *elf_dynstr; +strbuf_t *elf_relplt; +strbuf_t *elf_plt; +strbuf_t *elf_got; +int elf_header_len; int elf_code_start; int elf_data_start; int elf_rodata_start; int elf_bss_start; int elf_bss_size; +int elf_interp_start; +int elf_relplt_start; +int elf_plt_start; +int elf_got_start; +int relplt_size; +int plt_size; +int got_size; + +/* Dynamic linking flag */ +bool dynlink = false; /* Create a new arena block with given capacity. * @capacity: The capacity of the arena block. Must be positive. @@ -1193,8 +1212,6 @@ void strbuf_free(strbuf_t *src) */ void global_init(void) { - elf_code_start = ELF_START + elf_header_len; - MACROS_MAP = hashmap_create(MAX_ALIASES); /* Initialize arenas first so we can use them for allocation */ @@ -1236,10 +1253,19 @@ void global_init(void) elf_data = strbuf_create(MAX_DATA); elf_rodata = strbuf_create(MAX_DATA); elf_header = strbuf_create(MAX_HEADER); + elf_program_header = strbuf_create(MAX_PROGRAM_HEADER); elf_symtab = strbuf_create(MAX_SYMTAB); elf_strtab = strbuf_create(MAX_STRTAB); - elf_section = strbuf_create(MAX_SECTION); elf_bss_size = 0; + elf_shstr = strbuf_create(MAX_SHSTR); + elf_section_header = strbuf_create(MAX_SECTION_HEADER); + elf_interp = strbuf_create(MAX_INTERP); + elf_dynamic = strbuf_create(MAX_DYNAMIC); + elf_dynsym = strbuf_create(MAX_DYNSYM); + elf_dynstr = strbuf_create(MAX_DYNSTR); + elf_relplt = strbuf_create(MAX_RELPLT); + elf_plt = strbuf_create(MAX_PLT); + elf_got = strbuf_create(MAX_GOTPLT); } /* Forward declaration for lexer cleanup */ @@ -1355,20 +1381,28 @@ void global_release(void) arena_free(BB_ARENA); arena_free(HASHMAP_ARENA); arena_free(GENERAL_ARENA); /* free TYPES and PH2_IR_FLATTEN */ + hashmap_free(FUNC_MAP); + hashmap_free(INCLUSION_MAP); + hashmap_free(ALIASES_MAP); + hashmap_free(CONSTANTS_MAP); strbuf_free(SOURCE); strbuf_free(elf_code); strbuf_free(elf_data); strbuf_free(elf_rodata); strbuf_free(elf_header); + strbuf_free(elf_program_header); strbuf_free(elf_symtab); strbuf_free(elf_strtab); - strbuf_free(elf_section); - - hashmap_free(FUNC_MAP); - hashmap_free(INCLUSION_MAP); - hashmap_free(ALIASES_MAP); - hashmap_free(CONSTANTS_MAP); + strbuf_free(elf_shstr); + strbuf_free(elf_section_header); + strbuf_free(elf_interp); + strbuf_free(elf_dynamic); + strbuf_free(elf_dynsym); + strbuf_free(elf_dynstr); + strbuf_free(elf_relplt); + strbuf_free(elf_plt); + strbuf_free(elf_got); } /* Reports an error without specifying a position */ @@ -1422,6 +1456,8 @@ void print_indent(int indent) void dump_bb_insn(func_t *func, basic_block_t *bb, bool *at_func_start) { + if (!bb) + return; var_t *rd, *rs1, *rs2; if (bb != func->bbs && bb->insn_list.head) { @@ -1644,7 +1680,7 @@ void dump_bb_insn_by_dom(func_t *func, basic_block_t *bb, bool *at_func_start) { dump_bb_insn(func, bb, at_func_start); for (int i = 0; i < MAX_BB_DOM_SUCC; i++) { - if (!bb->dom_next[i]) + if (!bb || !bb->dom_next[i]) break; dump_bb_insn_by_dom(func, bb->dom_next[i], at_func_start); } @@ -1682,6 +1718,8 @@ void dump_insn(void) /* Handle implicit return */ for (int i = 0; i < MAX_BB_PRED; i++) { + if (!func->exit) + break; basic_block_t *bb = func->exit->prev[i].bb; if (!bb) continue; diff --git a/src/main.c b/src/main.c index ff32fa65..abfdfdac 100644 --- a/src/main.c +++ b/src/main.c @@ -59,6 +59,8 @@ int main(int argc, char *argv[]) hard_mul_div = true; else if (!strcmp(argv[i], "--no-libc")) libc = false; + else if (!strcmp(argv[i], "--dynlink")) + dynlink = true; else if (!strcmp(argv[i], "-o")) { if (i + 1 < argc) { out = argv[i + 1]; @@ -75,7 +77,7 @@ int main(int argc, char *argv[]) if (!in) { printf("Missing source file!\n"); printf( - "Usage: shecc [-o output] [+m] [--dump-ir] [--no-libc] " + "Usage: shecc [-o output] [+m] [--dump-ir] [--no-libc] [--dynlink] " "\n"); return -1; } @@ -133,9 +135,19 @@ int main(int argc, char *argv[]) if (dump_ir) dump_ph2_ir(); + /* + * ELF preprocess: + * 1. generate all sections except for .text section. + * 2. calculate the starting addresses of certain sections. + */ + elf_preprocess(); + /* generate code from IR */ code_generate(); + /* ELF postprocess: generate all ELF headers */ + elf_postprocess(); + /* output code in ELF */ elf_generate(out); diff --git a/src/parser.c b/src/parser.c index 32d6598f..7f422f45 100644 --- a/src/parser.c +++ b/src/parser.c @@ -5232,7 +5232,13 @@ void parse_internal(void) func->return_def.type = TY_int; func->num_params = 0; func->va_args = 1; - func->bbs = arena_calloc(BB_ARENA, 1, sizeof(basic_block_t)); + /* In dynamic mode, __syscall won't be implemented but needs to exist + * for parsing the built-in libc. It will be treated as external. */ + func->bbs = NULL; + if (!dynlink) { + /* Otherwise, allocate a basic block to implement in static mode. */ + func->bbs = arena_calloc(BB_ARENA, 1, sizeof(basic_block_t)); + } /* lexer initialization */ SOURCE->size = 0; diff --git a/src/reg-alloc.c b/src/reg-alloc.c index 36058334..276660e5 100644 --- a/src/reg-alloc.c +++ b/src/reg-alloc.c @@ -715,6 +715,11 @@ void reg_alloc(void) ir = bb_add_ph2_ir(bb, OP_address_of_func); ir->src0 = src0; strcpy(ir->func_name, insn->rs2->var_name); + if (dynlink) { + func_t *target_fn = find_func(ir->func_name); + if (target_fn) + target_fn->is_used = true; + } } else { /* FIXME: Register content becomes stale after store * operation. Current workaround causes redundant @@ -763,6 +768,9 @@ void reg_alloc(void) if (!callee_func->num_params) spill_alive(bb, insn); + if (dynlink) + callee_func->is_used = true; + ir = bb_add_ph2_ir(bb, OP_call); strcpy(ir->func_name, insn->str); diff --git a/src/riscv-codegen.c b/src/riscv-codegen.c index 90520ed5..8507a985 100644 --- a/src/riscv-codegen.c +++ b/src/riscv-codegen.c @@ -447,10 +447,6 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir) void code_generate(void) { - elf_data_start = elf_code_start + elf_offset; - elf_rodata_start = elf_data_start + elf_data->size; - elf_bss_start = elf_rodata_start + elf_rodata->size; - /* start: save original sp in s0; allocate global stack; run init */ emit(__addi(__s0, __sp, 0)); emit(__lui(__t0, rv_hi(GLOBAL_FUNC->stack_size))); diff --git a/src/ssa.c b/src/ssa.c index 8b2c1511..cf43e052 100644 --- a/src/ssa.c +++ b/src/ssa.c @@ -167,6 +167,8 @@ void build_idom(void) continue; bool changed; + if (!func->bbs) + continue; func->bbs->idom = func->bbs; @@ -308,6 +310,8 @@ void build_r_idom(void) continue; bool changed; + if (!func->bbs) + continue; func->exit->r_idom = func->exit; @@ -364,6 +368,8 @@ bool rdom_connect(basic_block_t *pred, basic_block_t *succ) void bb_build_rdom(func_t *func, basic_block_t *bb) { + if (!func->bbs) + return; for (basic_block_t *curr = bb; curr != func->exit; curr = curr->r_idom) { if (!rdom_connect(curr->r_idom, curr)) break; @@ -1897,7 +1903,8 @@ void optimize(void) continue; /* basic block level (control flow) optimizations */ - + if (!func->bbs) + continue; for (basic_block_t *bb = func->bbs; bb; bb = bb->rpo_next) { /* instruction level optimizations */ for (insn_t *insn = bb->insn_list.head; insn; insn = insn->next) { From 8a22ab92b0f11a63bf56ed15589700d21cc2f1c0 Mon Sep 17 00:00:00 2001 From: Yu-En Hsiao Date: Tue, 9 Sep 2025 21:41:35 +0800 Subject: [PATCH 2/2] Fix Arm calling convention by push/pop operations --- src/arm-codegen.c | 8 ++++++++ src/defs.h | 1 + src/reg-alloc.c | 19 ++++++++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/src/arm-codegen.c b/src/arm-codegen.c index 0d67d225..ede6e8d0 100644 --- a/src/arm-codegen.c +++ b/src/arm-codegen.c @@ -69,6 +69,8 @@ void update_elf_offset(ph2_ir_t *ph2_ir) return; case OP_read: case OP_write: + case OP_push: + case OP_pop: case OP_jump: case OP_call: case OP_load_func: @@ -284,6 +286,12 @@ void emit_ph2_ir(ph2_ir_t *ph2_ir) else abort(); return; + case OP_push: + emit(__stmdb(__AL, 1, __sp, rn)); + return; + case OP_pop: + emit(__add_i(__AL, __sp, __sp, rn * 4)); + return; case OP_branch: emit(__teq(rn)); if (ph2_ir->is_branch_detached) { diff --git a/src/defs.h b/src/defs.h index 5102d930..4244c45e 100644 --- a/src/defs.h +++ b/src/defs.h @@ -268,6 +268,7 @@ typedef enum { OP_call, /* function call */ OP_indirect, /* indirect call with function pointer */ OP_return, /* explicit return */ + OP_pop, /* eliminate arguments */ OP_allocat, /* allocate space on stack */ OP_assign, diff --git a/src/reg-alloc.c b/src/reg-alloc.c index 276660e5..2b2de3c7 100644 --- a/src/reg-alloc.c +++ b/src/reg-alloc.c @@ -768,12 +768,29 @@ void reg_alloc(void) if (!callee_func->num_params) spill_alive(bb, insn); - if (dynlink) + if (dynlink) { callee_func->is_used = true; + /* Push args to stack for Arm output */ + if (!callee_func->bbs && args > 4) { + int regs = 0; + for (int i = 4; i < args; i++) + regs |= (1 << i); + ir = bb_add_ph2_ir(bb, OP_push); + ir->src0 = regs; + } + } ir = bb_add_ph2_ir(bb, OP_call); strcpy(ir->func_name, insn->str); + if (dynlink) { + /* Pop args from stack for Arm output */ + if (!callee_func->bbs && args > 4) { + ir = bb_add_ph2_ir(bb, OP_pop); + ir->src0 = args - 4; + } + } + is_pushing_args = false; args = 0;