-
Notifications
You must be signed in to change notification settings - Fork 63
Add R_ARM_CALL runnable test #1225
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| // Distant callee function placed in a far section | ||
| // This will require a trampoline for ARM BL instruction | ||
|
|
||
| __attribute__((section(".text.distant"))) int distant_callee(void) { | ||
| return 42; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // Hidden symbol: non-preemptible, direct relocation, no PLT entry. | ||
| #define UART0_DR ((volatile unsigned int *)0x10009000) | ||
|
Steven6798 marked this conversation as resolved.
|
||
| #define UART0_FR ((volatile unsigned int *)0x10009018) | ||
|
|
||
| __attribute__((noinline, visibility("hidden"))) int hidden_callee() { | ||
| return 7; | ||
| } | ||
|
|
||
| int main(void) { | ||
| if (hidden_callee() != 7) { | ||
| return 1; | ||
| } | ||
| const char *s = "success\n"; | ||
|
|
||
| while (*s) { | ||
| while (*UART0_FR & (1 << 5)) | ||
| ; | ||
|
|
||
| *UART0_DR = *s++; | ||
| } | ||
| return 0; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| #define UART0_DR ((volatile unsigned int *)0x10009000) | ||
| #define UART0_FR ((volatile unsigned int *)0x10009018) | ||
|
|
||
| int main(void) { | ||
| const char *s = "success\n"; | ||
|
|
||
| while (*s) { | ||
| while (*UART0_FR & (1 << 5)) | ||
| ; | ||
| *UART0_DR = *s++; | ||
| } | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| ENTRY(_start) | ||
|
|
||
| SECTIONS | ||
| { | ||
| . = 0x60010000; | ||
|
|
||
| .text : { *(.text) } | ||
|
|
||
| . = ALIGN(8); | ||
| . = . + 0x1000; /* 4KB stack */ | ||
| stack_top = .; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| ENTRY(_start) | ||
|
|
||
| SECTIONS | ||
| { | ||
| . = 0x60010000; | ||
|
|
||
| .text : { *(.text) } | ||
|
|
||
| . = ALIGN(8); | ||
| . = . + 0x1000; /* 4KB stack */ | ||
| stack_top = .; | ||
|
|
||
| /* Place distant callee in a far section for trampoline testing */ | ||
| . = 0x62020000; | ||
| .text.distant : { *(.text.distant) } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| // Preemptible symbol in a shared library: R_ARM_CALL goes through PLT. | ||
|
|
||
| __attribute__((noinline)) int shared_callee() { return 3; } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| // Caller that uses a preemptible symbol resolved via PLT at runtime. | ||
| #include <stdio.h> | ||
| extern int shared_callee(); | ||
|
|
||
| int main() { | ||
| if (shared_callee() != 3) { | ||
| return 1; | ||
| } | ||
| puts("success"); | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,4 @@ | ||
| .global _start | ||
| _start: | ||
| ldr sp, =stack_top | ||
| bl main |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| // Thumb target: R_ARM_CALL from ARM caller triggers BL->BLX rewrite. | ||
| #define UART0_DR ((volatile unsigned int *)0x10009000) | ||
| #define UART0_FR ((volatile unsigned int *)0x10009018) | ||
|
|
||
| __attribute__((noinline, target("thumb"))) int thumb_callee() { return 5; } | ||
|
|
||
| __attribute__((target("arm"))) int main() { | ||
| if (thumb_callee() != 5) { | ||
| return 1; | ||
| } | ||
| const char *s = "success\n"; | ||
|
|
||
| while (*s) { | ||
| while (*UART0_FR & (1 << 5)) | ||
| ; | ||
|
|
||
| *UART0_DR = *s++; | ||
| } | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| // Caller that invokes a distant function | ||
| // This requires a trampoline to be created by the linker | ||
|
|
||
| #define UART0_DR ((volatile unsigned int *)0x10009000) | ||
| #define UART0_FR ((volatile unsigned int *)0x10009018) | ||
|
|
||
| extern int distant_callee(void); | ||
|
|
||
| int main(void) { | ||
| int result = distant_callee(); | ||
|
|
||
| // Print success if we got here (trampoline worked) | ||
| const char *s = "success\n"; | ||
|
|
||
| while (*s) { | ||
| while (*UART0_FR & (1 << 5)) | ||
| ; | ||
|
|
||
| *UART0_DR = *s++; | ||
| } | ||
| return result; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,19 @@ | ||
| // Weak undefined symbol: linker rewrites BL to NOP. | ||
| // The call must be silently skipped and execution must continue normally. | ||
| #define UART0_DR ((volatile unsigned int *)0x10009000) | ||
| #define UART0_FR ((volatile unsigned int *)0x10009018) | ||
|
|
||
| __attribute__((weak)) void weak_undef_func(); | ||
|
|
||
| int main(void) { | ||
| weak_undef_func(); // must not crash - linker converts BL to NOP | ||
| const char *s = "success\n"; | ||
|
|
||
| while (*s) { | ||
| while (*UART0_FR & (1 << 5)) | ||
| ; | ||
|
|
||
| *UART0_DR = *s++; | ||
| } | ||
| return 0; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| REQUIRES: run_test | ||
| #---R_ARM_CALL.test--------------------- Executable------------------# | ||
| #BEGIN_COMMENT | ||
| # Test the ARM R_ARM_CALL relocation. | ||
| # Covers: basic ARM-to-ARM call (static/PIE/dynamic), local and hidden | ||
| # (non-preemptible) symbols, weak undefined symbol NOP conversion, | ||
| # Thumb target BL->BLX rewrite, PLT call via shared library, and | ||
| # trampoline creation for out-of-range branches. | ||
| #END_COMMENT | ||
| #START_TEST | ||
| ## --- Basic ARM-to-ARM call: local and global callee --- | ||
| #RUN: %run_cc -nostdlib -c -o %t.startup.o %p/Inputs/startup.s | ||
| #RUN: %run_cc -nostdlib -c -o %t.main.o %p/Inputs/main.c | ||
| # Static | ||
| #RUN: %link -T %p/Inputs/script.ld -static -o %t.static.out %t.startup.o %t.main.o | ||
| #RUN: %not timeout .1 %run_sys -M vexpress-a9 -m 32M -kernel %t.static.out | %filecheck %s | ||
| # PIE | ||
| #RUN: %link -T %p/Inputs/script.ld -pie -o %t.pie.out %t.startup.o %t.main.o | ||
| #RUN: %not timeout .1 %run_sys -M vexpress-a9 -m 32M -kernel %t.pie.out | %filecheck %s | ||
| # Dynamic | ||
| #RUN: %link -T %p/Inputs/script.ld -Wl,-dy -o %t.dyn.out %t.startup.o %t.main.o | ||
| #RUN: %not timeout .1 %run_sys -M vexpress-a9 -m 32M -kernel %t.dyn.out | %filecheck %s | ||
|
|
||
| ## --- Weak undefined symbol: BL rewritten to NOP, execution continues --- | ||
| #RUN: %run_cc -nostdlib -c -o %t.weak.o %p/Inputs/weak.c | ||
| #RUN: %link -T %p/Inputs/script.ld -static -o %t.weak.out %t.startup.o %t.weak.o | ||
| #RUN: %not timeout .1 %run_sys -M vexpress-a9 -m 32M -kernel %t.weak.out | %filecheck %s | ||
|
|
||
| ## --- Hidden (non-preemptible) symbol: direct call, no PLT --- | ||
| #RUN: %run_cc -nostdlib -c -o %t.hidden.o %p/Inputs/hidden_callee.c | ||
| #RUN: %link -T %p/Inputs/script.ld -static -o %t.hidden.out %t.startup.o %t.hidden.o | ||
| #RUN: %not timeout .1 %run_sys -M vexpress-a9 -m 32M -kernel %t.hidden.out | %filecheck %s | ||
|
|
||
| ## --- Thumb target: BL rewritten to BLX at link time --- | ||
| #RUN: %run_cc -nostdlib -c -o %t.thumb.o %p/Inputs/thumb_callee.c | ||
| #RUN: %link -T %p/Inputs/script.ld -static -o %t.thumb.out %t.startup.o %t.thumb.o | ||
| #RUN: %not timeout .1 %run_sys -M vexpress-a9 -m 32M -kernel %t.thumb.out | %filecheck %s | ||
|
|
||
| ## --- Preemptible symbol: call goes through PLT at runtime --- | ||
| #RUN: %run_cc -fPIC -o %t.shared_callee.o -c %p/Inputs/shared_callee.c | ||
| #RUN: %run_cc -fPIC -o %t.shared_caller.o -c %p/Inputs/shared_caller.c | ||
| #RUN: %run_cc -shared -o %t.callee.so %t.shared_callee.o | ||
| #RUN: %run_cc -o %t.shared.out %t.shared_caller.o %t.callee.so -Wl,-dy | ||
| #RUN: %run %t.shared.out | ||
|
|
||
| ## --- Trampoline: out-of-range branch requires trampoline creation --- | ||
| #RUN: %run_cc -nostdlib -c -o %t.distant_callee.o %p/Inputs/distant_callee.c | ||
| #RUN: %run_cc -nostdlib -c -o %t.trampoline_caller.o %p/Inputs/trampoline_caller.c | ||
| #RUN: %link -T %p/Inputs/script_trampoline.ld -static -o %t.trampoline.out %t.startup.o %t.trampoline_caller.o %t.distant_callee.o | ||
| #RUN: %not timeout .1 %run_sys -M vexpress-a9 -m 64M -kernel %t.trampoline.out | %filecheck %s | ||
|
|
||
| #CHECK: success | ||
|
|
||
| #END_TEST |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -214,10 +214,12 @@ dirname = 'dirname' | |
| datalayout = '' | ||
| link_path = which(link) | ||
| run="" | ||
| run_sys="" | ||
| run_cc = '' | ||
| run_cxx = '' | ||
| sysroot = '' | ||
| emulator = '' | ||
| emulator_system = '' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you please explain why do we need system emulator? For which cases linux emulator is not enough?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shankar suggested using the system emulator to isolate the testing as much as possible. For example, when static linking a lot gets included in the binary, with this approach, only a minimal amount of code is included. What do you think?
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @quic-seaswara Hi Shankar, I am concerned that writing baremetal code and using system emulator requires more boilerplate and makes testing more difficult without providing any apparent benefit.
We are only removing the libc dependency -- all other dependency are still there, and we are unlikely to encounter bugs/corner-cases in libc when doing extended testing of different relocations computations because we won't be using any advanced feature of libc.
I agree with this, but I am not sure if less size is worth the benefit here. We can always delete the test artifacts after the testing. |
||
|
|
||
| # Control RISC-V Compression extension option, on by default | ||
| clangnorvcopts = '' | ||
|
|
@@ -540,16 +542,19 @@ if enable_run_tests: | |
| run_cxx = resolve_env_tool(f'ELD_{arch_env}_CXX_COMPILER') | ||
| sysroot = os.environ.get(f'ELD_{arch_env}_SYSROOT', '') | ||
| fallback_emulator = "qemu-" + arch_env.lower() | ||
| fallback_emulator_system = "qemu-system-" + arch_env.lower() | ||
| emulator = resolve_env_tool(f'ELD_{arch_env}_EMULATOR') or which(fallback_emulator) | ||
| emulator_system = resolve_env_tool(f'ELD_{arch_env}_EMULATOR_SYSTEM') or which(fallback_emulator_system) | ||
|
|
||
| if run_cc and sysroot and emulator: | ||
| if run_cc and sysroot and emulator and emulator_system: | ||
| config.available_features.add('run_test') | ||
| run_cc += f" --ld-path={link_path} --sysroot={sysroot}" | ||
| # This is required to ensure that libc.so from sysroot is loaded by | ||
| # the runtime loader. dynamic linker searches both the standard paths | ||
| # and the paths within the syroot to find libc. If the host sytem | ||
| # contains a valid libc.so, then the dynamic linker can select that. | ||
| run = f'{emulator} -E LD_LIBRARY_PATH={sysroot}/lib -L {sysroot}' | ||
| run_sys = f'{emulator_system} -nographic -audio none -display none' | ||
| if run_cxx: | ||
| run_cxx += f" --ld-path={link_path} --sysroot={sysroot}" | ||
| config.available_features.add('run_cxx_test') | ||
|
|
@@ -612,7 +617,9 @@ else: | |
| lit_config.note('using run_cc: {}'.format(run_cc)) | ||
| lit_config.note('using run_cxx: {}'.format(run_cxx)) | ||
| lit_config.note('using sysroot: {}'.format(sysroot)) | ||
| lit_config.note('using emulator_system: {}'.format(emulator_system)) | ||
| lit_config.note('using emulator: {}'.format(emulator)) | ||
| lit_config.note('using run_sys: {}'.format(run_sys)) | ||
| lit_config.note('using run: {}'.format(run)) | ||
| # Add substitutions. | ||
|
|
||
|
|
@@ -710,6 +717,7 @@ config.substitutions.append( ("%emulation","".join(config.emulation)) ) | |
| config.substitutions.append( ("%run_cc", run_cc) ) | ||
| config.substitutions.append( ("%run_cxx", run_cxx) ) | ||
| config.substitutions.append( ("%sysroot", sysroot) ) | ||
| config.substitutions.append( ("%run_sys","".join(run_sys)) ) | ||
| config.substitutions.append( ("%run","".join(run)) ) | ||
| if muslclang is not None: | ||
| config.substitutions.append( ("%musl-clang","".join(muslclang)) ) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do we need this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to use the qemu system emulator. the machine that runs the CI workflow is missing a library. Shakti suggested adding it to the workflow but clearly that did not work. If you have any suggestions about how to address this let me know.