asm 中的信号处理:为什么在调用 sys_pause 系统调用时会收到 SIGSEGV?
我正在尝试创建一个 x86_64 汇编程序,每当发送 SIGTERM
信号时,该程序都会显示“SIGTERM returned”。我的应用程序直接使用 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 ()
信号处理程序从未被调用,并且应用程序已崩溃。
我做错了什么?
I am trying to create an x86_64 assembly program that displays "SIGTERM received" whenever the SIGTERM
signal is sent. My application is using Linux syscalls directly:
%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
When I run the application, it hangs (as expected) at the sys_pause
syscall but when I send the SIGTERM
signal, it crashes with a segmentation fault.
So I loaded the application into GDB to figure out what was happening:
(gdb) break _start
Breakpoint 1 at 0x4000b0
(gdb) run
Starting program: [...]
Breakpoint 1, 0x00000000004000b0 in _start ()
(gdb) info proc
process 9639
(gdb) continue
Continuing.
The GDB session hung and I then opened another terminal and ran kill SIGTERM 9639
. This resulted in the following output:
Program received signal SIGTERM, Terminated.
0x00000000004000ec in _start ()
I then ran:
(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.
Then I continued the application:
(gdb) continue
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00000000004000ec in _start ()
The signal handler is never invoked and the application has crashed.
What am I doing wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
在应用程序正常运行之前,需要进行两项更正。
sa_restorer
Jester 向我指出了这个回答其中提到内核需要填写
sigaction
的sa_restorer
成员。修复此问题需要定义
SA_RESTORER
:...并初始化
sa_retorer
和sa_flags
成员:然后我为
restorer
添加了一个空存根函数:此时,处理程序被调用,没有错误,但应用程序仍然崩溃...
sys_rt_sigreturn
显然,sa_restorer 函数需要调用
sys_rt_sigreturn
系统调用。这需要定义sys_rt_sigreturn
:然后修改
restorer
函数:此时,应用程序运行而没有崩溃。
There were two corrections that needed to be made before the application worked correctly.
sa_restorer
Jester pointed me to this answer which mentioned that the kernel requires the
sa_restorer
member ofsigaction
to be filled in.Fixing this required defining
SA_RESTORER
:...and initializing the
sa_restorer
andsa_flags
members:I then added an empty stub for the
restorer
function:At this point, the handler was invoked without error but the application was still crashing...
sys_rt_sigreturn
Apparently, the
sa_restorer
function needs to invoke thesys_rt_sigreturn
syscall. This required definingsys_rt_sigreturn
:The
restorer
function was then modified:At this point, the application ran without crashing.
这是 Nathan Osman 的完整工作和更正程序。在这里回答是因为互联网上的其他地方似乎不存在这样的例子。
为了更容易测试,您可以通过取消注释第一个列表并注释第二个列表来将 SIGTERM 替换为 SIGINT。
将其保存到
signal.asm
并编译并运行在 SIGINT 版本中,按 Control-C 中断程序应该打印消息“SIGTERM收到”(也可以更改它以使其更准确) )。在 SIGTERM 版本中,改为在 gdb 中运行它
,现在按 Control-C
将按预期打印“SIGTERM returned”消息。
Here's the entire working and corrected program by Nathan Osman. Answering here because such an example doesn't seem to exist anywhere else on the internet.
For easier testing, you can replace SIGTERM with SIGINT by uncommenting the first list here and commenting the second.
Save it to
signal.asm
and compile and run withIn the SIGINT version, pressing Control-C to interrupt the program should print message 'SIGTERM received' (its also possible to change that to make it more accurate). In the SIGTERM version, instead run it in gdb
And now press Control-C
The 'SIGTERM received' message is printed as expected.