(转)linux下的汇编总结

发布于 2022-10-01 11:33:29 字数 2501 浏览 21 评论 0

和luster一起学习在linux下使用汇编语言(1)

摘要

  我开始学习在Linux使用汇编语言了。我以前从来没有在Linux下使用过汇编语言。我今天看到了一份文档,是Linux Assembly HOWTO,我看了看,决定学习学习。下面是我的学习笔记。让我一起学习吧。

(2002-08-25 14:25:47)

--------------------------------------------------------------------------------
By Wing, 出处:作者:luster

  在Linux下使用汇编语言

  by Wang Hui(hwang@ustc.edu)

  2001/9/11

  我开始学习在Linux使用汇编语言了。我以前从来没有在Linux下使用过汇编语言。我今天看到了一份文档,是Linux Assembly HOWTO,我看了看,决定学习学习。下面是我的学习笔记。让我一起学习吧。

  1. 你需要汇编语言吗?

  汇编语言的优点和缺点

  汇编语言可以很直接的表达比较低层次的:

  · 你可以通过汇编代码访问与机器硬件直接相关的存储器或者I/O口。

  · 你可以在一些关键的代码区准确的控制某些代码的行文,避免其他方法带来的多线程共同访问或者硬件设备共享引起的死锁。

  · 你可以通过使用汇编打破通常的编译器编译的结果,例如你可以根据自己的考虑优化一些东西。

  · 我们可以在代码分片之间建立一些接口。

  · 我们可以通过一些汇编代码使处理器进入不寻常的编程模式。例如16 bit mode to interface startup, firmware, or legacy code on Intel PCs.

  · 可以优化一些代码,提高运行速度。

  · 可以为特定的应该编写优化代码。

  · 可以为某语言的编译器编写代码。

  汇编语言是一种层次非常低的语言(仅仅高于手动编写二进制机器指令码),这就意味着:

  · 开始写起来会很长而且单调

  · 很容易出现bug

  · 程序中的bug非常难查找

  · 写出来的代码非常难懂,而且不好维护和修改

  · 而且结果是不能到其他体系结构的平台上运行。

  · 代码只能针对某特特定体系结构的特定型号的处理器优化。例如,在Intel兼容系列的CPU中,每一款CPU都有特殊的设计,变量也不一样,都有特定的优化考虑。CPU的时间已经有很多了,例如:Intel 386, 486, Pentium, PPro, PII, PIII, PIV; Cyrix 5x86, 6x86, M2; AMD K5, K6 (K6-2, K6-III), K7 (Athlon, Duron). 而且新的设计还不断地出现。

  · 编写汇编代码时候,我们会花费很多的时间在一些细节事情上,而不能专注一些小的或者大的算法设计,而算法的优化能更快的提高程序运行的速度。例如,你可以用汇编加快一些数组的访问和管理,但是总体上讲,使用hash表会更大程度上加快访问速度。

  · 一个算法上的小的改动,可能需要我们全部重新写一段很长的代码。

"compilers make it a lot easier to use complex data structures,
and compilers don't get bored halfway through and generate
reliably pretty good code."

  2. Linux 和汇编

  你可能已经注意到了,在linux下开发程序,通常不需要使用汇编语言。与DOS不一样,你不需要使用汇编来编写linux的驱动程序(当然,如果你非得用汇编写,也可以)。而且现在的编译器有很多优化器,可以针对不多的CPU优化尼的高级语言写的代码,例如C代码。当然既然我们在讨论这个问题,而且你都在阅读本文了,luster知道你肯定有自己的理由去使用汇编而不是C或者C++了。:)

  你可能需要使用汇编,或者你不得不使用汇编。简单的说,使用汇编的理由就是精简代码和libc无关性。还有一个理由就是,希望像一个以前的疯狂的hacker那样,这些20多岁的hacker的习惯就是用汇编写任何代码。

  然而,假如你要移植linux到某些嵌入式硬件环境下,你需要减少系统的大小,你需要将很多基本的系统精简到很小的空间。其中的一个可行的方法就是,重新用汇编写部分(或者全部?)代码。例如,用汇编写的简单的http服务器程序可能少于600 bytes,这样呢可以把建立一个webserver(包括一个内核和一个httpd),大小只有400KB或者更小。是不是很爽?:)

  今天就学习到这里。下面就让我们真正开始吧。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

怪我太投入 2022-10-08 11:33:29

和luster一起学习在linux下使用汇编语言(2)

摘要

  GNU C/C++编译器(GCC)是一个32-bit的编译器,是GNU计划的核心,能非常好的支持x86体系结构,它支持在C程序里面嵌入汇编代码,这样看来,寄存器可以自己指定也可以留给gcc来分配。GCC可以在多个平台下使用,除了linux,还有*BSD, VSTa, OS/2, *DOS, Win*等等。

(2002-08-25 14:26:20)

--------------------------------------------------------------------------------
By Wing, 出处:作者:luster

  2001/9/12

  3. 汇编器

  gcc内嵌编译器

  GNU C/C++编译器(GCC)是一个32-bit的编译器,是GNU计划的核心,能非常好的支持x86体系结构,它支持在C程序里面嵌入汇编代码,这样看来,寄存器可以自己指定也可以留给gcc来分配。GCC可以在多个平台下使用,除了linux,还有*BSD, VSTa, OS/2, *DOS, Win*等等。

  可以在GNU FTP站点下载GCC。链接是:

  ftp://prep.ai.mit.edu/pub/gnu/gcc/

  现在GCC已经分裂成两个分支(GCC 2.8和EGCS),但是它们又重新合并了起来,GCC的主页位于:http://gcc.gnu.org.

  因为内核头文件中定义的一些汇编例程函数都是extern inline函数,所以GCC必须要加-O(或者-O2,-O3等)编译标志,这样才能使得这些函数可以使用。否则,如果不加-O标志,代码也可以被编译,但是可能link会不正确。

  嵌入汇编语言可以通过“-fno-asm”来被禁止。不过通常来说,比较好的编译标志是如下的,这对于大多数的x86平台都是很不错的选择:

gcc -O2 -fomit-frame-pointer -W -Wall

  -O2在大部分情况下都是很好的代码优化级别。事实上,使用优化后会带来更多的编译时间,也会产生大一些地代码,而在速度上仅仅有一点点提高。

  -fomit-frame-pointer允许产生的代码中跳过一些错误的frame pointer maintenance,这样可以使得代码小一些而且快一些,而且会释放一些寄存器来获得更进一步的优化。

  -W –Wall使得我们可以获得很多有用的warning,这对于我们减少一些愚蠢的编码错误是非常有用的。

  我们还可以加一些编译参数来指明目标CPU的类型,这样就允许GCC来产生一些针对特定CPU的优化代码。在编译内核中使用指明CPU类型的参数是非常有用的。具体GCC支持什么类型CPU优化,我们可以参考所使用的GCC的文档。

  GCC对宏的支持

  GCC允许而且要求你指明你的嵌入汇编代码使用的寄存器,这样编译器的优化器才能知道如何优化。因此,嵌入汇编代码通常是一些patterns,而不是真正的代码(exact code)。因此,通常我们把汇编代码放到CPP的宏定义里面,或者定义成inline函数,这样就可以方便的就像使用C函数或宏一样使用这些代码。Inline函数和宏非常相似,不过inline函数比较起后者,使用起来更清晰。

  GAS

  GAS是GNU的汇编器,GCC实际上也依赖它。我们可以在任何能下载GCC的地方找到这个实用工具,它通常位于一个名叫binutils的包里面。下面的URL给出了最新的版本:

  ftp://ftp.varesearch.com/pub/support/hjl/binutils/.

  GAS是一个支持32-bit的unix环境下的汇编编译器,它使用标准的AT&T语法,它使用标准的m68k编译器的语法,这是UNIX world的标准。这个语法和Intel的语法不一样,如果你适应了这种语法,那么你就会发现它也是很好用的。下面是一些GAS语法的主要特点:

  · 所有的寄存器的名字都是以“%”最为前缀,所以可能的一些寄存器是%eax,%dl等等,而不是简单的eax,dl等。这样的话可以使得C语言中方便的嵌入一些汇编代码而不会有变量名字方面冲突的担心。

  · 操作数的顺序也是相反的。例如要把edx寄存器的内容拷贝到eax寄存器里面的话,GAS的语法是:mov %edx,%eax;而Intel平台下的汇编语句是:mov eax,edx。

  · 操作数的尺寸作为一个后缀加在操作名的后面。B表示(8-bit)byte,w表示(16-bit) word,l表示(32-bit) long。例如,上面例子中正确的语法应该是:movl %edx,%eax。不过,GAS并不需要严格的AT&T语法,所以这个后缀可可以省略的,这时候编译器就自己根据操作数判断或者置为缺省的位宽32-bit。

  · 立即数需要一个“$”前缀符号,例如:addl $5,%eax。

  · 如果没有操作数前缀,则表示它是内存地址,例如:movl $foo,%eax,把变量foo的地址送到了%eax寄存器。但是“movl foo,%eax”则把foo变量的内容送到eax寄存器中。

  已经有一些工具可以帮助你把你的使用Intel汇编语法的源程序转换成AT&T语法,或者做相反的转换。

  不过,已经有的好消息是Binutils 2.10开始,GAS已经可以支持Intel语法了。

  16-bit mode

  Binutils (2.9.1.0.25+) 可以全面支持i386PC体系下的16-bit模式(存储器和寻址)。使用“.code16”和“.code32”来切换汇编模式。

  还有很多的编译器,我们可以选择,明天再学习把。

  昨天晚上美国遭到恐怖袭击,去看电视直播去了。拜拜。今天到此。

  luster@linuxaid.com.cn

  hwang@ustc.edu

