From 9670d1029421aab511defcd161755dfc1bca0e82 Mon Sep 17 00:00:00 2001 From: grischka Date: Mon, 14 Jul 2025 21:55:37 +0200 Subject: [PATCH] fix github CI & stuff workflows/build.yml: - win32/64: install mingw32/64 gcc on msys (because the default gcc installed elsewhere seems to use ucrt, producing incompatible fp printf formats.) tccgen.c: - cleanup funcs: save any lvalues from return expressions. Also use get_temp_local_var() which however was causing a problem on i386 because its gfunc_call() removes the arguments from vstack and by that defeats the 'in-use' tracking of get_temp_local_var(). Fixed by: i386/arm/arm64/x86_64-gen.c: - in gfunc_call(): save_regs before anything else, fixes problems seen in arm64/i386 tccpp.c: - allow arm asm specific use of '#' in macros libtcc.c: - organize -M options, and: tccpe.c: - move the subsystem option parsing from libtcc.c tccelf.c: - improved error handling in tcc_load_ldscript() lib/atomic.S: - TCC_TARGET_... not defined when building the lib - endbrNN security feature not supported by TCC tests/tests2/136_atomic_gcc_style.c: - never use standard assert() in tests --- .github/workflows/build.yml | 28 +++++++++---- Makefile | 7 +++- arm-gen.c | 6 +-- arm64-gen.c | 6 +-- i386-gen.c | 4 +- lib/atomic.S | 20 +++++----- libtcc.c | 50 +++++++----------------- tcc.h | 1 + tccelf.c | 29 ++++++++------ tccgen.c | 12 +++--- tccpe.c | 30 +++++++++++++- tccpp.c | 5 +++ tests/tests2/136_atomic_gcc_style.c | 5 ++- tests/tests2/136_atomic_gcc_style.expect | 10 +++++ x86_64-gen.c | 15 +++---- 15 files changed, 133 insertions(+), 95 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 42e0ad99..1c83e5c9 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -29,29 +29,41 @@ jobs: - name: make & test tcc (aarch64-osx) run: ./configure && make && make test -k - test-x86-win32: - runs-on: windows-2022 - timeout-minutes: 6 + test-x86_64-win32: + runs-on: windows-2025 + timeout-minutes: 4 steps: - uses: actions/checkout@v4 - name: make & test tcc (x86_64-win32) shell: cmd run: | + echo ::group:: setup msys mingw64-gcc set MSYS2_PATH_TYPE=inherit set MSYSTEM=MINGW64 set CHERE_INVOKING=yes + C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm mingw-w64-x86_64-gcc" + echo ::endgroup:: C:\msys64\usr\bin\bash -l -c "./configure && make && make test -k" + + test-i386-win32: + runs-on: windows-2025 + timeout-minutes: 4 + steps: + - uses: actions/checkout@v4 - name: make & test tcc (i386-win32) shell: cmd run: | + echo ::group:: setup msys mingw32-gcc set MSYS2_PATH_TYPE=inherit set MSYSTEM=MINGW32 set CHERE_INVOKING=yes - C:\msys64\usr\bin\bash -l -c "./configure && make clean all && make test -k" + C:\msys64\usr\bin\bash -l -c "pacman -S --noconfirm mingw-w64-i686-gcc" + echo ::endgroup:: + C:\msys64\usr\bin\bash -l -c "./configure && make all && make test -k" test-armv7-linux: runs-on: ubuntu-22.04 - timeout-minutes: 6 + timeout-minutes: 8 steps: - uses: actions/checkout@v4 - uses: uraimo/run-on-arch-action@v3 @@ -69,14 +81,14 @@ jobs: test-aarch64-linux: runs-on: ubuntu-22.04 - timeout-minutes: 6 + timeout-minutes: 8 steps: - uses: actions/checkout@v4 - uses: uraimo/run-on-arch-action@v3 name: make & test tcc (aarch64-linux) with: arch: aarch64 - distro: ubuntu22.04 + distro: ubuntu24.04 githubToken: ${{ github.token }} install: | apt-get update -q -y @@ -87,7 +99,7 @@ jobs: test-riscv64-linux: runs-on: ubuntu-22.04 - timeout-minutes: 6 + timeout-minutes: 8 steps: - uses: actions/checkout@v4 - uses: uraimo/run-on-arch-action@v3 diff --git a/Makefile b/Makefile index 2a7b774b..70cec7ee 100644 --- a/Makefile +++ b/Makefile @@ -252,8 +252,11 @@ LDFLAGS += -g endif # convert "include/tccdefs.h" to "tccdefs_.h" -%_.h : include/%.h conftest.c - $S$(CC) -DC2STR $(filter %.c,$^) -o c2str.exe && ./c2str.exe $< $@ +%_.h : include/%.h c2str.exe + $S./c2str.exe $< $@ + +c2str.exe : conftest.c + $S$(CC) -DC2STR $< -o $@ # target specific object rule $(X)%.o : %.c $(LIBTCC_INC) diff --git a/arm-gen.c b/arm-gen.c index ec4a2e25..a74fa90e 100644 --- a/arm-gen.c +++ b/arm-gen.c @@ -1311,10 +1311,6 @@ again: if (++pass < 2) goto again; - /* Manually free remaining registers since next parameters are loaded - * manually, without the help of gv(int). */ - save_regs(nb_args); - if(todo) { o(0xE8BD0000|todo); /* pop {todo} */ for(pplan = plan->clsplans[CORE_STRUCT_CLASS]; pplan; pplan = pplan->prev) { @@ -1354,6 +1350,8 @@ void gfunc_call(int nb_args) gbound_args(nb_args); #endif + save_regs(nb_args + 1); + #ifdef TCC_ARM_EABI if (float_abi == ARM_HARD_FLOAT) { variadic = (vtop[-nb_args].type.ref->f.func_type == FUNC_ELLIPSIS); diff --git a/arm64-gen.c b/arm64-gen.c index ccb5a5c1..ead88824 100644 --- a/arm64-gen.c +++ b/arm64-gen.c @@ -997,6 +997,8 @@ ST_FUNC void gfunc_call(int nb_args) int variadic = (vtop[-nb_args].type.ref->f.func_type == FUNC_ELLIPSIS); int var_nb_arg = n_func_args(&vtop[-nb_args].type); + save_regs(nb_args + 1); + #ifdef CONFIG_TCC_BCHECK if (tcc_state->do_bounds_check) gbound_args(nb_args); @@ -1101,9 +1103,6 @@ ST_FUNC void gfunc_call(int nb_args) // value in floating-point registers if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { uint32_t j, sz, n = arm64_hfa(&vtop->type, &sz); - // save regs because struct may overwrite previous func call result - save_regs(0); - vtop->type.t = VT_PTR; gaddrof(); gv(RC_R30); @@ -1129,7 +1128,6 @@ ST_FUNC void gfunc_call(int nb_args) vswap(); } - save_regs(0); arm64_gen_bl_or_b(0); --vtop; if (stack & 0xfff) diff --git a/i386-gen.c b/i386-gen.c index 5814a398..342d045b 100644 --- a/i386-gen.c +++ b/i386-gen.c @@ -406,6 +406,8 @@ ST_FUNC void gfunc_call(int nb_args) gbound_args(nb_args); #endif + save_regs(nb_args + 1); + args_size = 0; for(i = 0;i < nb_args; i++) { if ((vtop->type.t & VT_BTYPE) == VT_STRUCT) { @@ -463,7 +465,7 @@ ST_FUNC void gfunc_call(int nb_args) } vtop--; } - save_regs(0); /* save used temporary registers */ + func_sym = vtop->type.ref; func_call = func_sym->f.func_call; /* fast call case */ diff --git a/lib/atomic.S b/lib/atomic.S index 1f9112be..8ac2b179 100644 --- a/lib/atomic.S +++ b/lib/atomic.S @@ -23,8 +23,10 @@ /* ---------------------------------------------- */ #if defined __i386__ + +#define endbr32 + .text - .align 2 .global _(__atomic_load_1) .type _(__atomic_load_1), %function @@ -297,9 +299,11 @@ _(atomic_flag_clear_explicit): #endif //__i386__ /* ---------------------------------------------- */ -#if defined __x86_64__ && !defined TCC_TARGET_PE +#if defined __x86_64__ && !defined _WIN32 + +#define endbr64 + .text - .align 2 .global _(__atomic_load_1) .type _(__atomic_load_1), %function @@ -508,12 +512,11 @@ _(atomic_flag_clear_explicit): ret .size _(atomic_flag_clear_explicit), .-_(atomic_flag_clear_explicit) -#endif //__x86_64__ && !TCC_TARGET_PE +#endif //__x86_64__ && !_WIN32 /* ---------------------------------------------- */ -#if defined __x86_64__ && defined TCC_TARGET_PE +#if defined __x86_64__ && defined _WIN32 .text - .align 2 .global _(__atomic_load_1) .type _(__atomic_load_1), %function @@ -700,12 +703,11 @@ _(atomic_flag_clear_explicit): ret .size _(atomic_flag_clear_explicit), .-_(atomic_flag_clear_explicit) -#endif //__x86_64__ && TCC_TARGET_PE +#endif //__x86_64__ && _WIN32 /* ---------------------------------------------- */ #if defined __arm__ .text - .align 2 #ifndef __TINYC__ .arch armv6k .syntax unified @@ -1496,7 +1498,6 @@ _(atomic_flag_clear_explicit): /* ---------------------------------------------- */ #if defined __aarch64__ .text - .align 2 .global _(__atomic_load_1) .type _(__atomic_load_1), %function @@ -2115,7 +2116,6 @@ _(atomic_flag_clear_explicit): /* ---------------------------------------------- */ #if defined __riscv .text - .align 2 .global _(__atomic_load_1) .type _(__atomic_load_1), %function diff --git a/libtcc.c b/libtcc.c index b6998ddf..7cf5e264 100644 --- a/libtcc.c +++ b/libtcc.c @@ -1465,31 +1465,9 @@ static int tcc_set_linker(TCCState *s, const char *optarg) } else if (link_option(&o, "stack=")) { s->pe_stack_size = strtoul(o.arg, &end, 10); } else if (link_option(&o, "subsystem=")) { -#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) - if (0==strcmp("native", o.arg)) { - s->pe_subsystem = 1; - } else if (0==strcmp("console", o.arg)) { - s->pe_subsystem = 3; - } else if (0==strcmp("gui", o.arg) || 0==strcmp("windows", o.arg)) { - s->pe_subsystem = 2; - } else if (0==strcmp("posix", o.arg)) { - s->pe_subsystem = 7; - } else if (0==strcmp("efiapp", o.arg)) { - s->pe_subsystem = 10; - } else if (0==strcmp("efiboot", o.arg)) { - s->pe_subsystem = 11; - } else if (0==strcmp("efiruntime", o.arg)) { - s->pe_subsystem = 12; - } else if (0==strcmp("efirom", o.arg)) { - s->pe_subsystem = 13; -#elif defined(TCC_TARGET_ARM) - if (0==strcmp("wince", o.arg)) { - s->pe_subsystem = 9; -#endif - } else + if (pe_setsubsy(s, o.arg) < 0) goto err; -#endif /* PE */ -#ifdef TCC_TARGET_MACHO +#elif defined TCC_TARGET_MACHO } else if (link_option(&o, "all_load")) { s->filetype |= AFF_WHOLE_ARCHIVE; } else if (link_option(&o, "force_load=")) { @@ -1653,11 +1631,10 @@ static const TCCOption tcc_options[] = { { "w", TCC_OPTION_w, 0 }, { "E", TCC_OPTION_E, 0}, { "M", TCC_OPTION_M, 0}, - { "MD", TCC_OPTION_MD, 0}, - { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG }, { "MM", TCC_OPTION_MM, 0}, - { "MMD,", TCC_OPTION_MMD, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, - { "MMD", TCC_OPTION_MMD, 0}, + { "MD", TCC_OPTION_MD, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "MMD", TCC_OPTION_MMD, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, + { "MF", TCC_OPTION_MF, TCC_OPTION_HAS_ARG }, { "MP", TCC_OPTION_MP, 0}, { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG }, /* tcctools */ @@ -2075,6 +2052,7 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv) case TCC_OPTION_P: s->Pflag = atoi(optarg) + 1; break; + case TCC_OPTION_M: s->include_sys_deps = 1; // fall through @@ -2084,29 +2062,31 @@ PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv) if (!s->deps_outfile) tcc_set_str(&s->deps_outfile, "-"); break; + case TCC_OPTION_MD: + s->include_sys_deps = 1; + // fall through case TCC_OPTION_MMD: s->gen_deps = 1; /* usually, only "-MMD" is used */ /* but the Linux Kernel uses "-MMD,depfile" */ - if ((optarg) && (*optarg != '\0')) - tcc_set_str(&s->deps_outfile, optarg); - break; - case TCC_OPTION_MD: - s->gen_deps = 1; - s->include_sys_deps = 1; - break; + if (*optarg != ',') + break; + ++optarg; + // fall through case TCC_OPTION_MF: tcc_set_str(&s->deps_outfile, optarg); break; case TCC_OPTION_MP: s->gen_phony_deps = 1; break; + case TCC_OPTION_dumpmachine: printf("%s\n", dumpmachine_str); exit(0); case TCC_OPTION_dumpversion: printf ("%s\n", TCC_VERSION); exit(0); + case TCC_OPTION_x: x = 0; if (*optarg == 'c') diff --git a/tcc.h b/tcc.h index 452a723a..4e416f3b 100644 --- a/tcc.h +++ b/tcc.h @@ -1790,6 +1790,7 @@ ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str); ST_FUNC int pe_load_file(struct TCCState *s1, int fd, const char *filename); ST_FUNC int pe_output_file(TCCState * s1, const char *filename); ST_FUNC int pe_putimport(TCCState *s1, int dllindex, const char *name, addr_t value); +ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg); #if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64 #endif #ifdef TCC_TARGET_X86_64 diff --git a/tccelf.c b/tccelf.c index 1efba539..3e23cf7b 100644 --- a/tccelf.c +++ b/tccelf.c @@ -3937,7 +3937,7 @@ static int ld_add_file(TCCState *s1, const char filename[]) { if (filename[0] == '-' && filename[1] == 'l') return tcc_add_library(s1, filename + 2); - { + if (CONFIG_SYSROOT[0] != '\0' || !IS_ABSPATH(filename)) { /* lookup via library paths */ int ret = tcc_add_dll(s1, tcc_basename(filename), 0); if (ret != FILE_NOT_FOUND) @@ -3981,19 +3981,19 @@ repeat: } else if (t != LD_TOK_NAME) { return tcc_error_noabort("unexpected token '%c'", t); } else if (!strcmp(filename, "AS_NEEDED")) { - ret = ld_add_file_list(s1, filename); + ret |= ld_add_file_list(s1, filename); } else if (c == 'I' || c == 'G' || c == 'A') { - ret = ld_add_file(s1, filename); + ret |= !!ld_add_file(s1, filename); } - if (ret) - return -1; + if (ret < 0) + return ret; t = ld_next(s1, filename, sizeof(filename)); if (t == ',') t = ld_next(s1, filename, sizeof(filename)); } - if (c == 'G' && new_undef_sym(s1, sym_offset)) + if (c == 'G' && ret == 0 && new_undef_sym(s1, sym_offset)) goto repeat; - return 0; + return ret; } /* interpret a subset of GNU ldscripts to handle the dummy libc.so @@ -4001,7 +4001,7 @@ repeat: ST_FUNC int tcc_load_ldscript(TCCState *s1, int fd) { char cmd[64]; - int t, ret = FILE_NOT_RECOGNIZED; + int t, ret = 0, noscript = 1; unsigned char *text_ptr, *saved_ptr; saved_ptr = s1->ld_p; @@ -4012,19 +4012,22 @@ ST_FUNC int tcc_load_ldscript(TCCState *s1, int fd) break; if (!strcmp(cmd, "INPUT") || !strcmp(cmd, "GROUP")) { - ret = ld_add_file_list(s1, cmd); + ret |= ld_add_file_list(s1, cmd); } else if (!strcmp(cmd, "OUTPUT_FORMAT") || !strcmp(cmd, "TARGET")) { /* ignore some commands */ - ret = ld_add_file_list(s1, cmd); - } else if (0 == ret) { + ret |= ld_add_file_list(s1, cmd); + } else if (noscript) { + ret = FILE_NOT_RECOGNIZED; + } else { ret = tcc_error_noabort("unexpected '%s'", cmd); } - if (ret) + if (ret < 0) break; + noscript = 0; } tcc_free(text_ptr); s1->ld_p = saved_ptr; - return ret; + return ret < 0 ? ret : -ret; } #endif /* !ELF_OBJ_ONLY */ diff --git a/tccgen.c b/tccgen.c index 5829037c..d02317aa 100644 --- a/tccgen.c +++ b/tccgen.c @@ -6839,15 +6839,15 @@ static void end_switch(void) /* ------------------------------------------------------------------------- */ /* __attribute__((cleanup(fn))) */ -/* save SValue of symbol to local stack */ -static void save_cleanup_sym(Sym *s) +/* protect symbol lvalues from further modification */ +static void save_lvalues(void) { SValue *sv = vtop; while (sv >= vstack) { - if (sv->sym == s) { + if (sv->sym && (sv->r & VT_LVAL)) { int align, size = type_size(&sv->type, &align); - loc = (loc - size) & -align; - vset(&sv->type, VT_LOCAL | VT_LVAL, loc); + int r2, l = get_temp_local_var(size, align, &r2); + vset(&sv->type, VT_LOCAL | VT_LVAL, l), vtop->r2 = r2; vpushv(sv), *sv = vtop[-1], vstore(), --vtop; } --sv; @@ -6860,7 +6860,7 @@ static void try_call_scope_cleanup(Sym *stop) for (; cls != stop; cls = cls->next) { Sym *fs = cls->cleanup_func; Sym *vs = cls->prev_tok; - save_cleanup_sym(vs); + save_lvalues(); vpushsym(&fs->type, fs); vset(&vs->type, vs->r, vs->c); vtop->sym = vs; diff --git a/tccpe.c b/tccpe.c index a77da08e..2ba3a5d7 100644 --- a/tccpe.c +++ b/tccpe.c @@ -1254,7 +1254,7 @@ static int pe_check_symbols(struct pe_info *pe) if (sym->st_shndx == SHN_UNDEF) { const char *name = (char*)symtab_section->link->data + sym->st_name; unsigned type = ELFW(ST_TYPE)(sym->st_info); - int imp_sym; + int imp_sym = 0; struct import_symbol *is; int _imp_, n; @@ -1928,6 +1928,34 @@ static void pe_add_runtime(TCCState *s1, struct pe_info *pe) pe->type = pe_type; } +ST_FUNC int pe_setsubsy(TCCState *s1, const char *arg) +{ + static const struct subsy { const char* p; int v; } x[] = { +#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) + { "native", 1 }, + { "gui", 2 }, + { "windows", 2 }, + { "console", 3 }, + { "posix", 7 }, + { "efiapp", 10 }, + { "efiboot", 11 }, + { "efiruntime", 12 }, + { "efirom", 13 }, +#elif defined(TCC_TARGET_ARM) + { "wince", 9 }, +#endif + { 0, -1 }}; + const struct subsy *y; + for (y = x;; ++y) { + if (!y->p) + return -1; + if (0 == strcmp(y->p, arg)) { + s1->pe_subsystem = y->v; + return 0; + } + } +} + static void pe_set_options(TCCState * s1, struct pe_info *pe) { if (PE_DLL == pe->type) { diff --git a/tccpp.c b/tccpp.c index d5c01fe4..2bb00b45 100644 --- a/tccpp.c +++ b/tccpp.c @@ -3076,6 +3076,11 @@ static int *macro_arg_subst(Sym **nested_list, const int *macro_str, Sym *args) cval.str.size = tokcstr.size; cval.str.data = tokcstr.data; tok_str_add2(&str, TOK_PPSTR, &cval); +#ifdef TCC_TARGET_ARM + } else if ((parse_flags & PARSE_FLAG_ASM_FILE) && t == TOK_PPNUM) { + /* for example: mov r1,#0 */ + --macro_str, tok_str_add(&str, '#'); +#endif } else { expect("macro parameter after '#'"); } diff --git a/tests/tests2/136_atomic_gcc_style.c b/tests/tests2/136_atomic_gcc_style.c index 520146b7..a99b33e3 100644 --- a/tests/tests2/136_atomic_gcc_style.c +++ b/tests/tests2/136_atomic_gcc_style.c @@ -1,8 +1,11 @@ #include #include -#include #include +// standard assert would popup a dialog box on windows +#define assert(x) \ + printf("assert \"%s\" : %s\n", #x, (x) ? "yes" : "no"); + int main() { // Test 1: Basic functionality of __atomic_store_n and __atomic_load_n { diff --git a/tests/tests2/136_atomic_gcc_style.expect b/tests/tests2/136_atomic_gcc_style.expect index 0c7cc001..f01f5ec8 100644 --- a/tests/tests2/136_atomic_gcc_style.expect +++ b/tests/tests2/136_atomic_gcc_style.expect @@ -1 +1,11 @@ +assert "loaded == 42" : yes +assert "success" : yes +assert "atomic_var == 200" : yes +assert "expected == 100" : yes +assert "!success" : yes +assert "atomic_var == 100" : yes +assert "expected == 100" : yes +assert "atomic_var == 60" : yes +assert "loaded_ptr == NULL" : yes +assert "__atomic_load_n(&atomic_var, __ATOMIC_RELAXED) == 10" : yes All atomic tests passed! diff --git a/x86_64-gen.c b/x86_64-gen.c index 3a430585..2af1fc1c 100644 --- a/x86_64-gen.c +++ b/x86_64-gen.c @@ -809,6 +809,8 @@ void gfunc_call(int nb_args) gbound_args(nb_args); #endif + save_regs(nb_args); + args_size = (nb_args < REGN ? REGN : nb_args) * PTR_SIZE; arg = nb_args; @@ -906,7 +908,7 @@ void gfunc_call(int nb_args) } vtop--; } - save_regs(0); + /* Copy R10 and R11 into RCX and RDX, respectively */ if (nb_args > 0) { o(0xd1894c); /* mov %r10, %rcx */ @@ -1249,6 +1251,8 @@ void gfunc_call(int nb_args) gbound_args(nb_args); #endif + save_regs(nb_args); + /* calculate the number of integer/float register arguments, remember arguments to be passed via stack (in onstack[]), and also remember if we have to align the stack pointer to 16 (onstack[i] == 2). Needs @@ -1364,9 +1368,6 @@ void gfunc_call(int nb_args) tcc_free(onstack); - /* XXX This should be superfluous. */ - save_regs(0); /* save used temporary registers */ - /* then, we prepare register passing arguments. Note that we cannot set RDX and RCX in this loop because gv() may break these temporary registers. Let's use R10 and R11 @@ -1416,12 +1417,6 @@ void gfunc_call(int nb_args) assert(gen_reg == 0); assert(sse_reg == 0); - /* We shouldn't have many operands on the stack anymore, but the - call address itself is still there, and it might be in %eax - (or edx/ecx) currently, which the below writes would clobber. - So evict all remaining operands here. */ - save_regs(0); - /* Copy R10 and R11 into RDX and RCX, respectively */ if (nb_reg_args > 2) { o(0xd2894c); /* mov %r10, %rdx */