背景:了解ioctl()从用户空间到内核空间的整个调用链。内核那边已经理清了,glibc这边不太好弄。晚上各种查,捣鼓出了一点结果。
首先从下载的glibc源码里找ioctl(),没发现比较直接的代码实现(主要是没看到陷入内核的相关代码)。后来不得已,干脆调试glibc代码得了,以前没调试过,不妨现在一试。
调试glibc代码,主要参考这两篇:使用gdb调试glibc和用gdb追踪glibc代码执行过程。大致过程是:
(1) 安装glibc符号表:$sudo apt-get install libc6-dbg
(2)下载glibc源码:$sudo apt-get source libc6-dev
a. 命令执行完后,在当前目录下生成eglibc-xxx目录,里面就是glibc源码。我自己生成的目录为~/eglibc-2.15
(3)编译生成带有调试信息的可执行文件:$gcc -g -o xxx xxx.c
(4)调试xxx:gdb xxx
a. 指定源码搜索路径:directory ~/eglibc-2.15
其实~/eglibc-2.15这个目录范围很广,如果知道自己要调试的库函数所在的具体路径,就直接指定这个路径。就鄙人这个情况,我是不知道ioctl()是在哪个路径哪个文件下的,只好指定根目录了。
b. 执行到ioctl()调用处,step进去,报信息如下:
ioctl () at ../sysdeps/unix/syscall-template.S:82
82 ../sysdeps/unix/syscall-template.S: No such file or directory.
指定搜索路径为 ~/eglibc-2.15/sysdeps/unix就好了。得到:
ioctl () at ../sysdeps/unix/syscall-template.S:82
82 T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)
现在到sysdeps/unix/syscall-template.S文件下看这行:T_PSEUDO (SYSCALL_SYMBOL, SYSCALL_NAME, SYSCALL_NARGS)。
这是个系统调用生成模板,跟ioctl()没直接关系,不过是在编译的时候把ioctl这个函数名和它的参数个数传进来。关键还得找T_PSEUDO 。
T_PSEUDO 在syscall-template.S里是 #define T_PSEUDO(SYMBOL, NAME, N)
PSEUDO (SYMBOL, NAME, N),所以继续找PSEUDO。
PSEUDO在sysdeps/unix/sysv/Linux/i386/sysdep.h有个定义如下:
#undef PSEUDO
#define PSEUDO(name, syscall_name, args) \
.text; \
ENTRY (name) \
DO_CALL (syscall_name, args); \
cmpl $-4095, %eax; \
jae SYSCALL_ERROR_LABEL; \
L(pseudo_end):
DO_CALL在同一个文件下是:
#undef DO_CALL
#define DO_CALL(syscall_name, args) \
PUSHARGS_##args \
DOARGS_##args \
movl $SYS_ify (syscall_name), %eax; \
ENTER_KERNEL \
POPARGS_##args
这些宏在同一个文件下基本都有定义,就不在这里一一展示其定义了。
总之,当syscall_name为ioctl,args为3,条件编译的宏I386_USE_SYSENTER和SHARED均定义时,PSEUDO展开得到:
#define PSEUDO(name, syscall_name, args) \
.text; \
ENTRY (name) \
pushl %ebx; \
cfi_adjust_cfa_offset (4); \
movl 16(%esp), %edx; \
movl 12(%esp), %ecx; \
movl 8(%esp), %ebx; \
movl $SYS_ify (syscall_name), %eax; \
call *%gs:SYSINFO_OFFSET \
popl %ebx; \
cfi_adjust_cfa_offset (-4); \
cfi_restore (ebx); \
cmpl $-4095, %eax; \
jae SYSCALL_ERROR_LABEL; \
L(pseudo_end):
我们最后再来看对ioctl()反汇编的代码:
图中,指令cmp $0xfffff001, %eax的0xfffff001,它的十进制值是-4095。
比较ioctl()反汇编代码和sysdep.h中PSEUDO的汇编代码,基本都对应上了。
ps. 没那么多时间,写的好潦草:P
参考
【1】使用gdb调试glibc,http://blog.chinaunix.NET/uid-24774106-id-3642925.html,2016.11.15.
【2】用GDB追踪glibc代码执行过程,http://blog.nlogn.cn/trace-glibc-by-using-gdb/,2016.11.15.
【3】在GLIBC里查找系统调用,http://blog.csdn.Net/caspiansea/article/details/39022377,2016.11.15.
【4】Linux Application-Level System Calls,http://www.cs.fsu.edu/~baker/devices/notes/systemcalls.html,2016.11.15.