GCC内联汇编入门【永利澳门游戏网站】,GCC内联汇编基础

原文作者 Sandeep.S英文原文
[]

原文:
GCC-Inline-Assembly-HOWTO

本文将介绍GCC编译环境下,在C语言代码中嵌入汇编代码的基本方法。阅读本文需要您具备80X86汇编语言和C语言的基础知识。为了使中文描述更加清楚自然,翻译过程中加入了稍许解释和意译部分。

1. 简介(Introduction.)

版权/反馈/勘误/感谢等信息。[^ 1][^
1]:这里信息价值不大,没有翻译。具体参加原文:

1.1 Copyright and License.

Copyright (C)2017 桂糊涂
Copyright (C)2003 Sandeep S.

This document is free; you can redistribute and/or modify this under the
terms of the GNU General Public License as published by the Free
Software Foundation; either version 2 of the License, or (at your
option) any later version.

This document is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
Public License for more details.

在讨论GCC内联汇编之前,我们先来搞搞清楚,到底什么是内联汇编?

1.2 反馈(略)

先看在C语言中,我们可以指定编译器将一个函数代码直接复制到调用其代码的地方执行。这种函数调用方式和默认压栈调用方式不同,我们称这种函数为内联函数。内联函数看起来很像宏?两者确实有许多共同之处。

1.3 背景(略)

希望将Windows项目BWAPI移植mac/linux时,遇到Visual
C内联汇编迁移到GCC的问题,于是研习此文并译之。

那么,内联函数有哪些优点呢?

2. 概览(Overview of the whole thing.)

  • 我们在此学习GCC内联汇编。内联是什么?

我们可以指导编译器将函数的代码直接插入调用的位置,这类函数叫做内联函数。听起来像是宏?事实上还真挺像。

  • 内联函数有什么好处?

内联的方法降低了函数调用的问题。而且如果任何参数是常量的话,在编译器将得到明显优化,而不是所有的内联函数代码都被包含。代码量会更少,取决于具体的情况。为了定义内联函数,我们使用关键字inline声明。

  • 什么是内联汇编?

内联汇编是写在内联函数中的汇编过程(assembly
routines)。它非常方便、快速,在系统编程中非常有用。我们主要关注学习GCC内联汇编函数的基础格式和用法。要声明内联汇编函数,我们使用关键字asm

内联汇编很重要,因为有能力操作并输出到C变量中。因为这些能力,asm作为了C和汇编指令间的接口。

很明显,内联函数降低了函数的调用开销:如果多次被调用的某个函数实参相同,那么它的返回值一定是相同的,这就给编译器留下了优化空间。此时编译器完全可以直接用这个返回值替代这个函数,而不必把该函数的代码插入到调用者的代码中再去计算结果了。如此一来,不但减少了代码量,还节省了计算资源。指定编译器将一个函数处理为内联函数,我们只要在函数申明前加上inline关键字即可。

3、GCC汇编语法(GCC Assembler Syntax.)

GCC使用AT&T/UNIX汇编语法。其与Intel语法区别较大,主要区别有:

基于对上述内联函数的认知,我们大概可以想象出内联汇编到底是怎么一回事了。内联汇编相当于用汇编语句写成的内联函数。它方便,快速,对系统编程有着举足轻重的作用。本文主要就GCC内联函数的格式和使用方法展开讨论。在GCC中声明一个内联汇编函数,我们要用asm这个关键字。

3.1. 源-目标顺序(Source-Destination Ordering)

Intel:Op-code dst src

AT&T:Op-code src dst

之所以内联汇编如此有用,主要是因为它可以操作C语言变量,比如可以输出值到C语言变量。这个特性使内联汇编成为汇编代码和调用其C程序之间的桥梁。

3.2. 寄存次命名(Registry Naming)

%为前缀,如:使用eax写作%eax

GCC (GNU Compiler for Linux)
使用AT&T/UNIX汇编语法。所以这篇文章将会用AT&T汇编格式来写汇编代码。如果你不熟悉AT&T汇编语法也没有关系,下面会有些简单的介绍。AT&T和Intel汇编语法差别比较大,二者主要不同之处如下:

3.3. 立即操作数(Immediate Operands)

AT&T立即操作数以$开头,对staic
“C”变量也前置$。16进制常量,Intel语法后缀h,AT&T前缀0x。所以对于16进制数,我们会先看到$,然后是0x,最后是常量。

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

3.4. 操作数大小(Operand Size)

译注:操作数(operand),很多情况下指操作对象,即寄存器或内存地址。

AT&T语法中操作数大小取决于操作码最后一个字符。操作码后缀b,w,l 对应
byte(8-bit), word(16-bit), 和
long(32-bit)。Intel语法中,通过在操作数(非操作码)前缀 byte ptr,
word ptr, 和 dword ptr 实现该功能。

因此, Intel 之 mov al, byte ptr foomovb foo, %al 于 AT&T.

3.5. 内存操作数(Memory Operands)

Intel语法中基址寄存器(The base
register)内于[]之间,而AT&T于()
之间。此外,间接内存引用(indirect memory reference)Intel风格为

section:[base + index*scale + disp] ,改变为

section:disp(base, index, scale)于 AT&T.

需指出,当常量使用disp/scale,$ 无需前置。

以上是Intel于AT&T语法的主要区别,完整信息请参加GNU Assembler
documentations。以下一些例子有助于我们更好的理解:

Intel Code AT&T Code
mov eax,1 movl $1,%eax
mov ebx,0ffh movl $0xff,%ebx
int 80h int $0x80
mov ebx, eax movl %eax, %ebx
mov eax,[ecx] movl (%ecx),%eax
mov eax,[ebx+3] movl 3(%ebx),%eax
mov eax,[ebx+20h] movl 0x20(%ebx),%eax
add eax,[ebx+ecx*2h] addl (%ebx,%ecx,0x2),%eax
lea eax,[ebx+ecx] leal (%ebx,%ecx),%eax
sub eax,[ebx+ecx*4h-20h] subl -0x20(%ebx,%ecx,0x4),%eax

发表评论

电子邮件地址不会被公开。 必填项已用*标注