乖不如嘢 2022-10-08 11:33:29

和luster一起学习在linux下使用汇编语言(3)

摘要

  Netwide Assebler Project计划提供了一个非常好用的 i386汇编器,这个汇编器是用C写的,可以支持很多种对象文件格式。

(2002-08-25 14:28:47)

--------------------------------------------------------------------------------
By Wing, 出处:作者:luster

  今天我们接着看看还有其他什么可以选择的汇编编译器:

  NASM

  Netwide Assebler Project计划提供了一个非常好用的 i386汇编器,这个汇编器是用C写的,可以支持很多种对象文件格式。

  我们可以在下面的网址获得nasm最新的版本:

  http://nasm.2y.net, http://www.cryogen.com/nasm/

  语法还是使用的Intel方式的,集成了很好的对宏指令的支持。支持的object文件格式有:bin,a.out,coff,elf,as86,obj(DOS),win32,rdf等。NASM可以用作免费的

  LCC编译器的backend。

  除非你使用16-bit的编译器BCC,否则,你可以很好的使用NASM,而不必要去使用其他的AS86或者MASM,因为NASM在几乎所有的平台上都可以很好的运行。另外,它还有一个反汇编程序,名字叫NDISASM。

  NASM是用的是一个人工编写语法分析器,比GAS速度要快。如果你喜欢用Intel风格的语法,而不习惯GAS的语法,那么NASM是你最好的选择了。

  AS86

  AS86是一个支持80x86的汇编编译器,同时支持16-bit和32-bit,也有集成的对宏指令的支持。在寻址表示方面有点区别外,其他语法都是何Intel的语法一样的。最新的版本可以在下面的网址获得:

  http://www.cix.co.uk/~mayday/,

  下面是个例子,是一个GNU Makefile里面,使用BCC来讲.s asm文件编译成aout .o 的目标文件和 .l 的listing文件:

