Skip to content

系统调用

Overview

好, 我最近感觉效率太低了, 于是想先写一点文档再开始写代码...

2 ways

  • soft int (x86)
  • sysenter / sysexit (syscall / sysret)

一般而言, 后者的实现更加高效, 我选择先实现前者

中断的方式在 64 位 linux 下并不推荐, 这里使用是为了实验, 使用 syscall 的传参

soft int

int $0x80

在这之前需要先 设置 tss, 要不然从 用户态 回到 内核态就不知道该用哪个栈了

sysenter / sysexit

这两个指令提供了一个比 中断 更加快速的方法访问内核.

在使用这些指令之前我们需要进行一些初始化工作.


MSR 寄存器:

  • IA32_SYSENTER_CS - 0x174
  • IA32_SYSENTER_ESP - 0x175
  • IA32_SYSENTER_EIP - 0x176

在 32位 下, 后两个寄存器是 32bit, 在 64位 下, 它们被扩展到 64bit.

syscall / sysret

  • kern_cs = IA32_STAR[47:32]
  • kern_ss = IA32_STAR[47:32] + 8
  • user_cs = IA32_STAR[63:48] + 16
  • user_ss = IA32_STAR[63:48] + 8
  • user32_ss = IA32_STAR[63:48]

the reason for use of the layout?

USER32 / USER64 -> compact / long mode 也就意味着 gdt 布局变成这样

1
2
3
4
5
6
7
8
+--------------+
| NULL         |
| KERN_CODE    |
| KERN_DATA    |
| USER_CODE32  |
| USER_DATA    |
| USER_CCODE64 |
+--------------+


  • syscall invoked
    • load rip -> rcx
    • rflags &= IA32_FMASK
    • load cs / ss
  • sysret invoked
    • load r11 -> eflags
    • load cs / ss
    • load rcx -> rip (ret to long mode)
    • load ecx -> rip (ret to compact mode)

register usage

man syscall

1
2
3
4
       Arch/ABI      arg1  arg2  arg3  arg4  arg5  arg6  arg7  Notes
       ──────────────────────────────────────────────────────────────
       i386          ebx   ecx   edx   esi   edi   ebp   -
       x86-64        rdi   rsi   rdx   r10   r8    r9    -

syscall nr

cat /usr/include/asm/unistd_64.h

  • SYS_
  • __NR_

references