GCC内联汇编

GCC,linux的GNU C编译器使用AT&T/UNIX汇编语法。

(一):AT&T汇编和intel汇编的不同

1:前缀

在intel语法中,寄存器和立即数都没有前缀,而在AT&T中,寄存器使用前缀“%”,而立即数前面使用前缀“$”;

在intel语法中,十六进制和二进制立即数后面缀以”h”和“b”,但在AT&T语法中,在前面缀以”0x”,

2:操作数的方向不同

AT&T和intel汇编操作数的方向正好相反。在intel语法中,第一个操作数是目的操作数,第二个操作数是源操作数,

而在AT&T语法中,第一个操作数是源操作数,第二个是目的操作数

例如:

在 intel语法中: mov eax,[ecx];

在AT&T语法中,movl (%ecx),%eax;

3:内存单元操作数

从上面的例子可以看出,内存操作数也是不同的,在intel语法中,基址寄存器用”[]”扩起来,而在AT&T中,使用”()”括起来

例如:

在intel语法中 mov eax,[ecx+5];

在AT&T语法中 movl 5(%ecx),%eax;

4:间接寻址方式

intel语法的间接寻址指令格式为seggreg:[base+index*scale+disp],而在AT&T语法中,间接寻址的指令格式为

%seggreg:disp(base,index,scale)

很明显,AT&T语法稍微有些难懂,这种寻址方式通常用在访问数据结构数组中某个元素内的一个字段,base为数组的起始地址,index为下表,

scale为每一个数组元素的大小,如果数组元素还是一个结构,则disp为具体字段在结构中的位移。

5:操作码的后缀

在上面的例子中还能看到,AT&T的操作码后面还有一个后缀,用来表示操作码的大小,”l”表示长整数,32位;”w“表示”字“,16位;”b“表示

字节,8位,而在intel语法中,则要在内存单元操作数中加上byte ptr,word ptr和dword ptr,其中dword代表“long”。

(二):AT&T汇编的相关知识

1:GNU汇编程序GAS

当编写完一段程序之后,需要对其进行汇编和链接,在linux下有两种方式,第一种就是使用汇编程序GAS和链接程序ld;第二种就是

使用gcc。首先学习一个GAS和ld的使用;

GAS将汇编语言源文件(.s)转换为目标文件(.o),基本语法为

as finame.s -o filename

ld将目标文件转换成可执行文件

ld finame.o -o filename

如果使用gcc的话,应该要这样写:

gcc filename.S -o filename //其中的扩展名一定要大写,gcc可以自动识别汇编程序中的C预处理指令

2:AT&T中的节(Section)

AT&T语法,一个节由关键字.section来识别,编写汇编语言的时候,至少需要以下三种节

.section .data : 这种节包含程序已初始化的数据,即包含具有初始值的变量,例如

.section data

hello : .string “hello world\n”

hello_len : .long 13

.section .bss : 这种节包含程序还未初始化的数据,也就是说,包含没有初值的变量,当操作系统将

装入这个程序的时候,这些变量都将变为0。例如

.section bss

name :.fill 30

name_len : .long 0

如果,一不小心在bss节中给变量赋值了,这个变量的值初始化的时候也会变成0.

注意:使用bss节相比使用data节的优势在于,使用bss节不占用磁盘空间,一个长整数就足以存放.bss节,

当程序装入内存的时候,操作系统也只分配给这个节4字节的大小。

编译程序把.data和.bss节在4字节上对齐,例如 .data总共有34字节,那么编译器将他对齐在36字节上。

.section .text : 这个字节包含程序的代码,他是只读节,而.data和.bss是读写节

3:汇编程序指令

上面的.section就是汇编程序指令的一种,GNU汇编程序提供了很多这样的汇编指令,这种指令都是一句点

开头的,后面跟指令名(小写字母)。

(1)、.ascii “string”…

.ascii表示零个或多个(用逗号隔开)字符串,并把每个字符串(结尾不自动加0字节)中的字符放在连续的地址单元。

还有一个与.ascii相似的.asciz,z代表0,即每个字符串的结尾自动加上一个“0”字节。

(2)、.byte表达式

.byte表达式表示0个或者是多个表达式,用逗号隔开,每个表达式被放在下一个字节单元中

(3)、.fill表达式

形式: .fill repeat,size,value

含义是反复拷贝repeat次size个字节,并以value填充

例如:

.fill NR_CPUS*4,8,0;

拷贝4次8个字节大小,并以0填充。

size和value为可选项,如果value值不存在,则默认为0,如果size不存在,则默认为1

(4)、.global symbol //相当于定义全局符号

.global 使得连接程序能够看得到symbol,如果你的局部程序中定义了symbol,那么与这个局部程序连接的其他局部

程序也能存取symbol.

例如:

.global SYMBOL_NAME(idt)

.globol SYMBOL_NAME(gdt)

定义idt和gdt全局变量

(5)、quad bugnums

.quad 表示零个或多个bignums,用逗号分隔,对于每一个bignums,缺省值是8个字节,如果超过8个字节,则会打印一个

警告信息,并只取bugnums最低的8个字节

例如,对于全局描述符的填充就是用到这个指令

.quad 0x00cf9a000000ffff /* 0x10 kernel 4G code at 0x00000000 */

(6)、.rept count

把.rept指令与.endr指令之间的行重复count次,如

.rept 3

.long 0

.endr

相当于:

.long 0

.long 0

.long 0

(7)、space size,fill

这个指令保留size个字节的空间,每个字节值为fill,如果逗号和fill被省略,则fill默认为0

.space 1024

表示保留1024个字节的空间,并且每个字节的值为0

(8)、.word expression

这个表达式表示任意一节中的一个或多个表达式,用逗号隔开,表达式的值占两个字节

例如

gdt_descr:

.word GDT_ENTRIES*8-1

变量gdt_descr的值置为GDT_ENTRIES*8-1

(9)、.long expression

与.word相似,不过这里是4个字节

(10)、.org new_lc,fill

把当前节的位置计数器提前到new_lc,new_lc或者是一个常量表达式,或者是一个与当前子节处于同一节的表达式。

也就是说.org不能横跨节,如果new_lc是个错误的值,则.org被忽略,.org只能增加位置计数器的值或者是保持不变,

但是绝不能让位置计数器的值倒退

例如:.org 0x200

ENTRY(pg0);

表示把位置计数器置为0x200,这个位置存放的就是临时页表pg0

(三):GCC嵌入式汇编

在linux内核源代码中,有很多C语言的函数中嵌入一段汇编语言执行段,这就是GCC提供的asm功能。

例如:

#deine read_cr0() ({        unsigned int __dummy;        __asm__(            "movl %%cr0,%0\n\t"    \
        : "=r"(__dummy));        __dummy;    })

读取寄存器CR0的一个宏。

这并不是标准C所定义的形式,而是GCC对C语言的扩充,一般情况下,嵌入式汇编语言要比单纯的汇编语言代码复杂的多,

因为这里存在怎样分配和使用寄存器,以及把代码中的变量应该存放在哪个寄存器中。

1:嵌入式语言的一般形式

__asm__ [__volatile__]("<asm_route>":output:input:modify);

其中,asm表示汇编程序的开始,而volatile 是可选的,避免asm指令被删除,移动或者是组合。

“”是汇编指令部分,例如,”movl %%cr0,%0\n\t”。数字前加前缀”%”,如%1,%2表示使用

寄存器的样板操作数。可以使用的操作数的总数取决于具体的CPU中通用寄存器的数量。由于在这些样板操作数

的前缀使用了%,因此,在用到具体的寄存器是就在前面加两个%,如%%cr0.

输出部分:

用以规定输出变量如何与寄存器结合的约束,输出部分可以有多个约束,互相以逗号分开。每个约束以“=”

开头,接着用一个字母来表示操作数的类型,然后是关于变量结合的约束。例如:

“=r”(__dummy)

“=r”表示相应的目标操作数,也就是指令部分的%0,可以使用任何一个通用寄存器,并且变量__dummy

存放在这个寄存器中。

但如果是”=m”就表示相应的目标操作数就存放在内存单元__dummy中

r:I/O,表示使用一个通用寄存器,由GCC在%eax/%ax/%al、%ebx/%bx/%bl、%ecx/%cx/%cl、%edx/%dx/%dl中选取一个GCC认为是合适的;

q:I/O,表示使用一个通用寄存器,与r的意义相同;

g:I/O,表示使用寄存器或内存地址;

m:I/O,表示使用内存地址;

a:I/O,表示使用%eax/%ax/%al;

b:I/O,表示使用%ebx/%bx/%bl;

c:I/O,表示使用%ecx/%cx/%cl;

d:I/O,表示使用%edx/%dx/%dl;

D:I/O,表示使用%edi/%di;

S:I/O,表示使用%esi/%si;

f:I/O,表示使用浮点寄存器;

t:I/O,表示使用第一个浮点寄存器;

u:I/O,表示使用第二个浮点寄存器;

A:I/O,表示把%eax与%edx组合成一个64位的整数值;

o:I/O,表示使用一个内存位置的偏移量;

V:I/O,表示仅仅使用一个直接内存位置;

i:I/O,表示使用一个整数类型的立即数;

n:I/O,表示使用一个带有已知整数值的立即数;

F:I/O,表示使用一个浮点类型的立即数;

输入部分:输入部分与输出部分相似,但没有”=”,如果输入部分的一个操作数所要求使用的寄存器

与前面输出部分某个约束所要求的是同一个寄存器,那就把对应操作数的编号放在约束条件中。

修改部分:这部分常常以”memory”为约束条件,以表示操作完成后,内存中的内容已有改变,如果原来

某个寄存器的内容来自内存,那么现在内存中这个单元的内容已经改变。

注意,指令部分是必选项,输出部分,输入部分以及修改部分为可选项,如果输入部分存在,而输出

