baremetal: get exit status working with on_exit :-)

This commit is contained in:
Ciro Santilli 六四事件 法轮功
2019-05-08 00:00:01 +00:00
parent 18ca0b3c9c
commit 406ee82cf3
16 changed files with 79 additions and 34 deletions

View File

@@ -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: <<m5-fail>> works on all archs
* user mode: QEMU forwards exit status, gem5 we do some log parsing: <<gem5-syscall-emulation-exit-status>>
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 <<baremetal-tests>>
* link:rootfs_overlay/lkmc/test_fail.sh[], which is used by <<test-userland-in-full-system>>
* 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 <<baremetal-bootloaders>>, 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

View File

@@ -0,0 +1,5 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
.global main
main:
mov x0, 1
ret

View File

@@ -0,0 +1,5 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
.global main
main:
mov r0, #1
bx lr

View File

@@ -1,6 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
int main(void) {
exit(0);
}

7
baremetal/exit0.c Normal file
View File

@@ -0,0 +1,7 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
#include <stdlib.h>
int main(void) {
exit(0);
}

View File

@@ -1,4 +1,5 @@
#include <stdio.h>
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
#include <stdlib.h>
int main(void) {

View File

@@ -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. */

View File

@@ -1,5 +1,16 @@
#include <lkmc.h>
.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

View File

@@ -1 +1,2 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
int main(void) { return 1; }

View File

@@ -1 +1,2 @@
/* https://github.com/cirosantilli/linux-kernel-module-cheat#magic-failure-string */
int main(void) { return 2; }

9
lkmc.c
View File

@@ -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)) { \

1
lkmc.h
View File

@@ -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 */

View File

@@ -1,21 +1,5 @@
#include <lkmc.h>
#include <stdio.h>
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();
}

View File

@@ -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': (

View File

@@ -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']: