Op 20-11-2025 om 07:13 schreef Herman ten Brugge:
Op 19-11-2025 om 13:11 schreef grischka:
On 17.11.2025 14:33, Herman ten Brugge via Tinycc-devel wrote:
Op 16-11-2025 om 11:07 schreef DFP:
It seems like when -run is used with -nostdlib, the tcc_run()
function calls the _start symbol as-if it was a function. Which is
not correct ...
I pushed a fix for this.
For example with
"Allow -nostdlib -run"
I'd rather remove the feature completely than have that patch in tinycc
code. Instead we can simply require 'main' with tcc -run always and
have 100 lines ugliness less.
Note the original commit was this one
https://repo.or.cz/tinycc.git/commitdiff/fa0ef91a247d68df919cfe11f2c7eaad7dc7e75c
Obviously it was incomplete. But nobody says that we have to fix it.
In principle one could make it much more cleanly, say with a
"runstart.o"
using a combination of alloca() and an indirect goto (goto *&_start);
But it isn't worth even that, I think.
I am happy with removing -nostdlib -run completely.
The reason I started working on it because the last commit was from you.
So I thought it should be supported.
I tried alloca but gcc and clang removed all code because the data in
the alloca area was not used. I used so tricks to avoid this but
this looked really ugly.
I also tried goto *program_start but this only works on gcc.
I needed to use assembly code for the goto *program_start so
I moved the code to the *gen files and then decided to also
use assembly code for the alloca.
But if you want to remove this I am completely happy with that.
I implemented the 'lib/run_nostdlib.o' change. It looks better then
*gen.c file change. I also included 'tests/nostdlib_test.c'.
But removing the feature is still ok.
Herman
diff --git a/Makefile b/Makefile
index b5427d7..981eed2 100644
--- a/Makefile
+++ b/Makefile
@@ -367,7 +367,7 @@ IR = $(IM) mkdir -p $2 && cp -r $1/. $2
IM = @echo "-> $2 : $1" ;
BINCHECK = $(if $(wildcard $(PROGS) *-tcc$(EXESUF)),,@echo "Makefile: nothing
found to install" && exit 1)
-EXTRA_O = runmain.o bt-exe.o bt-dll.o bt-log.o bcheck.o
+EXTRA_O = runmain.o run_nostdlib.o bt-exe.o bt-dll.o bt-log.o bcheck.o
# install progs & libs
install-unx:
diff --git a/arm-gen.c b/arm-gen.c
index df5a345..81fa185 100644
--- a/arm-gen.c
+++ b/arm-gen.c
@@ -1396,20 +1396,6 @@ void gfunc_call(int nb_args)
float_abi = def_float_abi;
}
-void tcc_run_start(int (*prog_main)(int, char **, char **), int cnt, char
**var)
-{
-#ifdef __arm__
- void *sp;
-
- __asm__("sub sp, sp, %1\n"
- "\tmov %0, sp"
- : "=r" (sp)
- : "r" ((((size_t) cnt + 1) & -2) * sizeof(char *)));
- memcpy(sp, var, cnt * sizeof(char *));
- __asm__("mov pc, %0" : : "r" (prog_main));
-#endif
-}
-
/* generate function prolog of type 't' */
void gfunc_prolog(Sym *func_sym)
{
diff --git a/arm64-gen.c b/arm64-gen.c
index ab55289..03ae56c 100644
--- a/arm64-gen.c
+++ b/arm64-gen.c
@@ -1167,25 +1167,6 @@ ST_FUNC void gfunc_call(int nb_args)
tcc_free(t);
}
-void tcc_run_start(int (*prog_main)(int, char **, char **), int cnt, char
**var)
-{
-#if defined(__aarch64__)
-#if defined(__TINYC__)
- // FIXME: immplement arm64 assembler
- fprintf(stderr, "tcc -nostdlib -run not implement for arm64\n");
-#else
- void *sp;
-
- __asm__("sub sp, sp, %1\n"
- "\tmov %0, sp"
- : "=r" (sp)
- : "r" ((((size_t) cnt + 1) & -2) * sizeof(char *)));
- memcpy(sp, var, cnt * sizeof(char *));
- __asm__("br %0" : : "r" (prog_main));
-#endif
-#endif
-}
-
static unsigned long arm64_func_va_list_stack;
static int arm64_func_va_list_gr_offs;
static int arm64_func_va_list_vr_offs;
diff --git a/i386-gen.c b/i386-gen.c
index d6a37a9..c8d8af5 100644
--- a/i386-gen.c
+++ b/i386-gen.c
@@ -504,24 +504,6 @@ ST_FUNC void gfunc_call(int nb_args)
vtop--;
}
-void tcc_run_start(int (*prog_main)(int, char **, char **), int cnt, char
**var)
-{
-#ifdef __i386__
-#ifdef TCC_TARGET_PE
- fprintf(stderr, "tcc -nostdlib -run not implement for TCC_TARGET_PE\n");
-#else
- void *sp;
-
- __asm("sub %1, %%esp\n"
- "\tmov %%esp, %0"
- : "=r" (sp)
- : "r" ((((size_t) cnt + 1) & -2) * sizeof(char *)));
- memcpy(sp, var, cnt * sizeof(char *));
- __asm__("jmp *%0" : : "r" (prog_main));
-#endif
-#endif
-}
-
#ifdef TCC_TARGET_PE
#define FUNC_PROLOG_SIZE (10 + USE_EBX)
#else
diff --git a/lib/Makefile b/lib/Makefile
index 942add9..b7c2770 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -50,13 +50,13 @@ Nat = $(if $X,no,)
Cbt = $(Nat)$(subst yes,,$(CONFIG_backtrace))
Cbc = $(Cbt)$(subst yes,,$(CONFIG_bcheck))
-$(Nat)COMMON_O += runmain.o tcov.o
+$(Nat)COMMON_O += runmain.o run_nostdlib.o tcov.o
$(Cbt)COMMON_O += bt-exe.o bt-log.o
$(Cbt)WIN_O += bt-dll.o
$(Cbc)COMMON_O += bcheck.o
# not in libtcc1.a
-EXTRA_O = runmain.o bt-exe.o bt-dll.o bt-log.o bcheck.o
+EXTRA_O = runmain.o run_nostdlib.o bt-exe.o bt-dll.o bt-log.o bcheck.o
OBJ-i386 = $(I386_O) $(LIN_O)
OBJ-x86_64 = $(X86_64_O) $(LIN_O)
diff --git a/lib/run_nostdlib.c b/lib/run_nostdlib.c
new file mode 100644
index 0000000..429910a
--- /dev/null
+++ b/lib/run_nostdlib.c
@@ -0,0 +1,87 @@
+/* ------------------------------------------------------------- */
+/* support for run_nostdlib() */
+
+// FIXME: implement arm64 assembler
+#if defined(__aarch64__)
+static void *alloca_arm64(unsigned long);
+__asm__(
+#ifdef __leading_underscore
+ "_alloca_arm64:\n\t"
+#else
+ "alloca_arm64:\n\t"
+#endif
+ ".int 0x91003c00\n\t" // add x0, x0, #15
+ ".int 0x927cec00\n\t" // and x0, x0, #-16
+ ".int 0xcb2063ff\n\t" // sub sp, sp, x0
+ ".int 0x910003e0\n\t" // mov x0, sp
+ ".int 0xd65f03c0" // ret
+);
+static void goto_arm64(void *start);
+__asm__(
+#ifdef __leading_underscore
+ "_goto_arm64:\n\t"
+#else
+ "goto_arm64:\n\t"
+#endif
+ ".int 0xd61f0000" // br x0
+);
+#endif
+
+int _run_nostdlib(void *start, int cnt, char **var)
+{
+#if defined(_WIN32)
+ return 1;
+#else
+ int i;
+ unsigned long n = (((unsigned long) cnt + 1) & -2) * sizeof(char *);
+ char **sp;
+
+ /* nostdlib so avoid alloca() */
+ /* also memcpy below will be removed because compiler detects dead store */
+#if defined(__aarch64__)
+ sp = alloca_arm64(n);
+#else
+#if defined(__aarch64__)
+ // use if arm64 assembler is present
+ __asm__("sub sp, sp, %1\n"
+ "\tmov %0, sp"
+#elif defined(__arm__)
+ __asm__("sub sp, sp, %1\n"
+ "\tmov %0, sp"
+#elif defined(__i386__)
+ __asm("sub %1, %%esp\n"
+ "\tmov %%esp, %0"
+#elif defined(__riscv)
+ __asm__("sub sp, sp, %1\n"
+ "\tmv %0, sp"
+#elif defined(__x86_64__)
+ __asm__("subq %1, %%rsp\n"
+ "\tmovq %%rsp, %0"
+#endif
+ : "=r" (sp)
+ : "r" (n));
+#endif
+ /* nostdlib so avoid memcpy() */
+ for (i = 0; i < cnt; i++)
+ sp[i] = var[i];
+
+ /* goto *start does not work for clang. Use assembly. */
+#if defined(__aarch64__)
+ goto_arm64(start);
+#else
+#if defined(__aarch64__)
+ // use if arm64 assembler is present
+ __asm__("br %0" : : "r" (start));
+#elif defined(__arm__)
+ __asm__("mov pc, %0" : : "r" (start));
+#elif defined(__i386__)
+ __asm__("jmp *%0" : : "r" (start));
+#elif defined(__riscv)
+ __asm__("jalr %0" : : "r" (start));
+#elif defined(__x86_64__)
+ __asm__("jmp *%0" : : "r" (start));
+#endif
+#endif
+ return 0;
+#endif
+}
diff --git a/riscv64-gen.c b/riscv64-gen.c
index 7532905..e03d6e4 100644
--- a/riscv64-gen.c
+++ b/riscv64-gen.c
@@ -769,20 +769,6 @@ done:
tcc_free(info);
}
-void tcc_run_start(int (*prog_main)(int, char **, char **), int cnt, char
**var)
-{
-#ifdef __riscv
- void *sp;
-
- __asm__("sub sp, sp, %1\n"
- "\tmv %0, sp"
- : "=r" (sp)
- : "r" ((((size_t) cnt + 1) & -2) * sizeof(char *)));
- memcpy(sp, var, cnt * sizeof(char *));
- __asm__("jalr %0" : : "r" (prog_main));
-#endif
-}
-
static int func_sub_sp_offset, num_va_regs, func_va_list_ofs;
ST_FUNC void gfunc_prolog(Sym *func_sym)
diff --git a/tcc.h b/tcc.h
index 058507f..1c2f694 100644
--- a/tcc.h
+++ b/tcc.h
@@ -1797,7 +1797,6 @@ ST_FUNC const char *dlerror(void);
ST_FUNC void *dlsym(void *handle, const char *symbol);
#endif
ST_FUNC void tcc_run_free(TCCState *s1);
-ST_FUNC void tcc_run_start(int (*prog_main)(int, char **, char **), int cnt,
char **var);
#endif
/* ------------ tcctools.c ----------------- */
diff --git a/tccrun.c b/tccrun.c
index aab3e40..db561f3 100644
--- a/tccrun.c
+++ b/tccrun.c
@@ -203,6 +203,7 @@ ST_FUNC void tcc_run_free(TCCState *s1)
LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
{
int (*prog_main)(int, char **, char **), ret;
+ int (*run_nostdlib)(void *start, int cnt, char **var);
const char *top_sym;
jmp_buf main_jb;
@@ -221,6 +222,7 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
tcc_add_symbol(s1, "__rt_exit", rt_exit);
if (s1->nostdlib) {
+ tcc_add_support(s1, "run_nostdlib.o");
s1->run_main = top_sym = s1->elf_entryname ? s1->elf_entryname :
"_start";
} else {
tcc_add_support(s1, "runmain.o");
@@ -233,6 +235,11 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
prog_main = (void*)get_sym_addr(s1, s1->run_main, 1, 1);
if ((addr_t)-1 == (addr_t)prog_main)
return -1;
+ if (s1->nostdlib) {
+ run_nostdlib = (void *)get_sym_addr(s1, "_run_nostdlib", 1, 1);
+ if ((addr_t)-1 == (addr_t)run_nostdlib)
+ return -1;
+ }
/* custom stdin for run_main, mainly if stdin is/was an input file.
* fileno(stdin) should remain 0, as posix mandates to use the smallest
@@ -267,7 +274,8 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv)
else
p[argc + 2] = NULL;
/* Probably never returns */
- tcc_run_start(prog_main, argc + n + 2, p);
+ if (run_nostdlib(prog_main, argc + n + 2, p))
+ fprintf(stderr, "tcc -nostdlib -run not implemented\n");
tcc_free(p);
}
else
diff --git a/tests/nostdlib_test.c b/tests/nostdlib_test.c
new file mode 100755
index 0000000..7c7f982
--- /dev/null
+++ b/tests/nostdlib_test.c
@@ -0,0 +1,155 @@
+#!/usr/local/bin/tcc -run -nostdlib
+
+// Not working on windows and apple because of different API.
+
+#include <unistd.h>
+#include <sys/syscall.h>
+#if defined __x86_64__
+__asm__ ("syscall:\n\t"
+ "mov %rdi,%rax\n\t"
+ "mov %rsi,%rdi\n\t"
+ "mov %rdx,%rsi\n\t"
+ "mov %rcx,%rdx\n\t"
+ "mov %r8,%r10\n\t"
+ "mov %r9,%r8\n\t"
+ "mov 0x8(%rsp),%r9\n\t"
+ "syscall\n\t"
+ "ret");
+__asm__ (".global _start\n\t"
+ "_start:\n\t"
+ "mov 0(%rsp), %rdi\n\t"
+ "lea 8(%rsp), %rsi\n\t"
+ "jmp print");
+#elif defined __i386__
+__asm__ ("syscall:\n\t"
+ "push %ebp\n\t"
+ "push %edi\n\t"
+ "push %esi\n\t"
+ "push %ebx\n\t"
+ "mov 0x2c(%esp),%ebp\n\t"
+ "mov 0x28(%esp),%edi\n\t"
+ "mov 0x24(%esp),%esi\n\t"
+ "mov 0x20(%esp),%edx\n\t"
+ "mov 0x1c(%esp),%ecx\n\t"
+ "mov 0x18(%esp),%ebx\n\t"
+ "mov 0x14(%esp),%eax\n\t"
+ // "call *%gs:0x10\n\t"
+ ".byte 0x65,0xff,0x15,0x10,0x00,0x00,0x00\n\t"
+ "pop %ebx\n\t"
+ "pop %esi\n\t"
+ "pop %edi\n\t"
+ "pop %ebp\n\t"
+ "ret");
+__asm__ (".global _start\n\t"
+ "_start:\n\t"
+ "pop %esi\n\t"
+ "mov %esp, %ecx\n\t"
+ "and $0xfffffff0,%esp\n\t"
+ "push %ecx\n\t"
+ "push %esi\n\t"
+ "call print");
+#elif defined __arm__
+__asm__ ("syscall:\n\t"
+ "mov r12, sp\n\t"
+ "push {r4, r5, r6, r7}\n\t"
+ "mov r7, r0\n\t"
+ "mov r0, r1\n\t"
+ "mov r1, r2\n\t"
+ "mov r2, r3\n\t"
+ "ldm r12, {r3, r4, r5, r6}\n\t"
+ "svc 0x00000000\n\t"
+ "pop {r4, r5, r6, r7}\n\t"
+ "mov pc, lr");
+__asm__ (".global _start\n\t"
+ "_start:\n\t"
+ "pop {r0}\n\t"
+ "mov r1, sp\n\t"
+ "bl print");
+#elif defined __aarch64__
+__asm__ ("syscall:\n\t"
+ ".int 0x2a0003e8\n\t" // mov w8, w0
+ ".int 0xaa0103e0\n\t" // x0, x1
+ ".int 0xaa0203e1\n\t" // mov x1, x2
+ ".int 0xaa0303e2\n\t" // mov x2, x3
+ ".int 0xaa0403e3\n\t" // mov x3, x4
+ ".int 0xaa0503e4\n\t" // mov x4, x5
+ ".int 0xaa0603e5\n\t" // mov x5, x6
+ ".int 0xaa0703e6\n\t" // mov x6, x7
+ ".int 0xd4000001\n\t" // svc #0x0
+ ".int 0xd65f03c0"); // ret
+__asm__ (".global _start\n\t"
+ "_start:\n\t"
+ ".int 0xf94003e0\n\t" // ldr x0, [sp]
+ ".int 0x910023e1\n\t" // add x1, sp, #08
+ ".reloc .,R_AARCH64_CALL26,print\n\t"
+ ".int 0x94000000"); // bl print
+#elif defined __riscv
+__asm__ ("syscall:\n\t"
+ "mv t1,a0\n\t"
+ "mv a0,a1\n\t"
+ "mv a1,a2\n\t"
+ "mv a2,a3\n\t"
+ "mv a3,a4\n\t"
+ "mv a4,a5\n\t"
+ "mv a5,a6\n\t"
+ "mv a6,a7\n\t"
+ "mv a7,t1\n\t"
+ "ecall\n\t"
+ "ret");
+__asm__ (".global _start\n\t"
+ "_start:\n\t"
+ "ld a0,0(sp)\n\t"
+ "addi a1,sp,8\n\t"
+ "jal print");
+#endif
+unsigned long strlen(const char *s)
+{
+ unsigned long len = 0;
+
+ while (*s++)
+ len++;
+ return len;
+}
+
+static void pr_num(int num)
+{
+ char val[20], *p = &val[20];
+
+ *--p = '\0';
+ do {
+ int a = num, b = 0;
+
+ while (a >= 10) {
+ a -= 10;
+ b++;
+ }
+ *--p = a + '0';
+ num = b;
+ } while (num);
+ syscall(SYS_write, 1, p, strlen(p));
+}
+
+static void pr_str(int n, char *s)
+{
+ pr_num(n);
+ syscall(SYS_write, 1, ": ", 2);
+ syscall(SYS_write, 1, s, strlen(s));
+ syscall(SYS_write, 1, "\n", 1);
+}
+
+void print(int argc, char **argv) {
+ int i;
+ char **envp = &argv[argc + 1];
+
+ syscall(SYS_write, 1, "argc: ", 6);
+ pr_num(argc);
+ syscall(SYS_write, 1, "\n", 1);
+ syscall(SYS_write, 1, "argv[]\n", 7);
+ for (i = 0; i < argc; i++)
+ pr_str(i, argv[i]);
+ syscall(SYS_write, 1, "envp[]\n", 7);
+ i = 0;
+ while (*envp)
+ pr_str(i++, *envp++);
+ syscall(SYS_exit, 0);
+}
diff --git a/x86_64-gen.c b/x86_64-gen.c
index 3783267..0e63e68 100644
--- a/x86_64-gen.c
+++ b/x86_64-gen.c
@@ -933,11 +933,6 @@ void gfunc_call(int nb_args)
vtop--;
}
-void tcc_run_start(int (*prog_main)(int, char **, char **), int cnt, char
**var)
-{
- fprintf(stderr, "tcc -nostdlib -run not implement for TCC_TARGET_PE\n");
-}
-
#define FUNC_PROLOG_SIZE 11
/* generate function prolog of type 't' */
@@ -1438,20 +1433,6 @@ void gfunc_call(int nb_args)
vtop--;
}
-void tcc_run_start(int (*prog_main)(int, char **, char **), int cnt, char
**var)
-{
-#ifdef __x86_64__
- void *sp;
-
- __asm__("subq %1, %%rsp\n"
- "\tmovq %%rsp, %0"
- : "=r" (sp)
- : "r" ((((size_t) cnt + 1) & -2) * sizeof(char *)));
- memcpy(sp, var, cnt * sizeof(char *));
- __asm__("jmp *%0" : : "r" (prog_main));
-#endif
-}
-
#define FUNC_PROLOG_SIZE 11
static void push_arg_reg(int i) {
_______________________________________________
Tinycc-devel mailing list
[email protected]
https://lists.nongnu.org/mailman/listinfo/tinycc-devel