GCC 扩展内联汇编简介

基本内联汇编

基本内联汇编格式比较直观,可以直接这样写:

asm("assembly code");

例如:

asm("movl %ecx, %eax"); /* 把 ecx 内容移动到 eax */
__asm__("movb %bh , (%eax)"); /* 把bh中一个字节的内容移动到eax指向的内存 */

扩展内联汇编

前面讨论的基本内联汇编只涉及到嵌入汇编指令,而在扩展形式中,我们还可以指定操作数,并且可以选择输入输出寄存器,以及指明要修改的寄存器列表。对于要访问的寄存器,并不一定要显式指明,也可以留给GCC自己去选择,这可能让GCC更好去优化代码。扩展内联汇编格式如下:

asm 汇编限定符 (汇编模板
	:"限定符1"(输出操作数1),"限定符2"(输出操作数2).......	//可选的
	:"限定符1"(输入操作数1),"限定符2"(输入操作数2).......	//可选的
	:已改写寄存器列表	 					//可选的
);

其中汇编模板为汇编指令部分。括号内的操作数都是C语言表达式中常量字符串。不同部分之间使用冒号分隔。相同部分语句中的每个小部分用逗号分隔。最多可以指定10个操作数,不过可能有的计算机平台有额外的文档说明可以使用超过10个操作数。

  • 第一个关键字asm,有时为了避免命名冲突,gcc也支持写作__asm__
  • 汇编限定符,例如限定符可以是__volatile__

汇编模板

#include <stdio.h>

void foo(void)
{
	int foo = 0; bar = 10;

	__asm__ __volatile__ (
		"movl %1, %%eax;\n"
		"movl %%eax, %0;\n"
		:"=r"(foo)
		:"r"(bar)
		:"%eax");

        printf("foo = bar; foo = %d\n", foo);
}

截取自上面冒号之前的两句就是汇编模板,他们可以一行写一句,甚至加上换行符\n,\r。非常类似printf的第一个参数,格式化字符串部分。

"movl %1, %%eax;\n"
"movl %%eax, %0;\n"

操作数

"constraint" (C expression) //"=r"(result)

接下来两个冒号之后的分别是输出/入操作数和它的约束属性。

"约束属性"(操作数)
"=r"(foo)

多个操作数的描述可以以,逗号隔开

"约束属性1"(操作数1),"约束属性2"(操作数2),"约束属性3"(操作数3)............

这些操作数最终会被汇编模板里面的%0, %1, %2, %3依次引用,例如上列中,汇编模板最终变为:

movl bar, %eax;
movl %eax, foo;

是不是和printf的参数列表特像?

输出操作数

  • 当内联汇编被执行完后,作为输出操作数的变量foo,值发生了改变。在__asm__内联汇编内部对操作数foo的修改,反应在了外部的C语言中。
movl bar, %eax;	@ bar(10)赋值给eax
movl %eax, foo; @ eax(10)赋值给foo

经过上面两句,foo的值由原来的0变为了10。输出操作数必须是左值,因为它最终要被内部的内联汇编赋值的。

输入操作数

  • 同理,输入操作数就是由外部的C表达式或者变量对内部内联汇编进行一个数值的传入。
movl bar, %eax;	@ bar(10)赋值给eax
movl %eax, foo; @ eax(10)赋值给foo

bar作为输入操作数,将值10带入了内联汇编内部。

已改写寄存器

  • 这个寄存器的内容在内联汇编中已经被修改。所以gcc不会用这个寄存器来存储其它值。例如上列里面的eax寄存器。

一些指令会修改一些寄存器的值。我们必须在受影响列表中列出那些寄存器,即内联汇编第三个:后的区域。这用于指示gcc我们将使用并修改它们。所以gcc将不会假设它加载到这些寄存器中的值是合法的。我们不应该列出输入和输出寄存器,因为gcc知道内联汇编使用它们(因为它们被明确指定为限制符(constraints))。如果指令使用了任何其他寄存器,显式或隐式的(并且这些寄存器没有出现在输入和输出列表上),那么那些寄存器必须在受影响列表中指定。

约束(constraints)

你可能已经感到我们之前经常提到的constraint是个很重要的内容了。不过之前我们并没有过多的讨论。constraint中可以指明一个操作数是否在寄存器中,在哪个寄存器中;可以指明操作数是否是内存引用,如何寻址;可以说明操作数是否是立即数常量,和其可能是的值(或值范围)。

常用constraints

虽然constraints有很多,但常用的并不多。下面我们就来看看这些常用的constraints。

a. 寄存器操作数限制符("r")

如果操作数指定了这个constraints,操作数将被存储在通用寄存器中。看下面的例子:

__asm__( "movl %%eax, %0" : "=r" (myval));

上面变量myval会被被保存在一个由GCC自己选择的寄存器中,eax中的值被拷贝到这个寄存器中去,并且在内存中的myval的值也会按这个寄存器值被更新。当constraints ”r” 被指定时,GCC可能会在任何一个可用的通用寄存器中保存这个值。当然,你也可以指定具体使用那个寄存器,用下表所列出的constraints:

限制符 寄存器
r 随机的通用寄存器
a %eax, %ax, %al
b %ebx, %bx, %bl
c %ecx, %cx, %cl
d %edx, %dx, %dl
S %esi, %si
D %edi, %di

b. 内存操作数限制符("m")

