From 406ee82cf33a6e3df0067b219b0414c59d7018b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciro=20Santilli=20=E5=85=AD=E5=9B=9B=E4=BA=8B=E4=BB=B6=20?= =?UTF-8?q?=E6=B3=95=E8=BD=AE=E5=8A=9F?= Date: Wed, 8 May 2019 00:00:01 +0000 Subject: [PATCH] baremetal: get exit status working with on_exit :-) --- README.adoc | 34 +++++++++++++++---- baremetal/arch/aarch64/return1.S | 5 +++ baremetal/arch/arm/return1.S | 5 +++ baremetal/exit.c | 6 ---- baremetal/exit0.c | 7 ++++ baremetal/exit1.c | 3 +- baremetal/lib/aarch64.S | 6 ++++ baremetal/lib/arm.S | 11 ++++++ .../{assert_fail.c => lkmc_assert_fail.c} | 0 baremetal/return1.c | 1 + baremetal/return2.c | 1 + lkmc.c | 9 +++-- lkmc.h | 1 + lkmc/assert_fail.c | 16 --------- path_properties.py | 4 ++- test-baremetal | 4 +-- 16 files changed, 79 insertions(+), 34 deletions(-) create mode 100644 baremetal/arch/aarch64/return1.S create mode 100644 baremetal/arch/arm/return1.S delete mode 100644 baremetal/exit.c create mode 100644 baremetal/exit0.c rename baremetal/{assert_fail.c => lkmc_assert_fail.c} (100%) diff --git a/README.adoc b/README.adoc index cad63da..28ad687 100644 --- a/README.adoc +++ b/README.adoc @@ -11916,7 +11916,7 @@ svc 0x00123456 and we can see from the docs that `0x18` stands for the `SYS_EXIT` command. -This is also how we implement the `exit(0)` system call in C for QEMU for link:baremetal/exit.c[] through the Newlib via the function `_exit` at link:baremetal/lib/kwargs['c'][]. +This is also how we implement the `exit(0)` system call in C for QEMU for link:baremetal/exit0.c[] through the Newlib via the function `_exit` at link:baremetal/lib/kwargs['c'][]. Other magic operations we can do with semihosting besides exiting the on the host include: @@ -11990,7 +11990,7 @@ sudo apt-get install gcc-arm-none-eabi qemu-system-arm However, there are as usual limitations to using prebuilts: -* certain examples fail to build with the Ubuntu packaged toolchain. E.g.: link:baremetal/exit.c[] fails with: +* certain examples fail to build with the Ubuntu packaged toolchain. E.g.: link:baremetal/exit0.c[] fails with: + .... /usr/lib/gcc/arm-none-eabi/6.3.1/../../../arm-none-eabi/lib/libg.a(lib_a-fini.o): In function `__libc_fini_array': @@ -14001,20 +14001,42 @@ For other arch / emulator combinations, we know how to do it: * gem5: <> works on all archs * user mode: QEMU forwards exit status, gem5 we do some log parsing: <> -For this reason, we just parse the terminal output for a magic failure string to check if tests failed. +Since we can't do it for QEMU arm, the only reliable solution is to just parse the guest serial output for a magic failure string to check if tests failed. -In order to cover all archs, our run scripts parse the serial output looking for a line line containing only exactly the magic regular expression: +Our run scripts parse the serial output looking for a line line containing only exactly the magic regular expression: .... lkmc_exit_status_(\d+) .... -and then exit with the given regular expression. +and then exit with the given regular expression, e.g.: + +.... +./run --arch aarch64 baremetal +.... This magic output string is notably generated by: -* the `exit()` baremetal function when `status != 1`. This is in turn called by failed assertions for example from `lkmc_assert_fail`, which is used by <> * link:rootfs_overlay/lkmc/test_fail.sh[], which is used by <> +* the `exit()` baremetal function when `status != 1`. ++ +Unfortunately the only way we found to set this up was with `on_exit`: link:https://github.com/cirosantilli/linux-kernel-module-cheat/issues/59[]. ++ +Trying to patch `_exit` directly fails since at that point some de-initialization has already happened which prevents the print. ++ +So setup this `on_exit` automatically from all our <>, so it just works automatically for the examples that use the bootloaders: https://stackoverflow.com/questions/44097610/pass-parameter-to-atexit/49659697#49659697 ++ +The following examples end up testing that our setup is working: ++ +* link:baremetal/lkmc_assert_fail.c[] +* link:baremetal/return1.c[] +* link:baremetal/return2.c[] +* link:baremetal/exit0.c[] +* link:baremetal/exit1.c[] +* link:baremetal/arch/arm/return1.S[] +* link:baremetal/arch/aarch64/return1.S[] + +Beware that on Linux kernel simulations, you cannot even echo that string from userland, since userland stdout shows up on the serial. ==== Non-automated tests diff --git a/baremetal/arch/aarch64/return1.S b/baremetal/arch/aarch64/return1.S new file mode 100644 index 0000000..3e9b898 --- /dev/null +++ b/baremetal/arch/aarch64/return1.S @@ -0,0 +1,5 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ +.global main +main: + mov x0, 1 + ret diff --git a/baremetal/arch/arm/return1.S b/baremetal/arch/arm/return1.S new file mode 100644 index 0000000..70123b0 --- /dev/null +++ b/baremetal/arch/arm/return1.S @@ -0,0 +1,5 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ +.global main +main: + mov r0, #1 + bx lr diff --git a/baremetal/exit.c b/baremetal/exit.c deleted file mode 100644 index 98bcda2..0000000 --- a/baremetal/exit.c +++ /dev/null @@ -1,6 +0,0 @@ -#include -#include - -int main(void) { - exit(0); -} diff --git a/baremetal/exit0.c b/baremetal/exit0.c new file mode 100644 index 0000000..736e9d3 --- /dev/null +++ b/baremetal/exit0.c @@ -0,0 +1,7 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ + +#include + +int main(void) { + exit(0); +} diff --git a/baremetal/exit1.c b/baremetal/exit1.c index 342579e..dc8ceb6 100644 --- a/baremetal/exit1.c +++ b/baremetal/exit1.c @@ -1,4 +1,5 @@ -#include +/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ + #include int main(void) { diff --git a/baremetal/lib/aarch64.S b/baremetal/lib/aarch64.S index 3c887ca..235de91 100644 --- a/baremetal/lib/aarch64.S +++ b/baremetal/lib/aarch64.S @@ -14,6 +14,12 @@ mystart: /* Prepare the stack for main, mandatory for C code. */ ldr x0, =stack_top mov sp, x0 + + /* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ + adr x0, lkmc_baremetal_on_exit_callback + bl on_exit + + /* Run main. */ bl main /* If main returns, exit. */ diff --git a/baremetal/lib/arm.S b/baremetal/lib/arm.S index 79fc1d0..d0fc20b 100644 --- a/baremetal/lib/arm.S +++ b/baremetal/lib/arm.S @@ -1,5 +1,16 @@ +#include + .global mystart mystart: + /* Prepare the stack for main, mandatory for C code. */ ldr sp, =stack_top + + /* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ + ldr r0, =lkmc_baremetal_on_exit_callback + bl on_exit + + /* Run main. */ bl main + + /* If main returns, exit. */ bl exit diff --git a/baremetal/assert_fail.c b/baremetal/lkmc_assert_fail.c similarity index 100% rename from baremetal/assert_fail.c rename to baremetal/lkmc_assert_fail.c diff --git a/baremetal/return1.c b/baremetal/return1.c index 98c444a..ef4cafb 100644 --- a/baremetal/return1.c +++ b/baremetal/return1.c @@ -1 +1,2 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ int main(void) { return 1; } diff --git a/baremetal/return2.c b/baremetal/return2.c index 51f253b..f160bf0 100644 --- a/baremetal/return2.c +++ b/baremetal/return2.c @@ -1 +1,2 @@ +/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */ int main(void) { return 2; } diff --git a/lkmc.c b/lkmc.c index e25c72d..b8669b7 100644 --- a/lkmc.c +++ b/lkmc.c @@ -12,11 +12,16 @@ void lkmc_assert(bool condition) { } void lkmc_assert_fail(void) { - printf("%s\n", __func__); - puts("lkmc_exit_status_1"); exit(1); } +void lkmc_baremetal_on_exit_callback(int status, void *arg) { + (void)arg; + if (status != 0) { + printf("lkmc_exit_status_%d\n", status); + } +} + #if defined(__aarch64__) #define LKMC_SYSREG_READ_WRITE(type, name) \ type LKMC_CONCAT(LKMC_CONCAT(LKMC_SYSREG_SYMBOL_PREFIX, name), _read(void)) { \ diff --git a/lkmc.h b/lkmc.h index 6ba10b9..7bcc931 100644 --- a/lkmc.h +++ b/lkmc.h @@ -11,6 +11,7 @@ void lkmc_assert(bool); void lkmc_assert_fail(); +void lkmc_baremetal_on_exit_callback(int status, void *arg); #endif /* https://stackoverflow.com/questions/1489932/how-to-concatenate-twice-with-the-c-preprocessor-and-expand-a-macro-as-in-arg */ diff --git a/lkmc/assert_fail.c b/lkmc/assert_fail.c index e2a3593..68fbefa 100644 --- a/lkmc/assert_fail.c +++ b/lkmc/assert_fail.c @@ -1,21 +1,5 @@ #include -#include - -void atexit_cb(void) { - puts("atexit"); -} - -void onexit_cb(int status, void *arg) { - printf("status %d\n", status); -} - -void __attribute__ ((constructor)) premain() { - printf("premain2()\n"); -} - int main(void) { - atexit(atexit_cb); - on_exit(onexit_cb, NULL); lkmc_assert_fail(); } diff --git a/path_properties.py b/path_properties.py index 80078e5..449155c 100644 --- a/path_properties.py +++ b/path_properties.py @@ -220,6 +220,7 @@ path_properties_tuples = ( 'semihost_exit.S': {'requires_semihosting': True}, } ), + 'return1.S': {'exit_status': 1}, 'semihost_exit.S': {'requires_semihosting': True}, }, @@ -235,12 +236,13 @@ path_properties_tuples = ( 'semihost_exit.S': {'requires_semihosting': True}, } ), + 'return1.S': {'exit_status': 1}, 'semihost_exit.S': {'requires_semihosting': True}, }, ) } ), - 'assert_fail.c': {'exit_status': 1}, + 'lkmc_assert_fail.c': {'exit_status': 1}, 'exit1.c': {'exit_status': 1}, 'infinite_loop.c': {'more_than_1s': True}, 'lib': ( diff --git a/test-baremetal b/test-baremetal index bed5c65..bb4a1d6 100755 --- a/test-baremetal +++ b/test-baremetal @@ -55,9 +55,9 @@ If given, run only the given tests. Otherwise, run all tests. 'run_obj': self.import_path_main('run'), 'test_id': path_relative_root, } - if path_relative_root != os.path.join('baremetal', 'assert_fail.c'): + # if path_relative_root != os.path.join('baremetal', 'lkmc_assert_fail.c'): # Workaround fro: https://github.com/cirosantilli/linux-kernel-module-cheat/issues/59 - test_args['expected_exit_status'] = 0 + # test_args['expected_exit_status'] = 0 error = thread_pool.submit(test_args) if error is not None: if self.env['quit_on_fail']: