zl程序教程

您现在的位置是:首页 >  后端

当前栏目

编程参考 - GCC中的Basic ASM

编程 参考 gcc ASM Basic
2023-09-11 14:22:08 时间

asm关键字允许你在C代码中嵌入汇编程序指令。GCC提供两种形式的内联asm语句。一种是基本asm语句,是没有操作数的语句(见基本asm),而另一种扩展asm语句(见扩展asm)包括一个或多个操作数。在函数内部混合使用C语言和汇编语言时,扩展形式是首选,但要在顶层(函数外)使用或包含汇编语言,你必须使用基本asm。

你也可以使用asm关键字来覆盖C语言中某个符号的汇编语言名称,或者将C语言变量放在一个特定的寄存器中。

基本asm / Basic Asm

一个基本的asm语句的语法如下:

asm asm-qualifiers ( AssemblerInstructions )

对于C语言来说,asm关键字是一个GNU扩展。编写的代码要能够用-ansi或-std选项来编译的话,即选择不使用GNU扩展的C语言,要使用__asm__而不是asm 关键字。对于C++语言,asm是一个标准的关键字,但__asm__可以用于选项-fno-asm下编译的代码。

修饰符 / Qualifiers

volatile

这个可选的volatile修饰符没有作用。所有基本的asm块都是隐含的volatile。

inline

使用inline修饰符,是为了将这些语句内联,asm语句的占用空间会尽可能最小。

参数 / Parameters

Assembler Instructions / 汇编指令

在修饰符后面,括号内包含的是汇编指令或汇编代码,是以字面字符串的形式出现。字符串内容可以包含任何能被汇编器识别的指令以及伪指令。GCC并不解析字符串中的汇编器指令,也不知道其含义,也不知道它们是否是有效的汇编代码。

指令 / Instructions:机器码的助记符,有对应的机器码。

伪指令 / Directives:没有对应的机器码,由编译器执行,计算机并不执行。

在一个asm基本语句中的汇编指令部分,可以包含多条汇编指令,分隔方法使用当前系统的汇编代码的分隔符即可。还有经常使用的换行显示,一般使用换行符加制表符,写作‘\n\t’。

注意,一些类型的汇编语言允许用分号作为行的分隔符。但是,还有些汇编语言使用分号作为注释起始符。

备注 / Remarks

使用扩展汇编形式,一般会产生更小、更安全和更有效的代码,在大多数情况下比基础汇编形式是更好的选择。

但是有两种情况,是只能使用基础汇编的:

1,扩展汇编代码只能放在C函数内部,如果是函数外的话,就只能使用基础汇编。或者说是file scope, top-level下,要写inline的汇编代码,只能用基础汇编。

可以写的内容,包括汇编指令、汇编宏,汇编函数等。在函数外的基本汇编形式的语句,不能加修饰符。

2,声明了naked属性的函数,里面只能使用基本汇编。

在基本asm里,要想安全的访问C数据和调用C函数比想象中复杂。要访问C语言的数据,最好使用扩展的asm。

不要期望一系列asm语句在编译后保持完全连续。如果某些指令需要在输出中保持连续,请将它们放在一个多指令asm语句中。注意,GCC的优化器可以改变asm语句相对于其他代码的位置,包括一些跳转语句(across jumps)。

asm语句不能跳转到其他asm语句。GCC无法识别这些跳转,在优化编译时就无法处理它们。仅在扩展asm中支持从asm跳转到C标签。

在某些情况下,GCC在优化时可能会复制(或删除重复的)汇编代码。如果汇编代码定义了符号或标签,这可能会导致编译期间出现意外的重定义符号错误。

警告:

C标准没有指定asm的语法,所以在不同编译器中,会出现不兼容的情况。而这些不兼容性可能不会产生编译器警告或错误。

GCC不解析基本asm的AssemblerInstructions,这意味着里面的代码无法向编译器传达信息。GCC也看不到asm中的符号,可能会将其作为未引用的符号丢弃。它也不知道汇编程序代码的副作用,例如对内存或寄存器的修改。与某些编译器不同,GCC假设汇编代码不会对通用寄存器进行任何更改。这一假设可能会在未来的版本中发生变化。

为了避免将来编译出现问题,比如语法的更改和编译器的兼容性问题,请考虑用扩展asm替换基本asm。

编译器将基本asm中的汇编程序指令逐字复制到汇编语言输出文件中,而不处理汇编代码,也包括可以在扩展asm中使用的“%”运算符。这导致了基本asm字符串和扩展asm模板之间的细微差异。例如,要引用寄存器,可以在基本asm中使用“%eax”,在扩展asm中可以使用“%%eax”。

在支持多种汇编语言的目标机器平台上(如x86),所有基本asm都使用-masm命令行选项来指定汇编语言(请参见x86选项)。基本asm不能为不同汇编语言提供不同汇编代码。

在包含非空的汇编语句的基本asm中,GCC假设这些其构成的汇编语句块,不会更改通用寄存器,但可以读写全局变量。

下面代码是在i386平台上的一个基本asm语句例子:

/* Note that this code will not compile with -masm=intel */

#define DebugBreak() asm("int $3")

简单的函数中使用例子:

int main()

{

asm("nop");   // 执行一条空语句

asm("movl $1, %eax"); // 将整数1赋值给eax寄存器

return 0;

}

参考:

Using Assembly Language with C (Using the GNU Compiler Collection (GCC))

GCC-Inline-Assembly-HOWTO