Closed hkalexling closed 1 month ago
If you want to fix this super quick, then use --use-stdlib=no
. I'm working on the full solution.
This should now properly work.
Weird I am still getting the same result. I rebuilt my c3c and --version
output seems correct
> c3c --version
C3 Compiler Version: 0.6.4 (Pre-release, Oct 12 2024 02:45:45)
Installed directory: /usr/lib/c3c/
Git Hash: 0da7f1b4de637458408480fe36a19fb870456037
Backends: LLVM
LLVM version: 17.0.6
LLVM default target: x86_64-pc-linux-gnu
I found another issue that seems to be related to the fix above.
lib.c3
:
module lib;
fn int* add(int a, int b) @export("add") {
int* c = mem::new(int);
*c = a + b;
return c;
}
app.c
:
#include "stdio.h"
extern int *add(int a, int b);
int main() {
int *c = add(1, 2);
printf("%d\n", *c);
return 0;
}
Build with
> c3c --version
C3 Compiler Version: 0.6.4 (Pre-release, Oct 12 2024 02:45:45)
Installed directory: /usr/lib/c3c/
Git Hash: 0da7f1b4de637458408480fe36a19fb870456037
Backends: LLVM
LLVM version: 17.0.6
LLVM default target: x86_64-pc-linux-gnu
> c3c static-lib lib.c3 -o libtest
Static library 'libtest.a' created.
> gcc -o test app.c -static -L. -ltes
When run, test
segfaults
[11] 1387745 segmentation fault (core dumped) ./test
I then built c3c from commit 2739c86881f63be22f0a4b2ee52b1bea29c60e51, then built the executable, and it worked as expected
> c3c --version
C3 Compiler Version: 0.6.4 (Pre-release, Oct 12 2024 11:42:47)
Installed directory: /usr/local/bin/
Git Hash: 2739c86881f63be22f0a4b2ee52b1bea29c60e51
Backends: LLVM
LLVM version: 17.0.6
LLVM default target: x86_64-alpine-linux-musl
> c3c static-lib lib.c3 -o libtest
Static library 'libtest.a' created.
> gcc -o test app.c -static -L. -ltest
> ./test
3
For the static libraries, you're comparing something that doesn't work (the last) with something that apparently has issues but is more correct. The thing is that a static library will not run initializers. So what happens here where it "works" is that the initializers are never run and everything looks great. Except for the fact that it actually doesn't.
For the static one, try -Wl,--whole-archive
(I think that's the option) to pull in the initializers.
For the dynamic case, can you please try repeating the whole thing but actually linking using c3c rather than forwarding it to the linker (e.g. c3c compile app.c3 -l ./test.so
)
Thanks for the insights. I tried what you suggested but still no luck
c3c info:
> c3c --version
C3 Compiler Version: 0.6.4 (Pre-release, Oct 12 2024 10:06:08)
Installed directory: /usr/local/bin/
Git Hash: 0da7f1b4de637458408480fe36a19fb870456037
Backends: LLVM
LLVM version: 17.0.6
LLVM default target: x86_64-alpine-linux-musl
Dynamic linking:
> make
c3c dynamic-lib lib.c3 -o libtest --linux-crt /usr/lib --linux-crtbegin /usr/lib/gcc/x86_64-alpine-linux-musl/13.2.1/
Dynamic library 'libtest.so' created.
c3c compile app.c3 -l ./libtest.so -o test
Program linked to executable 'test'.
gcc -o testc app.c -L. -ltest -Wl,-rpath=.
> ./test // <-- still hangs
Static linking:
> make
c3c static-lib lib.c3 -o libtest
Static library 'libtest.a' created.
gcc -o test app.c -static -L. -ltest -Wl,--whole-archive
> ./test
Segmentation fault (core dumped)
I do this:
c3c static-lib testc3.c3
cc testc.c ./lib.a -Wl,-all_load
./a.out
This is no MacOS so the linker command is slightly different. This works on MacOS. I am unable to reproduce this issue. It's possibly different on Linux, but I also notice that you compile it differently.
I am getting the same hanging 'test' program on an Arch linux derivative with c3c from Oct 11,24. Using clang or gcc and the dynamic lib.
I am not getting a segfault when building and using the static version, however. It works fine here with these commands:
gcc -o test app.c -static -L. -ltest
I don't use --whole-archive, as I get linking duplicates if I do...but otherwise it works fine here.
Is it hanging in dynamic_register for you too @wilsonk
@hkalexling can you get me the asm around that hang in c3_dynamic_register?
Yes, it hangs in the same place
Are you able to dig deeper and perhaps see why it hangs by stepping through it?
Maybe this will help you? Hangs at line 145 there. Not sure I can see much (https://codebrowser.dev/glibc/glibc/csu/libc-start.c.html):
Breakpoint 1.3, call_init (l=0x7ffff7fbf050, argc=1, argv=0x7fffffffda38, env=0x7fffffffda48) at dl-init.c:43
43 l->l_init_called = 1;
(gdb)
46 if (__builtin_expect (l->l_name[0], 'a') == '\0'
(gdb)
51 if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS))
(gdb)
59 if (ELF_INITFINI && l->l_info[DT_INIT] != NULL)
(gdb)
60 DL_CALL_DT_INIT(l, l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr, argc, argv, env);
(gdb)
63 ElfW(Dyn) *init_array = l->l_info[DT_INIT_ARRAY];
(gdb)
64 if (init_array != NULL)
(gdb)
70 jm = l->l_info[DT_INIT_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr));
(gdb)
73 for (j = 0; j < jm; ++j)
(gdb)
74 ((dl_init_t) addrs[j]) (argc, argv, env);
(gdb)
73 for (j = 0; j < jm; ++j)
(gdb)
74 ((dl_init_t) addrs[j]) (argc, argv, env);
(gdb)
73 for (j = 0; j < jm; ++j)
(gdb)
74 ((dl_init_t) addrs[j]) (argc, argv, env);
(gdb)
73 for (j = 0; j < jm; ++j)
(gdb)
74 ((dl_init_t) addrs[j]) (argc, argv, env);
(gdb)
73 for (j = 0; j < jm; ++j)
(gdb)
74 ((dl_init_t) addrs[j]) (argc, argv, env);
(gdb)
73 for (j = 0; j < jm; ++j)
(gdb)
74 ((dl_init_t) addrs[j]) (argc, argv, env);
(gdb)
73 for (j = 0; j < jm; ++j)
(gdb)
74 ((dl_init_t) addrs[j]) (argc, argv, env);
(gdb)
73 for (j = 0; j < jm; ++j)
(gdb)
74 ((dl_init_t) addrs[j]) (argc, argv, env);
(gdb)
73 for (j = 0; j < jm; ++j)
(gdb)
_dl_init (main_map=0x7ffff7ffe2e0, argc=1, argv=0x7fffffffda38, env=0x7fffffffda48) at dl-init.c:120
120 while (i-- > 0)
(gdb)
121 call_init (main_map->l_initfini[i], argc, argv, env);
(gdb)
Breakpoint 1.2, call_init (l=0x7ffff7ffe2e0, argc=1, argv=0x7fffffffda38, env=0x7fffffffda48) at dl-init.c:29
29 if (l != l->l_real)
(gdb)
35 assert (l->l_relocated || l->l_type == lt_executable);
(gdb)
37 if (l->l_init_called)
(gdb)
Breakpoint 1.3, call_init (l=0x7ffff7ffe2e0, argc=1, argv=0x7fffffffda38, env=0x7fffffffda48) at dl-init.c:43
43 l->l_init_called = 1;
(gdb)
46 if (__builtin_expect (l->l_name[0], 'a') == '\0'
(gdb)
_dl_init (main_map=0x7ffff7ffe2e0, argc=1, argv=0x7fffffffda38, env=0x7fffffffda48) at dl-init.c:120
120 while (i-- > 0)
(gdb)
0x00007ffff7fe3e20 in _dl_start_user () from /lib64/ld-linux-x86-64.so.2
(gdb)
Single stepping until exit from function _dl_start_user,
which has no line number information.
0x000055555555c2a0 in _start ()
(gdb)
Single stepping until exit from function _start,
which has no line number information.
__libc_start_main_impl (main=0x55555555c4e0 <main>, argc=1, argv=0x7fffffffda38, init=0x0, fini=0x0,
rtld_fini=0x7ffff7fcb200 <_dl_fini>, stack_end=0x7fffffffda28) at ../csu/libc-start.c:242
242 {
(gdb)
311 if (__glibc_likely (rtld_fini != NULL))
(gdb)
312 __cxa_atexit ((void (*) (void *)) rtld_fini, NULL, NULL);
(gdb)
337 if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))
(gdb)
340 if (init != NULL)
(gdb)
Breakpoint 1.1, call_init (argc=1, argv=0x7fffffffda38, env=0x7fffffffda48) at ../csu/libc-start.c:128
128 struct link_map *l = GL(dl_ns)[LM_ID_BASE]._ns_loaded;
(gdb)
134 if (ELF_INITFINI && l->l_info[DT_INIT] != NULL)
(gdb)
135 DL_CALL_DT_INIT(l, l->l_addr + l->l_info[DT_INIT]->d_un.d_ptr,
(gdb)
138 ElfW(Dyn) *init_array = l->l_info[DT_INIT_ARRAY];
(gdb)
139 if (init_array != NULL)
(gdb)
141 unsigned int jm
(gdb)
144 for (unsigned int j = 0; j < jm; ++j)
(gdb)
145 ((dl_init_t) addrs[j]) (argc, argv, env);
(gdb)
@hkalexling said it was in c3_dynamic_register, but I don't see that in your trace @wilsonk?
Hmm, right...I can't seem to get c3c to emit debug info for that function no matter what I do. CFLAGS/CXXFLAGS or cmake with Debug as the build type? @lerno
Looks like that is a generated llvm function, ugh, does that mean I have to produce all the .ll or .bc files and then compile them separately with debug info?
Hmm, right...I can't seem to get c3c to emit debug info for that function no matter what I do. CFLAGS/CXXFLAGS or cmake with Debug as the build type?
There is no debug info, but if you step into it you should see the asm
I disassembled the function from GDB, but I dont know assembly enough to see what went wrong
Dump of assembler code for function .c3_dynamic_register:
0x0000555555583e00 <+0>: sub rsp,0x28
0x0000555555583e04 <+4>: lea rax,[rip+0x2633d] # 0x5555555aa148 <$ct.std.io.File>
0x0000555555583e0b <+11>: add rax,0x10
0x0000555555583e0f <+15>: mov QWORD PTR [rsp+0x20],rax
0x0000555555583e14 <+20>: jmp 0x555555583e16 <.c3_dynamic_register+22>
0x0000555555583e16 <+22>: mov rax,QWORD PTR [rsp+0x20]
0x0000555555583e1b <+27>: mov QWORD PTR [rsp+0x10],rax
0x0000555555583e20 <+32>: mov rax,QWORD PTR [rax]
0x0000555555583e23 <+35>: mov QWORD PTR [rsp+0x18],rax
0x0000555555583e28 <+40>: cmp rax,0x0
0x0000555555583e2c <+44>: je 0x555555583e3e <.c3_dynamic_register+62>
0x0000555555583e2e <+46>: mov rax,QWORD PTR [rsp+0x18]
0x0000555555583e33 <+51>: add rax,0x10
0x0000555555583e37 <+55>: mov QWORD PTR [rsp+0x20],rax
0x0000555555583e3c <+60>: jmp 0x555555583e16 <.c3_dynamic_register+22>
0x0000555555583e3e <+62>: mov rax,QWORD PTR [rsp+0x10]
0x0000555555583e43 <+67>: lea rcx,[rip+0x26a2e] # 0x5555555aa878 <$ct.dyn.std.io.File.seek>
0x0000555555583e4a <+74>: mov QWORD PTR [rax],rcx
0x0000555555583e4d <+77>: lea rax,[rip+0x262f4] # 0x5555555aa148 <$ct.std.io.File>
0x0000555555583e54 <+84>: add rax,0x10
0x0000555555583e58 <+88>: mov QWORD PTR [rsp+0x8],rax
0x0000555555583e5d <+93>: mov rax,QWORD PTR [rsp+0x8]
0x0000555555583e62 <+98>: mov QWORD PTR [rsp-0x8],rax
0x0000555555583e67 <+103>: mov rax,QWORD PTR [rax]
=> 0x0000555555583e6a <+106>: mov QWORD PTR [rsp],rax
0x0000555555583e6e <+110>: cmp rax,0x0
0x0000555555583e72 <+114>: je 0x555555583e83 <.c3_dynamic_register+131>
0x0000555555583e74 <+116>: mov rax,QWORD PTR [rsp]
0x0000555555583e78 <+120>: add rax,0x10
0x0000555555583e7c <+124>: mov QWORD PTR [rsp+0x8],rax
0x0000555555583e81 <+129>: jmp 0x555555583e5d <.c3_dynamic_register+93>
0x0000555555583e83 <+131>: mov rax,QWORD PTR [rsp-0x8]
0x0000555555583e88 <+136>: lea rcx,[rip+0x26a01] # 0x5555555aa890 <$ct.dyn.std.io.File.write_byte>
0x0000555555583e8f <+143>: mov QWORD PTR [rax],rcx
0x0000555555583e92 <+146>: lea rax,[rip+0x262af] # 0x5555555aa148 <$ct.std.io.File>
0x0000555555583e99 <+153>: add rax,0x10
0x0000555555583e9d <+157>: mov QWORD PTR [rsp-0x10],rax
0x0000555555583ea2 <+162>: mov rax,QWORD PTR [rsp-0x10]
0x0000555555583ea7 <+167>: mov QWORD PTR [rsp-0x20],rax
0x0000555555583eac <+172>: mov rax,QWORD PTR [rax]
0x0000555555583eaf <+175>: mov QWORD PTR [rsp-0x18],rax
0x0000555555583eb4 <+180>: cmp rax,0x0
0x0000555555583eb8 <+184>: je 0x555555583eca <.c3_dynamic_register+202>
0x0000555555583eba <+186>: mov rax,QWORD PTR [rsp-0x18]
0x0000555555583ebf <+191>: add rax,0x10
0x0000555555583ec3 <+195>: mov QWORD PTR [rsp-0x10],rax
0x0000555555583ec8 <+200>: jmp 0x555555583ea2 <.c3_dynamic_register+162>
0x0000555555583eca <+202>: mov rax,QWORD PTR [rsp-0x20]
0x0000555555583ecf <+207>: lea rcx,[rip+0x269d2] # 0x5555555aa8a8 <$ct.dyn.std.io.File.close>
0x0000555555583ed6 <+214>: mov QWORD PTR [rax],rcx
0x0000555555583ed9 <+217>: lea rax,[rip+0x26268] # 0x5555555aa148 <$ct.std.io.File>
0x0000555555583ee0 <+224>: add rax,0x10
0x0000555555583ee4 <+228>: mov QWORD PTR [rsp-0x28],rax
0x0000555555583ee9 <+233>: mov rax,QWORD PTR [rsp-0x28]
0x0000555555583eee <+238>: mov QWORD PTR [rsp-0x38],rax
0x0000555555583ef3 <+243>: mov rax,QWORD PTR [rax]
0x0000555555583ef6 <+246>: mov QWORD PTR [rsp-0x30],rax
0x0000555555583efb <+251>: cmp rax,0x0
0x0000555555583eff <+255>: je 0x555555583f11 <.c3_dynamic_register+273>
0x0000555555583f01 <+257>: mov rax,QWORD PTR [rsp-0x30]
0x0000555555583f06 <+262>: add rax,0x10
0x0000555555583f0a <+266>: mov QWORD PTR [rsp-0x28],rax
0x0000555555583f0f <+271>: jmp 0x555555583ee9 <.c3_dynamic_register+233>
0x0000555555583f11 <+273>: mov rax,QWORD PTR [rsp-0x38]
0x0000555555583f16 <+278>: lea rcx,[rip+0x269a3] # 0x5555555aa8c0 <$ct.dyn.std.io.File.read>
0x0000555555583f1d <+285>: mov QWORD PTR [rax],rcx
0x0000555555583f20 <+288>: lea rax,[rip+0x26221] # 0x5555555aa148 <$ct.std.io.File>
0x0000555555583f27 <+295>: add rax,0x10
0x0000555555583f2b <+299>: mov QWORD PTR [rsp-0x40],rax
0x0000555555583f30 <+304>: mov rax,QWORD PTR [rsp-0x40]
0x0000555555583f35 <+309>: mov QWORD PTR [rsp-0x50],rax
0x0000555555583f3a <+314>: mov rax,QWORD PTR [rax]
0x0000555555583f3d <+317>: mov QWORD PTR [rsp-0x48],rax
0x0000555555583f42 <+322>: cmp rax,0x0
0x0000555555583f46 <+326>: je 0x555555583f58 <.c3_dynamic_register+344>
0x0000555555583f48 <+328>: mov rax,QWORD PTR [rsp-0x48]
0x0000555555583f4d <+333>: add rax,0x10
0x0000555555583f51 <+337>: mov QWORD PTR [rsp-0x40],rax
0x0000555555583f56 <+342>: jmp 0x555555583f30 <.c3_dynamic_register+304>
0x0000555555583f58 <+344>: mov rax,QWORD PTR [rsp-0x50]
0x0000555555583f5d <+349>: lea rcx,[rip+0x26974] # 0x5555555aa8d8 <$ct.dyn.std.io.File.write>
0x0000555555583f64 <+356>: mov QWORD PTR [rax],rcx
0x0000555555583f67 <+359>: lea rax,[rip+0x261da] # 0x5555555aa148 <$ct.std.io.File>
0x0000555555583f6e <+366>: add rax,0x10
0x0000555555583f72 <+370>: mov QWORD PTR [rsp-0x58],rax
0x0000555555583f77 <+375>: mov rax,QWORD PTR [rsp-0x58]
0x0000555555583f7c <+380>: mov QWORD PTR [rsp-0x68],rax
0x0000555555583f81 <+385>: mov rax,QWORD PTR [rax]
0x0000555555583f84 <+388>: mov QWORD PTR [rsp-0x60],rax
0x0000555555583f89 <+393>: cmp rax,0x0
0x0000555555583f8d <+397>: je 0x555555583f9f <.c3_dynamic_register+415>
0x0000555555583f8f <+399>: mov rax,QWORD PTR [rsp-0x60]
0x0000555555583f94 <+404>: add rax,0x10
0x0000555555583f98 <+408>: mov QWORD PTR [rsp-0x58],rax
0x0000555555583f9d <+413>: jmp 0x555555583f77 <.c3_dynamic_register+375>
0x0000555555583f9f <+415>: mov rax,QWORD PTR [rsp-0x68]
0x0000555555583fa4 <+420>: lea rcx,[rip+0x26945] # 0x5555555aa8f0 <$ct.dyn.std.io.File.read_byte>
0x0000555555583fab <+427>: mov QWORD PTR [rax],rcx
0x0000555555583fae <+430>: lea rax,[rip+0x26193] # 0x5555555aa148 <$ct.std.io.File>
0x0000555555583fb5 <+437>: add rax,0x10
0x0000555555583fb9 <+441>: mov QWORD PTR [rsp-0x70],rax
0x0000555555583fbe <+446>: mov rax,QWORD PTR [rsp-0x70]
0x0000555555583fc3 <+451>: mov QWORD PTR [rsp-0x80],rax
0x0000555555583fc8 <+456>: mov rax,QWORD PTR [rax]
0x0000555555583fcb <+459>: mov QWORD PTR [rsp-0x78],rax
0x0000555555583fd0 <+464>: cmp rax,0x0
0x0000555555583fd4 <+468>: je 0x555555583fe6 <.c3_dynamic_register+486>
0x0000555555583fd6 <+470>: mov rax,QWORD PTR [rsp-0x78]
0x0000555555583fdb <+475>: add rax,0x10
0x0000555555583fdf <+479>: mov QWORD PTR [rsp-0x70],rax
0x0000555555583fe4 <+484>: jmp 0x555555583fbe <.c3_dynamic_register+446>
0x0000555555583fe6 <+486>: mov rax,QWORD PTR [rsp-0x80]
0x0000555555583feb <+491>: lea rcx,[rip+0x26916] # 0x5555555aa908 <$ct.dyn.std.io.File.flush>
0x0000555555583ff2 <+498>: mov QWORD PTR [rax],rcx
0x0000555555583ff5 <+501>: add rsp,0x28
0x0000555555583ff9 <+505>: ret
End of assembler dump.
I tried stepping through it and it seems to be stuck in the following lines
mov rax, QWORD PTR [rsp]
add rax, 0x10
mov QWORD PTR [rsp+0x8], rax
jmp 0x555555583e5d <.c3_dynamic_register+93>
mov rax, QWORD PTR [rsp+0x8]
mov QWORD PTR [rsp-0x8], rax
mov rax, QWORD PTR [rax]
mov QWORD PTR [rsp], rax
cmp rax, 0x0
je 0x555555583e83 <.c3_dynamic_register+131>
mov rax, QWORD PTR [rsp]
Looks like that bit of code is looping over a linked list (checking the pointer and incrementing by 0x10 or 16 decimal, ie. a pointer size) and exiting (jumping to 131) when a null is encountered...so I assume something is wrong with the linked list? A cycle perhaps? Or it is the biggest linked list in history ;) Anyways, that is what it looks like to me:
0x0000555555583e5d <+93>: mov rax,QWORD PTR [rsp+0x8]
0x0000555555583e62 <+98>: mov QWORD PTR [rsp-0x8],rax
0x0000555555583e67 <+103>: mov rax,QWORD PTR [rax]
0x0000555555583e6a <+106>: mov QWORD PTR [rsp],rax
0x0000555555583e6e <+110>: cmp rax,0x0
0x0000555555583e72 <+114>: je 0x555555583e83 <.c3_dynamic_register+131>
0x0000555555583e74 <+116>: mov rax,QWORD PTR [rsp]
0x0000555555583e78 <+120>: add rax,0x10 <----------------------------------------------here
0x0000555555583e7c <+124>: mov QWORD PTR [rsp+0x8],rax
0x0000555555583e81 <+129>: jmp 0x555555583e5d <.c3_dynamic_register+93>
Makes sense, looks like the null check and jumping is from here. I can't seem to find where the next pointer is set though.
@wilsonk BTW RE linking with the static library, I had to use mem:new
in lib.c3
for it to segfault. Just FYI in case you are using the same lib.c3
file for both dynamic and static linking.
Ok, thank you both! This helped me figure out what happens. Presumably this same code is running twice, but the code doesn't take into account that something that should be added already is.
This should be fixed in dev now, coming soon in this pull request: https://github.com/c3lang/c3c/pull/1555
For dynamic linking, I confirm that it's working now. Thanks for the quick fix!
For static linking, I found out that -Wl,-whole-archive
is the right call, but its position matters. I.e., it must be placed before the static library. From the man page
--whole-archive For each archive mentioned on the command line after the --whole-archive option, include every object file in the archive in the link, rather than searching the archive for the required object files. This is normally used to turn an archive file into a shared library, forcing every object to be included in the resulting shared library. This option may be used more than once. Two notes when using this option from gcc: First, gcc doesn't know about this option, so you have to use -Wl,-whole-archive. Second, don't forget to use -Wl,-no-whole-archive after your list of archives, because gcc will add its own list of archives to your link and you may not want this flag to affect those as well.
So gcc -o test app.c -static -L. -Wl,-whole-archive -ltest -Wl,-no-whole-archive
produces a working executable.
I am trying to build a dynamic library from C3, and then link it with a C3 executable. The executable is compiled and linked successfully, but it hangs indefinitely when run. Not sure if it's a bug or if I am doing something wrong.
Here's a minimum reproducible example:
lib.c3
:app.c3
:Makefile
:Running
make test
builds and links the executable successfully, but it just hangs when run:GDB suggests that it's hanging at
c3_dynamic_register
:I tried linking to the same library with C with
app.c
:and it works as expected: