读懂 GCC 内联汇编

#define _set_gate(gate_addr, type, dpl, addr)  \
do {  \
  int __do, __d1;  \
  __asm__ __volatile__ ("movw %%dx, %%ax\n\t"  \
      "movw   %4, %%dx\n\t"  \
      "movl   %%eax, %0\n\t"  \
      "movl   %%edx, %1"  \
      : "=m" (*((long *)(gate_addr))),  \
        "=m" (*(1+(long *) (gate_addr))),  "=&a" (__d0), "=&d" (__d1)  \
      :"i" ((short)(0x8000+(dpl<<13)+(type<<8))),  \
        "3" ((char *)(addr)), "2" (__KERNEL_CS <<16); \
  } while(0)

对于输出部和输入部编号

编号 代码 含义
输出
%0 "=m" (*((long *)(gate_addr)) 表示%0要与 gate_addr 结合
%1 "=m" ((1+(long)(gate_addr))) %1 与(gate_addr + 1)结合
%2 "=&a" (__d0) %2与局部变量__d0结合,存放在%%eax 中
%3 "=&d" (__d1) %3与局部变量__d1 结合,存放在%%edx 中
输入
%4 "i" ((short)(0x8000+(dpl<<13)+(type<<8))) 表示%4与操作数(0x8000+(dpl<<13+(type<<8)))结合
%5 "3" ((char*)(addr)) 表示变量与%3相同,即%%edx,将 addr 存入到 %%edx中
%6 "2" (__KERNEL_CS<<16) 表示与%2相同,即%%eax, 将__KERNEL_CS << 16存入

输入代表在汇编开始的时候进行的赋值,或者与汇编中的占位相对应,在本例中,汇编中只用到%4,于是%4 的地方用((short)(0x8000+(dpl<<13)+(type<<8)))代替。于是执行过程

  1. 将 addr 的内存值存入 %edx 寄存器,将(__KERNEL_CS<<16)值存入%eax 寄存器。初始化完成
  2. 第一行汇编,将%edx 的低 16 位移入%eax 的低 16 位(即%dx 移入%ax)。
    此时 %eax 的高 16 位便是(__KERNEL_CS),低 16 位即为 addr 的低 16 位。
  3. 将(0x8000+(dpl<<3)+(type<<8))移入%edx 的低 16 位。(此时%edx 的高 16 位保存 addr 的高 16 位,低 16 位为(0x8000+(dpl<<3)+(type<<8)))
  4. 将%eax 的值保存到 *gate_addr 中。
  5. 将%edx中的地保存到*(gate_addr+1)中
  6. 根据输出部赋值。

--

内联汇编可以分为四个部分

指令部 : 输出部 : 输入部 : 损坏部

约束条件:

字母 含义
m / v / o 表示内存单元
r 表示任何寄存器
q 表示寄存器 eax, ebx, ecx, edx 之一
i / h 表示直接操作数
E / F 表示浮点数
g 表示任意
a , b , c , d 分表表示 eax, ebx, ecx, edx
S, D 分别表示 esi, edi
I 表示常数(0-31)

辅助的参考视频,最后一个例子,可以直接拉到 5 分 35 秒


为什么要读懂呢,因为 Linux内核很多地方都采用了 GCC 内联汇编。
以中断向量表为例.

void __init trap_init(void){
  set_trap_gate(0,&divide_error);

  set_trap_gate(0,&divide_error);
  set_trap_gate(1,&debug);
  set_intr_gate(2,&nmi);
  set_system_gate(3,&int3); /* int3-5 can be called from all */
  set_system_gate(4,&overflow);
  set_system_gate(5,&bounds);
  set_trap_gate(6,&invalid_op);
  set_trap_gate(7,&device_not_available);
  set_trap_gate(8,&double_fault);
  set_trap_gate(9,&coprocessor_segment_overrun);
  set_trap_gate(10,&invalid_TSS);
  set_trap_gate(11,&segment_not_present);
  set_trap_gate(12,&stack_segment);
  set_trap_gate(13,&general_protection);
  set_trap_gate(14,&page_fault);
  set_trap_gate(15,&spurious_interrupt_bug);
  set_trap_gate(16,&coprocessor_error);
  set_trap_gate(17,&alignment_check);
  set_trap_gate(18,&machine_check);
  set_trap_gate(19,&simd_coprocessor_error);

  set_system_gate(SYSCALL_VECTOR,&system_call);

  /*
  * default LDT is a single-entry callgate to lcall7 for iBCS
   * and a callgate to lcall27 for Solaris/x86 binaries
  */
  set_call_gate(&default_ldt[0],lcall7);
  set_call_gate(&default_ldt[4],lcall27);
}

static set_intr_gate(unsigned int n, void* addr){
  -set_gate(idt_table+n, 14, 0, addr);
}

static void __init set_trap_gate(unsigned int n, void* addr){
  _set_gate(idt_table+n, 15, 0, addr);
}

static void __init set_system_gate(unsigned int n, void* addr){
  _set_gate(idt_table+n, 15, 3, addr);
}

static void __init set_call_gate(void* a, void*addr){
  _set_gate(a, 12, 3, addr);
}

其中 idt_table 是就是中断向量表

struct desc_struct{
  unsigned long a, b;
};

struct desc_struct id_table[256] __attribute__((__section__(".data.idt"))) = {{0,0},}

于是在结合上面的 GCC 内联汇编代码,中断向量表的每一项 desc_struct,其
a赋值为

高 16 位 低 16 位
(__KERNEL_CS) addr 的低 16 位

b 赋值为

高 16 位 低 16 位
addr 的高 16 位 (0x8000+(dpl<<13)+(type<<8))

再结合各参数在 i386 中,(__KERNEL_CS)宏定义为0x10。

类型 dpl type (0x8000+(dpl<<13)+(type<<8))
set_intr_gate 14 0 0x24000(100100000000000000)
set_trap_gate 15 0 0x26300(100110000000000000)
set_system_gate 15 3 0x26300(100110000000000000)
set_call_gate 12 3 0x20300(100000001100000000)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容