现在的位置: 首页 > 综合 > 正文

gcc 使用教程

2013年11月16日 ⁄ 综合 ⁄ 共 28727字 ⁄ 字号 评论关闭
文章目录

gcc 使用教程



 

目 录

  1. gcc

    1. makefile写法
    2. gcc_egcs使用
    3. gdb使用
    4. gcc常用选项对代码的影响
      1. 一般情况
      2. -O 编译选项
      3. -O2 编译选项
      4. -fomit-frame-pointer 编译选项
      5. -fomit-frame-pointer && -O2
      6. -fPIC 编译选项
      7. -static 编译选项
    5. AT&T的汇编格式
    6. x86内联汇编
      1. 简述
      2. 内联汇编
      3. 程序模板
      4. 操作数
      5. 修饰寄存器列表
      6. 操作数约束
      7. 示例
        1. 寄存器约束
        2. 匹配约束
        3. 内存操作数约束
        4. 修饰寄存器
    7. 不同的CPU下最佳编译参数
    8. 代码维护
      1. 简单cvs
      2. automake
      3. diff
      4. rcs
      5. 内核重编译常见故障
      6. cvs
      7. 共享库工具
      8. 代码优化
      9. GNU 编码标准
      10. 书籍


gcc


[目录]



makefile写法


蓝森林 http://www.lslnet.com 2001年3月22日 08:44

作 者: 许明彦

Abstract:

    在 Unix 上写程式的人大概都碰过 Makefile,尤其是用 C 来开发程式的人。用 make来开发和编译程式的确很方便,可是要写出一个 Makefile就不简单了。偏偏介绍 Makefile 的文件不多,GNU Make 那份印出来要几百页的文件,光看完 Overview 就快阵亡了,难怪许多
人闻 Unix 色变。

    本文将介绍如何利用 GNU Autoconf 及 Automake 这两套软体来协助我们『自动』产生 Makefile 档,并且让开发出来的软体可以像 Apache, MySQL 和常见的 GNU 软体一样,只要会 ``./configure'', ``make'', ``make install'' 就可以把程式安装到系统中。如果您有
心开发 Open Source 的软体,或只是想在 Unix 系统下写写程式。希望这份介绍文件能帮助您轻松地进入 Unix Programming 的殿堂。

1. 简介

    Makefile 基本上就是『目标』(target), 『关连』(dependencies) 和『动作』三者所组成的一连串规则。而 make 就会根据 Makefile 的规则来决定如何编译 (compile) 和连结 (link) 程式。实际上,make 可做的不只是编译和连结程式,例如 FreeBSD 的 port collect
ion 中, Makefile 还可以做到自动下载原始程式套件,解压缩 (extract) ,修补 (patch),设定,然後编译,安装至系统中。

    Makefile 基本构造虽然简单,但是妥善运用这些规则就也可以变出许多不同的花招。却也因此,许多刚开始学习写 Makefile 时会感到没有规范可循,每个人写出来的 Makefile 长得都不太一样,不知道从何下手,而且常常会受限於自己的开发环境,只要环境变数不同或路
径改一下,可能Makefile 就得跟着修改。虽然有 GNU Makefile Conventions (GNU Makefile 惯例) 订出一些使用 GNU 程式设计时撰写 Makefile 的一些标准和规范,但是内容很长而且很复杂, 并且经常做些调整,为了减轻程式设计师维护 Makefile 的负担,因此有了Automake。

    程式设计师只需写一些预先定义好的巨集 (macro),交给 Automake 处理後会产生一个可供Autoconf 使用的 Makefile.in 档。再配合利用Autoconf 产生的自动设定档 configure即可产生一份符合 GNU Makefile惯例的 Makeifle 了。

2. 上路之前

    在开始试着用 Automake 之前,请先确认你的系统已经安装以下的软体:
1. GNU Automake
2. GNU Autoconf
3. GNU m4
4. perl
5. GNU Libtool (如果你需要产生 shared library)

    我会建议你最好也使用 GNU C/C++ 编译器 、GNU Make 以及其它 GNU 的工具程式来做为开发的环境,这些工具都是属於 Open Source Software不仅免费而且功能强大。如果你是使用Red Hat Linux 可以找到所有上述软体的 rpm 档,FreeBSD 也有现成的 package 可以直
接安装,或着你也可以自行下载这些软体的原始档回来 DIY。以下的范例是在 Red Hat Linux 5.2 + CLE2 的环境下所完成的。

3. 一个简单的例子

    Automake 所产生的 Makefile 除了可以做到程式的编译和连结,也已经把如何产生程式文件(如 manual page, info 档及 dvi 档) 的动作,还有把原始程式包装起来以供散 的动作都考虑进去了,所以原始程式所存放的目录架构最好符合 GNU 的标准惯例,接下来我拿hello.c 来做为例子。

    在工作目录下建立一个新的子目录 ``devel'',再在 devel 下建立一个``hello'' 的子目录,这个目录将作为我们存放 hello 这个程式及其相关档案的地方:

% mkdir devel
% cd devel
% mkdir hello
% cd hello

用编辑器写个 hello.c 档,
#include stdio.h
int main(int argc, char** argv)
{
printf(``Hello, GNU! '');
return 0;
}

    接下来就要用 Autoconf 及 Automake 来帮我们产生 Makefile 档了,

1. 用 autoscan 产生一个 configure.in 的雏型,执行 autoscan 後会产生一个configure.scan 的档案,我们可以用它做为configure.in档的蓝本。

% autoscan
% ls
configure.scan hello.c

2. 编辑 configure.scan 档,如下所示,并且把它的档名改成configure.in
dnl Process this file with autoconf to produce a con figure script.
AC_INIT(hello.c)
AM_INIT_AUTOMAKE(hello, 1.0)
dnl Checks for programs.
AC_PROG_CC
dnl Checks for libraries.
dnl Checks for header files.
dnl Checks for typedefs, structures, and compiler ch aracteristics.
dnl Checks for library functions.
AC_OUTPUT(Makefile)

3. 执行 aclocal 和 autoconf ,分别会产生 aclocal.m4 及 configure 两个档案
% aclocal
% autoconf
% ls
aclocal.m4 configure configure.in hello.c

4. 编辑 Makefile.am 档,内容如下
AUTOMAKE_OPTIONS= foreign
bin_PROGRAMS= hello
hello_SOURCES= hello.c

