Search code examples
assemblyx86segmentation-faultgtkgtk3

Call to gtk_main_quit causes "segmentation fault" in assembler code


I'm learning assembler (FASM) and I have weird problem, whenever i want to call gtk_main_quit() it always ends with "segmentation fault".

Why call gtk_main_quit causes segmentation fault?

test.asm

format ELF

extrn gtk_init
extrn gtk_main
extrn gtk_main_quit
extrn gtk_window_new
extrn gtk_widget_show
extrn g_signal_connect_data

public main

on_window_close:
    call gtk_main_quit ; <- segmentation fault
    ret

main:
    push 0
    push 0
    call gtk_init
    add esp, 8

    push 0
    call gtk_window_new
    add esp, 4
    mov [window_handle], eax

    push 0
    push 0
    push 0
    push on_window_close
    push on_close_signal
    push [window_handle]
    call g_signal_connect_data
    add esp, 24

    push [window_handle]
    call gtk_widget_show
    add esp, 8

    call gtk_main

window_handle dd 0
on_close_signal db 'destroy', 0

makefile

all:
    ~/apps/fasm/fasm ./test.asm
    gcc -o test test.o `pkg-config --cflags --libs gtk+-3.0`

Solution

  • When making function calls always ensure that you restore the stack properly after a call. Your code does this:

    push [window_handle]
    call gtk_widget_show
    add esp, 8
    

    You push one DWORD onto the stack as a parameter which is correct, but after your call to gtk_widget_show you add 8 to ESP. Since you only pushed 4 bytes on the stack, this restores ESP improperly. A side effect will be that the return address of the function main will now be in the wrong place which would likely yield a segmentation fault when your main function returns. The code should have been:

    push [window_handle]
    call gtk_widget_show
    add esp, 4
    

    That brings up the second issue. Your code:

        call gtk_main
    
    window_handle dd 0
    on_close_signal db 'destroy', 0
    

    After gtk_main returns it will start executing whatever instructions appear in memory after. In this case it happens to be some variables and whatever else is in memory. Since the C runtime called your function main like any other function, you should use ret to return back to the C runtime and let it shutdown your program cleanly.

    The code would look like:

        call gtk_main
        ret
    
    window_handle dd 0
    on_close_signal db 'destroy', 0