美文网首页
读懂 GCC 内联汇编

读懂 GCC 内联汇编

作者: Wi1ls努力努力再努力 | 来源:发表于2019-07-29 22:42 被阅读0次
#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)

相关文章

  • 读懂 GCC 内联汇编

    对于输出部和输入部编号 输入代表在汇编开始的时候进行的赋值,或者与汇编中的占位相对应,在本例中,汇编中只用到%4,...

  • gcc内联汇编

    文章来自这里:gcc内联汇编...... 在阅读Linux内核源码或对代码做性能优化时,经常会有在C语言中嵌入一段...

  • DPDK gcc内联汇编

    在DPDK中,使用gcc的内联汇编实现高效率的函数,比如自旋锁,cas操作等。今天简单介绍一下gcc内联汇编语法和...

  • GCC内联汇编基础

    原文作者 Sandeep.S英文原文 [https://www.ibiblio.org/gferg/ldp/GCC...

  • 译:GCC内联汇编入门

    原文: GCC-Inline-Assembly-HOWTO 1. 简介(Introduction.) 1.1 Co...

  • gcc内联汇编约束的测试

    关于gcc内联代码约束的测试! 所谓一切都是虚妄,我们来看一看生成的代码吧! 对应的汇编如下: 我稍微来说一下吧,...

  • 汇编语言如何与高级语言混编

    汇编混编的两种方式(内联汇编 和 外链汇编) 内联汇编 asm();这是内联汇编,编译器可以直接运行asm ( 汇...

  • 内联汇编

    lodsb: ds:[esi] -> al stosb: al -> es:[edi] SCASB: 比较 [ed...

  • 内联汇编

    AT&T汇编语法 GCC只支持AT&T汇编语法内嵌在C语言中。 Intel和AT&T汇编风格对比: AT&T寻址 ...

  • 内联汇编

    内联汇编是指在 C/C++ 代码中嵌入的汇编代码,与全部是汇编的汇编源文件不同,它们被嵌入到 C/C++ 的大环境...

网友评论

      本文标题:读懂 GCC 内联汇编

      本文链接:https://www.haomeiwen.com/subject/mxzirctx.html