今天就来用详细实例代码来运用一下昨天所说的仅仅个工具的使用方法吧
这几个实例基本的目的是来熟悉一下汇编相关工具的使用方法及应用一下昨天刚说的汇编程序模板。
我们用到的工具主要有as,ld,gcc,gdb,当然。它们是执行在linux系统下的
废话少说,直接来样例了。嗯,再说一句,以下的样例是參考或来自《汇编语言程序设计》Richard Blum的
例一:打印出"hello,world!"
#hellowrold.s print "hello,world!" .section .data output: .ascii "hello,world " .section .text .globl _start _start: movl $4, %eax movl $1, %ebx movl $output,%ecx movl $12,%edx int $0x80 movl $1, %eax movl $0, %ebx int $0x80
简单说一下代码:
首先,在数据段中声明一个字符串:
output: .ascii "hello,world "
.asscii声明使用ASCII字符声明一个文本字符串。字符串元素被提前定义而且放在内存中,其起始内存位置由标签output指示。
以下是声明程序的指令码段和一般的起始标签,_start是链接器默认的起始代码:
.section .text .globl _start _start:以下是直接调用write系统调用来显示文本内容
movl $4, %eax movl $1, %ebx movl $output,%ecx movl $12,%edx int $0x80Linux下write系统调用的參数:
EAX包括系统调用值。write是4
EBX包括要写入的文件描写叙述符,我们知道,Linux终端中0表示标准输入。1表示标准输出。2表示错误输出,这里将1传入EBX,也就是表示标准输出
ECX包括字符串的开头
EDX包括字符串的长度
用样。以下也是系统调用 ,1表示退出函数,返回值为0
movl $1, %eax movl $0, %ebx int $0x80
编译执行结果例如以下:
先解释编译參数,
第一步:首先编译成二进制文件 as --32 -o hellowrold.o hellowrold.s
as表示用as汇编器。
--32表示将目标代码编译成ia-32代码格式
-o hellowrold.o 表示目标文件是hellowrold.o(好像,写错文件名称了Orz)
hellowrold.s就是源码了(本来要定成helloworld.s的,错了就错了吧)
第二步:然后。将hellowrold.o链接成可运行文件
ld -m elf_i386 -o hellowrold hellowrold.o
ld表示是用ld链接
-m elf_i386 表示生成32 elf位 elf格式文件
-o hellowrold表示生成的文件是hellowrold
hellowrold.o 是在第一阶段生成的二进制文件
再来试试gdb这个调试工具。汇编器as的多了个參数 -g,表示生成debug 代码。gdb hellowrold执行调试,界面例如以下:
gdb的使用方法主要有几个:list显示代码,break设置段点。 info register显示全部寄存器的值。print打印特定变量的值。x显示特定内存位置的值,step下一指令,run执行代码。
演示一下:
list,列出代码
break设置断点。这里是在特定的标签中设置,break有下面方式设置断点:
1.到达某个标签
2.到达源码中的某个行号
3.数据值到达特定值
4.函数运行了指宝的次数之后
print 打印出对应的值。print 的输出格式有:
print/d 输出十进制值
print/t 输出二进制值
print/x 输出十六进制值
info register 打印出全部寄存器值
当然,我们的样例仅仅要改一下,将 代码入口标签_start改成main就能够用gcc来编译。
gcc -m32 -o hellowrold hellowrold.s
就能够编译成功了。
例二、以下再说个在汇编语言中调用c函数库的样例。
.section .data output: .ascii "The number is %d " .section .bss .lcomm buffer,18 .section .text .globl _start _start: pushl $520 pushl $output call printf addl $8,%esp pushl $0 call exit例如以下方法编译该代码,能够看出。ld链接的时候多了几个參数。
让我来一一说一下多出来的两个參数的含义吧。
我们知道 ,在linux中,把C函数连接到汇编语言程序有两种方法。第一种中做静态链接(static linking).静态链接把函数目标代码直接连接到应用 程序的可运行程序文件里。
这样会创建巨大的可运行程序。并且,假设同一时候运行程序的多个实例,会造 成内在浪费(每一个函数都有其自己的同样函数拷贝)
另外一种方法是动态链接。
在Linux中,标准C的动态库位于lib.so.x文件里,在我的系统(ubutnu 14.04 )中,这个文件是libc.so.6。因为我採用兼容方式执行,所以,我的系统有两个该文件,一个是32位的(/lib/i386-linux-gnu/libc.so.6),另一个是64位的(/lib/x86_64-linux-gnu/libc.so.6)。在使用gcc时。gcc是自己主动将c语言链接到该库。我们使用ld。为了链接libc.so文件。必须使用gnu连接器的-l 參数,不用指定完整的库名称。连接器如果在它能找到的位置存在libxso文件。基中x是命令行參数指定的库名称。我们的是c,故使用
-lc
理论上,我们不用加參数 -dynamic-linker就能够执行了。可其实,编译是通过了。可是执行不了。
bash: ./print: No such file or directory为什么呢?
问题在于连接器是可以解析C函数了,可是函数本身没有包括在终于可执行程序中。链接器如果执行时程序可以找到该库文件。所以编译进不出错。但其实。我们的程序找不到该库文件。为了解决问题,还必须指定在程序执行时载入动态库的程序。对于LINUX,这个程序是linux.so.2,在我的系统下,它位于/lib下。为了指定这个程序,必须使用gnu链接器的 -dynamic-linker,故还要加入參数
-dynamic-linker事实上,我们也能够直接用 gcc编译,仅仅要把_start标签改成 main就能够例如以下方法 编译了
gcc -o print print.s