参考:《Linux内核完全注释:基于0.11内核》
该书下载地址:http://www.oldlinux.org/download/clk011c-3.0.pdf
阅读Linux内核完全注释traps.c,发现有个函数赵博士没有说明。好吧,自己动手丰衣足食。
traps.c里源码第72行是这样的:
printk("base: %p, limit: %p\n", get_base(current->ldt[1]), get_limit(0x17));
get_base()函数
这个get_base()函数在sched.h里定义的:
#define get_base(ldt) _get_base( ((char *)&(ldt)) )
...
#define _get_base(addr) ({\
unsigned long __base; \
__asm__("movb %3,%%dh\n\t" \
"movb %2,%%dl\n\t" \
"shll $16,%%edx\n\t" \
"movw %1,%%dx" \
:"=d" (__base) \
:"m" (*((addr)+2)), \
"m" (*((addr)+4)), \
"m" (*((addr)+7))); \
__base;})
光看这个肯定是一头雾水。好在参数current->ldt[1]
提供了线索。这个参数表示当前任务的代码段,这个可以从《Linux内核完全剖析:基于0.11内核v3.0》第132页看出。
既然是代码段了那当然要看一看段描述符通用格式,如图:
addr指向当前任务代码段字节0处,所以:
(addr)+2 ------ 上图第2行16-31位(指令是movw %1,%%dx
,所以是16位);
(addr)+4 ------ 上图第1行0-7位(指令是movb %2,%%dl
,所以是8位);
(addr)+7 ------ 上图第1行24-31位(指令是movb %3,%%dh
,所以是8位)
所以整个过程就是把三个基地址片段组装成完整的段基地址存入edx寄存器
get_limit()函数
#define get_limit(segment) ({ \
unsigned long __limit; \
__asm__("lsll %1,%0\n\tincl %0":"=r" (__limit):"r" (segment)); \
__limit;})
lsll指令加载段限长。然后再加1作为返回值。
此外这个背后又是满满的一页…
0x17就是截图里的第四个例子