没有合适的资源?快使用搜索试试~ 我知道了~
首页AT&T汇编语言--GCC下反汇编格式
资源详情
资源评论
资源推荐

AT&T ASM Syntax
(转载)
0.3.1 Overview
开发一个 OS,尽管绝大部分代码只需要用 C/C++等高级语言就可以了,但至少和硬件相关
部分的代码需要使用汇编语言,另外,由于启动部分的代码有大小限制,使用精练的汇编可
以缩小目标代码的 Size。另外,对于某些需要被经常调用的代码,使用汇编来写可以提高性
能。所以我们必须了解汇编语言,即使你有可能并不喜欢它。
如果你是计算机专业的话,在大学里你应该学习过 Intel 格式的 8086/80386 汇编,这里就不
再讨论。如果我们选择的 OS 开发工具是 GCC 以及 GAS 的话,就必须了解 AT&T 汇编语言
语法,因为 GCC/GAS 只支持这种汇编语法。
本书不会去讨论 8086/80386 的汇编编程,这类的书籍很多,你可以参考它们。这里只会讨
论 AT &T 的汇编语法,以及 GCC 的内嵌汇编语法。
0.3.2 Syntax
1.寄存器引用
引用寄存器要在寄存器号前加百分号%,如“movl %eax, %ebx”。
80386 有如下寄存器:
• 8 个 32-bit 寄存器 %eax,%ebx,%ecx,%edx,%edi,%esi,%ebp,%esp;
• 8 个 16-bit 寄存器,它们事实上是上面 8 个 32-bit 寄存器的低 16 位:%ax,%bx,
%cx,%dx,%di,%si,%bp,%sp;
• 8 个 8-bit 寄存器:%ah,%al,%bh,%bl,%ch,%cl,%dh,%dl。它们事实上是
寄存器%ax,%bx,%cx,%dx 的高 8 位和低 8 位;
• 6 个段寄存器:%cs(code),%ds(data),%ss(stack), %es,%fs,%gs;
• 3 个控制寄存器:%cr0,%cr2,%cr3;
• 6 个 debug 寄存器:%db0,%db1,%db2,%db3,%db6,%db7;
• 2 个测试寄存器:%tr6,%tr7;
• 8 个浮点寄存器栈:%st(0),%st(1),%st(2),%st(3),%st(4),%st(5),%st(6),%st(7)。
2. 操作数顺序
操作数排列是从源(左)到目的(右),如“movl %eax(源), %ebx(目的)”

3. 立即数
使用立即数,要在数前面加符号$, 如“movl $0x04, %ebx”
或者:
para = 0x04
movl $para, %ebx
指令执行的结果是将立即数 04h 装入寄存器 ebx。
4. 符号常数
符号常数直接引用 如
value: .long 0x12a3f2de
movl value , %ebx
指令执行的结果是将常数 0x12a3f2de 装入寄存器 ebx。
引用符号地址在符号前加符号$, 如“ movl $value, % ebx”则是将符号 value 的地址装入寄存
器 ebx。
5. 操作数的长度
操作数的长度用加在指令后的符号表示 b(byte, 8-bit), w(word, 16-bits), l(long, 32-bits),如
“movb %al, %bl”,“movw %ax, %bx”,“movl %eax, %ebx ”。
如果没有指定操作数长度的话,编译器将按照目标操作数的长度来设置。比如指令“mov %ax,
%bx”,由于目标操作数 bx 的长度为 word,那么编译器将把此指令等同于“movw %ax, %bx”。
同样道理,指令“mov $4, %ebx”等同于指令“movl $4, %ebx”,“push %al”等同于“pushb
%al”。对于没有指定操作数长度,但编译器又无法猜测的指令,编译器将会报错,比如指
令“push $4”。
6. 符号扩展和零扩展指令
绝大多数面向 80386 的 AT&T 汇编指令与 Intel 格式的汇编指令都是相同的,符号扩展指令
和零扩展指令则是仅有的不同格式指令。
符号扩展指令和零扩展指令需要指定源操作数长度和目的操作数长度,即使在某些指令中这
些操作数是隐含的。
在 AT&T 语法中,符号扩展和零扩展指令的格式为,基本部分"movs"和"movz"(对应 Intel
语法的 movsx 和 movzx),后面跟上源操作数长度和目的操作数长度。movsbl 意味着 movs

