1
0
mirror of https://github.com/upx/upx.git synced 2025-08-11 22:52:30 +08:00

SIGSEGV: handle and pretty-print on amd64-linux

This commit is contained in:
Markus F.X.J. Oberhumer
2025-08-01 00:49:27 +02:00
parent bbf52cee67
commit 9866b9bca9
3 changed files with 211 additions and 0 deletions

View File

@ -423,6 +423,8 @@ tc.amd64-linux.elf.gcc = amd64-linux-gcc-3.4.4 -fPIC -m64 -nostdinc -MMD -MT $@
tc.amd64-linux.elf.gcc += -fno-exceptions -fno-asynchronous-unwind-tables
tc.amd64-linux.elf.gcc += -Wall -W -Wcast-align -Wcast-qual -Wstrict-prototypes -Wwrite-strings -Werror
#amd64-linux.elf-entry.h amd64-linux.elf-fold.h: tc.amd64-linux.elf.gcc += -DTEST_SIGSEGV
amd64-linux.elf-entry.h: $(srcdir)/src/$$T.S
@echo; echo TARGET=$@ PATH=$(PATH); echo
$(call tc,gcc) -c -x assembler-with-cpp $< -o tmp/$T.bin

View File

@ -95,6 +95,45 @@ _start: .globl _start
push %rcx // argc
push %rdx // param for atexit()
#if TEST_SIGSEGV
// install SIGSEGV handler for debugging
SIGSEGV= 11
SA_SIGINFO= 4 // /usr/include/bits/sigaction.h
SA_RESTORER= 0x04000000
__NR_rt_sigaction= 13 // /usr/include/asm/unistd_64.h
push %rdi // save reg
sub %edx,%edx // arg3= 0 ==> do not save old sigaction
push %rdx // .sa_mask: 64 bits (8 bytes) of flags for signals
lea __restore_rt(%rip),%rax; push %rax // .sa_restorer
push $SA_RESTORER | SA_SIGINFO // .sa_flags
lea sigsegv_sigaction(%rip),%rax; push %rax // .sa_sigaction
push %rsp; pop %rsi // arg2= &new struct sigaction
push $SIGSEGV; pop %rdi // arg1= signum
push $8; pop %r10 // sys4= sizeof(__sigset_t) ==> 64 bits
push $__NR_rt_sigaction; pop %rax; syscall
add $(3 + 1) * NBPW,%rsp // toss struct sigaction
pop %rdi // restore reg
#if 0 //{ TEST ONLY
movl $0x18181818,%r8d
movl $0x19191919,%r9d
movl $0x1a1a1a1a,%r10d
movl $0x1b1b1b1b,%r11d
movl $0x1c1c1c1c,%r12d
movl $0x1d1d1d1d,%r13d
movl $0x1e1e1e1e,%r14d
movl $0x1f1f1f1f,%r15d
movl $0xaaaaaaaa,%eax
movl $0xbbbbbbbb,%ebx
movl $0xcccccccc,%ecx
movl $0xdddddddd,%edx
movl $0x55555555,%ebp
movl $0x66666666,%esi
movl $0x77777777,%edi
movl (%rdx),%edx // force SIGSEGV
#endif //}
#endif // TEST_SIGSEGV
#define old_sp %rbp
F_FRAME= 7*NBPW
F_ENTR= 6*NBPW; F_PMASK= F_ENTR
@ -295,6 +334,165 @@ eof_n2b:
add $D_FOLD,%rax // beyond .data
jmp *%rax // goto unfolded stub
#if TEST_SIGSEGV
__NR_rt_sigreturn= 15
.balign 16
__restore_rt:
endbr64
mov $__NR_rt_sigreturn,%eax
syscall
.balign 16
sigsegv_sigaction:
endbr64
push %rdx // save &ucontext_t
// print /proc/self/maps of child (same as parent: the beauty of fork())
mov $end_announce_sigaction - announce_sigaction,%edx // arg3 len
lea announce_sigaction(%rip),%rsi // arg2 buf
push $2; pop %rdi // arg1 fd_stderr
push $__NR_write; pop %eax; syscall // ignore error on write()
xor %esi,%esi // arg2 O_RDONLY
lea proc_self_maps(%rip),%arg1
push $__NR_open; pop %rax; syscall
mov %rax,%r12 // fd_maps
BUFLEN= 4096
mov $BUFLEN,%ebx; sub %rbx,%rsp // allocate buffer
loop_maps:
mov %ebx,%edx // arg3 buflen
push %rsp; pop %rsi // arg2 buffer
mov %r12,%rdi // arg1 fd_maps
xor %eax,%eax; syscall // __NR_read
test %eax,%eax; jle done_maps // ignore error on read()
mov %eax,%edx // arg3 buflen
push %rsp; pop %rsi // arg2 buf
push $2; pop %rdi // arg1 fd_stderr
push $__NR_write; pop %rax; syscall // ignore error on write()
jmp loop_maps
done_maps:
mov %r12,%rdi //arg1 fd_maps
add %rbx,%rsp // discard buffer
push $__NR_close; pop %rax; syscall
// end printing of /proc/self/maps
pop %rdx // restore &ucontext_t
__NR_fork= 57
push $__NR_fork; pop %rax; call sys_check
test %eax,%eax; je child
parent:
jmp parent // spin; paused by gdb
proc_self_cmdline:
.asciz "/proc/self/cmdline"
announce_sigaction:
.asciz "\n\nSIGSEGV address space:\n"
end_announce_sigaction:
proc_self_maps:
.asciz "/proc/self/maps"
minus_q:
.asciz "-q"
path_gdb:
.asciz "/usr/bin/gdb"
commands_gdb:
.ascii "info inferiors\n"
.ascii "print \"r8, r9\"\n"
.ascii "x/2xg $rdx + 5*8\n"
.ascii "print \"r10, r11\"\n"
.ascii "x/2xg\n"
.ascii "print \"r12, r13\"\n"
.ascii "x/2xg\n"
.ascii "print \"r14, r15\"\n"
.ascii "x/2xg\n"
.ascii "print \"rdi, rsi\"\n"
.ascii "x/2xg\n"
.ascii "print \"rbp, rbx\"\n"
.ascii "x/2xg\n"
.ascii "print \"rdx, rax\"\n"
.ascii "x/2xg\n"
.ascii "print \"rcx, rsp\"\n"
.ascii "x/2xg\n"
.ascii "print \"rip, efl\"\n"
.ascii "x/2xg\n"
.ascii "set $pc = *(long *)($rdx + 168)\n"
.ascii "print \"faulting instr\"\n"
.ascii "x/i $pc\n"
.ascii "print \"fault context\"\n"
.ascii "x/24i $pc - 0x20\n"
.ascii "kill\n"
.ascii "quit 1"
.byte 0
commands_gdb_end:
child:
PATH_MAX= 4096
sub $PATH_MAX,%rsp
lea proc_self_cmdline(%rip),%arg1
O_RDONLY= 0
push $O_RDONLY; pop %arg2
push $__NR_open; pop %rax; call sys_check
push %rax; pop %arg1 // fd
push %rsp; pop %arg2 // buffer
push $PATH_MAX; pop %arg3
__NR_read= 0
push $__NR_read; pop %rax; call sys_check
push $__NR_close; pop %eax; call sys_check
__NR_getppid= 110
push $__NR_getppid; pop %eax; syscall
push %rax; pop %rsi
xor %eax,%eax; push %rax; push %rax // decimal(pid) fits in 16 bytes
push %rsp; pop %rdi; call unsimal
// argv
push %rsp; pop %rsi // fence post: &pid.unsimal
push $0 // argv[4]
push %rsi // arg3 pid
add $16,%rsi; push %rsi // arg2 exename
lea minus_q(%rip),%rax; push %rax // arg1 "-q"
add $(path_gdb - minus_q),%rax; push %rax // arg[0] "/usr/bin/gdb"
#if 1 //{ pipe input to gdb
xor %edi,%edi; push $__NR_close; pop %rax; syscall
push %rax; push %rsp; pop %rdi // &fd_pipe[2]; 4 bytes each
__NR_pipe= 22
push $__NR_pipe; pop %rax; call sys_check
pop %rdi; shr $32,%rdi // arg1 write side of pipe
push $commands_gdb_end - commands_gdb; pop %arg3
lea commands_gdb(%rip),%arg2
__NR_write= 1
push $__NR_write; pop %rax; call sys_check
push $__NR_close; pop %rax; syscall
#endif //}
push $0; pop %arg3 // _environ BUG
push %rsp; pop %arg2 // argv
movq (%arg2),%arg1 // "/usr/bin/gdb"
__NR_execve= 59
push $__NR_execve; pop %rax; call sys_check
hlt
unsimal: // (dst, value)
push $10; pop %rcx // radix
mov %esi,%eax // value
call 0f
movb $0,(%rdi) // terminator
ret
0:
xor %edx,%edx; div %ecx; push %rdx // eax= quo(%edx:%eax / %ecx); edx= rem
// 'div' undefines all flags!
test %eax,%eax; je 1f; call 0b
1:
pop %rax; add $'0',%eax
stosb
ret
#endif // TEST_SIGSEGV
sys_check:
push %rax // save __NR_ for debug
syscall

View File

@ -177,6 +177,17 @@ no_env_pse:
mov %ebp,%ebx # fd
movq %rax,3*NBPW(%rsp) # entry
#if TEST_SIGSEGV
// Uninstall SIGSEGV handler
SIGSEGV= 11
__NR_rt_sigaction= 13 // /usr/include/asm/unistd_64.h
push $8; pop %sys4 // sys_arg4 minimal byte count
xor %edx,%edx // no old
xor %esi,%esi // no new
push $SIGSEGV; pop %rdi
push $__NR_rt_sigaction; pop %eax; syscall
#endif // TEST_SIGSEGV
sz_Ehdr= 8*NBPW
e_type= 16
ET_EXEC= 2