diff --git a/src/crt/crt0.S b/src/crt/crt0.S index 3a7112997..2f4804905 100644 --- a/src/crt/crt0.S +++ b/src/crt/crt0.S @@ -320,36 +320,51 @@ ___libload_libs_ret: call _main #endif .equ __start._main, $ - 3 + +;------------------------------------------------------------------------------- +; call atexit/on_exit and fini functions +;------------------------------------------------------------------------------- + .global ___exithl ___exithl: -#if HAS_ATEXIT || HAS_FINI_ARRAY || HAS_ABORT - push hl - push de + +#if HAS_ATEXIT || HAS_FINI_ARRAY + push hl ; preserve exit status #endif +#if HAS_EXIT + ; jr .L.exit_function_start + db 0x3E ; ld a, * .global _exit .type _exit, @function _exit: + ; exit status is currently at (sp + 3), so we need to fix that + pop bc ; destroy return address +#endif #if HAS_ATEXIT + ; input: (sp + 0) = exit status jr .L.exit_function_start .L.exit_function_loop: ld hl, (ix + 1 + 0 * 3) ld (__atexit_functions), hl - pop hl - ld de, (ix + 1 + 2 * 3) - push hl - push de - push hl - ld hl, (ix + 1 + 1 * 3) - push hl + pop hl ; exit status + push hl ; exit status + ld de, (ix + 1 + 2 * 3) ; arg + push de ; arg + push hl ; exit status + ld hl, (ix + 1 + 1 * 3) ; func + push hl ; func pea ix + 1 call _free - pop bc - pop hl + pop bc ; reset SP + pop hl ; func + ; atexit : void (*func)(void) + ; on_exit : void (*func)(int status, void *arg) call __indcallhl - pop bc - pop bc + pop bc ; reset SP + pop bc ; reset SP .L.exit_function_start: ld ix, (__atexit_functions) + ; NULL indicates no more atexit functions ld bc, -1 add ix, bc jr c, .L.exit_function_loop @@ -375,10 +390,27 @@ _exit: .extern __fini_array_start .extern __fini_array_end #endif -#if HAS_ATEXIT || HAS_FINI_ARRAY || HAS_ABORT - pop de - pop hl + +#if HAS_C99__EXIT + ; jr .L.skip.__Exit + db 0x3E ; ld a, * + .global __Exit + .type __Exit, @function +__Exit: + ; exit status is currently at (sp + 3), so we need to fix that + pop bc ; destroy return address +.L.skip.__Exit: +#endif + +#if HAS_ATEXIT || HAS_FINI_ARRAY || HAS_EXIT || HAS_C99__EXIT + pop hl ; restore exit status #endif + +;------------------------------------------------------------------------------- +; We have now called all atexit/on_exit and fini functions +; HL = exit status +;------------------------------------------------------------------------------- + #if HAS_ABORT jr .L.skip._abort .global _abort diff --git a/src/libc/atexit.src b/src/libc/atexit.src index 99be5f152..af4f0f12e 100644 --- a/src/libc/atexit.src +++ b/src/libc/atexit.src @@ -8,7 +8,7 @@ .type _on_exit, @function _atexit: _on_exit: - ld hl, 3*3 + ld hl, 3 * 3 push hl call _malloc pop bc @@ -16,23 +16,28 @@ _on_exit: scf sbc hl, hl add hl, de - ret nc + ret nc ; malloc returned NULL, return non-zero value ld hl, (__atexit_functions) ex de, hl ld (__atexit_functions), hl ld (hl), de - pop de -.rept 2 + pop iy ; return address inc hl inc hl inc hl - pop bc + pop bc ; func pointer + ld (hl), bc + inc hl + inc hl + inc hl + pop bc ; arg pointer (on_exit) ld (hl), bc -.endr push bc push bc - ex de, hl - jp (hl) + ; return zero on success + or a, a + sbc hl, hl + jp (iy) .section .bss .global __atexit_functions diff --git a/test/standalone/_Exit/autotest.json b/test/standalone/_Exit/autotest.json new file mode 100644 index 000000000..42ba741c1 --- /dev/null +++ b/test/standalone/_Exit/autotest.json @@ -0,0 +1,39 @@ +{ + "transfer_files": [ + "bin/DEMO.8xp" + ], + "target": { + "name": "DEMO", + "isASM": true + }, + "sequence": [ + "action|launch", + "delay|1000", + "hashWait|1", + "key|enter", + "delay|300", + "hashWait|2" + ], + "hashes": { + "1": { + "description": "test for errors from on_exit/atexit", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "A1280E53" + ] + }, + "2": { + "description": "test _Exit()", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "FFAF89BA", + "101734A5", + "9DA19F44", + "A32840C8", + "349F4775" + ] + } + } +} diff --git a/test/standalone/_Exit/makefile b/test/standalone/_Exit/makefile new file mode 100644 index 000000000..2b7b27349 --- /dev/null +++ b/test/standalone/_Exit/makefile @@ -0,0 +1,19 @@ +# ---------------------------- +# Makefile Options +# ---------------------------- + +NAME = DEMO +ICON = icon.png +DESCRIPTION = "CE C Toolchain Demo" +COMPRESSED = NO +ARCHIVED = NO + +CFLAGS = -ffreestanding -Wall -Wextra -Wshadow -Oz +CXXFLAGS = -ffreestanding -Wall -Wextra -Wshadow -Oz + +PREFER_OS_LIBC = NO +PREFER_OS_CRT = NO + +# ---------------------------- + +include $(shell cedev-config --makefile) diff --git a/test/standalone/_Exit/src/main.c b/test/standalone/_Exit/src/main.c new file mode 100644 index 000000000..ac6be1e62 --- /dev/null +++ b/test/standalone/_Exit/src/main.c @@ -0,0 +1,41 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +void atexit_func(void) { + printf("atexit_func called\n"); + while (!os_GetCSC()); +} + +void on_exit_func(int status, void *arg) { + printf("on_exit_func called\n"); + printf("status: %d\narg: %p\n", status, arg); + while (!os_GetCSC()); +} + +int main(void) { + errno = 0; + os_ClrHome(); + if (on_exit(on_exit_func, NULL) != 0) { + perror("Failed on_exit(on_exit_func, NULL)"); + while (!os_GetCSC()); + return 0; + } + if (atexit(atexit_func) != 0) { + perror("Failed on_exit(on_exit_func, NULL)"); + while (!os_GetCSC()); + return 0; + } + + printf("errno: %d\n", errno); + + while (!os_GetCSC()); + + _Exit(EXIT_SUCCESS); +} diff --git a/test/standalone/exit/autotest.json b/test/standalone/exit/autotest.json new file mode 100644 index 000000000..f38453114 --- /dev/null +++ b/test/standalone/exit/autotest.json @@ -0,0 +1,50 @@ +{ + "transfer_files": [ + "bin/DEMO.8xp" + ], + "target": { + "name": "DEMO", + "isASM": true + }, + "sequence": [ + "action|launch", + "delay|1000", + "hashWait|1", + "key|enter", + "delay|300", + "hashWait|2", + "key|enter", + "delay|300", + "hashWait|3" + ], + "hashes": { + "1": { + "description": "test for errors from on_exit", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "A1280E53" + ] + }, + "2": { + "description": "is the correct return status present", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "15EB5BAA" + ] + }, + "3": { + "description": "Exit", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "FFAF89BA", + "101734A5", + "9DA19F44", + "A32840C8", + "349F4775" + ] + } + } +} diff --git a/test/standalone/exit/makefile b/test/standalone/exit/makefile new file mode 100644 index 000000000..2b7b27349 --- /dev/null +++ b/test/standalone/exit/makefile @@ -0,0 +1,19 @@ +# ---------------------------- +# Makefile Options +# ---------------------------- + +NAME = DEMO +ICON = icon.png +DESCRIPTION = "CE C Toolchain Demo" +COMPRESSED = NO +ARCHIVED = NO + +CFLAGS = -ffreestanding -Wall -Wextra -Wshadow -Oz +CXXFLAGS = -ffreestanding -Wall -Wextra -Wshadow -Oz + +PREFER_OS_LIBC = NO +PREFER_OS_CRT = NO + +# ---------------------------- + +include $(shell cedev-config --makefile) diff --git a/test/standalone/exit/src/main.c b/test/standalone/exit/src/main.c new file mode 100644 index 000000000..d0e7fba83 --- /dev/null +++ b/test/standalone/exit/src/main.c @@ -0,0 +1,34 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +void cleanup(int status, void *arg) { + if (arg != NULL) { + printf("expected NULL: %p\n", arg); + } + printf("Exit status: %d\n", status); + + while (!os_GetCSC()); +} + +int main(void) { + errno = 0; + os_ClrHome(); + if (on_exit(cleanup, NULL) != 0) { + perror("Failed on_exit(cleanup, NULL)"); + while (!os_GetCSC()); + return 0; + } + + printf("errno: %d\n", errno); + + while (!os_GetCSC()); + + exit(42); +} diff --git a/test/standalone/on_exit/autotest.json b/test/standalone/on_exit/autotest.json new file mode 100644 index 000000000..234c87b80 --- /dev/null +++ b/test/standalone/on_exit/autotest.json @@ -0,0 +1,61 @@ +{ + "transfer_files": [ + "bin/DEMO.8xp" + ], + "target": { + "name": "DEMO", + "isASM": true + }, + "sequence": [ + "action|launch", + "delay|1000", + "hashWait|1", + "key|enter", + "delay|300", + "hashWait|2", + "key|enter", + "delay|300", + "hashWait|3", + "key|enter", + "delay|300", + "hashWait|4" + ], + "hashes": { + "1": { + "description": "test for errors from atexit/on_exit", + "start": "vram_start", + "size": "vram_8_size", + "expected_CRCs": [ + "0A3F477E" + ] + }, + "2": { + "description": "is the correct return status present", + "start": "vram_start", + "size": "vram_8_size", + "expected_CRCs": [ + "FEF6F9F4" + ] + }, + "3": { + "description": "check that gfx_End() was called", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "4BD20C6F" + ] + }, + "4": { + "description": "Exit", + "start": "vram_start", + "size": "vram_16_size", + "expected_CRCs": [ + "FFAF89BA", + "101734A5", + "9DA19F44", + "A32840C8", + "349F4775" + ] + } + } +} diff --git a/test/standalone/on_exit/makefile b/test/standalone/on_exit/makefile new file mode 100644 index 000000000..2b7b27349 --- /dev/null +++ b/test/standalone/on_exit/makefile @@ -0,0 +1,19 @@ +# ---------------------------- +# Makefile Options +# ---------------------------- + +NAME = DEMO +ICON = icon.png +DESCRIPTION = "CE C Toolchain Demo" +COMPRESSED = NO +ARCHIVED = NO + +CFLAGS = -ffreestanding -Wall -Wextra -Wshadow -Oz +CXXFLAGS = -ffreestanding -Wall -Wextra -Wshadow -Oz + +PREFER_OS_LIBC = NO +PREFER_OS_CRT = NO + +# ---------------------------- + +include $(shell cedev-config --makefile) diff --git a/test/standalone/on_exit/src/main.c b/test/standalone/on_exit/src/main.c new file mode 100644 index 000000000..141d18c31 --- /dev/null +++ b/test/standalone/on_exit/src/main.c @@ -0,0 +1,59 @@ +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +void cleanup(int status, void *arg) { + const char *message = (char*)arg; + + gfx_PrintStringXY(message, 10, 30); + + gfx_PrintStringXY("Exit status: ", 10, 50); + gfx_PrintInt(status, 1); + + while (!os_GetCSC()); +} + +void final_func(void) { + os_ClrHome(); + puts("Finished tests"); + while (!os_GetCSC()); +} + +int main(void) { + errno = 0; + os_ClrHome(); + const char *msg = "cleanup func"; + if (atexit(final_func) != 0) { + perror("Failed atexit(final_func)"); + while (!os_GetCSC()); + return 0; + } + if (atexit(gfx_End) != 0) { + perror("Failed atexit(gfx_End)"); + while (!os_GetCSC()); + return 0; + } + if (on_exit(cleanup, (void*)msg) != 0) { + perror("Failed on_exit(cleanup, msg)"); + while (!os_GetCSC()); + return 0; + } + + gfx_Begin(); + + gfx_PrintStringXY("errno: ", 10, 10); + gfx_PrintInt(errno, 1); + + while (!os_GetCSC()); + + return 7184; +} diff --git a/tools/cedev-obj/src/main.c b/tools/cedev-obj/src/main.c index 5f72c6b1e..8b83bc120 100644 --- a/tools/cedev-obj/src/main.c +++ b/tools/cedev-obj/src/main.c @@ -122,7 +122,15 @@ static void write_header_defines(FILE *out, const char *elf_file, struct elf_fil fprintf(out, "#define HAS_INIT_ARRAY %d\n", elf_has_section(elf, ".init_array") ? 1 : 0); fprintf(out, "#define HAS_FINI_ARRAY %d\n", elf_has_section(elf, ".fini_array") ? 1 : 0); fprintf(out, "#define HAS_CLOCK %d\n", elf_has_symbol(elf, "_clock") ? 1 : 0); - fprintf(out, "#define HAS_ABORT %d\n", elf_has_symbol(elf, "_abort") ? 1 : 0); + #if 0 + fprintf(out, "#define HAS_ABORT %d\n", elf_has_symbol(elf, "_abort") ? 1 : 0); + fprintf(out, "#define HAS_EXIT %d\n", elf_has_symbol(elf, "_exit") ? 1 : 0); + #else + // elf_has_symbol has some false negatives, so always emit these functions for now. + fprintf(out, "#define HAS_ABORT %d\n", /* _abort */ 1); + fprintf(out, "#define HAS_EXIT %d\n", /* _exit */ 1); + #endif + fprintf(out, "#define HAS_C99__EXIT %d\n", elf_has_symbol(elf, "__Exit") ? 1 : 0); fprintf(out, "#define HAS_RUN_PRGM %d\n", elf_has_symbol(elf, "_os_RunPrgm") ? 1 : 0); fprintf(out, "#define HAS_MAIN_ARGC_ARGV %d\n", elf_has_defined_symbol(elf, "___main_argc_argv") ? 1 : 0); fprintf(out, "#define HAS_ATEXIT %d\n", elf_has_symbol(elf, "__atexit_functions") ? 1 : 0);