前言
在main.c函数中,存在两个函数 fork.c pause.c,当我们尝试单步跟进去之后,却发现失败了,我们来分析其原因。
void main(void) /* This really IS void, no error here. */
{
....
if (!fork()) { /* we count on this going ok */
init();
}
for(;;) pause();
}
_syscall0(type,name) 宏的实现
我们在main函数中存在下面这些定义,我们现在尝试分析一下。
static inline _syscall0(int,fork)
static inline _syscall0(int,pause)
static inline _syscall1(int,setup,void *,BIOS)
static inline _syscall0(int,sync)
其_syscall0(type,name)定义如下,我们尝试对此展开一下。
#define _syscall0(type,name) \
type name(void) \
{ \
long __res; \
__asm__ volatile ("int $0x80" \
: "=a" (__res) \
: "0" (__NR_##name)); \
if (__res >= 0) \
return (type) __res; \
errno = -__res; \
return -1; \
}
我们现在对pause( )展开,内容如下,就是调用 int 0x80,然后eax来传递中断号,返回时将返回值存入变量__res中,判断是否成功,否则设置错误号。
#define __NR_pause 29
static inline _syscall0(int,pause)
int pause()
{
long __res;
__asm__ volatile(
" int $0x80"
: "=a" (__res) // mov __res,eax
: "0" (__NR_pause)); // mov eax, __NR_pause
/* intel
mov eax, __NR_pause
mov int 0x80
mov __res,eax
*/
if (__res >= 0)
return int __res;
error = -__res;
return -1;
}
int 0x80后发生了什么呢
我们尝试在vscode中全局搜索“0x80”,在sched.c中看到如下函数,因此我们知道是在void sched_init(void)中完成对int 0x80中断的初始化的。
void sched_init(void)
{
...
...
...
set_system_gate(0x80,&system_call);
}
system_call是一段汇编代码,在kernel\kernel_call.s中,核心代码如下,其通过sys_call_table这张表来查询到的,然后 call sys_call_table[eax*4] 来间接跳转到对应函数位置。
system_call:
....
....
mov %dx,%es
movl $0x17,%edx # fs points to local data space
mov %dx,%fs
call *sys_call_table(,%eax,4)
pushl %eax
movl current,%eax
...
...
我们使用readelf工具查看syscall_call.o文件,看到sys_call_table还没有被定位。
readelf -s system_call.o
Symbol table '.symtab' contains 55 entries:
Num: Value Size Type Bind Vis Ndx Name
....
....
39: 00000000 0 NOTYPE GLOBAL DEFAULT UND sys_call_table
....
....
全局搜索sys_call_table,在sys.h这个文件中找到该变量,内容如下。通过继续分析,我们可以发现其仅在sched.c中引用过这个头文件。
sys.h
....
fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,
sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,
sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,
sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,
sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,
sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,
sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,
sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,
sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,
sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,
sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,
sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,
sys_setreuid,sys_setregid, sys_iam, sys_whoami };
但是我们通过对sched.c文件的分析,发现其没有直接使用sys_call_table[],因此我们可以推断出该步的目的是将syscall.h编译到sched.o的全局符号表中,等到链接的时候被syscall.o发现。
sched.o
19: 00000000 296 OBJECT GLOBAL DEFAULT 8 sys_call_table
system_call.o
39: 00000000 0 NOTYPE GLOBAL DEFAULT UND sys_call_table
后记
我们这篇文字简单分析了下Linux0.11中的系统调用方式,Linux0.11是个比较简单的操作系统,分析起来应该很简单,但是刚开始被这小小的系统调用难住了半天,记录一下。
1497



被折叠的 条评论
为什么被折叠?



