diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 81a7c242..e5b22a57 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -21,7 +21,7 @@ jobs: # Bootstrap each target and run the consolidated behavioral suite against # the stage 0 and stage 2 compilers. host-x86: - name: ${{ matrix.architecture }}/${{ matrix.link_mode }} (${{ matrix.compiler }}) + name: ${{ matrix.architecture }}/${{ matrix.link_mode }}/${{ matrix.binding_mode }} (${{ matrix.compiler }}) runs-on: ubuntu-24.04 timeout-minutes: 30 strategy: @@ -31,11 +31,47 @@ jobs: matrix: compiler: [gcc, clang] architecture: [arm, arm64, riscv, x64] - link_mode: [static, dynamic] + link_mode: [dynamic] + binding_mode: [lazy, immediate] + include: + - compiler: gcc + architecture: arm + link_mode: static + binding_mode: none + - compiler: clang + architecture: arm + link_mode: static + binding_mode: none + - compiler: gcc + architecture: riscv + link_mode: static + binding_mode: none + - compiler: clang + architecture: riscv + link_mode: static + binding_mode: none + - compiler: gcc + architecture: arm64 + link_mode: static + binding_mode: none + - compiler: clang + architecture: arm64 + link_mode: static + binding_mode: none + - compiler: gcc + architecture: x64 + link_mode: static + binding_mode: none + - compiler: clang + architecture: x64 + link_mode: static + binding_mode: none env: CC: ${{ matrix.compiler }} ARCH: ${{ matrix.architecture }} DYNLINK: ${{ matrix.link_mode == 'dynamic' && '1' || '0' }} + # This variable is ignored when verifying static linking mode + BINDING: ${{ matrix.binding_mode == 'immediate' && 'now' || 'lazy' }} steps: - name: Checkout code uses: actions/checkout@v7 @@ -45,18 +81,18 @@ jobs: architecture: ${{ matrix.architecture }} link-mode: ${{ matrix.link_mode }} github-token: ${{ github.token }} - # ARCH and DYNLINK are named on the command line rather than left to the - # environment: the build session the tree records assigns ARCH outright, + # ARCH, DYNLINK and BINDING are named on the command line rather than left + # to the environment: the build session the tree records assigns ARCH outright, # and a makefile assignment wins over the environment. - name: Build artifacts - run: make ARCH="$ARCH" DYNLINK="$DYNLINK" + run: make ARCH="$ARCH" DYNLINK="$DYNLINK" BINDING="$BINDING" - name: Unit tests - run: make check ARCH="$ARCH" DYNLINK="$DYNLINK" + run: make check ARCH="$ARCH" DYNLINK="$DYNLINK" BINDING="$BINDING" - name: Upload the test logs if: failure() uses: actions/upload-artifact@v7 with: - name: logs-${{ matrix.compiler }}-${{ matrix.architecture }}-${{ matrix.link_mode }} + name: logs-${{ matrix.compiler }}-${{ matrix.architecture }}-${{ matrix.link_mode }}-${{ matrix.binding_mode }} path: | out/*.log if-no-files-found: ignore @@ -68,7 +104,7 @@ jobs: # sanitizer step of host-x86 used to do. Building "sanitizer" first, in a # job that builds nothing else, is what puts the instrumentation in. sanitizer: - name: Sanitizers ${{ matrix.architecture }}/${{ matrix.link_mode }} + name: Sanitizers ${{ matrix.architecture }}/${{ matrix.link_mode }}/${{ matrix.binding_mode }} runs-on: ubuntu-24.04 timeout-minutes: 30 strategy: @@ -81,10 +117,26 @@ jobs: # roughly fifteen hours. Restoring the dimension means finding out # why first. architecture: [arm, arm64, riscv, x64] - link_mode: [static, dynamic] + link_mode: [dynamic] + binding_mode: [lazy, immediate] + include: + - architecture: arm + link_mode: static + binding_mode: none + - architecture: riscv + link_mode: static + binding_mode: none + - architecture: arm64 + link_mode: static + binding_mode: none + - architecture: x64 + link_mode: static + binding_mode: none env: ARCH: ${{ matrix.architecture }} DYNLINK: ${{ matrix.link_mode == 'dynamic' && '1' || '0' }} + # This variable is ignored when verifying static linking mode + BINDING: ${{ matrix.binding_mode == 'immediate' && 'now' || 'lazy' }} steps: - name: Checkout code uses: actions/checkout@v7 @@ -95,9 +147,9 @@ jobs: link-mode: ${{ matrix.link_mode }} github-token: ${{ github.token }} - name: Build the stage 0 compiler with sanitizers - run: make sanitizer ARCH="$ARCH" DYNLINK="$DYNLINK" + run: make sanitizer ARCH="$ARCH" DYNLINK="$DYNLINK" BINDING="$BINDING" - name: Sanitizer-enabled stage 0 tests - run: make check-sanitizer ARCH="$ARCH" DYNLINK="$DYNLINK" + run: make check-sanitizer ARCH="$ARCH" DYNLINK="$DYNLINK" BINDING="$BINDING" # Preprocess shecc with itself, then compile the result: the stage 1 source # is the largest input the preprocessor gets, and the only one that exercises @@ -135,17 +187,27 @@ jobs: # AArch64 that is the only place the real loader is exercised: QEMU-user maps # the image itself, and on an x86-64 host it always presents 4 KiB pages. host-arm: - name: ${{ matrix.architecture }}/${{ matrix.link_mode }} on Arm64 + name: ${{ matrix.architecture }}/${{ matrix.link_mode }}/${{ matrix.binding_mode }} on Arm64 runs-on: ubuntu-24.04-arm timeout-minutes: 30 strategy: fail-fast: false matrix: architecture: [arm, arm64] - link_mode: [static, dynamic] + link_mode: [dynamic] + binding_mode: [lazy, immediate] + include: + - architecture: arm + link_mode: static + binding_mode: none + - architecture: arm64 + link_mode: static + binding_mode: none env: ARCH: ${{ matrix.architecture }} DYNLINK: ${{ matrix.link_mode == 'dynamic' && '1' || '0' }} + # This variable is ignored when verifying static linking mode + BINDING: ${{ matrix.binding_mode == 'immediate' && 'now' || 'lazy' }} steps: - name: Checkout code uses: actions/checkout@v7 @@ -156,14 +218,14 @@ jobs: link-mode: ${{ matrix.link_mode }} github-token: ${{ github.token }} - name: Build artifacts - run: make ARCH="$ARCH" DYNLINK="$DYNLINK" + run: make ARCH="$ARCH" DYNLINK="$DYNLINK" BINDING="$BINDING" - name: Unit tests - run: make check ARCH="$ARCH" DYNLINK="$DYNLINK" + run: make check ARCH="$ARCH" DYNLINK="$DYNLINK" BINDING="$BINDING" - name: Upload the test logs if: failure() uses: actions/upload-artifact@v7 with: - name: logs-arm64-host-${{ matrix.architecture }}-${{ matrix.link_mode }} + name: logs-arm64-host-${{ matrix.architecture }}-${{ matrix.link_mode }}-${{ matrix.binding_mode }} path: | out/*.log out/tests/*.log @@ -174,15 +236,21 @@ jobs: # build that produces the object files, and that job has already made them # without instrumentation. host-arm-sanitizer: - name: Sanitizers arm/${{ matrix.link_mode }} on Arm64 + name: Sanitizers arm/${{ matrix.link_mode }}/${{ matrix.binding_mode }} on Arm64 runs-on: ubuntu-24.04-arm timeout-minutes: 30 strategy: fail-fast: false matrix: - link_mode: [static, dynamic] + link_mode: [dynamic] + binding_mode: [lazy, immediate] + include: + - link_mode: static + binding_mode: none env: DYNLINK: ${{ matrix.link_mode == 'dynamic' && '1' || '0' }} + # This variable is ignored when verifying static linking mode + BINDING: ${{ matrix.binding_mode == 'immediate' && 'now' || 'lazy' }} steps: - name: Checkout code uses: actions/checkout@v7 @@ -193,9 +261,9 @@ jobs: link-mode: ${{ matrix.link_mode }} github-token: ${{ github.token }} - name: Build the stage 0 compiler with sanitizers - run: make sanitizer ARCH=arm DYNLINK="$DYNLINK" + run: make sanitizer ARCH=arm DYNLINK="$DYNLINK" BINDING="$BINDING" - name: Sanitizer-enabled stage 0 tests - run: make check-sanitizer ARCH=arm DYNLINK="$DYNLINK" + run: make check-sanitizer ARCH=arm DYNLINK="$DYNLINK" BINDING="$BINDING" coding-style: name: Coding style diff --git a/Makefile b/Makefile index 434ca363..9fbc6972 100644 --- a/Makefile +++ b/Makefile @@ -84,12 +84,13 @@ TRANSLATION_DEFS = "\#define SHECC_TRANSLATION_DATE \"$(TRANSLATION_DATE)\"\n\#d 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))) @@ -236,23 +237,23 @@ uninstall-hooks: check-stage0: $(OUT)/$(STAGE0) tests/driver.sh $(VECHO) " TEST STAGE 0\n" - tests/driver.sh 0 $(DYNLINK) + tests/driver.sh 0 $(DYNLINK) $(BINDING) check-stage2: $(OUT)/$(STAGE2) tests/driver.sh $(VECHO) " TEST STAGE 2\n" - tests/driver.sh 2 $(DYNLINK) + tests/driver.sh 2 $(DYNLINK) $(BINDING) check-sanitizer: $(OUT)/$(STAGE0)-sanitizer tests/driver.sh $(VECHO) " TEST STAGE 0 (with sanitizers)\n" $(Q)cp $(OUT)/$(STAGE0)-sanitizer $(OUT)/shecc - tests/driver.sh 0 $(DYNLINK) + tests/driver.sh 0 $(DYNLINK) $(BINDING) $(Q)rm $(OUT)/shecc check-abi-stage0: $(OUT)/$(STAGE0) - tests/$(ARCH)-abi.sh 0 $(DYNLINK); + tests/$(ARCH)-abi.sh 0 $(DYNLINK) $(BINDING); check-abi-stage2: $(OUT)/$(STAGE2) - tests/$(ARCH)-abi.sh 2 $(DYNLINK); + tests/$(ARCH)-abi.sh 2 $(DYNLINK) $(BINDING); # Both prerequisites are order-only, and both exist because "make -j" would # otherwise let a compile start beside the thing it reads. Selecting a target diff --git a/README.md b/README.md index bb7147a4..8f7bc617 100644 --- a/README.md +++ b/README.md @@ -164,6 +164,20 @@ out/shecc-stage2.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dy out/shecc-stage2.elf: ELF 64-bit LSB executable, ARM aarch64, dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, no section header ``` +When using `DYNLINK=1`, adding `BINDING=now` can also enable the use of **immediate binding**. +```shell +# Dynamic linking with lazy binding +$ make DYNLINK=1 +$ make DYNLINK=1 BINDING=lazy + +# Dynamic linking with immediate binding +$ make DYNLINK=1 BINDING=now + +# BINDING is ignored by the build system if the static linking mode is enabled. +$ make BINDING=now +$ make DYNLINK=0 BINDING=lazy +``` + For development builds with memory safety checks: ```shell $ make sanitizer @@ -172,7 +186,7 @@ $ make check-sanitizer File `out/shecc` is the first stage compiler. Its usage: ```shell -$ shecc [-o output] [+m] [--dot] [--no-libc] [--dump-ir] [--dynlink] [-E] +$ shecc [-o output] [+m] [--dot] [--no-libc] [--dump-ir] [--dynlink] [-z ] [-E] ``` Compiler options: @@ -182,6 +196,9 @@ Compiler options: - `--no-libc` : Exclude embedded C library (default: embedded) - `--dump-ir` : Dump intermediate representation (IR) - `--dynlink` : Use dynamic linking (default: disabled) +- `-z`: Use lazy binding or immediate binding (default: lazy) + - This option is valid only when `--dynlink` is also enabled. Otherwise, the compiler + reports an error message if this option is used with static linking. - `-E` : Preprocess only; write the expanded token stream and stop Example 1: static linking mode diff --git a/docs/dynamic-linking.md b/docs/dynamic-linking.md index a9f1c283..f1acd0a0 100644 --- a/docs/dynamic-linking.md +++ b/docs/dynamic-linking.md @@ -5,8 +5,13 @@ Build the dynamically linked version of shecc: ```shell +# The 'DYNLINK' variable can control the use of dynamic linking mode $ make ARCH=arm DYNLINK=1 $ make ARCH=riscv DYNLINK=1 + +# Enable immediate binding using the 'BINDING' variable (default: lazy) +$ make ARCH=arm DYNLINK=1 BINDING=now +$ make ARCH=riscv DYNLINK=1 BINDING=now ``` Next, you can use shecc to build dynamically linked programs by adding the `--dynlink` flag: @@ -17,6 +22,9 @@ $ out/shecc --dynlink -o # Use the stage 1 or stage 2 compiler $ qemu-arm -L out/shecc-stage2.elf --dynlink -o $ qemu-riscv32 -L out/shecc-stage2.elf --dynlink -o +# Enable immediate binding by the '-z now' option +$ qemu-arm -L out/shecc-stage2.elf --dynlink -z now -o +$ qemu-riscv32 -L out/shecc-stage2.elf --dynlink -z now -o # Execute the compiled program $ qemu-arm -L @@ -331,7 +339,6 @@ For the current implementation of dynamic linking, note the following: * GOT is located in a writable segment (`.data` segment). * The `PT_GNU_RELRO` program header has not yet been implemented. -* `DT_BIND_NOW` (force immediate binding) is not set. This implies that: diff --git a/mk/arm64.mk b/mk/arm64.mk index a3a4151a..9eab2908 100644 --- a/mk/arm64.mk +++ b/mk/arm64.mk @@ -22,11 +22,10 @@ ARCH_DEFS = \ \#define DYN_LINKER \"/lib/ld-linux-aarch64.so.1\"\n$\ \#define LIBC_SO \"libc.so.6\"\n$\ \#define LIBDL_SO \"libdl.so.2\"\n$\ - \#define PLT_FIXUP_SIZE 0\n$\ + \#define PLT_FIXUP_SIZE 32\n$\ \#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 98c73210..877c1ed8 100644 --- a/mk/x64.mk +++ b/mk/x64.mk @@ -17,7 +17,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/arm64-codegen.c b/src/arm64-codegen.c index a66246d7..dcbbe435 100644 --- a/src/arm64-codegen.c +++ b/src/arm64-codegen.c @@ -650,10 +650,42 @@ void emit_ph2_ir(ph2_ir_t *p) */ void plt_generate(void) { - int entries = dynamic_sections.plt_size / PLT_ENT_SIZE; + int entries = (dynamic_sections.plt_size - PLT_FIXUP_SIZE) / PLT_ENT_SIZE; + int ent = dynamic_sections.elf_plt_start; + int got = + dynamic_sections.elf_got_start + PTR_SIZE * (RESERVED_GOT_NUM - 1); + + /* _dl_runtime_resolve() requires: + * + * - ip0: Address of got[2] (&got[2]) + * - ip1: got[2] + * - [sp]: Address of got[N + 3] (Address of the N-th function's GOT stub.) + * - [sp + 8]: Value stored in register lr + * + * PLT[0] uses the STP, ADRP, LDR, ADD and BR instructions to prepare the + * above arguments and jump to the resolver. + * + * The remaining 3 nop instructions are used to pad PLT[0] to 32 bytes, + * allowing the later sections to stay aligned. + */ + elf_write_int(dynamic_sections.elf_plt, + a64_stp_pre_insn(A64_IP0, A64_LR, A64_SP, -16)); + elf_write_int(dynamic_sections.elf_plt, + a64_adrp_insn(A64_IP0, ent + 4, got)); + elf_write_int(dynamic_sections.elf_plt, + a64_add_imm_insn(true, A64_IP0, A64_IP0, got & 0xfff)); + elf_write_int(dynamic_sections.elf_plt, + a64_mem_scaled_insn(A64_LOAD, 8, A64_IP1, A64_IP0, 0)); + elf_write_int(dynamic_sections.elf_plt, a64_br_insn(A64_IP1)); + elf_write_int(dynamic_sections.elf_plt, a64_nop_insn()); + elf_write_int(dynamic_sections.elf_plt, a64_nop_insn()); + elf_write_int(dynamic_sections.elf_plt, a64_nop_insn()); + ent += PLT_FIXUP_SIZE; for (int i = 0; i < entries; i++) { - int ent = dynamic_sections.elf_plt_start + i * PLT_ENT_SIZE; - int got = + /* Each PLT stub uses the ADRP, ADD, LDR and BR instructions to pass + * &got[N + 3] and got[N + 3] to ip0 and ip1, respectively. + */ + got = dynamic_sections.elf_got_start + PTR_SIZE * (RESERVED_GOT_NUM + i); elf_write_int(dynamic_sections.elf_plt, a64_adrp_insn(A64_IP0, ent, got)); @@ -662,6 +694,7 @@ void plt_generate(void) elf_write_int(dynamic_sections.elf_plt, a64_mem_scaled_insn(A64_LOAD, 8, A64_IP1, A64_IP0, 0)); elf_write_int(dynamic_sections.elf_plt, a64_br_insn(A64_IP1)); + ent += PLT_ENT_SIZE; } } diff --git a/src/arm64.c b/src/arm64.c index 1fca24b7..da61e83b 100644 --- a/src/arm64.c +++ b/src/arm64.c @@ -337,3 +337,8 @@ int a64_adrp_pages_insn(int rd, int pages) return 0x90000000 | ((pages & 3) << 29) | (((pages >> 2) & 0x7ffff) << 5) | rd; } + +int a64_nop_insn() +{ + return 0xd503201f; +} diff --git a/src/defs.h b/src/defs.h index 647b6799..a3be7731 100644 --- a/src/defs.h +++ b/src/defs.h @@ -192,13 +192,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 c2ff605f..b6bb6855 100644 --- a/src/elf.c +++ b/src/elf.c @@ -1112,14 +1112,13 @@ void elf_generate_dynamic_sections(void) elf_write_dyn(dynamic_sections.elf_dynamic, 0x1, 0x1); if (libdl_name) elf_write_dyn(dynamic_sections.elf_dynamic, 0x1, libdl_name); -#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 2dad38be..3ba7e0c9 100644 --- a/src/globals.c +++ b/src/globals.c @@ -116,6 +116,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 4b3aa5d6..24fc2fe0 100644 --- a/src/main.c +++ b/src/main.c @@ -105,6 +105,7 @@ int main(int argc, char *argv[]) char *in = NULL; token_stream_t *libc_token_stream = NULL, *token_stream; token_t *tk; + bool has_binding_option = false; for (int i = 1; i < argc; i++) { if (!strcmp(argv[i], "--dump-ir")) @@ -121,7 +122,19 @@ 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\""); + + has_binding_option = true; + 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\""); + i++; + } else if (!strcmp(argv[i], "-E")) expand_only = true; else if (!strcmp(argv[i], "-I")) { if (i + 1 >= argc) @@ -147,12 +160,16 @@ int main(int argc, char *argv[]) if (!in) { printf( - "Usage: shecc [-I directory] [-o output] [+m] [--dot] [--dump-ir] " + "Usage: shecc [-I directory] [-o output] [+m] [--dot] " + "[--dump-ir] " "[--warn-string-literals] [--std=c99] [--no-libc] " - "[--dynlink] [-E] \n"); + "[--dynlink] [-z ] [-E] \n"); usage_error("Missing source file"); } + if (!dynlink && has_binding_option) + usage_error("-z cannot be used under static linking."); + /* --dot stops the pipeline at a different phase than these do. */ if (dump_dot && expand_only) usage_error("--dot cannot be combined with -E"); diff --git a/tests/arm-abi.sh b/tests/arm-abi.sh index 263ccc19..911245f7 100755 --- a/tests/arm-abi.sh +++ b/tests/arm-abi.sh @@ -41,9 +41,10 @@ fi # Command Line Arguments if [ "$#" -lt 1 ]; then - echo "Usage: $0 []" + echo "Usage: $0 [ []]" echo " stage: 0 (host compiler), 1 (stage1), or 2 (stage2)" echo " dynlink: 0 (static linking), 1 (dynamic linking)" + echo " binding: lazy, now" echo "" echo "Environment Variables:" echo " VERBOSE=1 Enable verbose output" @@ -73,6 +74,7 @@ case "$1" in esac DYNLINK="${2:-0}" +BINDING="${3:-lazy}" # Banner echo -e "${BLUE}${BOLD}========================================${NC}" @@ -144,7 +146,7 @@ run_abi_test() # Compile local compile_cmd="$SHECC" if [[ "$DYNLINK" == "1" ]]; then - compile_cmd="$compile_cmd --dynlink" + compile_cmd="$compile_cmd --dynlink -z $BINDING" fi compile_cmd="$compile_cmd -o /tmp/shecc_abi_test_$$.elf $test_file" diff --git a/tests/arm64-abi.sh b/tests/arm64-abi.sh index 9a986cb4..5ad6c546 100755 --- a/tests/arm64-abi.sh +++ b/tests/arm64-abi.sh @@ -4,7 +4,7 @@ set -eu if [ "$#" -lt 1 ]; then - echo "Usage: $0 []" >&2 + echo "Usage: $0 [ []]" >&2 exit 2 fi @@ -23,7 +23,7 @@ case "$1" in esac if [ "${2:-0}" = 1 ]; then - shecc+=(--dynlink) + shecc+=(--dynlink -z "${3:-lazy}") link_mode=dynamic else link_mode=static diff --git a/tests/driver.sh b/tests/driver.sh index db80f40f..c291c4bf 100755 --- a/tests/driver.sh +++ b/tests/driver.sh @@ -72,9 +72,10 @@ PROGRESS_COUNT=0 # Command Line Arguments if [ "$#" -lt 1 ]; then - echo "Usage: $0 []" + echo "Usage: $0 [] []" echo " stage: 0 (host compiler), 1 (stage1), or 2 (stage2)" echo " dynlink: 0 (static linking), 1 (dynamic linking)" + echo " binding: lazy, now" echo "" echo "Environment Variables:" echo " VERBOSE=1 Enable verbose output" @@ -106,7 +107,7 @@ case "$1" in esac if [ $# -ge 2 ] && [ "$2" = "1" ]; then - readonly SHECC_CFLAGS="--dynlink" + readonly SHECC_CFLAGS="--dynlink -z ${3:-lazy}" readonly LINK_MODE="dynamic" else readonly SHECC_CFLAGS="" diff --git a/tests/riscv-abi.sh b/tests/riscv-abi.sh index 34c3aadb..1ea3ca51 100755 --- a/tests/riscv-abi.sh +++ b/tests/riscv-abi.sh @@ -41,9 +41,10 @@ fi # Command Line Arguments if [ "$#" -lt 1 ]; then - echo "Usage: $0 []" + echo "Usage: $0 [ []]" echo " stage: 0 (host compiler), 1 (stage1), or 2 (stage2)" echo " dynlink: 0 (static linking), 1 (dynamic linking)" + echo " binding: lazy, now" echo "" echo "Environment Variables:" echo " VERBOSE=1 Enable verbose output" @@ -73,6 +74,7 @@ case "$1" in esac DYNLINK="${2:-0}" +BINDING="${3:-lazy}" # Banner echo -e "${BLUE}${BOLD}========================================${NC}" @@ -144,7 +146,7 @@ run_abi_test() # Compile local compile_cmd="$SHECC" if [[ "$DYNLINK" == "1" ]]; then - compile_cmd="$compile_cmd --dynlink" + compile_cmd="$compile_cmd --dynlink -z $BINDING" fi compile_cmd="$compile_cmd -o /tmp/shecc_abi_test_$$.elf $test_file" diff --git a/tests/x64-abi.sh b/tests/x64-abi.sh index cf91f280..12e75fc2 100755 --- a/tests/x64-abi.sh +++ b/tests/x64-abi.sh @@ -42,9 +42,10 @@ fi # Command Line Arguments if [ "$#" -lt 1 ]; then - echo "Usage: $0 []" + echo "Usage: $0 [ []]" echo " stage: 0 (host compiler), 1 (stage1), or 2 (stage2)" echo " dynlink: 0 (static linking), 1 (dynamic linking)" + echo " binding: lazy, now" echo "" echo "Environment Variables:" echo " VERBOSE=1 Enable verbose output" @@ -74,6 +75,7 @@ case "$1" in esac DYNLINK="${2:-0}" +BINDING="${3:-lazy}" # Banner echo -e "${BLUE}${BOLD}========================================${NC}" @@ -149,7 +151,7 @@ run_abi_test() # Compile local compile_cmd="$SHECC" if [[ "$DYNLINK" == "1" ]]; then - compile_cmd="$compile_cmd --dynlink" + compile_cmd="$compile_cmd --dynlink -z $BINDING" fi compile_cmd="$compile_cmd -o /tmp/shecc_abi_test_$$.elf $test_file"