%.o %.l: %.s bcc -3 -G -c -A-d -A-l –A$*.l -o $*.o $<

  如果你不需要listing文件,就把“%.l,-A –l”和”-A$* .l”删除。如果你想编译成其他的东西,而不是a.out文件,那么可以修改BCC的参数。

  其他汇编器

  还有很多的可以使用的汇编器。下面列举几个,供大家参考:

  Free Pascal

  Free Pascal 集成了一个32-bit的汇编器。

  Win32Forth assembler

  Win32Forth是一个免费的32-bit的ASN FORTH系统,可以在Win32s,Win95,WinNT下正常使用。它包括了一个32-bit的汇编器,这个编译器嵌入到了FORTH语言当中了。可以在下面的站点下载:

  ftp://ftp.forth.org/pub/Forth/Compilers/native/windows/Win32For/.

  SHASM

  SHASM是用GNU Bash Version 2编写的一个汇编器,可以在其他unix-style的“shell”命令解释器下运行。可以在下面的网址下载最新版本:

  ftp://linux01.gwdg.de/pub/cLIeNUX/interim/shasm.TGZ

  TDASM

  TDASM是Table Driven Assembler的,可以对各种版本的汇编语言做交叉编译。可以到下面的网站下载:

  http://www.penguin.cz/~niki/tdasm/

  Terse

  Terse是一个编程工具,它为x86系列提供了最紧凑的汇编语法编译。详细情况参考:

  http://www.terse.com/

  HLA

  HLA 是a High Level Assembly language的缩写。它的变量声明、过程声明、过程调用等都使用高级语言类似的语法)例如使用与Pascal,C/C++类似的语法)。HLA是免费的,不过只能在Win32下面使用。访问下面的网站可以获得更多的资料:

  http://webster.cs.ucr.edu/

  TALC

  TALC 是另外一个基于MASM/Win32地免费的编译器。TAL是Typed Assembly Language的缩写。可以访问下面的网站:

  http://www.cs.cornell.edu/talc/

  待续。。。

  luster@linuxaid.com.cn

  hwang@ustc.edu

空宴 2022-10-08 11:33:29

和luster一起学习在linux下使用汇编语言(4)

摘要

  汇编代码编写程序是非常枯燥的,但是通常是程序的一些关键的部分. 你应该根据自己的任务选择合适的开发工具,

(2002-08-25 14:28:53)

--------------------------------------------------------------------------------
By Wing, 出处:作者:luster

  4. Metaprogramming

  汇编代码编写程序是非常枯燥的,但是通常是程序的一些关键的部分. 你应该根据自己的任务选择合适的开发工具, 如果不适合用汇编的场合最好还是别用, C, Ocaml, perl, Scheme, 都是其他可以选择使用的好的开发工具. 但是, 某些场合这些开发工具可能不能提供足够的对机器的控制能力, 此时, 汇编语言就可以发挥作用了.这些场合,你常常学要结合macroprocessing和metaprogramming. 纯粹的汇编代码通常是不够用的, 你要把自己的代码和C 语言代码link起来.

  External filters

  无论你的汇编器支持怎么样的宏,或者你使用什么样的语言(例如C), 这种语言应该对于你来说是足够清晰的,你可以在Makefile里面使用这样的规则来用一些扩展的filters来过滤你的文件:

%.s: %.S other_dependencies $(FILTER) $(FILTER_OPTIONS) < $< >; $@

  Metaprogramming

  不使用扩展的filter来展开这些宏,也可以写一些程序来产生其他的程序的一部分或者全部.

  例如,你可以使用程序来输出一些源码:

  · 产生标准的数学函数sin/cos等数学函数值查找表.

  · 将我们的bitmap图片文件快速显示的函数

  · 用自己写的perl/shell/scheme脚本来产生一些我们自己的特定的汇编代码.

  · 用程序来生产初始化和结束处理的代码,对数据表的描述代码等.

  · 等等

  待续。。。

ぃ双果 2022-10-08 11:33:29

和luster一起学习在linux下使用汇编语言(5)

摘要

  好吧,我们已经铺垫了很多东西了,而且看上去用汇编写程序似乎是一个非常恐怖的事情了。不过既然我们感兴趣,还是应该开始我们的“hello world”程序。

(2002-08-25 14:29:00)

