在没有stdlib的情况下链接汇编代码时,为什么会出现僵尸? [英] Why do I get a zombie when I link assembly code without stdlib?

查看:83
本文介绍了在没有stdlib的情况下链接汇编代码时,为什么会出现僵尸?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我发现如果不将对象文件与gcc链接到标准库时,发现我的应用程序变成了僵尸,我正在尝试使用汇编代码和GTK + 3库.这是我的stdlib -free应用程序的代码

I was experimenting with assembly code and the GTK+ 3 libraries when I discovered that my application turns into a zombie if I don't link the object file with gcc against the standard library. Here is my code for the stdlib-free application

%include "gtk.inc"
%include "glib.inc"

global _start

SECTION .data    
destroy         db "destroy", 0     ; const gchar*
strWindow       db "Window", 0              ; const gchar*

SECTION .bss    
window         resq 1 ; GtkWindow *

SECTION .text    
_start:
    ; gtk_init (&argc, &argv);
    xor     rdi, rdi
    xor     rsi, rsi
    call    gtk_init

    ; window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    xor     rdi, rdi
    call    gtk_window_new
    mov     [window], rax

    ; gtk_window_set_title (GTK_WINDOW (window), "Window");
    mov     rdi, rax
    mov     rsi, strWindow
    call    gtk_window_set_title

    ; g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
    mov     rdi, [window]
    mov     rsi, destroy
    mov     rdx, gtk_main_quit
    xor     rcx, rcx
    xor     r8, r8
    xor     r9, r9
    call    g_signal_connect_data

    ; gtk_widget_show (window);
    mov     rdi, [window]
    call    gtk_widget_show

    ; gtk_main ();
    call    gtk_main

    mov     rax, 60 ; SYS_EXIT
    xor     rdi, rdi
    syscall

这是要与标准库链接的相同代码

And here is the same code meant to be linked against the standard library

%include "gtk.inc"
%include "glib.inc"

global main

SECTION .data    
destroy         db "destroy", 0     ; const gchar*
strWindow       db "Window", 0              ; const gchar*

SECTION .bss
window         resq 1 ; GtkWindow *

SECTION .text    
main:
    push    rbp
    mov     rbp, rsp

    ; gtk_init (&argc, &argv);
    xor     rdi, rdi
    xor     rsi, rsi
    call    gtk_init

    ; window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    xor     rdi, rdi
    call    gtk_window_new
    mov     [window], rax

    ; gtk_window_set_title (GTK_WINDOW (window), "Window");
    mov     rdi, rax
    mov     rsi, strWindow
    call    gtk_window_set_title

    ; g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL);
    mov     rdi, [window]
    mov     rsi, destroy
    mov     rdx, gtk_main_quit
    xor     rcx, rcx
    xor     r8, r8
    xor     r9, r9
    call    g_signal_connect_data

    ; gtk_widget_show (window);
    mov     rdi, [window]
    call    gtk_widget_show

    ; gtk_main ();
    call    gtk_main

    pop     rbp
    ret

两个应用程序都创建一个GtkWindow.但是,当关闭窗口时,两者的行为有所不同.前者导致僵尸进程,我需要按Ctrl+C键.后者表现出预期的行为,即,应用程序在关闭窗口后立即终止.

Both applications create a GtkWindow. However, the two behave differently when the window is closed. The former leads to a zombie process and I need to press Ctrl+C. The latter exhibits the expected behaviour, i.e. the application terminates as soon as the window is closed.

我的感觉是标准库正在执行一些我在第一个代码示例中忽略的基本操作,但是我不能知道它是什么.

My feeling is that the standard lib is performing some essential operations that I am neglecting in the first code sample, but I can't tell what it is.

所以我的问题是:第一个代码示例中缺少什么?

So my question is: what's missing in the first code sample?

推荐答案

感谢@MichaelPetch这个想法,它完美地解释了所有观察到的症状:

Thanks @MichaelPetch for this idea which explains all the observed symptoms perfectly:

如果gtk_main返回时让任何线程运行,则两个程序之间最重要的区别是eax=60/syscall仅退出当前线程.请参见 _exit(2)手册页中的文档.指出自glibc2.3起,glibc的_exit()包装函数已使用exit_group.

If gtk_main leaves any threads running when it returns, the most important difference between your two programs is that eax=60/syscall only exits the current thread. See the documentation in the _exit(2) man page, which points out that glibc's _exit() wrapper function has used exit_group since glibc2.3.

exit_group(2) eax=231/syscall在x86-64 ABI中.这是main()返回时CRT启动/清除代码的运行方式.

exit_group(2) is eax=231 / syscall in the x86-64 ABI. This is what the CRT startup/cleanup code runs when main() returns.

您可以通过在两个版本上使用strace ./a.out来看到这一点.

You can see this by using strace ./a.out on both versions.

这至少让我感到惊讶:初始线程已退出,但其他线程仍在运行的进程显示为僵尸.我在自己的台式机上进行了尝试(有关构建命令和extern声明,请参见此答案的末尾,因此您不需要gtk.inc),并且您确实得到了报告为

