Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GC_init segfaults if called from asm code on Linux/x86_64 #654

Open
gyps opened this issue Aug 13, 2024 · 2 comments
Open

GC_init segfaults if called from asm code on Linux/x86_64 #654

gyps opened this issue Aug 13, 2024 · 2 comments

Comments

@gyps
Copy link

gyps commented Aug 13, 2024

Dear all,
Since I'm currently writing a compiler for a functional language I thought about using bdwgc as a garbage collector.
Since the compiler generates code for the x86_64 plattform (using nasm as assembler) I wanted to use the GC in assembler code directly, avoiding parts written in C/C++. So far it worked well but today I dicovered a strange phenomenon: the following code works fine:

extern  GC_malloc, GC_init, exit

section .text
	global main

main:
	push rbp
	call GC_init
	pop rbp
	mov edi, 0 
	call exit  

while

extern  GC_malloc, GC_init, exit

section .text
	global main

main:
	push rbp
	push rbp
	call GC_init
	pop rbp
	pop rbp
	mov edi, 0 
	call exit  

segfaults. How can pushing and popping rbp to/from the stack twice can influence the behavior of GC_init? Or am I calling it completely wrong?

The commands used to assemble the program are:

$ nasm -o test_gc.o -f elf64 test_gc.asm
$ gcc -o test_gc test_gc.o -lgc
$ ./test_gc
@ivmai ivmai changed the title Usage of GC in assmbler code Aug 13, 2024
@ivmai
Copy link
Owner

ivmai commented Aug 13, 2024

Sorry, I cannot compile/test your sample at this moment. Would be nice if you dig into the crash details and provide more information - e.g. does it segfaults during initial collection or when detecting stack bottom? (I am assuming you are using fresh bdwgc master.)

@gyps
Copy link
Author

gyps commented Aug 14, 2024

I have a guix-Linux distribution with libgc version 8.2.2 installed.
I just checked out the current code, compiled it via autogen/configure and rebuild my test using gcc -L./bdwgc/.libs -o test2_gc test2_gc.o -lgc. This should have used the newly build library but the effect is still the same. I debuged this version using gdb and the error appears in the function GC_get_main_stack_base() where something goes wrong in if (pthread_getattr_np(pthread_self(), &attr) == 0). I attached the (Sorry) very long trace of a gdb session until it actually breaks.

(gdb) start
Temporary breakpoint 1 at 0x401140
Starting program: /home/aster/src/test2_gc 
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/gnu/store/ln6hxqjvz6m9gdd9s97pivlqck7hzs99-glibc-2.35/lib/libthread_db.so.1".