--------------------------------------------------------------------------------
By Wing, 出处:作者:luster

  5. 我们开始写hello world吧

  好吧,我们已经铺垫了很多东西了,而且看上去用汇编写程序似乎是一个非常恐怖的事情了。不过既然我们感兴趣,还是应该开始我们的“hello world”程序。

  下面的代码中,我们准备采取直接使用内核中的系统调用的方法,这是调用系统内核服务的最快的方法,我们的代码不链接到其他函数库,也不使用ELF解释器,而是直接和内核通讯。

  我们分别使用nasm和gas两种汇编器来编译我们的程序,这样我们可以看到Intel和AT&T两种语法格式了。

  使用的工具

  当然首先我们需要汇编编译器nasm和gas。然后我们需要链接器-ld,因为汇编编译器是生成的只是object代码。一般的发行包的binutils里面包括了gas和ld这两个实用工具。而对于大多数的发行包(例如,Debian,SuSe,Mandrake)都有nasm。

Hello, world!

  Linux是一个32位的,运行在保护模式下的操作系统,使用的是flat memory 模式,使用ELF格式的二进制代码。

  一个程序可以划分为下面几个部分: .text,.data,.bss。.text是一些只读的代码,.data是可读可写的数据区,.bss则是可读可写的没有初始化的数据区。当然可以有其他一些标准的部分,也可以使用户自己定义的sections,但是我们这里不关心。一个程序至少有.text部分。

  下面就是我们的第一个程序“hello,world”。我们给出两个版本,分别是nasm和gas两种。

NASM (hello.asm)
--------------
section .data ;section declarationmsg db "Hello, world!",
0xa ;our dear stringlen equ $ - msg ;length of our dear stringsection .text ;
section declaration ;we must export the entry point to the ELF linker
or global _start ;loader. They conventionally recognize _start as their ;
entry point. Use ld -e foo to override the default._start:;write our string
to stdout mov edx,len ;third argument: message length mov ecx,msg ;second
argument: pointer to message to write mov ebx,1 ;first argument: file handle
(stdout) mov eax,4 ;system call number (sys_write) int 0x80 ;call kernel;
and exit mov ebx,0 ;first syscall argument: exit code mov eax,1 ;system
call number (sys_exit) int 0x80 ;
call kernel
GAS (hello.S)
--------------
.data # section declarationmsg: .string "Hello, world! "
# our dear string len = . - msg # length of our dear string.text
# section declaration # we must export the entry point to the ELF linker or
.global _start # loader. They conventionally recognize _start as their
# entry point. Use ld -e foo to override the default._start:
# write our string to stdout movl $len,%edx # third argument:
message length movl $msg,%ecx # second argument: pointer to message to
write movl $1,%ebx # first argument: file handle (stdout) movl $4,%eax
# system call number (sys_write) int $0x80 # call kernel# and exit movl
$0,%ebx # first argument: exit code movl $1,%eax # system call number
(sys_exit) int $0x80 # call kernel

  建立可运行的程序

  要生成一个可执行的代码,首先就是用源代码编译生产一个object文件。

  对于nasm,下面的语法:

$ nasm -f elf hello.asm

  而对于gas,而用下面的语法:

$ as -o hello.o hello.S

  这样就得到了hello.o这个object文件了。

  然后我们就要使用这个object文件来生成可执行代码。这里使用链接器链接:

$ ld -s -o hello hello.o

  这样我们就获得了我们的可以执行的代码“hello,world”。

  我们的学习就告一段落了。更多的信息可以去参考:

  http://linuxassembly.org/list

  by Luster(luster@linuxaid.com.cn)

  2001/9/21

油饼 2022-10-08 11:33:29

网上关于AT&T汇编的资料也就这些,你说的也就这些,还不是一个样子,just add some english informations,just so so !

寻梦旅人 2022-10-08 11:33:29

对汇编很感兴趣,但是感觉汇编有点难,
看了一下,有一点不明白:

  · 如果没有操作数前缀,则表示它是内存地址,例如:movl $foo,%eax,把变量foo的地址送到了%eax寄存器。但是“movl foo,%eax”则把foo变量的内容送到eax寄存器中。

movl $foo,%eax
是不是把foo这个数送到%eax?

谢谢!!

花开柳相依 2022-10-08 11:33:29

怎么没有转完全啊,而且代码格式也有点乱

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文