(from)byte (to)long;movbw 意味着 movs (from)byte (to)word;movswl 意味着
movs (from)word (to)long。对于 movz 指令也一样。比如指令“movsbl %al, %edx”意
味着将 al 寄存器的内容进行符号扩展后放置到 edx 寄存器中。
其它的 Intel 格式的符号扩展指令还有:
• cbw -- sign-extend byte in %al to word in %ax;
• cwde -- sign-extend word in %ax to long in %eax;
• cwd -- sign-extend word in %ax to long in %dx:%ax;
• cdq -- sign-extend dword in %eax to quad in %edx:%eax;
对应的 AT&T 语法的指令为 cbtw,cwtl,cwtd,cltd。
7. 调用和跳转指令
段内调用和跳转指令为"call","ret"和"jmp",段间调用和跳转指令为"lcall","lret"和"ljmp"。
段间调用和跳转指令的格式为“lcall/ljmp $SECTION, $OFFSET”,而段间返回指令则为“lret
$STACK-ADJUST”。
8. 前缀
操作码前缀被用在下列的情况:
• 字符串重复操作指令(rep,repne);
• 指定被操作的段(cs,ds,ss,es,fs,gs);
• 进行总线加锁(lock);
• 指定地址和操作的大小(data16,addr16);
在 AT&T 汇编语法中,操作码前缀通常被单独放在一行,后面不跟任何操作数。例如,对
于重复 scas 指令,其写法为:
repne
scas
上述操作码前缀的意义和用法如下:
• 指定被操作的段前缀为 cs,ds,ss,es,fs,和 gs。在 AT&T 语法中,只需要按照
section:memory-operand 的格式就指定了相应的段前缀。比如:
lcall %cs:realmode_swtch
• 操作数/地址大小前缀是“data16”和"addr16",它们被用来在 32-bit 操作数/地址
代码中指定 16-bit 的操作数/地址。
• 总线加锁前缀“lock”,它是为了在多处理器环境中,保证在当前指令执行期间禁止
一切中断。这个前缀仅仅对 ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG,DEC, INC,
NEG, NOT, OR, SBB, SUB, XOR, XADD,XCHG 指令有效,如果将 Lock 前缀用在其
它指令之前,将会引起异常。

• 字符串重复操作前缀"rep","repe","repne"用来让字符串操作重复“%ecx”次。
9. 内存引用
Intel 语法的间接内存引用的格式为:
section:[base+index*scale+displacement]
而在 AT&T 语法中对应的形式为:
section:displacement(base,index,scale)
其中,base 和 index 是任意的 32-bit base 和 index 寄存器。scale 可以取值 1,2,4,8。如果
不指定 scale 值,则默认值为 1。section 可以指定任意的段寄存器作为段前缀,默认的段寄
存器在不同的情况下不一样。如果你在指令中指定了默认的段前缀,则编译器在目标代码中
不会产生此段前缀代码。
下面是一些例子:
-4(%ebp):base=%ebp,displacement=-4,section 没有指定,由于 base=%ebp,所以默认的
section=%ss,index,scale 没有指定,则 index 为 0。
foo(,%eax,4):index=%eax,scale=4,displacement=foo。其它域没有指定。这里默认的
section=%ds。
foo(,1):这个表达式引用的是指针 foo 指向的地址所存放的值。注意这个表达式中没有 base
和 index,并且只有一个逗号,这是一种异常语法,但却合法。
%gs:foo:这个表达式引用的是放置于%gs 段里变量 foo 的值。
如果 call 和 jump 操作在操作数前指定前缀“*”,则表示是一个绝对地址调用/跳转,也就
是说 jmp/call 指令指定的是一个绝对地址。如果没有指定"*",则操作数是一个相对地址。
任何指令如果其操作数是一个内存操作,则指令必须指定它的操作尺寸(byte,word,long),
也就是说必须带有指令后缀(b,w,l)。
0.3.3 GCC Inline ASM
GCC 支持在 C/C++代码中嵌入汇编代码,这些汇编代码被称作 GCC Inline ASM——GCC 内联
汇编。这是一个非常有用的功能,有利于我们将一些 C/C++语法无法表达的指令直接潜入
C/C++代码中,另外也允许我们直接写 C/C++代码中使用汇编编写简洁高效的代码。
1.基本内联汇编
GCC 中基本的内联汇编非常易懂,我们先来看两个简单的例子:

__asm__("movl %esp,%eax"); // 看起来很熟悉吧!
或者是
__asm__("
movl $1,%eax // SYS_exit
xor %ebx,%ebx
int $0x80
");
或
__asm__(
"movl $1,%eax\r\t" \
"xor %ebx,%ebx\r\t" \
"int $0x80" \
);
基本内联汇编的格式是
__asm__ __volatile__("Instruction List");
1、__asm__
__asm__是 GCC 关键字 asm 的宏定义:
#define __asm__ asm
__asm__或 asm 用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头
的,是必不可少的。
2、Instruction List
Instruction List 是汇编指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或
__asm__ ("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。但并非
所有 Instruction List 为空的内联汇编表达式都是没有意义的,比如:__asm__
("":::"memory"); 就非常有意义,它向 GCC 声明:“我对内存作了改动”,GCC 在编译的
时候,会将此因素考虑进去。
我们看一看下面这个例子:
$ cat example1.c
int main(int __argc, char* __argv[])
{
剩余28页未读,继续阅读


















安全验证
文档复制为VIP权益,开通VIP直接复制

评论1