部分不存在的话,冒号“:”要保留,如果”memory”存在的时候,三个分号都要保留。

时间: 2024-11-05 09:32:53

GCC内联汇编的相关文章

最牛X的GCC 内联汇编

导读 正如大家知道的,在C语言中插入汇编语言,其是Linux中使用的基本汇编程序语法.本文将讲解 GCC 提供的内联汇编特性的用途和用法.对于阅读这篇文章,这里只有两个前提要求,很明显,就是 x86 汇编语言和 C 语言的基本认识. 1. 简介 1.1 版权许可 Copyright (C) 2003 Sandeep S. 本文档自由共享:你可以重新发布它,并且/或者在遵循自由软件基金会发布的 GNU 通用公共许可证下修改它:也可以是该许可证的版本 2 或者(按照你的需求)更晚的版本. 发布这篇文

操作系统学习之GCC内联汇编

GCC内联汇编(INLINE ASSEMBLY) 什么是内联汇编(Inline assembly)? 1.这是GCC对C语言的扩张,就是在C代码里面去写汇编代码 2.可以直接在C的语句中插入汇编指令 有何用处? 1.C语言不足以完成所有CPU的指令, 特别是有一些特权指令,比如加载gdt表(Global Descriptor Table 全局描述符表),从而使用汇编代码来完成 2.用汇编在C语言中手动优化,特别是在操作系统当中,使用汇编对操作系统的掌控更为精准,更加准确. 如何工作? 1.用给定

Android漫游记(5)---ARM GCC 内联汇编烹饪书(附实例分析)

原文链接(点击打开链接) 关于本文档 GNU C编译器针对ARM RISC处理器,提供了内联汇编支持.利用这一非常酷炫的特性,我们可以用来优化软件代码中的关键部分,或者可以使用针对特定处理的汇编处理指令. 本文假定,你已经熟悉ARM汇编语言.本文不是一篇ARM汇编教程,也不是C语言教程. GCC汇编声明 让我们从一个简单的例子开始.下面的一条ARM汇编指令,你可以添加到C源码中. /* NOP example-空操作 */ asm("mov r0,r0"); 上面的指令,讲r0寄存器的

GCC内联汇编入门

原文为GCC-Inline-Assembly-HOWTO,在google上可以找到原文,欢迎指出翻译错误. 中文版说明 由于译者水平有限,故译文出错之处,还请见谅.C语言的关键字不译,一些单词或词组(如colbber等)由于恐怕译后词不达意,故并不翻译,由下面的单词表代为解释,敬请见谅. 英文原文中的单词和词组: operand:操作数,可以是寄存器,内存,立即数. volatile:易挥发的,是C语言的关键字. constraint: 约束. register: 本文指CPU寄存器. asm:

内核bug.h以及GCC内联汇编的学习

所有的学习的内容都在注释当中,我的学习过程是,看到这个代码段之后,将其中需要的只是去大体的学习一遍.争取能够做到理解整体的部分. 下面展示出我的学习的代码: #ifndef _I386_BUG_H #define _I386_BUG_H /* * Tell the user there is some problem. * 告诉用户出现了一些问题 * The offending file and line are encoded after the "officially * undefined

[翻译] GCC 内联汇编 HOWTO

GCC 内联汇编 HOWTO v0.1, 01 March 2003. * * * 本 HOWTO 文档将讲解 GCC 提供的内联汇编特性的用途和用法.对于阅读这篇文章,这里只有两个前提要求,很明显,就是 x86 汇编语言和 C 语言的基本认识. [TOC] 原文链接与说明 http://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html 本翻译文档原文选题自 Linux中国 ,翻译文档版权归属 Linux中国 所有 1. 简介 1.

【转贴】GCC内联汇编基础

原文作者 Sandeep.S英文原文 [https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html] 本文将介绍GCC编译环境下,在C语言代码中嵌入汇编代码的基本方法.阅读本文需要您具备80X86汇编语言和C语言的基础知识.为了使中文描述更加清楚自然,翻译过程中加入了稍许解释和意译部分. 简介 版权/反馈/勘误/感谢等信息.[^ 1][^ 1]:这里信息价值不大,没有翻译.具体参加原文:https://www.ibiblio.

GCC 内联汇编约束字符&quot;m&quot;的用法

首先是这么一段代码(例一): #include<stdio.h> void main() {     char c;     //int tmp;     char *s="abcdefg";     asm("movb %1,%0\n\t"     :"=d"(c)     :"m"(*s));     printf("out:%c\n",c); } 这段代码运行后会出现什么结果呢?很显然,是

VC内联汇编和GCC内联汇编的语法区别

VC: #include <stdio.h> main(){ int a = 1; int b = 2; int c; __asm{ mov eax,a mov ebx,b mov ecx,1h add eax,ebx mov c,ecx } printf("%x\n", c); } GCC: #include <stdio.h> main(){ int a = 1; int b = 2; int c; asm( "add %2,%0" //