编辑见下面的“自我答案”
我一直试图在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它在发送信号时成功地等待子节点,但在返回时立即中断故障。我试着阅读越来越多的信号和信号处理,但在这一点上,这一切都在我的头脑中一起运行。对不起,NASM代码是丑陋的或不标准的。我不仅在学习,而且我可能已经重写了每个部分至少25次(也许是100+的handle)。
发布于 2016-06-29 20:59:46
很长一段时间后,我终于想出来了!问题是在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代码所做的事情发现了这一点。我找到了我正在制作的同一个syscall的位置,并将字节转储给它们传递给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程序,我想。所以我继续把这个函数复制到恢复器标签中,并把它放在我的struc中合适的位置,然后它就起作用了。
这是现在正在运行的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\0";
const char *childmsg = "from child\n\0";
const char *handlemsg = "in handle\n\0";
const char *forkfailed = "fork failed\n\0";
const char *parentexit = "parent exiting\n\0";
const char *sleepfailed = "sleep failed\n\0";
const char *sleepinterrupted = "sleep interrupted\n\0";
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 (在彼得的帮助下,希望它看起来更好,功能也更好)
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发布于 2016-06-29 04:29:09
信号处理程序是正常的功能。带ret返回,而不是iret返回。(从那以后你修改了你的问题来解决这个问题,所以我想你还有其他的问题)。
看看gcc是如何在handler()上编译戈德波特编译器浏览器的。
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已经过时,新程序应该使用waitid。但是,如果您不需要更多的功能,wait4就可以了。glibc在wait(3) Linux系统调用的基础上实现wait4,而不是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的返回值,请执行以下操作:
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仍然崩溃,它可能是成功返回,但实际上崩溃在您的主程序。使用调试器找出答案。
您的字符串常量应该放在.rodata部分。您不需要在运行时修改它们,所以不要将它们放在.data中。
您也不需要call bzero,因为您的act在bss中。如果您想在堆栈上分配它,而不是静态地分配它,您应该像gcc 5.3在您的rep stosq中所做的那样,用main()对它进行零化。(它插入bzero,就像你在“上帝”上看到的。
顺便说一句,你的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
ENDSTRUChttps://stackoverflow.com/questions/38087981
复制相似问题