c和汇编混编语法.docx

上传人:b****1 文档编号:2479645 上传时间:2022-10-30 格式:DOCX 页数:30 大小:36.36KB
下载 相关 举报
c和汇编混编语法.docx_第1页
第1页 / 共30页
c和汇编混编语法.docx_第2页
第2页 / 共30页
c和汇编混编语法.docx_第3页
第3页 / 共30页
c和汇编混编语法.docx_第4页
第4页 / 共30页
c和汇编混编语法.docx_第5页
第5页 / 共30页
点击查看更多>>
下载资源
资源描述

c和汇编混编语法.docx

《c和汇编混编语法.docx》由会员分享,可在线阅读,更多相关《c和汇编混编语法.docx(30页珍藏版)》请在冰豆网上搜索。

c和汇编混编语法.docx

c和汇编混编语法

AT&T汇编与GCC内嵌汇编语法

来源:

ChinaUnix博客 日期:

2006.07.1709:

34 (共有0条评论)我要评论

 

    

    

    

    

    

    

    

    

讨论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

movlvalue,

%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-extendbytein%altowordin%ax;

cwde

--sign-extendwordin%axtolongin%eax;

cwd

--sign-extendwordin%axtolongin%dx:

%ax;

cdq

--sign-extenddwordin%eaxtoquadin

%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)。

.3

GCCInlineASM

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

InstructionList是汇编指令序列。

它可以是空的,比如:

__asm__

__volatile__("");或__asm__

("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。

但并非所有Instruction

List为空的内联汇编表达式都是没有意义的,比如:

__asm__

("":

:

:

"memory");

就非常有意义,它向GCC声明:

“我对内存作了改动”,GCC在编译的时候,会将此因素考虑进去。

我们看一看下面这个例子:

$

catexample1.c

intmain(int__argc,char*__argv[])

{

int*__p=(int*)__argc;

(*__p)=9999;

//__asm__("":

:

:

"memory");

if((*__p)

==9999)

return5;

return(*__p);

}

这段代码中,那条内联汇编是被注释掉的。

在这条内联汇编之前,内存指针__p所指向的内存被赋值为9999,随即在内联汇编之后,一条if语句判断__p

所指向的内存与9999是否相等。

很明显,它们是相等的。

GCC在优化编译的时候能够很聪明的发现这一点。

我们使用下面的命令行对其进行编译:

$

gcc-O-S

example1.c

选项-O表示优化编译,我们还可以指定优化等级,比如-O2表示优化等级为2;选项-S表示将C/C++源文件编译为汇编文件,文件名和C/C++文件一样,只不过扩展名由.c变为.s。

我们来查看一下被放在example1.s中的编译结果,我们这里仅仅列出了使用gcc

2.96在redhat

7.3上编译后的相关函数部分汇编代码。

为了保持清晰性,

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 职业规划

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1