5. 执行 automake --add-missing ,Automake 会根据 Makefile.am 档产生一些档案,包含最重要的 Makefile.in
% automake --add-missing
automake: configure.in: installing `./install-sh'
automake: configure.in: installing `./mkinstalldirs'
automake: configure.in: installing `./missing'

6. 最後执行 ./configure ,
% ./configure
creating cache ./config.cache
checking for a BSD compatible install... /usr/bin/in stall -c
checking whether build environment is sane... yes
checking whether make sets ${MAKE}... yes
checking for working aclocal... found
checking for working autoconf... found
checking for working automake... found
checking for working autoheader... found
checking for working makeinfo... found
checking for gcc... gcc
checking whether the C compiler (gcc ) works... yes
checking whether the C compiler (gcc ) is a cross-co mpiler... no
checking whether we are using GNU C... yes
checking whether gcc accepts -g... yes
updating cache ./config.cache
creating ./config.status
creating Makefile

    现在你的目录下已经产生了一个 Makefile 档,下个 ``make'' 指令就可以开始编译 hello.c 成执行档,执行 ./hello 和 GNU 打声招呼吧!

% make
gcc -DPACKAGE="hello" -DVERSION="1.0" -I. -I. -g -O2 -c he llo.c
gcc -g -O2 -o hello hello.o
% ./hello
Hello! GNU!

    你还可以试试 ``make clean'',''make install'',''make dist'' 看看会有什麽结果。你也可以把产生出来的 Makefile 秀给你的老板,让他从此对你刮目相看 :-)

4. 一探究竟

    上述产生 Makefile 的过程和以往自行编写的方式非常不一样,舍弃传统自行定义 make 的规则,使用 Automake 只需用到一些已经定义好的巨集即可。我们把巨集及目标 (target)写在 Makefile.am 档内,Automake读入 Makefile.am 档後会把这一串已经定义好的巨集展
开并且产生对应的Makefile.in 档, 然後再由 configure 这个 shell script 根据Makefile.in 产生适合的 Makefile。
    在此范例中可藉由 Autoconf 及 Automake 工具所产生的档案有 configure.scan、aclocal.m4、configure、Makefile.in,需要我们加入设定者为 configure.in 及 Makefile.am。

4.1 编辑 configure.in 档

    Autoconf 是用来产生 'configure' 档的工具。'configure' 是一个shell script,它可以自动设定原始程式以符合各种不同平台上 Unix 系统的特性,并且根据系统叁数及环境产生合适的 Makefile 档或是C 的标头档 (header file),让原始程式可以很方便地在这些不同
的平台上被编译出来。Autoconf 会读取 configure.in 档然後产生 'configure' 这个shell script。

    configure.in 档的内容是一连串 GNU m4 的巨集,这些巨集经过autoconf 处理後会变成检查系统特徵的 shell script。configure.in 内巨集的顺序并没有特别的规定,但是每一个 configure.in 档必须在所有巨集前加入 AC_INIT 巨集,然後在所有巨集的最後面加上 AC
_OUTPUT 巨集。我们可先用 autoscan 扫描原始档以产生一个 configure.scan 档,再对 configure.scan 做些修改成 configure.in 档。在范例中所用到的巨集如下:

dnl
    这个巨集後面的字不会被处理,可视为注解。

AC_INIT(FILE)
    这个巨集用来检查原始码所在的路径,autoscan 会自动产生,我们不必修改它。

AM_INIT_AUTOMAKE(PACKAGE,VERSION)
    这是使用 Automake 所必备的巨集,PACKAGE 是我们所要产生软体套件的名称,VERSION 是版本编号。

AC_PROG_CC
    检查系统可用的 C 编译器,如果原始程式是用 C 写的就需要这个巨集。

AC_OUTPUT(FILE)
    设定 configure 所要产生的档案,如果是 Makefile 的话,configure 便会把它检查出来的结果带入 Makefile.in 档然後产生
合适的 Makefile。

    实际上,我们使用 Automake 时,还须要一些其它的巨集,这些额外的巨集我们用 aclocal来帮我们产生。执行 aclocal 会产生 aclocal.m4档,如果没有特别的用途,我们可以不必修改它,用 aclocal 所产生的巨集会告诉 Automake 怎麽做。

    有了 configure.in 及 aclocal.m4 两个档案後,便可以执行 autoconf来产生 configure档了。

4.2 编辑 Makefile.am 档

    接下来我们要编辑 Makefile.am 档,Automake 会根据 configure.in 中的巨集把Makefile.am 转成 Makefile.in 档。Makefile.am 档定义我们所要产的目标:

AUTOMAKE_OPTIONS
    设定 automake 的选项。Automake 主要是帮助开发 GNU 软体的人员维护软体套件,所以在执行 automake 时,会检查目录下是否存在标准 GNU 软体套件中应具备的文件档案,例如 'NEWS'、'AUTHOR'、'ChangeLog' 等文件档。设成 foreign 时,automake 会改用一般软
体套件的标准来检查。

bin_PROGRAMS
    定义我们所要产生的执行档档名。如果要产生多个执行档,每个档名用空白字元隔开。

hello_SOURCES
    定义 'hello' 这个执行档所需要的原始档。如果 'hello' 这个程式是由多个原始档所产生,必须把它所用到的原始档都列出来,以空白字元隔开。假设 'hello' 这个程式需要 'hello.c'、'main.c'、

'hello.h'
    三个档案的话,则定义
        hello_SOURCES= hello.c main.c hello.h
    如果我们定义多个执行档,则对每个执行档都要定义相对的filename_SOURCES。

    编辑好 Makefile.am 档,就可以用 automake --add-missing 来产生Makefile.in。加上 --add-missing 选项是告诉 automake 顺便帮我们加入包装一个软体套件所必备的档案。Automake 产生出来的 Makefile.in档是完全符合 GNU Makefile 的惯例,我们只要执行 confi
gure 这个shell script 便可以产生合适的 Makefile 档了。

4.3 使用 Makefile

    利用 configure 所产生的 Makefile 档有几个预设的目标可供使用,我们只拿其中几个简述如下:

make all
    产生我们设定的目标,即此范例中的执行档。只打 make 也可以,此时会开始编译原始码,然後连结,并且产生执行档。

make clean
    清除之前所编译的执行档及目的档 (object file, *.o)。

make distclean
    除了清除执行档和目的档外,也把 configure 所产生的 Makefile也清除掉。

make install
    将程式安装至系统中。如果原始码编译无误,且执行结果正确,便可以把程式安装至系统预设的执行档存放路径。如果我们用bin_PROGRAMS 巨集的话,程式会被安装至 /usr/local/bin 这个目录。

make dist
    将程式和相关的档案包装成一个压缩档以供散播 (distribution) 。执行完在目录下会产生一个以 PACKAGE-VERSION.tar.gz 为名称的档案。PACKAGE 和 VERSION 这两个变数是根据 configure.in 档中AM_INIT_AUTOMAKE(PACKAGE, VERSION) 的定义。在此范例中会产生
'hello-1.0.tar.gz' 的档案。

make distcheck
    和 make dist 类似,但是加入检查包装後的压缩档是否正常。这个目标除了把程式和相关档案包装成 tar.gz 档外,还会自动把这个压
缩档解开,执行 configure,并且进行 make all 的动作,确认编译无误後,会显示这个 tar.gz 档已经准备好可供散播了。这个检查非
常有用,检查过关的套件,基本上可以给任何一个具备 GNU 发展境的人去重新编译。就 hello-1.tar.gz 这个范例而言,除了在 Red
Hat Linux 上,在 FreeBSD 2.2.x 版也可以正确地重新编译。

    要注意的是,利用 Autoconf 及 Automake 所产生出来的软体套件是可以在没有安装 Autoconf 及 Automake 的环境上使用的,因为 configure 是一个 shell script,它己被设计可以在一般 Unix 的 sh 这个 shell 下执行。但是如果要修改 configure.in 及 Makefile.a
m 档再产生新的configure 及 Makefile.in 档时就一定要有 Autoconf 及 Automake 了。

5. 相关讯息

    Autoconf 和 Automake 功能十分强大,你可以从它们所附的 info 档找到详细的用法。你也可以从许多现存的 GNU 软体或 Open Source 软体中找到相关的 configure.in 或 Makefile.am 档,它们是学习 Autoconf 及Automake 更多技巧的最佳范例。

    这篇简介只用到了 Autoconf 及 Automake 的皮毛罢了,如果你有心加入Open Source 软体开发的行列,希望这篇文件能帮助你对产生 Makefile有个简单的依据。其它有关开发 GNU程式或 C 程式设计及 Makefile 的详细运用及技巧,我建议你从 GNU Coding Standards3
(GNU 编码标准规定) 读起,里面包含了 GNU Makefile 惯例,还有发展 GNU 软体套件的标准程序和惯例。这些 GNU 软体的线上说明文件可以在http://www.gnu.org/ 这个网站上找到。

6. 结语

    经由 Autoconf 及 Automake 的辅助,产生一个 Makefile 似乎不再像以前那麽困难了,而使用 Autoconf 也使得我们在不同平台上或各家 Unix之间散播及编译程式变得简单,这对於在 Unix 系统上开发程式的人员来说减轻了许多负担。妥善运用这些 GNU 的工具软体,可
以帮助我们更容易去发展程式,而且更容易维护原始程式码。

    一九九八年是 Open Source 运动风起云涌的一年,许多 Open Source 的软体普遍受到网路上大众的欢迎和使用。感谢所有为 Open Source 奉献的人们,也希望藉由本文能吸引更多的人加入『自由』、『开放』的 OpenSource 行列。

About this document ...

轻轻松松产生 Makefile1

This document was generated using the LaTeX2HTML translator Version 98.2 beta6 (August 14th, 1998) Copyright (C) 1993, 1994, 1995, 1996, Nikos Drakos, ComputerBased Learning Unit, University of Leeds.Copyright (C) 1997, 1998, Ross Moore, Mathematics Department,Macquarie
University, Sydney.

The command line arguments were:
latex2html -split 0 -show_section_numbers automake.tex
The translation was initiated by on 1999-02-11

Footnotes
... itle1
本文件使用 ChiLaTeX 制作。
... CLE2
CLE (Chinese Linux Extension,Linux 中文延伸套件),
http://cle.linux.org.tw/

... Standards3
GNU Coding Standards, Richard Stallman.

 

 

[目录]



gcc_egcs使用


1.使用egcs

    Linux 中最重要的软件开发工具是 GCC。GCC 是 GNU 的 C 和 C++ 编译器。实际上,GCC 能够编译三种语言:C、C++ 和 Object C(C 语言的一种面向对象扩展)。利用 gcc 命令可同时编译并连接 C 和 C++ 源程序。

        #DEMO#: hello.c

    如果你有两个或少数几个 C 源文件,也可以方便地利用 GCC 编译、连接并生成可执行文件。例如,假设你有两个源文件 main.c 和 factorial.c 两个源文件,现在要编译生成一个计算阶乘的程序。

-----------------------
清单 factorial.c
-----------------------
#include <stdio.h>
#include <stdlib.h>

int factorial (int n)
{
    if (n <= 1)
        return 1;

    else
        return factorial (n - 1) * n;
}
-----------------------

-----------------------
清单  main.c
-----------------------
#include <stdio.h>
#include <stdlib.h>

int factorial (int n);

int main (int argc, char **argv)
{
    int n;

    if (argc < 2) {
        printf ("Usage: %s n/n", argv [0]);
        return -1;
    }
    else {
        n = atoi (argv[1]);
        printf ("Factorial of %d is %d./n", n, factorial (n));
    }

    return 0;
}
-----------------------

    利用如下的命令可编译生成可执行文件,并执行程序:

$ gcc -o factorial main.c factorial.c
$ ./factorial 5
Factorial of 5 is 120.

    GCC 可同时用来编译 C 程序和 C++ 程序。一般来说,C 编译器通过源文件的后缀名来判断是 C 程序还是 C++ 程序。在 Linux 中,C 源文件的后缀名为 .c,而 C++ 源文件的后缀名为 .C 或 .cpp。

    但是,gcc 命令只能编译 C++ 源文件,而不能自动和 C++ 程序使用的库连接。因此,通常使用 g++ 命令来完成 C++ 程序的编译和连接,该程序会自动调用 gcc 实现编译。假设我们有一个如下的 C++ 源文件(hello.C):

#include <iostream.h>

void main (void)
{
    cout << "Hello, world!" << endl;
}

    则可以如下调用 g++ 命令编译、连接并生成可执行文件:

$ g++ -o hello hello.C
$ ./hello
Hello, world!

2.  gcc/egcs 的主要选项

                表 1-3  gcc 命令的常用选项
选项                解释
-ansi               只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色,
                    例如 asm 或 typeof 关键词。
-c                  只编译并生成目标文件。
-DMACRO             以字符串“1”定义 MACRO 宏。
-DMACRO=DEFN        以字符串“DEFN”定义 MACRO 宏。
-E                  只运行 C 预编译器。
-g                  生成调试信息。GNU 调试器可利用该信息。
-IDIRECTORY         指定额外的头文件搜索路径DIRECTORY。
-LDIRECTORY         指定额外的函数库搜索路径DIRECTORY。
-lLIBRARY           连接时搜索指定的函数库LIBRARY。
-m486               针对 486 进行代码优化。
-o FILE             生成指定的输出文件。用在生成可执行文件时。
-O0                 不进行优化处理。
-O 或 -O1           优化生成代码。
-O2                 进一步优化。
-O3                 比 -O2 更进一步优化,包括 inline 函数。
-shared             生成共享目标文件。通常用在建立共享库时。
-static             禁止使用共享连接。
-UMACRO             取消对 MACRO 宏的定义。
-w                  不生成任何警告信息。
-Wall               生成所有警告信息。

#DEMO#

 

 

[目录]



gdb使用


1.简介

    GNU 的调试器称为 gdb,该程序是一个交互式工具,工作在字符模式。在 X Window 系统中,有一个 gdb 的前端图形工具,称为 xxgdb。gdb 是功能强大的调试程序,可完成如下的调试任务:

* 设置断点;
* 监视程序变量的值;
* 程序的单步执行;
* 修改变量的值。
    在可以使用 gdb 调试程序之前,必须使用 -g 选项编译源文件。可在 makefile 中如下定义CFLAGS 变量:

CFLAGS = -g
运行 gdb 调试程序时通常使用如下的命令:

gdb progname

    在 gdb 提示符处键入help,将列出命令的分类,主要的分类有:

* aliases:命令别名
* breakpoints:断点定义;
* data:数据查看;
* files:指定并查看文件;
* internals:维护命令;
* running:程序执行;
* stack:调用栈查看;
* statu:状态查看;
* tracepoints:跟踪程序执行。

键入 help 后跟命令的分类名,可获得该类命令的详细清单。

2.gdb 的常用命令
                表 1-4  常用的 gdb 命令
命令                        解释
break NUM               在指定的行上设置断点。
bt                      显示所有的调用栈帧。该命令可用来显示函数的调用顺序。
clear                   删除设置在特定源文件、特定行上的断点。其用法为:clear FILENAME:NUM。
continue                继续执行正在调试的程序。该命令用在程序由于处理信号或断点而
                        导致停止运行时。
display EXPR            每次程序停止后显示表达式的值。表达式由程序定义的变量组成。
file FILE               装载指定的可执行文件进行调试。
help NAME               显示指定命令的帮助信息。
info break              显示当前断点清单,包括到达断点处的次数等。
info files              显示被调试文件的详细信息。
info func               显示所有的函数名称。
info local              显示当函数中的局部变量信息。
info prog               显示被调试程序的执行状态。
info var                显示所有的全局和静态变量名称。
kill                    终止正被调试的程序。
list                    显示源代码段。
make                    在不退出 gdb 的情况下运行 make 工具。
next                    在不单步执行进入其他函数的情况下,向前执行一行源代码。
print EXPR              显示表达式 EXPR 的值。

3.gdb 使用范例

-----------------
清单  一个有错误的 C 源程序 bugging.c
-----------------
#include <stdio.h>
#include <stdlib.h>

static char buff [256];
static char* string;
int main ()
{

    printf ("Please input a string: ");
    gets (string);

    printf ("/nYour string is: %s/n", string);
}
-----------------

    上面这个程序非常简单,其目的是接受用户的输入,然后将用户的输入打印出来。该程序使用了一个未经过初始化的字符串地址 string,因此,编译并运行之后,将出现 Segment Fault 错误:

$ gcc -o test -g test.c
$ ./test
Please input a string: asfd
Segmentation fault (core dumped)

为了查找该程序中出现的问题,我们利用 gdb,并按如下的步骤进行:

1.运行 gdb bugging 命令,装入 bugging 可执行文件;
2.执行装入的 bugging 命令;
3.使用 where 命令查看程序出错的地方;
4.利用 list 命令查看调用 gets 函数附近的代码;
5.唯一能够导致 gets 函数出错的因素就是变量 string。用 print 命令查看 string 的值;
6.在 gdb 中,我们可以直接修改变量的值,只要将 string 取一个合法的指针值就可以了,为
此,我们在第 11 行处设置断点;
7.程序重新运行到第 11 行处停止,这时,我们可以用 set variable 命令修改 string 的取值;
8.然后继续运行,将看到正确的程序运行结果。

 

 

[目录]



gcc常用选项对代码的影响


by alert7
2001-12-21
测试环境 redhat 6.2

★ 前言
    本文讨论gcc的一些常用编译选项对代码的影响。当然代码变了,它的内存布局也就会变了,随之exploit也就要做相应的变动。
gcc的编译选项实在太多,本文检了几个最常用的选项。

★ 演示程序
[alert7@redhat62 alert7]$ cat > test.c
#include
void hi(void)
{
printf("hi");
}
int main(int argc, char *argv[])
{
        hi();
        return 0;
}

 

 

[目录]



一般情况


★ 一般情况
[alert7@redhat62 alert7]$ gcc -o test test.c
[alert7@redhat62 alert7]$ wc -c test
  11773 test
[alert7@redhat62 alert7]$ gdb -q test
(gdb) disass main
Dump of assembler code for function main:
0x80483e4 :       push   %ebp
0x80483e5 :     mov    %esp,%ebp
0x80483e7 :     call   0x80483d0
0x80483ec :     xor    %eax,%eax
0x80483ee :    jmp    0x80483f0
0x80483f0 :    leave
0x80483f1 :    ret
....
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483d0 :        push   %ebp
0x80483d1 :       mov    %esp,%ebp
0x80483d3 :       push   $0x8048450
0x80483d8 :       call   0x8048308
0x80483dd :      add    $0x4,%esp
0x80483e0 :      leave
0x80483e1 :      ret
0x80483e2 :      mov    %esi,%esi
End of assembler dump.
来看看部分的内存映象
                   (内存高址)
                              +--------+
                              |bffffbc4| argv的地址(即argv[0]的地址)
                   0xbffffb84 +--------+
                              |00000001| argc的值
                   0xbffffb80 +--------+
                              |400309cb|main的返回地址
                   0xbffffb7c +--------+ <-- 调用main函数前的esp
                              |bffffb98| 调用main函数前的ebp
                   0xbffffb78 +--------+ <-- main函数的ebp
                              |080483ec| hi()的返回地址
                   0xbffffb74 +--------+
                              |bffffb78| 调用hi()前的esp
                   0xbffffb70 +--------+
                              |08048450| "hi"的地址
                   0xbffffb6c +--------+
                              | ...... |
                   (内存低址)
leave    指令所做的操作相当于MOV ESP,EBP 然后 POP EBP
ret    指令所做的操作相当于POP EIP

 

 

[目录]



-O 编译选项


★ -O 编译选项
With `-O', the compiler tries to reduce code size and execution time.
When you specify `-O', the two options `-fthread-jumps' and
`-fdefer-pop' are turned  on
优化,减少代码大小和执行的时间
[alert7@redhat62 alert7]$ gcc -O -o test test.c
[alert7@redhat62 alert7]$ wc -c test
  11757 test
[alert7@redhat62 alert7]$ gdb -q test
(gdb) disass main
Dump of assembler code for function main:
0x80483d8 :       push   %ebp
0x80483d9 :     mov    %esp,%ebp
0x80483db :     call   0x80483c8
0x80483e0 :     xor    %eax,%eax
0x80483e2 :    leave
0x80483e3 :    ret
0x80483e4 :    nop
...
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483c8 :        push   %ebp
0x80483c9 :       mov    %esp,%ebp
0x80483cb :       push   $0x8048440
0x80483d0 :       call   0x8048308
0x80483d5 :      leave
0x80483d6 :      ret
0x80483d7 :      nop
End of assembler dump.

    在main()中,把一条jmp指令优化掉了,很显然,这条指令是可以不需要的。
    在hi()中,把add $0x4,%esp优化掉了,这会不会使stack不平衡呢?

    来看看部分的内存映象

                   (内存高址)
                              +--------+
                              |bffffbc4| argv的地址(即argv[0]的地址)
                   0xbffffb84 +--------+
                              |00000001| argc的值
                   0xbffffb80 +--------+
                              |400309cb|main的返回地址
                   0xbffffb7c +--------+ <-- 调用main函数前的esp
                              |bffffb98| 调用main函数前的ebp
                   0xbffffb78 +--------+ <-- main函数的ebp
                              |080483e0| hi()的返回地址
                   0xbffffb74 +--------+
                              |bffffb78| 调用hi()前的esp
                   0xbffffb70 +--------+
                              |08048440| "hi"的地址
                   0xbffffb6c +--------+
                              | ...... |
                   (内存低址)
    leave指令所做的操作相当于把MOV ESP,EBP 然后 POP EBP。看到leave指令操作了没有,先把ebp-->esp,再pop ebp,这样即使在过程内堆栈的esp,ebp是不平衡的,但只要返回时候碰到leave指令就会平衡了,所以把add $0x4,%esp优化掉也是没有问题的。

 

[目录]



-O2 编译选项


★ -O2 编译选项
-O2
    Optimize  even more.  Nearly all supported optimizations that do
    not involve a space-speed tradeoff are performed.  Loop unrolling
    and function inlining are not done, for example.  As compared to -O,
    this option increases both compilation time and the performance of
    the generated code.
[alert7@redhat62 alert7]$ gcc -O2 -o test test.c
[alert7@redhat62 alert7]$ wc -c test
  11757 test
[alert7@redhat62 alert7]$ gdb -q test
(gdb) disass main
Dump of assembler code for function main:
0x80483d8 :       push   %ebp
0x80483d9 :     mov    %esp,%ebp
0x80483db :     call   0x80483c8
0x80483e0 :     xor    %eax,%eax
0x80483e2 :    leave
0x80483e3 :    ret
...
0x80483ef :    nop
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483c8 :        push   %ebp
0x80483c9 :       mov    %esp,%ebp
0x80483cb :       push   $0x8048440
0x80483d0 :       call   0x8048308
0x80483d5 :      leave
0x80483d6 :      ret
0x80483d7 :      nop
End of assembler dump.
由于程序比较简单,再优化也没有好优化的了,所以跟-O出来的一样。

[目录]



-fomit-frame-pointer 编译选项


★ -fomit-frame-pointer 编译选项
-fomit-frame-pointer
              Don't keep the frame pointer in a register for functions
          that don't need one.  This avoids the  instructions to save,
          set up and restore frame pointers; it also makes an extra
          register available in many functions.  It also makes
          debugging impossible on most machines.

    忽略帧指针。这样在程序就不需要保存,安装,和恢复ebp了。这样ebp也就是一个free的register了,在函数中就可以随便使用了。

[alert7@redhat62 alert7]$ gcc -fomit-frame-pointer -o test test.c
[alert7@redhat62 alert7]$ wc -c test
  11773 test
[alert7@redhat62 alert7]$ gdb -q test
(gdb) disass main
Dump of assembler code for function main:
0x80483e0 :       call   0x80483d0
0x80483e5 :     xor    %eax,%eax
0x80483e7 :     jmp    0x80483f0
0x80483e9 :     lea    0x0(%esi,1),%esi
0x80483f0 :    ret
....
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483d0 :        push   $0x8048450
0x80483d5 :       call   0x8048308
0x80483da :      add    $0x4,%esp
0x80483dd :      ret
0x80483de :      mov    %esi,%esi
End of assembler dump.
在main()和hi()中都去掉了以下指令
push   %ebp
mov    %esp,%ebp//这两条指令安装
leave//这条指令恢复
来看看部分的内存映象
                   (内存高址)
                              +--------+
                              |bffffbc4| argv的地址(即argv[0]的地址)
                   0xbffffb84 +--------+
                              |00000001| argc的值
                   0xbffffb80 +--------+
                              |400309cb|main的返回地址
                   0xbffffb7c +--------+
                              |080483e5| hi()的返回地址
                   0xbffffb78 +--------+
                              |08048450|  "hi"字符串的地址
                   0xbffffb74 +--------+
                              | ...... |
                   (内存低址)
没有保存上层执行环境的ebp.

 

 

[目录]



-fomit-frame-pointer && -O2


★ -fomit-frame-pointer && -O2
-fomit-frame-pointer编译选项去掉了
push   %ebp
mov    %esp,%ebp//这两条指令安装
leave//这条指令恢复
-O2编译选项去掉了
add    $0x4,%esp
两个加起来会不会这四条指令一起去掉,从而使stack不平衡呢?
[alert7@redhat62 alert7]$ gcc -fomit-frame-pointer -O2 -o test test.c
[alert7@redhat62 alert7]$ wc -c test
  11741 test
[alert7@redhat62 alert7]$ gdb -q test
(gdb) disass main
Dump of assembler code for function main:
0x80483d8 :       call   0x80483c8
0x80483dd :     xor    %eax,%eax
0x80483df :     ret
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483c8 :        push   $0x8048430
0x80483cd :       call   0x8048308
0x80483d2 :      add    $0x4,%esp
0x80483d5 :      ret
0x80483d6 :      mov    %esi,%esi
End of assembler dump.
来看看部分的内存映象
                   (内存高址)
                              +--------+
                              |bffffbc4| argv的地址(即argv[0]的地址)
                   0xbffffb84 +--------+
                              |00000001| argc的值
                   0xbffffb80 +--------+
                              |400309cb|main的返回地址
                   0xbffffb7c +--------+
                              |080483dd| hi()的返回地址
                   0xbffffb78 +--------+
                              |08048430|  "hi"字符串的地址
                   0xbffffb74 +--------+
                              | ...... |
                   (内存低址)
此时就没有把add    $0x4,%esp优化掉,如果优化掉的话,整个stack就
会变的不平衡,从而会导致程序出错。

 

 

[目录]



-fPIC 编译选项


★ -fPIC 编译选项
-fPIC    If  supported for the target machine, emit position-independent
    code, suitable for dynamic linking,even if branches need large
    displacements.

    产生位置无关代码(PIC),一般创建共享库时用到。
    在x86上,PIC的代码的符号引用都是通过ebx进行操作的。

[alert7@redhat62 alert7]$ gcc -fPIC -o test test.c
[alert7@redhat62 alert7]$ wc -c test
  11805 test
[alert7@redhat62 alert7]$ gdb -q test
(gdb) disass main
Dump of assembler code for function main:
0x80483f8 :       push   %ebp
0x80483f9 :     mov    %esp,%ebp
0x80483fb :     push   %ebx
0x80483fc :     call   0x8048401
0x8048401 :     pop    %ebx//取得该指令的地址
0x8048402 :    add    $0x1093,%ebx//此时ebx里面存放着是GOT表的地址
0x8048408 :    call   0x80483d0
0x804840d :    xor    %eax,%eax
0x804840f :    jmp    0x8048411
0x8048411 :    mov    0xfffffffc(%ebp),%ebx
0x8048414 :    leave
0x8048415 :    ret
...
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80483d0 :        push   %ebp
0x80483d1 :       mov    %esp,%ebp
0x80483d3 :       push   %ebx
0x80483d4 :       call   0x80483d9
0x80483d9 :       pop    %ebx
0x80483da :      add    $0x10bb,%ebx
0x80483e0 :      lea    0xffffefdc(%ebx),%edx
0x80483e6 :      mov    %edx,%eax
0x80483e8 :      push   %eax
0x80483e9 :      call   0x8048308
0x80483ee :      add    $0x4,%esp
0x80483f1 :      mov    0xfffffffc(%ebp),%ebx
0x80483f4 :      leave
0x80483f5 :      ret
0x80483f6 :      mov    %esi,%esi
End of assembler dump.
来看看部分的内存映象

    (内存高址)
              +--------+
              |bffffbc4| argv的地址(即argv[0]的地址)
   0xbffffb84 +--------+
              |00000001| argc的值
   0xbffffb80 +--------+
              |400309cb|main的返回地址
   0xbffffb7c +--------+ <-- 调用main函数前的esp
              |bffffb98| 调用main函数前的ebp
   0xbffffb78 +--------+ <-- main函数的ebp
              |401081ec| 保存的ebx
   0xbffffb74 +--------+
              |0804840d| (存放过call 0x8048401的下一条指令地址)
   0xbffffb70 +--------+
              |bffffb78| 调用hi()前的esp
   0xbffffb6c +--------+
              |08049494| GOT表地址
   0xbffffb68 +--------+
              |08048470|(存放过call 0x80483d9的下一条指令地址)
   0xbffffb64 +--------+
              | ...... |
     (内存低址)

 

 

[目录]



-static 编译选项


★ -static 编译选项
-static
    On systems that support dynamic linking, this prevents
    linking with the shared libraries.  On other  systems,
    this option has no effect.
把一些函数都静态的编译到程序中,而无需动态链接了。
[alert7@redhat62 alert7]$ gcc -o test -static test.c
[alert7@redhat62 alert7]$ wc -c test
962808 test
[alert7@redhat62 alert7]$ gdb -q test
(gdb) disass main
Dump of assembler code for function main:
0x80481b4 :       push   %ebp
0x80481b5 :     mov    %esp,%ebp
0x80481b7 :     call   0x80481a0
0x80481bc :     xor    %eax,%eax
0x80481be :    jmp    0x80481c0
0x80481c0 :    leave
0x80481c1 :    ret
...
End of assembler dump.
(gdb) disass hi
Dump of assembler code for function hi:
0x80481a0 :        push   %ebp
0x80481a1 :       mov    %esp,%ebp
0x80481a3 :       push   $0x8071528
0x80481a8 :       call   0x804865c
0x80481ad :      add    $0x4,%esp
0x80481b0 :      leave
0x80481b1 :      ret
0x80481b2 :      mov    %esi,%esi
End of assembler dump.
[alert7@redhat62 alert7]$ ldd test

        not a dynamic executable
-static出来的代码已经没有PLT了,GOT虽然有,已经全部为0了。

 

 

[目录]



AT&T的汇编格式


一 基本语法

语法上主要有以下几个不同.

★ 寄存器命名原则

AT&T: %eax Intel: eax

★源/目的操作数顺序

AT&T: movl %eax,%ebx Intel: mov ebx,eax

★常数/立即数的格式

AT&T: movl $_value,%ebx Intel: mov eax,_value
把_value的地址放入eax寄存器

AT&T: movl $0xd00d,%ebx Intel: mov ebx,0xd00d

★ 操作数长度标识

AT&T: movw %ax,%bx Intel: mov bx,ax

★寻址方式

AT&T: immed32(basepointer,indexpointer,indexscale)
Intel: [basepointer + indexpointer*indexscale + imm32)

Linux工作于保护模式下,用的是32位线性地址,所以在计算地址时不用考虑segment:offset的问题.上式中的地址应为:
imm32 + basepointer + indexpointer*indexscale

下面是一些例子:

★直接寻址

AT&T: _booga ; _booga是一个全局的C变量

注意加上$是表示地址引用,不加是表示值引用.
注:对于局部变量,可以通过堆栈指针引用.

Intel: [_booga]

★寄存器间接寻址

AT&T: (%eax)
Intel: [eax]

★变址寻址

AT&T: _variable(%eax)
Intel: [eax + _variable]

AT&T: _array(,%eax,4)
Intel: [eax*4 + _array]

AT&T: _array(%ebx,%eax,8)
Intel: [ebx + eax*8 + _array]

二 基本的行内汇编

    ·基本的行内汇编很简单,一般是按照下面的格式:
        asm("statements");
例如:asm("nop"); asm("cli");

    ·asm 和 __asm__是完全一样的.

    ·如果有多行汇编,则每一行都要加上 "/n/t"
例如:

asm( "pushl %eax/n/t"
"movl $0,%eax/n/t"
"popl %eax");

    实际上gcc在处理汇编时,是要把asm(...)的内容"打印"到汇编文件中,所以格式控制字符是必要的.

再例如:
asm("movl %eax,%ebx");
asm("xorl %ebx,%edx");
asm("movl $0,_booga);

    在上面的例子中,由于我们在行内汇编中改变了edx和ebx的值,但是由于gcc的特殊的处理方法,即先形成汇编文件,再交给GAS去汇编,所以GAS并不知道我们已经改变了edx和ebx的值,如果程序的上下文需要edx或ebx作暂存,这样就会引起严重的后果.对于变量_booga也存在一样的问题.为了解决这个问题,就要用到扩展的行内汇编语法.

三 扩展的行内汇编

    扩展的行内汇编类似于Watcom.

    基本的格式是:
asm ( "statements" : output_regs : input_regs : clobbered_regs);
clobbered_regs指的是被改变的寄存器.

下面是一个例子(为方便起见,我使用全局变量):

int count=1;
int value=1;
int buf[10];
void main()
{
asm(
"cld /n/t"
"rep /n/t"
"stosl"
:
: "c" (count), "a" (value) , "D" (buf[0])
: "%ecx","%edi" );
}

得到的主要汇编代码为:

movl count,%ecx
movl value,%eax
movl buf,%edi
#APP
cld
rep
stosl
#NO_APP

    cld,rep,stos就不用多解释了.这几条语句的功能是向buf中写上count个value值.冒号后的语句指明输入,输出和被改变的寄存器.通过冒号以后的语句,编译器就知道你的指令需要和改变哪些寄存器,从而可以优化寄存器的分配.
    其中符号"c"(count)指示要把count的值放入ecx寄存器

类似的还有:

a eax
b ebx
c ecx
d edx
S esi
D edi
I 常数值,(0 - 31)
q,r 动态分配的寄存器
g eax,ebx,ecx,edx或内存变量
A 把eax和edx合成一个64位的寄存器(use long longs)

我们也可以让gcc自己选择合适的寄存器.
如下面的例子:
asm("leal (%1,%1,4),%0"
: "=r" (x)
: "0" (x) );

这段代码实现5*x的快速乘法.
得到的主要汇编代码为:
movl x,%eax
#APP
leal (%eax,%eax,4),%eax
#NO_APP
movl %eax,x

几点说明:

1.使用q指示编译器从eax,ebx,ecx,edx分配寄存器.使用r指示编译器从eax,ebx,ecx,edx,esi,edi分配寄存器.

2.我们不必把编译器分配的寄存器放入改变的寄存器列表,因为寄存器已经记住了它们.

3."="是标示输出寄存器,必须这样用.

4.数字%n的用法:

    数字表示的寄存器是按照出现和从左到右的顺序映射到用"r"或"q"请求的寄存器.如果我们要重用"r"或"q"请求的寄存器的话,就可以使用它们.

5.如果强制使用固定的寄存器的话,如不用%1,而用ebx,则asm("leal (%%ebx,%%ebx,4),%0"

: "=r" (x)
: "0" (x) );

注意要使用两个%,因为一个%的语法已经被%n用掉了.

下面可以来解释letter 4854-4855的问题:

1、变量加下划线和双下划线有什么特殊含义吗?
    加下划线是指全局变量,但我的gcc中加不加都无所谓.

2、以上定义用如下调用时展开会是什么意思?
#define _syscall1(type,name,type1,arg1) /
type name(type1 arg1) /
{ /
long __res; /
/* __res应该是一个全局变量 */
__asm__ volatile ("int $0x80" /
/* volatile 的意思是不允许优化,使编译器严格按照你的汇编代码汇编*/
: "=a" (__res) /
/* 产生代码 movl %eax, __res */
: "0" (__NR_##name),"b" ((long)(arg1))); /
/* 如果我没记错的话,这里##指的是两次宏展开.
  即用实际的系统调用名字代替"name",然后再把__NR_...展开.
  接着把展开的常数放入eax,把arg1放入ebx */
if (__res >= 0) /
return (type) __res; /
errno = -__res; /
return -1; /
}

 

 

[目录]



x86内联汇编


    本文提供了在 Linux 平台上使用和构造 x86 内联汇编的概括性介绍。他介绍了内联汇编及其各种用法的基础知识,提供了一些基本的内联汇编编码指导,并解释了在 Linux 内核中内联汇编代码的一些实例。
    如果您是 Linux 内核的开发人员,您会发现自己经常要对与体系结构高度相关的功能进行编码或优化代码路径。您很可能是通过将汇编语言指令插入到 C 语句的中间(又称为内联汇编的一种方法)来执行这些任务的。让我们看一下 Linux 中内联汇编的特定用法。(我们将讨论限制在 IA32 汇编。)
[目录]



简述


GNU 汇编程序简述
    让我们首先看一下 Linux 中使用的基本汇编程序语法。GCC(用于 Linux 的 GNU C 编译器)使用 AT&T 汇编语法。下面列出了这种语法的一些基本规则。(该列表肯定不完整;只包括了与内联汇编相关的那些规则。)

寄存器命名
    寄存器名称有 % 前缀。即,如果必须使用 eax,它应该用作 %eax。

源操作数和目的操作数的顺序
    在所有指令中,先是源操作数,然后才是目的操作数。这与将源操作数放在目的操作数之后的 Intel 语法不同。
    mov %eax, %ebx, transfers the contents of eax to ebx.

操作数大小
    根据操作数是字节 (byte)、字 (word) 还是长型 (long),指令的后缀可以是 b、w 或 l。这并不是强制性的;GCC 会尝试通过读取操作数来提供相应的后缀。但手工指定后缀可以改善代码的可读性,并可以消除编译器猜测不正确的可能性。

    movb %al, %bl -- Byte move
    movw %ax, %bx -- Word move
    movl %eax, %ebx -- Longword move

立即操作数
    通过使用 $ 指定直接操作数。

    movl $0xffff, %eax -- will move the value of 0xffff into eax register.

间接内存引用
    任何对内存的间接引用都是通过使用 ( ) 来完成的。

    movb (%esi), %al -- will transfer the byte in the memory
    pointed by esi into al register

 

 

[目录]



内联汇编


内联汇编
    GCC 为内联汇编提供特殊结构,它具有以下格式:
    asm ( assembler template
        : output operands               (optional)
        : input operands                (optional)
        : list of clobbered registers   (optional)
        );

    本例中,汇编程序模板由汇编指令组成。输入操作数是充当指令输入操作数使用的 C 表达式。输出操作数是将对其执行汇编指令输出的 C 表达式。

    内联汇编的重要性体现在它能够灵活操作,而且可以使其输出通过 C 变量显示出来。因为它具有这种能力,所以 "asm" 可以用作汇编指令和包含它的 C 程序之间的接口。

    一个非常基本但很重要的区别在于简单内联汇编只包括指令,而扩展内联汇编包括操作数。要说明这一点,考虑以下示例:

内联汇编的基本要素

{
    int a=10, b;
    asm ("movl %1, %%eax;
          movl %%eax, %0;"
          :"=r"(b)  /* output */
          :"r"(a)       /* input */
         :"%eax"); /* clobbered register */
}

    在上例中,我们使用汇编指令使 "b" 的值等于 "a"。请注意以下几点:
    "b" 是输出操作数,由 %0 引用,"a" 是输入操作数,由 %1 引用。
    "r" 是操作数的约束,它指定将变量 "a" 和 "b" 存储在寄存器中。请注意,输出操作数约束应该带有一个约束修饰符 "=",指定它是输出操作数。
    要在 "asm" 内使用寄存器 %eax,%eax 的前面应该再加一个 %,换句话说就是 %%eax,因为 "asm" 使用 %0、%1 等来标识变量。任何带有一个 % 的数都看作是输入/输出操作数,而不认为是寄存器。
第三个冒号后的修饰寄存器 %eax 告诉将在 "asm" 中修改 GCC %eax 的值,这样 GCC 就不使用该寄存器存储任何其它的值。
    movl %1, %%eax 将 "a" 的值移到 %eax 中,movl %%eax, %0 将 %eax 的内容移到 "b" 中。
因为 "b" 被指定成输出操作数,因此当 "asm" 的执行完成后,它将反映出更新的值。换句话说,对 "asm" 内 "b" 所做的更改将在 "asm" 外反映出来。

[目录]



程序模板


    汇编程序模板是一组插入到 C 程序中的汇编指令(可以是单个指令,也可以是一组指令)。每条指令都应该由双引号括起,或者整组指令应该由双引号括起。每条指令还应该用一个定界符结尾。有效的定界符为新行 (/n) 和分号 (;)。 '/n' 后可以跟一个 tab(/t) 作为格式化符号,增加 GCC 在汇编文件中生成的指令的可读性。 指令通过数 %0、%1 等来引用 C 表达式(指定为操作数)。

    如果希望确保编译器不会在 "asm" 内部优化指令,可以在 "asm" 后使用关键字 "volatile"。如果程序必须与 ANSI C 兼容,则应该使用 __asm__ 和 __volatile__,而不是 asm 和 volatile。

 

 

[目录]



操作数


   C 表达式用作 "asm" 内的汇编指令操作数。在汇编指令通过对 C 程序的 C 表达式进行操作来执行有意义的作业的情况下,操作数是内联汇编的主要特性。

    每个操作数都由操作数约束字符串指定,后面跟用括弧括起的 C 表达式,例如:"constraint" (C expression)。操作数约束的主要功能是确定操作数的寻址方式。

    可以在输入和输出部分中同时使用多个操作数。每个操作数由逗号分隔开。

    在汇编程序模板内部,操作数由数字引用。如果总共有 n 个操作数(包括输入和输出),那么第一个输出操作数的编号为 0,逐项递增,最后那个输入操作数的编号为 n-1。总操作数的数目限制在 10,如果机器描述中任何指令模式中的最大操作数数目大于 10,则使用后者作为限制。

 

 

[目录]



修饰寄存器列表


    如果 "asm" 中的指令指的是硬件寄存器,可以告诉 GCC 我们将自己使用和修改它们。这样,GCC 就不会假设它装入到这些寄存器中的值是有效值。通常不需要将输入和输出寄存器列为 clobbered,因为 GCC 知道 "asm" 使用它们(因为它们被明确指定为约束)。不过,如果指令使用任何其它的寄存器,无论是明确的还是隐含的(寄存器不在输入约束列表中出现,也不在输出约束列表中出现),寄存器都必须被指定为修饰列表。修饰寄存器列在第三个冒号之后,其名称被指定为字符串。

    至于关键字,如果指令以某些不可预知且不明确的方式修改了内存,则可能将 "memory" 关键字添加到修饰寄存器列表中。这样就告诉 GCC 不要在不同指令之间将内存值高速缓存在寄存器中。

 

 

[目录]



操作数约束


    前面提到过,"asm" 中的每个操作数都应该由操作数约束字符串描述,后面跟用括弧括起的 C 表达式。操作数约束主要是确定指令中操作数的寻址方式。约束也可以指定:

    ·是否允许操作数位于寄存器中,以及它可以包括在哪些种类的寄存器中
    ·操作数是否可以是内存引用,以及在这种情况下使用哪些种类的地址
    ·操作数是否可以是立即数

约束还要求两个操作数匹配。

常用约束

    在可用的操作数约束中,只有一小部分是常用的;下面列出了这些约束以及简要描述。有关操作数约束的完整列表,请参考 GCC 和 GAS 手册。

寄存器操作数约束 (r)
    使用这种约束指定操作数时,它们存储在通用寄存器中。请看下例:
           asm ("movl %%cr3, %0/n" :"=r"(cr3val));
    这里,变量 cr3val 保存在寄存器中,%cr3 的值复制到寄存器上,cr3val 的值从该寄存器更新到内存中。指定 "r" 约束时,GCC 可以将变量 cr3val 保存在任何可用的 GPR 中。要指定寄存器,必须通过使用特定的寄存器约束直接指定寄存器名。

a   %eax
b   %ebx
c   %ecx
d   %edx
S   %esi
D   %edi

内存操作数约束 (m)
    当操作数位于内存中时,任何对它们执行的操作都将在内存位置中直接发生,这与寄存器约束正好相反,后者先将值存储在要修改的寄存器中,然后将它写回内存位置中。但寄存器约束通常只在对于指令来说它们是绝对必需的,或者它们可以大大提高进程速度时使用。当需要在 "asm" 内部更新 C 变量,而您又确实不希望使用寄存器来保存其值时,使用内存约

抱歉!评论已关闭.