一个简单系统调用用例分析

罗冲 + 原创 + 《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-1000029000

1. 代码分析

利用系统调用

#include <sys/stat.h>
#include <sys/types.h>
#include <stdio.h>

int main()
{
      int tt;
    char* path = "/home/lch/study/chapter4/test";
    unsigned short mod = 0750;

    tt = mkdir(path, mod); 

    printf("tt:%d\n",tt);
}

利用汇编:

#include <stdio.h>

int main()
{
      int tt;
    char* path = "/home/lch/study/chapter4/test";
    unsigned short mod = 0750;
    asm volatile(
      "mov $0x27, %%eax\n\t"
      "int $0x80\n\t"
      "mov %%eax, %0\n\t"
      :"=m"(tt)
      :"b"(path),"c"(mod)
    );
    printf("tt:%x\n",tt);
}

2. 函数调用

程序编译与说明

首先将代码进行编译,在这里使用静态编译的方式:
这里写图片描述
在测试用例中,是调用函数

int mkdir(const char *pathname, mode_t mode);

mkdir()函数是由libc.a提供,使用静态编译的主要原因是为避免动态调用时,对于我们代码分析的影响。

分析说明

利用gdb,分析程序在调用mkdir的过程,并在main函数处打上断点。然后查看其汇编语言:
这里写图片描述
结合C语言与汇编语言,可以知道:

char* path = "/home/lch/study/chapter4/test";

与汇编程序

movl   $0x80b366c,0x18(%esp)

是一致的。这也说明了。这是因为path被赋的值为一个常量,因此操作系统会直接把常量的地址给程序。这里我们需在关注是的mkdir()两个参数的寄存器赋值。即path的地址被保存在(ebx + 18)这个地址,而mod的值被保存在(ebx+1e)这个地方。然后通过eax寄存,调整其它堆栈中的位置。在执行

call   0x8053d50 <mkdir>

之前,堆栈的值为:这里写图片描述

继续查看mkdir的汇编语言:
这里写图片描述
分析其汇编语言,再结合在调用call 函数之前的堆栈信息

   0x08053d52 <+2>: mov    0x8(%esp),%ecx
   0x08053d56 <+6>: mov    0x4(%esp),%ebx

可以知道ecx中保存的是mod的值,也就是0755,而ebx则保存的是path的值。

0x08053d5a <+10>:   mov    $0x27,%eax

接着将0x27这个值保存在eax中,而0x27这个立即数对应的中断号就是mkdir的中断号。当中断号与参数保存之后,就是开始准备中断了。也就是

 0x08053d5f <+15>:  call   *0x80d66b4

它是调用 *0x80d66b4这个函数地址,查看一下其值与所对应的汇编:
这里写图片描述
而这里开始系统调用的中断是则是sysenter

   0x00110419 <+5>: sysenter 

通过查询资料可知,“在 2.6 内核中,内核代码同时包含了对 int 0x80 中断方式和 sysenter 指令方式调用的支持,因此内核会给用户空间提供一段入口代码,内核启动时根据 CPU 类型,决定这段代码采取哪种系统调用方式。对于 glibc 来说,无需考虑系统调用方式,直接调用这段入口代码,即可完成系统调用。”
对于sysenter的实现是通过汇编语言:entry_32.S

sysenter_do_call:
    cmpl $(NR_syscalls), %eax
    jae sysenter_badsys
    call *sys_call_table(,%eax,4)

eax中存储系统调用号,此时它再通过sys_call_table列表,找到对应的系统函数

3. 使用嵌套汇编语言调用

通过objdump查看其编译后的汇编语言:

[lch@bogon chapter4]$ objdump -S mkdir_asm

mkdir_asm:     file format elf32-i386
... .... 

080483c4 <main>:
#include <stdio.h>
#include <time.h>

int main()
{
 80483c4:   55                      push   %ebp
 80483c5:   89 e5                   mov    %esp,%ebp
 80483c7:   83 e4 f0                and    $0xfffffff0,%esp
 80483ca:   53                      push   %ebx
 80483cb:   83 ec 2c                sub    $0x2c,%esp
      int tt;
    char* path = "/home/lch/study/chapter4/test";
 80483ce:   c7 44 24 18 e4 84 04    movl   $0x80484e4,0x18(%esp)
 80483d5:   08 
    unsigned short mod = 0750;
 80483d6:   66 c7 44 24 1e e8 01    movw   $0x1e8,0x1e(%esp)
    asm volatile(
 80483dd:   8b 44 24 18             mov    0x18(%esp),%eax
 80483e1:   0f b7 54 24 1e          movzwl 0x1e(%esp),%edx
 80483e6:   89 c3                   mov    %eax,%ebx
 80483e8:   89 d1                   mov    %edx,%ecx
 80483ea:   b8 27 00 00 00          mov    $0x27,%eax
 80483ef:   cd 80                   int    $0x80
 80483f1:   89 44 24 14             mov    %eax,0x14(%esp)
      "int $0x80\n\t"
      "mov %%eax, %0\n\t"
      :"=m"(tt)
      :"b"(path),"c"(mod)
    );
    printf("tt:%x\n",tt);

相比与之前的汇编语言,只有一条不相同,即是int 0x80。系统通过int 0x80的方式进入内核态,然后,执行过程与系统函数mkdir相同。

4. 结论

1)sysenter与int 0x80所起的作用相同
2) 系统调用是通过eax传递系统调用号。这是由entry_32.S里面的汇编语言决定的。
3)整个系统调用流程如下:
这里写图片描述

参考资料:
1. Linux 2.6 对新型 CPU 快速系统调用的支持 http://oss.org.cn/html/16/n-3916.html
2. 使用 Linux 系统调用的内核命令 http://www.ibm.com/developerworks/cn/linux/l-system-calls/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值