处理 SIGCHLD NASM
Handling SIGCHLD NASM
编辑 请参阅下面我自己的回答
我一直在尝试在 NASM
中复制这个 C 程序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
static void handle(int sig) {
int status;
wait(&status);
}
int main(int argc, char* argv[]) {
struct sigaction act;
bzero(&act, sizeof(act));
act.sa_handler = &handle;
sigaction(SIGCLD, &act, NULL);
pid_t pid;
if ( (pid = fork()) == 0) {
printf("message from child\n");
exit(0);
}
printf("message from parent\n");
pause();
exit(0);
}
我的 NASM 代码如下所示:
USE64
STRUC sigact
.handler resq 1
.mask resq 16
.flag resd 1
.restorer resq 1
.pad resb 4
ENDSTRUC
section .text
global _start
_start:
; register SIGCHLD handler
mov rdi, act
mov rsi, sigact_size
call bzero
mov QWORD [act], handle
; still need to figure out what these mean
; yanked out of gdb right before the same syscall
; and the act struct had these set :\
mov QWORD [act+8], 0x4000000
mov DWORD [act+16], 0xf7a434a0
mov DWORD [act+20], 0x7fff
mov rax, 13
mov rdi, 17
lea rsi, [act]
mov rdx, 0x0
mov r10, 0x8
syscall
cmp rax, 0
jne sigaction_fail
mov rax, 57
syscall
cmp rax, 0
je child
mov rax, parentmsg
call print
mov rax, 34
syscall
mov rax, parentexit
call print
mov rax, 60
mov rdi, 0
syscall
sigaction_fail:
enter 0, 0
mov rax, safailed
call print
mov rax, 60
mov rdi, -1
syscall
handle:
enter 0x10, 0
push rax
push rsi
push rdi
push rdx
push r10
lea rsi, [rbp-0x10]
mov rax, 61
mov rdi, -1
xor rdx, rdx
xor r10, r10
syscall
cmp rax, -1
jne .handle_success
mov rax, hdfailed
call print
mov rax, 60
mov rdi, -1
syscall
.handle_success:
mov rax, hdsuccess
call print
pop r10
pop rdx
pop rdi
pop rsi
pop rax
leave
ret
child:
mov rax, childmsg
call print
mov rax, 60
mov rdi, 0
syscall
; print a null terminated string stored in rax
print:
enter 0, 0
push rbx
push rdx
push rdi
push rsi
mov rbx, rax
call strlen
mov rdx, rax
mov rax, 1
mov rdi, 1 ; stdout
mov rsi, rbx
syscall
pop rsi
pop rdi
pop rdx
pop rbx
leave
ret
bzero:
; rdi pointer to uint8_t
; uint32_t rsi length
enter 0, 0
mov rcx, rsi
dec rcx ; err..
.bzeroloop:
lea rax, [rdi + rcx]
xor rax, rax
cmp rcx, 0
je .bzerodone
dec rcx
jmp .bzeroloop
.bzerodone:
leave
ret
strlen:
enter 0, 0
push rbx
mov rbx, rax
.strlen_countchar:
cmp BYTE [rax], 0 ; compare it to null byte
jz .strlen_exit
inc rax
jmp .strlen_countchar
.strlen_exit:
sub rax, rbx
pop rbx
leave
ret
section .data
childmsg: db "from child", 0xa, 0 ; null terminated
parentmsg db "from parent", 0xa, 0
handlemsg db "in handle", 0xa, 0
safailed db "failed to set signal handler", 0xa, 0
hdfailed db "failed waiting for child", 0xa, 0
hdsuccess db "successfully waited on child", 0xa, 0
parentexit db "parent exiting", 0xa, 0
section .bss
act: resb sigact_size
status: resq 1
它在发送信号时成功等待子进程,但立即在 return 上出现段错误。我已经尝试阅读越来越多的关于信号和信号处理的文章,但在这一点上,所有 运行 都在我的脑海中变得一团糟。抱歉 NASM 代码丑陋或不标准。我不仅在学习,而且我可能每个部分至少重写了 25 次(handle
可能超过 100 次)。
信号处理程序是普通函数。 Return 与 ret
,而不是 iret
。 (你已经编辑了你的问题来解决这个问题,所以我猜你还有其他问题)。
看看gcc如何编译handler()
,在Godbolt compiler explorer.
static void handle(int sig) {
int status;
wait(&status);
}
sub rsp, 24
xor eax, eax # you forgot to include sys/wait.h, so the compiler has no prototype for wait(), so has to follow the ABI for variadic functions (al = number of FP args in xmm regs)
lea rdi, [rsp+12]
call wait
add rsp, 24
ret
将库调用变成 wait4(2)
的内联调用并不难。
请注意,手册页上说 wait4
已过时,新程序应使用 waitpid
or waitid
。但是,如果您不需要更多功能,wait4
就可以了。 glibc 在 wait4
Linux 系统调用之上实现 wait(3)
,而不是 waitid
。如果使用 wait4
有任何问题,glibc 将直接使用 waitid
。
handle:
mov eax, 61 ; __NR_wait4 from /usr/include/x86_64-linux-gnu/asm/unistd_64.h
mov edi, -1 ; pid_t is a 32bit type, so we don't need to sign-extend into rdi.
lea rsi, [rsp-4] ; status = a pointer into the red zone below rsp.
xor edx,edx ; options = 0
xor r10d,r10d ; rusage = NULL
syscall
; eax = pid waited for, or -1 to indicate error
; dword [rsp-4] = status. unlike function calls, syscalls don't clobber the stack
ret
要使用 wait4
中的 return 值,请执行以下操作:
cmp rax, -1 ;;;; THIS WAS A BUG: pid_t is a 32bit type; expect garbage or zeros in the upper 32 bits.
cmp eax, -1
je .error
...
如果要调试,请在handle
中设置断点。这比在 asm 中使用调试打印 容易得多。
如果在您 ret
时仍然崩溃,则可能 return 成功但实际上在您的主程序中崩溃了。使用调试器找出答案。
您的字符串常量应放在 .rodata
部分。你不需要在运行时修改它们,所以不要把它们放在 .data
.
您也不需要 call bzero
,因为您的 act
在 bss 中。如果你想在堆栈上分配它,而不是静态分配,你应该用 rep stosq
将它归零,就像 gcc 5.3 在你的 main()
中所做的那样。 (内联 bzero,as you can see on Godbolt)。
顺便说一句,问题中的 NASM 结构在错误的位置填充。可能对你未来的冒险值得注意,即使事实证明它不是这个问题的答案的一部分。 (您的代码在定义后甚至没有使用 NASM 结构语法。)
实际的struct sigaction
定义在/usr/include/x86_64-linux-gnu/bits/sigaction.h
,你可以在echo '#include <signal.h>' | gcc -E - | less
.
中搜索找到。
struct sigaction {
union {
__sighandler_t sa_handler; // your code uses this one, because it leaves SA_SIGINFO unset in sa_flags
void (*sa_sigaction) (int, siginfo_t *, void *);
}; // with some macros to sort this out
__sigset_t sa_mask; // 1024 bits = 128B
int sa_flags;
void (*sa_restorer) (void);
};
您的 NASM 结构在错误的位置填充:
STRUC sigact
.handler resq 1 ; 64bit pointer: correct
.mask resq 16 ; 16 qwords for sigset_t: correct, it's 128 bytes
.flag resd 1 ; 32bit flags: correct
;; The padding goes here, to align the 64bit member that follows
.pad resb 4
.restorer resq 1 ; 64bit
;; There's no padding here
ENDSTRUC
戳了好久终于搞明白了!问题是在 sigact 结构中正确设置恢复器。
当我检查 sigaction(2)
以获取结构定义时,它最终完全不是我想的那样。我明白了:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
但那是结构的 C 定义(不完全是,正如手册页提到的,前两个可能是联合,我就是这种情况)。
然而,经过更多的探索,我发现我需要构建的结构看起来更像这样:
struct asm_sigaction {
void (*sa_handler)(int);
[unsigned?] long sa_flags;
void (*sa restorer)(void);
sigset_t sa_mask;
};
我通过深入了解我的 C 代码的实际用途发现了这一点。我找到了我正在制作的相同系统调用的位置,并转储了它们为 sigaction 结构传递的字节:
(gdb) x/38wx $rsi
0x7fffffffddc0: 0x004007f5 0x00000000 0x14000000 0x00000000
0x7fffffffddd0: 0xf7a434a0 0x00007fff 0x00000000 0x00000000
0x7fffffffdde0: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffddf0: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde00: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde10: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde20: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde30: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde40: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffddd0
的部分对我来说像是一个地址,所以我检查了一下:
(gdb) disas 0x00007ffff7a434a0
Dump of assembler code for function __restore_rt:
0x00007ffff7a434a0 <+0>: mov rax,0xf
0x00007ffff7a434a7 <+7>: syscall
0x00007ffff7a434a9 <+9>: nop DWORD PTR [rax+0x0]
果然他们正在设置正在调用 sigreturn
(在我的例子中是 rt_sigreturn
)系统调用的恢复器!手册页说应用程序通常不会搞砸,但我猜这是针对典型的 C 程序的。所以我继续将这个函数复制到恢复器标签中,并将它放在我的结构中的适当位置,哇哦它起作用了。
这是现在工作的 NASM,我用一个新的 C 程序稍微改变了一些东西,我试图让它的外观和行为更像我的 NASM 程序,然后关闭 pause
对于 nanosleep
.
新的 C 程序:
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
const char *parentmsg = "from parent\n[=14=]";
const char *childmsg = "from child\n[=14=]";
const char *handlemsg = "in handle\n[=14=]";
const char *forkfailed = "fork failed\n[=14=]";
const char *parentexit = "parent exiting\n[=14=]";
const char *sleepfailed = "sleep failed\n[=14=]";
const char *sleepinterrupted = "sleep interrupted\n[=14=]";
void print(const char *msg) {
write(STDIN_FILENO, msg, strlen(msg));
}
static void handle(int sig) {
print(handlemsg);
waitid(P_ALL, -1, NULL, WEXITED|WSTOPPED|WCONTINUED);
}
int main(int argc, char* argv[]) {
struct timespec tsreq;
struct timespec tsrem;
tsreq.tv_sec = 2;
struct sigaction act;
act.sa_handler = &handle;
sigaction(SIGCHLD, &act, NULL);
pid_t pid;
if ( (pid = fork()) == 0 ) {
print(childmsg);
exit(0);
}
print(parentmsg);
if (nanosleep((const struct timespec*)&tsreq, &tsrem) == -1) {
if (errno == EINTR) {
print(sleepinterrupted);
nanosleep((const struct timespec*)&tsrem, NULL);
} else {
print(sleepfailed);
}
}
print(parentexit);
exit(0);
}
以及新的工作 NASM(在 Peter 的帮助下希望它看起来和功能更好)
USE64
STRUC sigact
.handler resq 1
.flag resq 1
.restorer resq 1
.mask resq 16
ENDSTRUC
STRUC timespec
.tv_sec resq 1
.tv.nsec resq 1
ENDSTRUC
section .text
global _start
_start:
; register SIGCHLD handler
mov DWORD [act+sigact.handler], handle
mov QWORD [act+sigact.restorer], restorer
mov DWORD [act+sigact.flag], 0x04000000
mov rax, 13
mov rdi, 17
lea rsi, [act]
xor rdx, rdx
mov r10, 0x8
syscall
cmp eax, 0
jne sigaction_fail
mov rax, 57
syscall
cmp eax, -1
je fork_failed
cmp eax, 0
je child
mov rax, parentmsg
call print
mov rax, 35
mov QWORD [tsreq+timespec.tv_sec], 2
lea rdi, [tsreq]
lea rsi, [tsrem]
syscall
cmp eax, -1
je .exit
mov rax, sleepagain
call print
mov rax, 35
mov rdi, tsrem
xor rsi, rsi
syscall
.exit:
mov rax, parentexit
call print
mov rax, 60
xor rdi, rdi
syscall
restorer:
mov rax, 15
syscall
fork_failed:
mov rax, forkfailed
call print
mov rax, 60
mov rdi, -1
syscall
sigaction_fail:
mov rax, safailed
call print
mov rax, 60
mov rdi, -1
syscall
handle:
mov rax, handlemsg
call print
lea rsi, [rsp-0x4]
mov rax, 247
xor rdi, rdi
xor rdx, rdx
mov r10, 14
syscall
cmp eax, -1
jne .success
mov rax, hdfailed
call print
mov rax, 60
mov rdi, -1
syscall
.success:
mov rax, hdsuccess
call print
ret
child:
mov rax, childmsg
call print
mov rax, 60
xor rdi, rdi
syscall
; print a null terminated string stored in rax
print:
push rbx
push rdx
push rdi
push rsi
mov rbx, rax
call strlen
mov rdx, rax
mov rax, 1
mov rdi, 1 ; stdout
mov rsi, rbx
syscall
pop rsi
pop rdi
pop rdx
pop rbx
ret
strlen:
push rbp
mov rbp, rsp
push rbx
mov rbx, rax
.countchar:
cmp BYTE [rax], 0 ; compare it to null byte
jz .exit
inc rax
jmp .countchar
.exit:
sub rax, rbx
pop rbx
mov rsp, rbp
pop rbp
ret
section .data
childmsg: db "from child", 0xa, 0 ; null terminated
parentmsg db "from parent", 0xa, 0
handlemsg db "in handle", 0xa, 0
safailed db "failed to set signal handler", 0xa, 0
hdfailed db "failed waiting for child", 0xa, 0
hdsuccess db "successfully waited on child", 0xa, 0
parentexit db "parent exiting", 0xa, 0
forkfailed db "fork failed", 0xa, 0
sleepagain db "sleeping again", 0xa, 0
section .bss
tsreq: resb timespec_size
tsrem: resb timespec_size
act: resb sigact_size
编辑 请参阅下面我自己的回答
我一直在尝试在 NASM
中复制这个 C 程序#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
static void handle(int sig) {
int status;
wait(&status);
}
int main(int argc, char* argv[]) {
struct sigaction act;
bzero(&act, sizeof(act));
act.sa_handler = &handle;
sigaction(SIGCLD, &act, NULL);
pid_t pid;
if ( (pid = fork()) == 0) {
printf("message from child\n");
exit(0);
}
printf("message from parent\n");
pause();
exit(0);
}
我的 NASM 代码如下所示:
USE64
STRUC sigact
.handler resq 1
.mask resq 16
.flag resd 1
.restorer resq 1
.pad resb 4
ENDSTRUC
section .text
global _start
_start:
; register SIGCHLD handler
mov rdi, act
mov rsi, sigact_size
call bzero
mov QWORD [act], handle
; still need to figure out what these mean
; yanked out of gdb right before the same syscall
; and the act struct had these set :\
mov QWORD [act+8], 0x4000000
mov DWORD [act+16], 0xf7a434a0
mov DWORD [act+20], 0x7fff
mov rax, 13
mov rdi, 17
lea rsi, [act]
mov rdx, 0x0
mov r10, 0x8
syscall
cmp rax, 0
jne sigaction_fail
mov rax, 57
syscall
cmp rax, 0
je child
mov rax, parentmsg
call print
mov rax, 34
syscall
mov rax, parentexit
call print
mov rax, 60
mov rdi, 0
syscall
sigaction_fail:
enter 0, 0
mov rax, safailed
call print
mov rax, 60
mov rdi, -1
syscall
handle:
enter 0x10, 0
push rax
push rsi
push rdi
push rdx
push r10
lea rsi, [rbp-0x10]
mov rax, 61
mov rdi, -1
xor rdx, rdx
xor r10, r10
syscall
cmp rax, -1
jne .handle_success
mov rax, hdfailed
call print
mov rax, 60
mov rdi, -1
syscall
.handle_success:
mov rax, hdsuccess
call print
pop r10
pop rdx
pop rdi
pop rsi
pop rax
leave
ret
child:
mov rax, childmsg
call print
mov rax, 60
mov rdi, 0
syscall
; print a null terminated string stored in rax
print:
enter 0, 0
push rbx
push rdx
push rdi
push rsi
mov rbx, rax
call strlen
mov rdx, rax
mov rax, 1
mov rdi, 1 ; stdout
mov rsi, rbx
syscall
pop rsi
pop rdi
pop rdx
pop rbx
leave
ret
bzero:
; rdi pointer to uint8_t
; uint32_t rsi length
enter 0, 0
mov rcx, rsi
dec rcx ; err..
.bzeroloop:
lea rax, [rdi + rcx]
xor rax, rax
cmp rcx, 0
je .bzerodone
dec rcx
jmp .bzeroloop
.bzerodone:
leave
ret
strlen:
enter 0, 0
push rbx
mov rbx, rax
.strlen_countchar:
cmp BYTE [rax], 0 ; compare it to null byte
jz .strlen_exit
inc rax
jmp .strlen_countchar
.strlen_exit:
sub rax, rbx
pop rbx
leave
ret
section .data
childmsg: db "from child", 0xa, 0 ; null terminated
parentmsg db "from parent", 0xa, 0
handlemsg db "in handle", 0xa, 0
safailed db "failed to set signal handler", 0xa, 0
hdfailed db "failed waiting for child", 0xa, 0
hdsuccess db "successfully waited on child", 0xa, 0
parentexit db "parent exiting", 0xa, 0
section .bss
act: resb sigact_size
status: resq 1
它在发送信号时成功等待子进程,但立即在 return 上出现段错误。我已经尝试阅读越来越多的关于信号和信号处理的文章,但在这一点上,所有 运行 都在我的脑海中变得一团糟。抱歉 NASM 代码丑陋或不标准。我不仅在学习,而且我可能每个部分至少重写了 25 次(handle
可能超过 100 次)。
信号处理程序是普通函数。 Return 与 ret
,而不是 iret
。 (你已经编辑了你的问题来解决这个问题,所以我猜你还有其他问题)。
看看gcc如何编译handler()
,在Godbolt compiler explorer.
static void handle(int sig) {
int status;
wait(&status);
}
sub rsp, 24
xor eax, eax # you forgot to include sys/wait.h, so the compiler has no prototype for wait(), so has to follow the ABI for variadic functions (al = number of FP args in xmm regs)
lea rdi, [rsp+12]
call wait
add rsp, 24
ret
将库调用变成 wait4(2)
的内联调用并不难。
请注意,手册页上说 wait4
已过时,新程序应使用 waitpid
or waitid
。但是,如果您不需要更多功能,wait4
就可以了。 glibc 在 wait4
Linux 系统调用之上实现 wait(3)
,而不是 waitid
。如果使用 wait4
有任何问题,glibc 将直接使用 waitid
。
handle:
mov eax, 61 ; __NR_wait4 from /usr/include/x86_64-linux-gnu/asm/unistd_64.h
mov edi, -1 ; pid_t is a 32bit type, so we don't need to sign-extend into rdi.
lea rsi, [rsp-4] ; status = a pointer into the red zone below rsp.
xor edx,edx ; options = 0
xor r10d,r10d ; rusage = NULL
syscall
; eax = pid waited for, or -1 to indicate error
; dword [rsp-4] = status. unlike function calls, syscalls don't clobber the stack
ret
要使用 wait4
中的 return 值,请执行以下操作:
cmp rax, -1 ;;;; THIS WAS A BUG: pid_t is a 32bit type; expect garbage or zeros in the upper 32 bits.
cmp eax, -1
je .error
...
如果要调试,请在handle
中设置断点。这比在 asm 中使用调试打印 容易得多。
如果在您 ret
时仍然崩溃,则可能 return 成功但实际上在您的主程序中崩溃了。使用调试器找出答案。
您的字符串常量应放在 .rodata
部分。你不需要在运行时修改它们,所以不要把它们放在 .data
.
您也不需要 call bzero
,因为您的 act
在 bss 中。如果你想在堆栈上分配它,而不是静态分配,你应该用 rep stosq
将它归零,就像 gcc 5.3 在你的 main()
中所做的那样。 (内联 bzero,as you can see on Godbolt)。
顺便说一句,问题中的 NASM 结构在错误的位置填充。可能对你未来的冒险值得注意,即使事实证明它不是这个问题的答案的一部分。 (您的代码在定义后甚至没有使用 NASM 结构语法。)
实际的struct sigaction
定义在/usr/include/x86_64-linux-gnu/bits/sigaction.h
,你可以在echo '#include <signal.h>' | gcc -E - | less
.
struct sigaction {
union {
__sighandler_t sa_handler; // your code uses this one, because it leaves SA_SIGINFO unset in sa_flags
void (*sa_sigaction) (int, siginfo_t *, void *);
}; // with some macros to sort this out
__sigset_t sa_mask; // 1024 bits = 128B
int sa_flags;
void (*sa_restorer) (void);
};
您的 NASM 结构在错误的位置填充:
STRUC sigact
.handler resq 1 ; 64bit pointer: correct
.mask resq 16 ; 16 qwords for sigset_t: correct, it's 128 bytes
.flag resd 1 ; 32bit flags: correct
;; The padding goes here, to align the 64bit member that follows
.pad resb 4
.restorer resq 1 ; 64bit
;; There's no padding here
ENDSTRUC
戳了好久终于搞明白了!问题是在 sigact 结构中正确设置恢复器。
当我检查 sigaction(2)
以获取结构定义时,它最终完全不是我想的那样。我明白了:
struct sigaction {
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
但那是结构的 C 定义(不完全是,正如手册页提到的,前两个可能是联合,我就是这种情况)。
然而,经过更多的探索,我发现我需要构建的结构看起来更像这样:
struct asm_sigaction {
void (*sa_handler)(int);
[unsigned?] long sa_flags;
void (*sa restorer)(void);
sigset_t sa_mask;
};
我通过深入了解我的 C 代码的实际用途发现了这一点。我找到了我正在制作的相同系统调用的位置,并转储了它们为 sigaction 结构传递的字节:
(gdb) x/38wx $rsi
0x7fffffffddc0: 0x004007f5 0x00000000 0x14000000 0x00000000
0x7fffffffddd0: 0xf7a434a0 0x00007fff 0x00000000 0x00000000
0x7fffffffdde0: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffddf0: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde00: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde10: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde20: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde30: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffde40: 0x00000000 0x00000000 0x00000000 0x00000000
0x7fffffffddd0
的部分对我来说像是一个地址,所以我检查了一下:
(gdb) disas 0x00007ffff7a434a0
Dump of assembler code for function __restore_rt:
0x00007ffff7a434a0 <+0>: mov rax,0xf
0x00007ffff7a434a7 <+7>: syscall
0x00007ffff7a434a9 <+9>: nop DWORD PTR [rax+0x0]
果然他们正在设置正在调用 sigreturn
(在我的例子中是 rt_sigreturn
)系统调用的恢复器!手册页说应用程序通常不会搞砸,但我猜这是针对典型的 C 程序的。所以我继续将这个函数复制到恢复器标签中,并将它放在我的结构中的适当位置,哇哦它起作用了。
这是现在工作的 NASM,我用一个新的 C 程序稍微改变了一些东西,我试图让它的外观和行为更像我的 NASM 程序,然后关闭 pause
对于 nanosleep
.
新的 C 程序:
#include <signal.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
const char *parentmsg = "from parent\n[=14=]";
const char *childmsg = "from child\n[=14=]";
const char *handlemsg = "in handle\n[=14=]";
const char *forkfailed = "fork failed\n[=14=]";
const char *parentexit = "parent exiting\n[=14=]";
const char *sleepfailed = "sleep failed\n[=14=]";
const char *sleepinterrupted = "sleep interrupted\n[=14=]";
void print(const char *msg) {
write(STDIN_FILENO, msg, strlen(msg));
}
static void handle(int sig) {
print(handlemsg);
waitid(P_ALL, -1, NULL, WEXITED|WSTOPPED|WCONTINUED);
}
int main(int argc, char* argv[]) {
struct timespec tsreq;
struct timespec tsrem;
tsreq.tv_sec = 2;
struct sigaction act;
act.sa_handler = &handle;
sigaction(SIGCHLD, &act, NULL);
pid_t pid;
if ( (pid = fork()) == 0 ) {
print(childmsg);
exit(0);
}
print(parentmsg);
if (nanosleep((const struct timespec*)&tsreq, &tsrem) == -1) {
if (errno == EINTR) {
print(sleepinterrupted);
nanosleep((const struct timespec*)&tsrem, NULL);
} else {
print(sleepfailed);
}
}
print(parentexit);
exit(0);
}
以及新的工作 NASM(在 Peter 的帮助下希望它看起来和功能更好)
USE64
STRUC sigact
.handler resq 1
.flag resq 1
.restorer resq 1
.mask resq 16
ENDSTRUC
STRUC timespec
.tv_sec resq 1
.tv.nsec resq 1
ENDSTRUC
section .text
global _start
_start:
; register SIGCHLD handler
mov DWORD [act+sigact.handler], handle
mov QWORD [act+sigact.restorer], restorer
mov DWORD [act+sigact.flag], 0x04000000
mov rax, 13
mov rdi, 17
lea rsi, [act]
xor rdx, rdx
mov r10, 0x8
syscall
cmp eax, 0
jne sigaction_fail
mov rax, 57
syscall
cmp eax, -1
je fork_failed
cmp eax, 0
je child
mov rax, parentmsg
call print
mov rax, 35
mov QWORD [tsreq+timespec.tv_sec], 2
lea rdi, [tsreq]
lea rsi, [tsrem]
syscall
cmp eax, -1
je .exit
mov rax, sleepagain
call print
mov rax, 35
mov rdi, tsrem
xor rsi, rsi
syscall
.exit:
mov rax, parentexit
call print
mov rax, 60
xor rdi, rdi
syscall
restorer:
mov rax, 15
syscall
fork_failed:
mov rax, forkfailed
call print
mov rax, 60
mov rdi, -1
syscall
sigaction_fail:
mov rax, safailed
call print
mov rax, 60
mov rdi, -1
syscall
handle:
mov rax, handlemsg
call print
lea rsi, [rsp-0x4]
mov rax, 247
xor rdi, rdi
xor rdx, rdx
mov r10, 14
syscall
cmp eax, -1
jne .success
mov rax, hdfailed
call print
mov rax, 60
mov rdi, -1
syscall
.success:
mov rax, hdsuccess
call print
ret
child:
mov rax, childmsg
call print
mov rax, 60
xor rdi, rdi
syscall
; print a null terminated string stored in rax
print:
push rbx
push rdx
push rdi
push rsi
mov rbx, rax
call strlen
mov rdx, rax
mov rax, 1
mov rdi, 1 ; stdout
mov rsi, rbx
syscall
pop rsi
pop rdi
pop rdx
pop rbx
ret
strlen:
push rbp
mov rbp, rsp
push rbx
mov rbx, rax
.countchar:
cmp BYTE [rax], 0 ; compare it to null byte
jz .exit
inc rax
jmp .countchar
.exit:
sub rax, rbx
pop rbx
mov rsp, rbp
pop rbp
ret
section .data
childmsg: db "from child", 0xa, 0 ; null terminated
parentmsg db "from parent", 0xa, 0
handlemsg db "in handle", 0xa, 0
safailed db "failed to set signal handler", 0xa, 0
hdfailed db "failed waiting for child", 0xa, 0
hdsuccess db "successfully waited on child", 0xa, 0
parentexit db "parent exiting", 0xa, 0
forkfailed db "fork failed", 0xa, 0
sleepagain db "sleeping again", 0xa, 0
section .bss
tsreq: resb timespec_size
tsrem: resb timespec_size
act: resb sigact_size