程序11-3 linux/kernel/math/ea.c
1 /*
2 * linux/kernel/math/ea.c
3 *
4 * (C) 1991 Linus Torvalds
5 */
6
7 /*
8 * Calculate the effective address.
9 */
/*
* 计算有效地址。
*/
10
11 #include <stddef.h> // 标准定义头文件。本程序使用了其中的offsetof()定义。
12
13 #include <linux/math_emu.h> // 协处理器头文件。定义临时实数结构和387寄存器操作宏等。
14 #include <asm/segment.h> // 段操作头文件。定义了有关段寄存器操作的嵌入式汇编函数。
15
// info结构中各个寄存器在结构中的偏移位置。offsetof()用于求指定字段在结构中的偏移位
// 置。参见include/stddef.h文件。
16 static int __regoffset[] = {
17 offsetof(struct info,___eax),
18 offsetof(struct info,___ecx),
19 offsetof(struct info,___edx),
20 offsetof(struct info,___ebx),
21 offsetof(struct info,___esp),
22 offsetof(struct info,___ebp),
23 offsetof(struct info,___esi),
24 offsetof(struct info,___edi)
25 };
26
// 取info结构中指定位置处寄存器内容。
27 #define REG(x) (*(long *)(__regoffset[(x)]+(char *) info))
28
// 求2字节寻址模式中第2操作数指示字节SIB(Scale,Index,Base)的值。
29 static char * sib(struct info * info, int mod)
30 {
31 unsigned char ss,index,base;
32 long offset = 0;
33
// 首先从用户代码段中取得SIB字节,然后取出各个字段比特位值。
34 base = get_fs_byte((char *) EIP);
35 EIP++;
36 ss = base >> 6; // 比例因子大小ss。
37 index = (base >> 3) & 7; // 索引值索引代号index。
38 base &= 7; // 基地址代号base。
// 如果索引代号为0b100,表示无索引偏移值。否则索引偏移值offset=对应寄存器内容*比例因子。
39 if (index == 4)
40 offset = 0;
41 else
42 offset = REG(index);
43 offset <<= ss;
// 如果上一MODRM字节中的MOD不为零,或者Base不等于0b101,则表示有偏移值在base指定的
// 寄存器中。因此偏移offset需要再加上base对应寄存器中的内容。
44 if (mod || base != 5)
45 offset += REG(base);
// 如果MOD=1,则表示偏移值为1字节。否则,若MOD=2,或者base=0b101,则偏移值为4字节。
46 if (mod == 1) {
47 offset += (signed char) get_fs_byte((char *) EIP);
48 EIP++;
49 } else if (mod == 2 || base == 5) {
50 offset += (signed) get_fs_long((unsigned long *) EIP);
51 EIP += 4;
52 }
// 最后保存并返回偏移值。
53 I387.foo = offset;
54 I387.fos = 0x17;
55 return (char *) offset;
56 }
57
// 根据指令中寻址模式字节计算有效地址值。
58 char * ea(struct info * info, unsigned short code)
59 {
60 unsigned char mod,rm;
61 long * tmp = &EAX;
62 int offset = 0;
63
// 首先取代码中的MOD字段和R/M字段值。如果MOD=0b11,表示是单字节指令,没有偏移字段。
// 如果R/M字段=0b100,并且MOD不为0b11,表示是2字节地址模式寻址,因此调用sib()求
// 出偏移值并返回即可。
64 mod = (code >> 6) & 3; // MOD字段。
65 rm = code & 7; // R/M字段。
66 if (rm == 4 && mod != 3)
67 return sib(info,mod);
// 如果R/M字段为0b101,并且MOD为0,表示是单字节地址模式编码且后随32字节偏移值。
// 于是取出用户代码中4字节偏移值,保存并返回之。
68 if (rm == 5 && !mod) {
69 offset = get_fs_long((unsigned long *) EIP);
70 EIP += 4;
71 I387.foo = offset;
72 I387.fos = 0x17;
73 return (char *) offset;
74 }
// 对于其余情况,则根据MOD进行处理。首先取出R/M代码对应寄存器内容的值作为指针tmp。
// 对于MOD=0,无偏移值。对于MOD=1,代码后随1字节偏移值。对于MOD=2,代码后有4字节
// 偏移值。最后保存并返回有效地址值。
75 tmp = & REG(rm);
76 switch (mod) {
77 case 0: offset = 0; break;
78 case 1:
79 offset = (signed char) get_fs_byte((char *) EIP);
80 EIP++;
81 break;
82 case 2:
83 offset = (signed) get_fs_long((unsigned long *) EIP);
84 EIP += 4;
85 break;
86 case 3:
87 math_abort(info,1<<(SIGILL-1));
88 }
89 I387.foo = offset;
90 I387.fos = 0x17;
91 return offset + (char *) *tmp;
92 }
93