当操作数是在内存中时,任何在它上的操作将直接在内存位置进行,而寄存器限制符,则优先存于寄存器而后修改再写回内存。但寄存器限制符通常只在指令必需或者明显提升性能时使用。当C变量需在asm中修改且无需寄存器保持其值时,内存限制符可最大化性能。如,将idtr的值存储于loc的内存位置中:

__asm__("sidt %0\n" : :"m"(loc));

c. 匹配(数字)限制符

有时,一个单独变量既是输入也是输出操作符,这时可使用匹配限制符。

asm ("incl %0" :"=a"(var):"0"(var));

我们在操作数一节看到了类似的例子,在这个例子中寄存器%eax既是输入也是输出变量。var输入读入%eax并更新到%eax最后在自增后存入var。这里的"0"指定了和输出变量一样的第0个限制符。也就是说,它指定了var的输出过程应该只存于%eax中。这类限制符可用于:

  • 输入输出是统一变量,或变量被修改并被写会同一变量时。
  • 将输入和输出操作符分开是不必要的时候。

使用匹配限制符最重要的效果是使可用寄存器的使用更有效。

一些其他的限制符有:

  • m: 接受内存操作数,机器支持任意的地址。
  • o: 接受内存操作数,只接受偏移地址(offsettable)。即对某个合法地址添加一个微小的偏移量。
  • V: 非偏移内存操作数。换句话说,任何符合"m"但不符合"o"限制符的地址。
  • i: 立即整型操作数,允许在编译期(assembly-time)可知常量符号。
  • n: 立即整型操作数,允许已知数字值。许多系统不支持小于16-bit的(word wide)编译期(assembly-time)常量作为操作数。这些操作数应该使用n而不是i。
  • g: 任何寄存器,内存或立即整型操作数都可用,要求寄存器不是常规寄存器(general registers)。
  • cc:如果我们的指令可以修改条件码寄存器(the condition code register),我们必须增加cc到受影响寄存器列表。
  • memory:如果我们的指令用一个不可预期的方法(fashion)修改了内存,添加memory到受影响寄存器。这会使gcc在汇编指令期间不在寄存器内保持内存值的缓存。我们也必须添加__volatile__关键字,如果内存影响(memory affected)未列在asm的输入和输出中。

对于ARM处理器核,GCC v4提供了如下一系列的限定性字符串:

Constraint Usage in ARM state Usage in Thumb state
f Floating point registers f0 … f7 Not available
h Not available Registers r8…r15
G Immediate floating point constant Not available
H Same a G, but negated Not available
I(大写i) Immediate value in data processing instructions e.g. ORR R0, R0, #operand Constant in the range 0 … 255 e.g. SWI operand
J Indexing constants -4095 … 4095 e.g. LDR R1, [PC, #operand] Constant in the range -255 … -1 e.g. SUB R0, R0, #operand
K Same as I, but inverted Same as I, but shifted
L Same as I, but negated Constant in the range -7 … 7 e.g. SUB R0, R1, #operand
l(小写L) Same as r Registers r0…r7 e.g. PUSH operand
M Constant in the range of 0 … 32 or a power of 2 e.g. MOV R2, R1, ROR #operand Constant that is a multiple of 4 in the range of 0 … 1020 e.g. ADD R0, SP, #operand
m Any valid memory address
N Not available Constant in the range of 0 … 31 e.g. LSL R0, R1, #operand
O Not available Constant that is a multiple of 4 in the range of -508 … 508 e.g. ADD SP, #operand
r General register r0 … r15 e.g. SUB operand1, operand2, operand3 Not available
w Vector floating point registers s0 … s31 Not available
X Any operand

限制符之前可以添加单个的限定性修饰符。如果不添加修饰符,则表明该操作数是只读的。可以使用的修饰符如下所示:

修饰符 说明
默认操作数是只读的
= 只写操作数,通常被用来修饰输出操作数
+ 可读可写操作数,只能用来修饰输出操作数
& 意味着这个操作数为一个早期的改动操作数,其在该指令完成前通过使用输入操作数被修改了。因此,这个操作数不可以位于一个被用作输出操作数或任何内存地址部分的寄存器。如果在旧值被写入之前它仅用作输入而已,一个输入操作数可以为一个早期改动操作数。

原文地址:https://www.cnblogs.com/thammer/p/12591383.html

时间: 2024-10-12 20:04:10

GCC 扩展内联汇编简介的相关文章

最牛X的GCC 内联汇编

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

GCC内联汇编入门

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

[翻译] 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.

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

内联汇编使用简介

用3个实例实现将变量a和变量b相乘,得到的值存在result中. 简单实例 asm表示使用内联汇编, volatile表示不对内联汇编进行优化,避免造成误删. #include<stdio.h> int a = 10; int b = 20; int result; int main(){ __asm__ __volatile__( "movl a, %eax\n\t" "movl b, %ebx\n\t" "imull %ebx, %eax\

内核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

linux平台学x86汇编(十八):内联汇编

[版权声明:尊重原创,转载请保留出处:blog.csdn.net/shallnet,文章仅供学习交流,请勿用于商业用途] 使用汇编语言笔编程最常见的方式是在高级语言(C和C++)程序内编写汇编函数,这种吧汇编语言直接写到C和C++语言程序内的技术称为内联汇编. GNU的C编译器使用asm关键字指出使用汇编语言编写的源代码段落.asm段的基本格式如下: asm("as code"): 括号中的汇编指令必须在括号,指令超过一条的话必须使用新的行分隔汇编语言代码每一行,因为编译器逐字地取得a