This surprised me at least: A process where the initial thread has exited, but other threads are still running, is shown as a zombie. I tried it on my own desktop (see the end of this answer for build commands and extern declarations so you don't need gtk.inc), and you really do get a process that's reported as a zombie, but that you can ctrl-c to kill the other threads that gtk leaves running when gtk_main returns.

./thread-exit &   # or in the foreground, and do the following commands in another shell
[1] 20592

$ ps m -LF -p $(pidof thread-exit)
UID        PID  PPID   LWP  C NLWP    SZ   RSS PSR STIME TTY      STAT   TIME CMD
peter    20592  7749     -  0    3 109031 21920  - 06:28 pts/12   -      0:00 ./thread-exit
peter        -     - 20592  0    -     -     -   0 06:28 -        Sl     0:00 -
peter        -     - 20593  0    -     -     -   0 06:28 -        Sl     0:00 -
peter        -     - 20594  0    -     -     -   0 06:28 -        Sl     0:00 -

然后关闭窗口:该进程没有退出,仍然有两个正在运行的线程+ 1个僵尸.

Then close the window: the process doesn't exit, and still has two threads running + 1 zombie.

$ ps m -LF -p $(pidof thread-exit)
UID        PID  PPID   LWP  C NLWP    SZ   RSS PSR STIME TTY      STAT   TIME CMD
peter    20592  7749     -  0    3     0     0   - 06:28 pts/12   -      0:00 [thread-exit] <defunct>
peter        -     - 20592  0    -     -     -   0 06:28 -        Zl     0:00 -
peter        -     - 20593  0    -     -     -   0 06:28 -        Sl     0:00 -
peter        -     - 20594  0    -     -     -   0 06:28 -        Sl     0:00 -

我不确定ps m -LF是否是执行此操作的最佳命令,但它似乎有效.它表示关闭窗口后,只有主线程退出了,而其他2个线程仍在运行.您甚至可以直接查看/proc/$(pidof thread-exit)/task,而无需使用ps来完成.

I'm not sure if ps m -LF is the best command for this, but it seems to work. It indicates that only the main thread has exited after you close the window, and 2 other threads are still running. You can even look at /proc/$(pidof thread-exit)/task directly, instead of using ps to do that for you.

re:关于不想链接libc的评论:

re: comments about not wanting to link libc:

避免glibc的CRT启动/清理(通过定义_start而不是_main)与避免libc是不同的.您的代码不会直接调用任何libc函数,而libgtk会直接调用. ldd /usr/lib/x86_64-linux-gnu/libgtk-3.so.0显示libgtk依赖libc,因此动态链接程序无论如何都会将libc映射到您的进程中.实际上,即使您没有直接将-lc放在链接程序命令行上,您自己的程序上的ldd也会这样说.

Avoiding the glibc's CRT startup / cleanup (by defining _start instead of _main) isn't the same thing as avoiding libc. Your code doesn't call any libc functions directly, but libgtk does. ldd /usr/lib/x86_64-linux-gnu/libgtk-3.so.0 shows that libgtk depends on libc, so the dynamic linker will map libc into your process anyway. In fact, ldd on your own program says that, even if you don't put -lc on the linker command line directly.

因此,您只需链接libc并调用 exit(3) 来自您的_start.

So you could just link libc and call exit(3) from your _start.

请参阅此问题与解答;有关构建静态或动态二进制文件的信息,该二进制文件是否链接libc并使用NASM或gas定义_start或main .

旁注:定义main的版本不需要使用rbp构成堆栈框架.

Side-note: the version that defines main doesn't need to make a stack frame with rbp.

如果省略了push rbp/mov rbp, rsp,您仍然需要做一些事情来对齐call之前的堆栈,但是也可以是push rax,或者如果想要的话,仍可以是push rbp.令人困惑.所以:

If you leave out the push rbp / mov rbp, rsp, you still have to do something to align the stack before the call, but it can be push rax, or still push rbp if you want to be confusing. So:

main:
    push    rax              ; align the stack
    ...
    call    gtk_widget_show

    pop     rax              ; restore stack to function-entry state
    jmp     gtk_main         ; optimized tail-call

如果要保留帧指针内容,仍然可以进行尾部调用,但是pop rbp/jmp gtk_main.

If you want to keep the frame-pointer stuff, you could still do the tail call, but pop rbp / jmp gtk_main.

PS:对于那些想自己尝试的人,此更改使您可以构建它而不必寻找gtk.inc:

PS: for those who want to try it themselves, this change lets you build it without having to go looking for for a gtk.inc:

;%include "gtk.inc"
;%include "glib.inc"

extern gtk_init
extern gtk_window_new
extern g_signal_connect_data
extern gtk_window_set_title
extern gtk_widget_show
extern gtk_main
extern gtk_main_quit

构建方式:

yasm -felf64 -Worphan-labels -gdwarf2 thread-exit.asm &&
gcc -nostdlib -o thread-exit thread-exit.o $(pkg-config --libs gtk+-3.0)

这篇关于在没有stdlib的情况下链接汇编代码时,为什么会出现僵尸?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