Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
X86 如果在64位代码中使用32位int 0x80 Linux ABI,会发生什么情况?_X86_Linux_Assembly_X86 64_System Calls_Abi - Fatal编程技术网

X86 如果在64位代码中使用32位int 0x80 Linux ABI,会发生什么情况?

X86 如果在64位代码中使用32位int 0x80 Linux ABI,会发生什么情况?,x86,linux,assembly,x86-64,system-calls,abi,X86,Linux,Assembly,X86 64,System Calls,Abi,Linux上的int 0x80始终调用32位ABI,不管它是从什么模式调用的:ebx中的args,ecx。。。以及来自/usr/include/asm/unistd_32.h的系统调用号码。(或在未使用配置IA32\u仿真编译的64位内核上崩溃) 64位代码应使用syscall,呼叫号码来自/usr/include/asm/unistd_64.h,参数位于rdi,rsi等。请参阅。如果您的问题被标记为与此重复,请查看该链接,了解如何使用32位或64位代码进行系统调用的详细信息。如果您想了解到底发

Linux上的
int 0x80
始终调用32位ABI,不管它是从什么模式调用的:ebx中的args,
ecx
。。。以及来自
/usr/include/asm/unistd_32.h
的系统调用号码。(或在未使用
配置IA32\u仿真编译的64位内核上崩溃)

64位代码应使用
syscall
,呼叫号码来自
/usr/include/asm/unistd_64.h
,参数位于
rdi
rsi
等。请参阅。如果您的问题被标记为与此重复,请查看该链接,了解如何使用32位或64位代码进行系统调用的详细信息。如果您想了解到底发生了什么,请继续阅读

(有关32位与64位
sys_write
的示例,请参阅)


syscall
系统调用比
int 0x80
系统调用快,因此请使用本机64位
syscall
,除非您编写的多语言机器代码在作为32位或64位执行时运行相同的代码。(
sysenter
总是以32位模式返回,因此它在64位用户空间中没有用处,尽管它是一条有效的x86-64指令。)

相关:有关如何进行
int 0x80
syscenter
32位系统调用,或
syscall
64位系统调用,或调用vDSO进行“虚拟”系统调用,如
gettimeofday
。加上系统调用的背景知识


使用
int 0x80
可以编写将以32位或64位模式组装的内容,因此在微基准标记或其他内容的末尾使用
exit_group()
非常方便

标准化函数和系统调用约定的官方i386和x86-64 System V psABI文档的当前PDF链接自

有关初学者指南、x86手册、官方文档和性能优化指南/资源,请参阅标签


但是,由于人们不断发布关于使用32位代码的问题,或者无意中来自32位代码的问题,我想知道在当前的Linux上到底发生了什么?

是否保存/恢复所有64位寄存器?它是否将任何寄存器截断为32位?如果传递上半部分为非零的指针参数,会发生什么情况?


传递32位指针是否有效?

int 0x80
在正确使用时有效,只要任何指针适合32位(堆栈指针不适合)。但是要注意,除非您使用的是最新的strace+内核,否则strace会将其解码错误

int 0x80
将r8-r11置零,并保留所有其他内容。使用它就像在32位代码中一样,使用32位电话号码。(或者更好,不要使用它!)

并非所有系统都支持
int0x80
:Linux的Windows子系统(WSL)严格来说仅为64位:。也可以构建Linux内核。(不支持32位可执行文件,不支持32位系统调用)


详细信息:保存/还原的内容,内核使用的regs的哪些部分
int 0x80
使用
eax
(不是完整的
rax
)作为系统调用号,分配到32位用户空间
int 0x80
使用的相同函数指针表。(这些指针指向内核内部本机64位实现的
sys\u实现或包装器。系统调用实际上是跨越用户/内核边界的函数调用。)

只传递arg寄存器的低32位保留
rbx
-
rbp
的上半部分,但被
int 0x80
系统调用忽略。
请注意,向系统调用传递错误指针不会导致SIGSEGV;相反,系统调用返回
-EFAULT
。如果不检查错误返回值(使用调试器或跟踪工具),它将显示为自动失败

所有寄存器(当然,eax除外)都被保存/恢复(包括RFLAG和整数寄存器的上32个),除了r8-r11被归零
r12-r15
在x86-64 SysV ABI的函数调用约定中保留了调用,因此通过64位的
int 0x80
归零的寄存器是AMD64添加的“新”寄存器的调用结束子集

这种行为在内核内部如何实现寄存器保存的一些内部更改中得到了保留,内核中的注释提到它可以从64位开始使用,因此这个ABI可能是稳定的。(也就是说,你可以指望r8-r11被归零,而其他一切都被保留。)

返回值经过符号扩展以填充64位
rax
。)这意味着指针返回值(如来自
void*mmap()
)在64位寻址模式中使用之前需要进行零扩展

sysenter
不同,它保留了
cs
的原始值,因此它以调用时的相同模式返回用户空间。(使用
sysenter
会导致内核将
cs
设置为
$\uuuu USER32\u cs
,为32位代码段选择描述符。)


