From 53b0d9119c36a6a37f8945b3a2928c1f9fefa5cd Mon Sep 17 00:00:00 2001 From: Yu-En Hsiao Date: Wed, 9 Sep 2026 21:08:31 +0800 Subject: [PATCH 1/2] Implement immediate binding for dynamic linking Since the original codebase already implemented the immediate binding flag for the x86-64 and AArch64 architectures, this commit introduces a "-z" option so that the Arm32 and RV32 targets can also use immediate binding. When the command-line arguments contain "-z now", the compiler sets its internal flag (imm_binding) to true and generates the Elf32_Dyn/Elf64_Dyn objects to enable exectuables to perform immediate binding at runtime. For 64-bit targets, immediate binding is always used under the dynamic linking mode as is. --- mk/arm64.mk | 1 - mk/x64.mk | 1 - src/defs.h | 7 ------- src/elf.c | 11 +++++++---- src/globals.c | 1 + src/main.c | 25 +++++++++++++++++++++++-- 6 files changed, 31 insertions(+), 15 deletions(-) diff --git a/mk/arm64.mk b/mk/arm64.mk index c6b402b2..cb60023d 100644 --- a/mk/arm64.mk +++ b/mk/arm64.mk @@ -25,7 +25,6 @@ ARCH_DEFS = \ \#define PLT_ENT_SIZE 16\n$\ \#define RESERVED_GOT_NUM 3\n$\ \#define R_ARCH_JUMP_SLOT 1026 /* R_AARCH64_JUMP_SLOT */\n$\ - \#define DYN_BIND_NOW 1\n$\ " # An Arm64 Linux host runs this target's output itself, so nothing has to stand diff --git a/mk/x64.mk b/mk/x64.mk index 85ed95a8..cff726ac 100644 --- a/mk/x64.mk +++ b/mk/x64.mk @@ -16,7 +16,6 @@ ARCH_DEFS = \ \#define RESERVED_GOT_NUM 3\n$\ \#define R_ARCH_JUMP_SLOT 7 /* R_X86_64_JUMP_SLOT */\n$\ \#define REG_CNT 11 /* rdi rsi rdx rcx r8 r9 rax rbx r14 r12 r13 */\n$\ - \#define DYN_BIND_NOW 1 /* this PLT has no lazy-resolution path */\n$\ \#define HAVE_COND_MOVE 1 /* CMOVcc */\n$\ \#define CALLEE_SAVED_REGS 4 /* the file ends rbx r14 r12 r13 */\n$\ " diff --git a/src/defs.h b/src/defs.h index 3534f2fe..3580c8a2 100644 --- a/src/defs.h +++ b/src/defs.h @@ -177,13 +177,6 @@ #define ALIGN_UP(val, align) (((val) + (align) - 1) & ~((align) - 1)) #endif -/* Targets whose PLT has no lazy-resolution path ask the loader to bind every - * PLT entry at load time. - */ -#ifndef DYN_BIND_NOW -#define DYN_BIND_NOW 0 -#endif - #define ELF_MACHINE_ARM32 0x28 #define ELF_MACHINE_RV32 0xf3 #define ELF_MACHINE_X86_64 0x3e diff --git a/src/elf.c b/src/elf.c index 66ec0e34..316d8e69 100644 --- a/src/elf.c +++ b/src/elf.c @@ -1086,14 +1086,17 @@ void elf_generate_dynamic_sections(void) elf_write_dyn(dynamic_sections.elf_dynamic, 0x3, dynamic_sections.elf_got_start); elf_write_dyn(dynamic_sections.elf_dynamic, 0x1, 0x1); -#if DYN_BIND_NOW == 1 + /* Resolve every PLT entry at load time. This target's PLT[0] does not * arrange the GOT[1]/GOT[2] hand-off the lazy resolver needs, so the loader * writes the final addresses straight into the GOT instead. */ - elf_write_dyn(dynamic_sections.elf_dynamic, 0x18, 0x0); /* DT_BIND_NOW */ - elf_write_dyn(dynamic_sections.elf_dynamic, 0x1e, 0x8); /* DF_BIND_NOW */ -#endif + if (imm_binding) { + elf_write_dyn(dynamic_sections.elf_dynamic, 0x18, + 0x0); /* DT_BIND_NOW */ + elf_write_dyn(dynamic_sections.elf_dynamic, 0x1e, + 0x8); /* DF_BIND_NOW */ + } elf_write_dyn(dynamic_sections.elf_dynamic, 0x0, 0x0); } diff --git a/src/globals.c b/src/globals.c index dd89767b..147aa133 100644 --- a/src/globals.c +++ b/src/globals.c @@ -101,6 +101,7 @@ dynamic_sections_t dynamic_sections; /* Command line compilation flags */ bool dynlink = false; +bool imm_binding = false; bool libc = true; bool expand_only = false; bool dump_ir = false; diff --git a/src/main.c b/src/main.c index 1ccd5134..e3282ffa 100644 --- a/src/main.c +++ b/src/main.c @@ -117,7 +117,17 @@ int main(int argc, char *argv[]) libc = false; else if (!strcmp(argv[i], "--dynlink")) dynlink = true; - else if (!strcmp(argv[i], "-E")) + else if (!strcmp(argv[i], "-z")) { + if (i + 1 >= argc) + usage_error("-z requires \"lazy\" or \"now\""); + + if (!strcmp(argv[i + 1], "lazy")) + imm_binding = false; + else if (!strcmp(argv[i + 1], "now")) + imm_binding = true; + else + usage_error("-z requires \"lazy\" or \"now\""); + } else if (!strcmp(argv[i], "-E")) expand_only = true; else if (!strcmp(argv[i], "-o")) { if (i + 1 < argc) { @@ -131,10 +141,21 @@ int main(int argc, char *argv[]) in = argv[i]; } + if (dynlink) { + switch (ELF_MACHINE) { + /* The following 64-bit targets have no lazy-resolution path, so + * immediate binding must be used. + */ + case ELF_MACHINE_X86_64: + case ELF_MACHINE_AARCH64: + imm_binding = true; + } + } + if (!in) { printf( "Usage: shecc [-o output] [+m] [--dot] [--dump-ir] [--no-libc] " - "[--dynlink] [-E] \n"); + "[--dynlink] [-z ] [-E] \n"); usage_error("Missing source file"); } From 0d1e98b258feb0ebf37dfec2f019ac227883e281 Mon Sep 17 00:00:00 2001 From: Yu-En Hsiao Date: Wed, 9 Sep 2026 21:26:17 +0800 Subject: [PATCH 2/2] Introduce a Makefile variable to determine the binding strategy Since the compiler supports the "-z" option for lazy or immediate binding, a new "BINDING" variable has benn added to the Makefile so that users can choose which binding mode to use. For example, users can build dynamically linked compilers with immediate binding as follows: $ make DYNLINK=1 BINDING=now --- Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 6661e623..73dc8026 100644 --- a/Makefile +++ b/Makefile @@ -53,12 +53,13 @@ BUILTIN_LIBC_HEADER := c.h STAGE0_FLAGS ?= --dump-ir STAGE1_FLAGS ?= DYNLINK ?= 0 +BINDING ?= lazy COMMENTFLOW ?= commentflow SHFMT ?= shfmt ifeq ($(DYNLINK),1) - STAGE0_FLAGS += --dynlink - STAGE1_FLAGS += --dynlink + STAGE0_FLAGS += --dynlink -z $(BINDING) + STAGE1_FLAGS += --dynlink -z $(BINDING) endif SRCS := $(wildcard $(patsubst %,%/main.c, $(SRCDIR)))