【发布时间】:2017-09-28 22:53:26
【问题描述】:
我正在尝试创建一个 x86_64 汇编程序,该程序在发送SIGTERM 信号时显示“收到SIGTERM”。我的应用程序直接使用 Linux 系统调用:
%define sys_write 0x01
%define sys_rt_sigaction 0x0d
%define sys_pause 0x22
%define sys_exit 0x3c
%define SIGTERM 0x0f
%define STDOUT 0x01
; Definition of sigaction struct for sys_rt_sigaction
struc sigaction
.sa_handler resq 1
.sa_flags resq 1
.sa_restorer resq 1
.sa_mask resq 1
endstruc
section .data
; Message shown when a syscall fails
error_msg db 'syscall error', 0x0a
error_msg_len equ $ - error_msg
; Message shown when SIGTERM is received
sigterm_msg db 'SIGTERM received', 0x0a
sigterm_msg_len equ $ - sigterm_msg
section .bss
act resb sigaction_size
val resd 1
section .text
global _start
_start:
; Initialize act
lea rax, [handler]
mov [act + sigaction.sa_handler], rax
; Set the handler
mov rax, sys_rt_sigaction
mov rdi, SIGTERM
lea rsi, [act]
mov rdx, 0x00
mov r10, 0x08
syscall
; Ensure the syscall succeeded
cmp rax, 0
jne error
; Pause until a signal is received
mov rax, sys_pause
syscall
; Upon success, jump to exit
jmp exit
error:
; Display an error message
mov rax, sys_write
mov rdi, STDOUT
mov rsi, error_msg
mov rdx, error_msg_len
syscall
; Set the return value to one
mov dword [val], 0x01
exit:
; Terminate the application gracefully
mov rax, sys_exit
mov rdi, [val]
syscall
handler:
; Display a message
mov rax, sys_write
mov rdi, STDOUT
mov rsi, sigterm_msg
mov rdx, sigterm_msg_len
syscall
ret
当我运行应用程序时,它在sys_pause 系统调用处挂起(如预期的那样),但是当我发送SIGTERM 信号时,它因分段错误而崩溃。
所以我将应用程序加载到 GDB 中以了解发生了什么:
(gdb) break _start
Breakpoint 1 at 0x4000b0
(gdb) run
Starting program: [...]
Breakpoint 1, 0x00000000004000b0 in _start ()
(gdb) info proc
process 9639
(gdb) continue
Continuing.
GDB 会话挂起,然后我打开另一个终端并运行 kill SIGTERM 9639。这导致了以下输出:
Program received signal SIGTERM, Terminated.
0x00000000004000ec in _start ()
然后我跑了:
(gdb) disas _start
Dump of assembler code for function _start:
0x00000000004000b0 <+0>: lea 0x400123,%rax
0x00000000004000b8 <+8>: mov %rax,0x600160
0x00000000004000c0 <+16>: mov $0xd,%eax
0x00000000004000c5 <+21>: mov $0xf,%edi
0x00000000004000ca <+26>: lea 0x600160,%rsi
0x00000000004000d2 <+34>: mov $0x0,%edx
0x00000000004000d7 <+39>: mov $0x8,%r10d
0x00000000004000dd <+45>: syscall
0x00000000004000df <+47>: cmp $0x0,%rax
0x00000000004000e3 <+51>: jne 0x4000ee <error>
0x00000000004000e5 <+53>: mov $0x22,%eax
0x00000000004000ea <+58>: syscall
=> 0x00000000004000ec <+60>: jmp 0x400114 <exit>
End of assembler dump.
然后我继续申请:
(gdb) continue
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000ec in _start ()
信号处理程序从未被调用,应用程序已崩溃。
我做错了什么?
【问题讨论】:
-
我相信this 也适用于你。 PS:不要混淆来自 asm 的信号 ;)
-
您是否尝试在
strace下运行它以查看系统调用参数和返回值?或者在gdb中,print $rax在syscall之后,看看是不是-EFAULT什么的。查看x86 tag wiki底部的调试提示。 -
@PeterCordes 不幸地不起作用。系统调用不返回错误,只有在尝试处理内核故障信号时,如果没有设置恢复器。我认为这是一个内核错误。
-
哦,nvm,GDB 显示
RIP指向syscall之后的指令,但SIGSEGV先交付。这在 GDB 方面可能是虚假的(或者在 Linux 方面,用于向 GDB 显示错误的 RIP,而不是在信号处理程序或恢复器或其他方面)。当您回复@jester 时,已将其编辑到我之前的评论中一半:P -
@PeterCordes 我设置了
sa_restorer并添加了SA_RESTORER标志,它现在调用处理程序。但是,它仍然在之后崩溃。我会继续调试。
标签: linux assembly x86-64 nasm signal-handling