Older
strace
对64位进程的
int 0x80
进行错误解码。它解码时,就好像进程使用了
syscall
而不是
int0x80
。可以是。e、 g.
strace
eax=1
/
int$0x80
打印
write(0,NULL,12
),这实际上是
退出(ebx)
,而不是
写入(rdi,rsi,rdx)

我不知道添加了
PTRACE\u GET\u SYSCALL\u INFO
功能的确切版本,但Linux内核5.5/strace 5.5处理了它。它误导性地说进程“以32位模式运行”,但确实如此
/*
 * 32-bit legacy system call entry.
 *
 * 32-bit x86 Linux system calls traditionally used the INT $0x80
 * instruction.  INT $0x80 lands here.
 *
 * This entry point can be used by 32-bit and 64-bit programs to perform
 * 32-bit system calls.  Instances of INT $0x80 can be found inline in
 * various programs and libraries.  It is also used by the vDSO's
 * __kernel_vsyscall fallback for hardware that doesn't support a faster
 * entry method.  Restarted 32-bit system calls also fall back to INT
 * $0x80 regardless of what instruction was originally used to do the
 * system call.
 *
 * This is considered a slow path.  It is not used by most libc
 * implementations on modern hardware except during process startup.
 ...
 */
 ENTRY(entry_INT80_compat)
 ...  (see the github URL for the full source)
if (likely(nr < IA32_NR_syscalls)) {
  /*
   * It's possible that a 32-bit syscall implementation
   * takes a 64-bit parameter but nonetheless assumes that
   * the high bits are zero.  Make sure we zero-extend all
   * of the args.
   */
  regs->ax = ia32_sys_call_table[nr](
      (unsigned int)regs->bx, (unsigned int)regs->cx,
      (unsigned int)regs->dx, (unsigned int)regs->si,
      (unsigned int)regs->di, (unsigned int)regs->bp);
}

syscall_return_slowpath(regs);
global _start
_start:
    mov  rax, 0x123456789abcdef
    mov  rbx, rax
    mov  rcx, rax
    mov  rdx, rax
    mov  rsi, rax
    mov  rdi, rax
    mov  rbp, rax
    mov  r8, rax
    mov  r9, rax
    mov  r10, rax
    mov  r11, rax
    mov  r12, rax
    mov  r13, rax
    mov  r14, rax
    mov  r15, rax

    ;; 32-bit ABI
    mov  rax, 0xffffffff00000004          ; high garbage + __NR_write (unistd_32.h)
    mov  rbx, 0xffffffff00000001          ; high garbage + fd=1
    mov  rcx, 0xffffffff00000000 + .hello
    mov  rdx, 0xffffffff00000000 + .hellolen
    ;std
after_setup:       ; set a breakpoint here
    int  0x80                   ; write(1, hello, hellolen);   32-bit ABI
    ;; succeeds, writing to stdout
;;; changes to registers:   r8-r11 = 0.  rax=14 = return value

    ; ebx still = 1 = STDOUT_FILENO
    push 'bye' + (0xa<<(3*8))
    mov  rcx, rsp               ; rcx = 64-bit pointer that won't work if truncated
    mov  edx, 4
    mov  eax, 4                 ; __NR_write (unistd_32.h)
    int  0x80                   ; write(ebx=1, ecx=truncated pointer,  edx=4);  32-bit
    ;; fails, nothing printed
;;; changes to registers: rax=-14 = -EFAULT  (from /usr/include/asm-generic/errno-base.h)

    mov  r10, rax               ; save return value as exit status
    mov  r8, r15
    mov  r9, r15
    mov  r11, r15               ; make these regs non-zero again

    ;; 64-bit ABI
    mov  eax, 1                 ; __NR_write (unistd_64.h)
    mov  edi, 1
    mov  rsi, rsp
    mov  edx, 4
    syscall                     ; write(edi=1, rsi='bye\n' on the stack,  rdx=4);  64-bit
    ;; succeeds: writes to stdout and returns 4 in rax
;;; changes to registers: rax=4 = length return value
;;; rcx = 0x400112 = RIP.   r11 = 0x302 = eflags with an extra bit set.
;;; (This is not a coincidence, it's how sysret works.  But don't depend on it, since iret could leave something else)

    mov  edi, r10d
    ;xor  edi,edi
    mov  eax, 60                ; __NR_exit (unistd_64.h)
    syscall                     ; _exit(edi = first int 0x80 result);  64-bit
    ;; succeeds, exit status = low byte of first int 0x80 result = 14

section .rodata
_start.hello:    db "Hello World!", 0xa, 0
_start.hellolen  equ   $ - _start.hello
yasm -felf64 -Worphan-labels -gdwarf2 abi32-from-64.asm
ld -o abi32-from-64 abi32-from-64.o
(gdb)  set disassembly-flavor intel
(gdb)  layout reg
(gdb)  b  after_setup
(gdb)  r
(gdb)  si                     # step instruction
    press return to repeat the last command, keep stepping