c3lang / c3c

Compiler for the C3 language
https://c3-lang.org
GNU Lesser General Public License v3.0
3.03k stars 183 forks source link

Issue linking dynamic library produced by c3 #1549

Closed hkalexling closed 1 month ago

hkalexling commented 1 month ago

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:

.
├── app.c
├── app.c3
├── lib.c3
└── Makefile

lib.c3:

module lib;

fn int add(int a, int b) @export("add") {
    return a + b;
}

app.c3:

module app;

import std::io;

extern fn int add(int a, int b);

fn void main() {
    io::printn(add(1, 2));
}

Makefile:

.PHONY: clean all

CRT_DIR := /usr/lib
CRT_BEGIN_DIR := $(dir $(wildcard /usr/lib/gcc/*/*/crtbegin.o))

all: test testc

test: app.c3 libtest.so
    c3c compile app.c3 -z -L. -z -ltest -o test -z -Wl,-rpath=.

testc: app.c libtest.so
    gcc -o testc app.c -L. -ltest -Wl,-rpath=.

libtest.so: lib.c3
    c3c dynamic-lib lib.c3 -o libtest --linux-crt $(CRT_DIR) --linux-crtbegin $(CRT_BEGIN_DIR) 

clean:
    rm -f libtest.so test testc *.o

Running make test builds and links the executable successfully, but it just hangs when run:

> make test
c3c dynamic-lib lib.c3 -o libtest --linux-crt /usr/lib --linux-crtbegin /usr/lib/gcc/x86_64-pc-linux-gnu/14.1.1/
Dynamic library 'libtest.so' created.
c3c compile app.c3 -z -L. -z -ltest -o test -z -Wl,-rpath=.
Program linked to executable 'test'.
> ./test // <-- hangs indefinitely 

GDB suggests that it's hanging at c3_dynamic_register:

gef➤  bt
#0  0x0000555555584a91 in c3_dynamic_register ()
#1  0x00007ffff7b9ef44 in __libc_start_main () from /usr/lib/libc.so.6
#2  0x000055555555c2c5 in _start ()

I tried linking to the same library with C with

app.c:

#include "stdio.h"

extern int add(int a, int b);

int main() {
  printf("%d\n", add(1, 2));
  return 0;
}

and it works as expected:

> make testc
gcc -o testc app.c -L. -ltest -Wl,-rpath=.
> ./testc
3
lerno commented 1 month ago

If you want to fix this super quick, then use --use-stdlib=no. I'm working on the full solution.

lerno commented 1 month ago

This should now properly work.

hkalexling commented 1 month ago

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
hkalexling commented 1 month ago

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
lerno commented 1 month ago

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.

lerno commented 1 month ago

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)

hkalexling commented 1 month ago

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)
lerno commented 1 month ago

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.

wilsonk commented 1 month ago

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.

lerno commented 1 month ago

Is it hanging in dynamic_register for you too @wilsonk

lerno commented 1 month ago

@hkalexling can you get me the asm around that hang in c3_dynamic_register?

wilsonk commented 1 month ago

Yes, it hangs in the same place

lerno commented 1 month ago

Are you able to dig deeper and perhaps see why it hangs by stepping through it?

wilsonk commented 1 month ago

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)
lerno commented 1 month ago

@hkalexling said it was in c3_dynamic_register, but I don't see that in your trace @wilsonk?

wilsonk commented 1 month ago

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?

lerno commented 1 month ago

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

hkalexling commented 1 month ago

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]
wilsonk commented 1 month ago

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>
hkalexling commented 1 month ago

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.

https://github.com/c3lang/c3c/blob/9f6a4eb300ce9f112139e1be1398b5b5532a3a8c/src/compiler/llvm_codegen_function.c#L613-L616

hkalexling commented 1 month ago

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

lerno commented 1 month ago

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.

lerno commented 1 month ago

This should be fixed in dev now, coming soon in this pull request: https://github.com/c3lang/c3c/pull/1555

hkalexling commented 1 month ago

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.