2016-07-18 4 views
1

표준 라이브러리에 대해 gcc과 오브젝트 파일을 연결하지 않으면 응용 프로그램이 좀비가된다는 것을 발견했을 때 어셈블리 코드와 GTK + 3 라이브러리를 실험하고있었습니다. 여기에 표준 라이브러리stdlib없이 어셈블리 코드를 링크 할 때 왜 좀비가 생깁니 까?

%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을 만들에 링크하기위한 것 같은 코드가 여기에있는 stdlib - 무료 응용 프로그램

%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 

내 코드와. 그러나 두 창은 창을 닫을 때 다르게 동작합니다. 전자는 좀비 프로세스로 연결되며 Ctrl+C을 눌러야합니다. 후자는 예상되는 동작을 나타내며, 즉, 윈도우가 닫히 자마자 애플리케이션이 종료된다.

표준 lib가 첫 번째 코드 샘플에서 간과하고있는 몇 가지 필수 작업을 수행하고 있지만 제 생각에는 그 것이 무엇인지 알 수 없습니다.

제 질문은 : 첫 번째 코드 샘플에 무엇이 누락 되었습니까?

+1

: 빌드

;%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 

: 그것을 자신을 시도하려는 사람들을 위해,이 변화는 gtk.inc A에 대한 찾고 이동하려면 당신이 필요없이 구축 할 수 있습니다 'mov rax, 60; SYS_EXIT xor rdi, rdi syscall' 정상적인 종료 절차를 단락시킵니다. 당신이 조립하는 법/링크를 보여주지 않았기 때문에 이것은 최소한의 입증 가능한 예제는 아닙니다 (사용하는 헤더는 질문의 일부가 아닙니다). 말하기 어렵습니다. 한 가지 가능성은 _C_ 라이브러리'exit'가 호출되어야한다는 것입니다. –

+0

sys_exit을 호출하지 않아도 나는 여전히 좀비로 끝납니다. 포함 된 % 파일은 생성하기 쉽지만 외부 기호에 대한 extern 선언 만 포함합니다. 나는 "nasm -f elf64 ..."와 "ld -I/path/to/interpreter'pkg-config --libs gtk + -3.0' ..."을 사용했다. 나는 다른 것을 호출 할 필요가 있다고 생각하는데 아마도 C의 종료에 의해 수행 된 일종의 프로세스 정리 일 것이다. 그러나 나는이 정리가 무엇인지 잘 모릅니다. 나는 -1과 함께 sys_wait4 호출을 pid로 포함 시키려고했지만, 심지어는 여전히 좀비를 얻는다. – Phoenix87

+0

'mov rax, 60;을 제거하면, SYS_EXIT xor rdi, rdi syscall'은 프로그램이 임의의 메모리를 걸러 낼 가능성이 있기 때문에 작동하지 않을 것입니다. 내가 언급 한 _C_ 라이브러리'exit' 함수는 보통 (GTK가 제대로 종료 될 수 있도록) 프로그램을 따로 설정할 필요가있는 클린업을 수행합니다 (리콜 할 때 일부 스레드 관련 정리를 수행합니다). 'gtk.inc'와 다른 inc는 생성하기가 쉽지 않지만 누군가가 이것을 심각하게 받아들이 길 원한다면 (또는 여러분의 코드를 시도해보십시오) 여러분은 그것을 제공하기를 원할 수 있습니다. 그들 없이는 이것은 최소한의 완전한 검증 가능한 예가 아닙니다. –

답변

3

감사 @MichaelPetch 완벽하게 모든 관찰 된 증상 설명이 아이디어 : gtk_main 그것을 반환 할 때 실행되는 스레드를 떠나면

, 당신의 두 프로그램 사이의 가장 중요한 차이점은 eax=60/syscall은 현재 스레드를 종료한다는 것입니다 . _exit(2) man page에있는 glibc의 _exit() 래퍼 함수가 exit_group을 사용했다고 지적한 설명서를 참조하십시오.

exit_group(2)은 x86-64 ABI에서 eax=231/syscall입니다. 이것은 main()이 반환 할 때 CRT 시작/정리 코드가 실행되는 것입니다.