Temporary breakpoint 1, 0x0000000000401140 in main ()
(gdb) step
Single stepping until exit from function main,
which has no line number information.
GC_init () at extra/../misc.c:995
995         if (EXPECT(GC_is_initialized, TRUE)) return;
(gdb) 
989     GC_API void GC_CALL GC_init(void)
(gdb) 
GC_init () at extra/../misc.c:1011
1011        DISABLE_CANCEL(cancel_state);
(gdb) 
1072        GC_setpagesize();
(gdb) 
GC_setpagesize () at extra/../os_dep.c:814
814           GC_page_size = (size_t)GETPAGESIZE();
(gdb) 
816             if (0 == GC_page_size)
(gdb) 
827             if (((GC_page_size-1) & GC_page_size) != 0)
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
831             log_pgsize++;
(gdb) 
830           for (pgsize = GC_page_size; pgsize > 1; pgsize >>= 1)
(gdb) 
832           GC_log_pagesize = log_pgsize;
(gdb) 
GC_init () at extra/../misc.c:1085
1085            if (0 != GETENV("GC_PRINT_VERBOSE_STATS")) {
(gdb) 
1087            } else if (0 != GETENV("GC_PRINT_STATS")) {
(gdb) 
1096              const char *fname = TRUSTED_STRING(GETENV("GC_LOG_FILE"));
(gdb) 
1101                if (0 != fname)
(gdb) 
1134          if (0 != GETENV("GC_DUMP_REGULARLY")) {
(gdb) 
1148        if (0 != GETENV("GC_FIND_LEAK")) {
(gdb) 
1152          if (0 != GETENV("GC_FINDLEAK_DELAY_FREE")) {
(gdb) 
1156        if (0 != GETENV("GC_ALL_INTERIOR_POINTERS")) {
(gdb) 
1159        if (0 != GETENV("GC_DONT_GC")) {
(gdb) 
1167        if (0 != GETENV("GC_PRINT_BACK_HEIGHT")) {
(gdb) 
1170        if (0 != GETENV("GC_NO_BLACKLIST_WARNING")) {
(gdb) 
1174          const char *str = GETENV("GC_TRACE");
(gdb) 
1176          if (str != NULL) {
(gdb) 
1202            const char *str = GETENV("GC_PAUSE_TIME_TARGET");
(gdb) 
1204            if (str != NULL) {
(gdb) 
1215            const char *str = GETENV("GC_FULL_FREQUENCY");
(gdb) 
1217            if (str != NULL) {
(gdb) 
1226          char const *str = GETENV("GC_LARGE_ALLOC_WARN_INTERVAL");
(gdb) 
1228          if (str != NULL) {
(gdb) 
1240            const char *str = GETENV("GC_FREE_SPACE_DIVISOR");
(gdb) 
1242            if (str != NULL) {
(gdb) 
1251            const char *str = GETENV("GC_UNMAP_THRESHOLD");
(gdb) 
1253            if (str != NULL) {
(gdb) 
1266            const char *str = GETENV("GC_FORCE_UNMAP_ON_GCOLLECT");
(gdb) 
1268            if (str != NULL) {
(gdb) 
1278            const char *str = GETENV("GC_USE_ENTIRE_HEAP");
(gdb) 
1280            if (str != NULL) {
(gdb) 
1291          GET_TIME(GC_init_time);
(gdb) 
1293        maybe_install_looping_handler();
(gdb) 
maybe_install_looping_handler () at extra/../misc.c:822
822         if (!installed_looping_handler && 0 != GETENV("GC_LOOP_ON_ABORT")) {
(gdb) 
824           installed_looping_handler = TRUE;
(gdb) 
GC_init () at extra/../misc.c:1296
1296          if (EXTRA_BYTES != 0)
(gdb) 
1297            GC_obj_kinds[NORMAL].ok_descriptor
(gdb) 
1300        GC_exclude_static_roots_inner(beginGC_arrays, endGC_arrays);
(gdb) 
GC_exclude_static_roots_inner (start=start@entry=0x7ffff7f80aa0 <GC_arrays> "", finish=finish@entry=0x7ffff7fad4b0 <GC_no_dls> "")
    at extra/../mark_rts.c:609
609         next = GC_next_exclusion(start);
(gdb) 
GC_next_exclusion (start_addr=start_addr@entry=0x7ffff7f80aa0 <GC_arrays> "") at extra/../mark_rts.c:582
582         if (EXPECT(0 == GC_excl_table_entries, FALSE)) return NULL;
(gdb) 
GC_exclude_static_roots_inner (start=start@entry=0x7ffff7f80aa0 <GC_arrays> "", finish=finish@entry=0x7ffff7fad4b0 <GC_no_dls> "")
    at extra/../mark_rts.c:610
610         if (next != NULL) {
(gdb) 
626         next_index = GC_excl_table_entries;
(gdb) 
627         if (next_index >= MAX_EXCLUSIONS) ABORT("Too many exclusions");
(gdb) 
640         GC_excl_table[next_index].e_start = start;
(gdb) 
641         GC_excl_table[next_index].e_end = finish;
(gdb) 
642         ++GC_excl_table_entries;
(gdb) 
GC_init () at extra/../misc.c:1301
1301        GC_exclude_static_roots_inner(beginGC_obj_kinds, endGC_obj_kinds);
(gdb) 
GC_exclude_static_roots_inner (start=start@entry=0x7ffff7f805c0 <GC_obj_kinds> "\200\024\370\367\377\177", finish=finish@entry=0x7ffff7f80a40 "")
    at extra/../mark_rts.c:609
609         next = GC_next_exclusion(start);
(gdb) 
GC_next_exclusion (start_addr=start_addr@entry=0x7ffff7f805c0 <GC_obj_kinds> "\200\024\370\367\377\177") at extra/../mark_rts.c:582
582         if (EXPECT(0 == GC_excl_table_entries, FALSE)) return NULL;
(gdb) 
583         high = GC_excl_table_entries - 1;
(gdb) 
584         while (high > low) {
(gdb) 
594         if (ADDR_GE(start_addr, GC_excl_table[low].e_end)) return NULL;
(gdb) 
596         return GC_excl_table + low;
(gdb) 
GC_exclude_static_roots_inner (start=start@entry=0x7ffff7f805c0 <GC_obj_kinds> "\200\024\370\367\377\177", finish=finish@entry=0x7ffff7f80a40 "")
    at extra/../mark_rts.c:610
610         if (next != NULL) {
(gdb) 
611           if (ADDR_LT(next -> e_start, finish)) {
(gdb) 
615           if (ADDR(next -> e_start) == ADDR(finish)) {
(gdb) 
626         next_index = GC_excl_table_entries;
(gdb) 
627         if (next_index >= MAX_EXCLUSIONS) ABORT("Too many exclusions");
(gdb) 
631           next_index = (size_t)(next - GC_excl_table);
(gdb) 
632           for (i = GC_excl_table_entries; i > next_index; --i) {
(gdb) 
640         GC_excl_table[next_index].e_start = start;
(gdb) 
641         GC_excl_table[next_index].e_end = finish;
(gdb) 
642         ++GC_excl_table_entries;
(gdb) 
GC_init () at extra/../misc.c:1315
1315          if (GC_stackbottom == 0) {
(gdb) 
1316            GC_stackbottom = GC_get_main_stack_base();
(gdb) 
GC_get_main_stack_base () at extra/../os_dep.c:1310
1310            if (pthread_getattr_np(pthread_self(), &attr) == 0)
(gdb) 

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7d939b6 in __vfscanf_internal () from /gnu/store/ln6hxqjvz6m9gdd9s97pivlqck7hzs99-glibc-2.35/lib/libc.so.6
(gdb) 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
2 participants