在汇编代码中调用 gtk_main_quit 导致 "segmentation fault"

Call to gtk_main_quit causes "segmentation fault" in assembler code

我正在学习汇编程序 (FASM),我遇到了一个奇怪的问题,每当我想调用 gtk_main_quit() 它总是以 "segmentation fault".

结尾

为什么调用gtk_main_quit会导致段错误?

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

生成文件

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

gtk_main_quit 的调用导致 gtk_main 到 return,在调用执行 window 句柄之后继续执行,可能还有文本 "destroy"。很难准确预测会发生什么。

call gtk_main之后添加ret

进行函数调用时,始终确保在调用后正确恢复堆栈。您的代码执行此操作:

push [window_handle]
call gtk_widget_show
add esp, 8

你将一个 DWORD 作为正确的参数压入堆栈,但在你调用 gtk_widget_show 之后你将 8 添加到 ESP。由于您只将 4 个字节压入堆栈,因此无法正确恢复 ESP。副作用是函数 main 的 return 地址现在将位于错误的位置,这可能会在您的 main 函数 return 时产生分段错误。代码应该是:

push [window_handle]
call gtk_widget_show
add esp, 4

那就引出了第二个问题。您的代码:

    call gtk_main

window_handle dd 0
on_close_signal db 'destroy', 0

gtk_main returns 之后它将开始执行之后出现在内存中的任何指令。在这种情况下,它恰好是一些变量和内存中的任何其他内容。由于 C 运行时调用你的函数 main 就像任何其他函数一样,你应该使用 ret 到 return 回到 C 运行时并让它干净地关闭你的程序。

代码如下所示:

    call gtk_main
    ret

window_handle dd 0
on_close_signal db 'destroy', 0