두 버전 모두에서 strace ./a.out을 사용하여 확인할 수 있습니다. 초기 스레드가 종료 된 프로세스를하지만, 다른 스레드가 여전히 실행, 좀비로 표시됩니다 :


적어도 나를 놀라게했다. 내 자신의 바탕 화면에 시도 했으므로 ( gtk.inc이 필요하지 않으므로 빌드 명령과 extern 선언에 대한이 대답의 끝 부분을 참조하십시오) 실제로는 zombie으로보고 된 프로세스를 얻지 만 Ctrl-c를 사용하여 gtk_main이 돌아 오면 gtk가 실행중인 다른 스레드를 강제 종료하십시오.

./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 개의 좀비를 실행중인 두 개의 스레드가 있습니다.

$ 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이에 가장 적합한 명령입니다 있는지 확실하지 않습니다, 그러나 작동하는 것 같다. 창을 닫은 후에 주 스레드 만 종료되었고 나머지 두 개의 스레드가 여전히 실행 중임을 나타냅니다. ps를 사용하는 대신 /proc/$(pidof thread-exit)/task을 직접 볼 수도 있습니다.


재 :

(대신 _main_start을 정의하여) glibc는의 CRT 시작/정리를 방지 libc의 피와 같은 것이 아니다 :하지 libc의 링크를 원하는에 대한 의견. 코드가 libc 함수를 직접 호출하지는 않지만 libgtk은 직접 호출합니다. ldd /usr/lib/x86_64-linux-gnu/libgtk-3.so.0은 libgtk가 libc에 의존한다는 것을 보여 주므로 동적 링커는 libc를 프로세스에 매핑합니다. 사실, 직접 프로그램에있는 ldd은 링커 명령 행에 -lc을 직접 입력하지 않더라도 그렇게 말합니다.

libc를 연결하면 _start에서 exit(3)으로 전화 할 수 있습니다.

this Q&A for info on building static vs. dynamic binaries that link libc or not and define _start or main, with NASM or gas을 참조하십시오.


사이드 노트 : rbp와 스택 프레임을 만들 필요가 없습니다 main을 정의하는 버전.

당신이 push rbp/mov rbp, rsp을두면

, 당신은 여전히 ​​ call 전에 스택을 정렬하기 위해 뭔가를해야하지만, 당신이 혼란 할 경우는 push rax, 또는 정지 push rbp 될 수 있습니다. 그래서 :

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.


추신 :

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

답변 해 주셔서 감사합니다. exit_group은 프로세스 문제를 해결합니다. 좀비에 관해서는, 이것은 창을 닫은 후에 시스템 모니터가보고 한 것입니다. 자녀의 귀환 가치에 귀 기울이면서 기다리지 않고 부모 과정을 끝내기 시작한 이래로 나에게 의미가있었습니다. 어쨌든이 경우 libc를 선호하는 이유가 무엇인지 알 수 있습니다. 문제는 첫 번째 코드에서 내가 어디로 잘못 가고 있는지를 이해하고 배우려는 정신에 있었고,이 경우 libc 내부 역학에 대한 더 깊은 통찰력을 얻으려고했다. – Phoenix87

+0

@ Phoenix87 : 좋은 질문입니다, 저는 방금 용어에 대해 짜증났습니다. 하지만 프로세스 뷰어 도구가 좀비로보고했다고 말하면 직접 시도해야 할 수도 있습니다. 방금 실행중인 스레드가 있으면 좀비로 표시되지 않을 것이라고 가정했지만, 초기 PID가 종료 된 경우에는 수행됩니다. (스레드는 PID와 동일한 번호 매기기 공간을 사용합니다. 즉, 일종의 고유 한 PID가 있지만 getpid()는 초기 스레드의 PID를 반환합니다. 따라서 스레드의 "PID"는 실제로 스레드 ID입니다./proc/*를 참조하십시오./task. –

+0

또한 프로세스가 스스로 기다릴 필요가 없다는 것을 명심하십시오. 쉘은 모든 자식을 기다립니다. 그래서 쉘에서 직접 실행하는 프로세스는 종료 될 때마다 즉시 수확됩니다. 좀비 프로세스가 필요하다 .bash에서 실행할 때 정상적인 단일 쓰레드 프로그램에서는 일어나지 않는다. –

관련 문제