上一篇文章学习了链接脚本的语法与相关概念:链接脚本的概念
在继续学习链接器的内容的同时,先学习一个新内容:内嵌汇编。
GCC编译器一般支持C/C++内嵌汇编语言,这样可以实现语言本身无法实现的内容。我们本文主要介绍C语言中的内嵌汇编,C++语言也是一样的规则。
首先要知道以下内容
x86汇编的两种语法:intel语法和AT&T语法
x86汇编一直存在两种不同的语法,在intel的官方文档中使
用intel语法,Windows也使用intel语法,而UNIX平台的汇编器一
直使用AT&T语法,所以本文是AT&T语法。 mov %edx,%eax 这条
指令如果用intel语法来写,就是 mov eax,edx ,寄存器名不加 % 号,
并且源操作数和目标操作数的位置互换。本文不详细讨论这两种
语法之间的区别,读者可以参考[AssemblyHOWTO]。
介绍x86汇编的书很多,UNIX平台的书都采用AT&T语法,例
如[GroudUp],其它书一般采用intel语法,例如[x86Assembly]。
上述的解释还不是很好理解,我们可以看一个示例:
看了上面的解释,我们大概学会了内嵌汇编的组成,大致有汇编指令,这是必须存在的,可选参数,这是不必须存在的。
我们注意到上面的限制符 r 代表编译器指定一个通用寄存器关联变量,这是让编译器做主。但是我们也可以自己做主,自己指定一个寄存器来关联我们的变量。那么,都有哪些限制符以及他们对应的寄存器呢?常用的见下表:
上述的r代表通用寄存器,很明显与我们刚学习的一样。
在知道了上述规则之后,我们来看看一个例子:
9-1.c
#include
int main()
{
int result = 0;
int input = 1;
int a = 1;
int b = 2;
asm volatile (
"movl %1, %0\n"
: "=r"(result)
: "r"(input)
);
printf("result = %d\n", result);
printf("input = %d\n", input);
asm volatile (
"movl %%eax, %%ecx\n"
"movl %%ebx, %%eax\n"
"movl %%ecx, %%ebx\n"
: "=a"(a), "=b"(b)
: "a"(a), "b"(b)
);
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
}
编译:
分析第二个代码块
"=a"(a), "=b"(b)
代表输出参数,且将EAX寄存器与变量a关联,将EBX寄存器与b相连"a"(a), "b"(b)
代表输入参数,且将EAX寄存器与变量a关联,将EBX寄存器与b相连movl %%eax, %%ecx\n"
将EAX寄存器的值(也就是a的值)传送给ECX寄存器movl %%ebx, %%eax\n"
将EBX寄存器的值(也就是b的值)传送给EAX,相当于将b赋给amovl %%ecx, %%ebx\n"
将ECX寄存器的值(也就是a的值)传送给EBX,相当于将a赋给b经过了上面的操作,就交换了a与b的值。
在程序中我们经常使用printf打印东西。中所周知,printf是一个系统函数,我们如何在不使用printf的前提下打印字符串?
使用内嵌汇编可以进行系统调用。
可以通过INT 0X80H
指令进行系统调用:
上面的解释是非常清楚的。注意区分传递的是立即数还是占位符即可。
同时,上面的例子 加了保留列表。它的意思是保留寄存器,不用于关联变量,因为这些寄存器已经被我们用于做系统调用以及传参数了。
那么上述汇编代码执行后参数s与参数l会被传给系统调用函数sys_write 。
下面再给出一个示例来看看与上面的示例有什么区别:
上面这个示例没有可选参数与保留列表。也就是没有输入输出参数,没有保留列表。
除了上述的区别,还有什么区别???
很明显,这个寄存器的前面是一个%,而刚刚那个是%%两个百分号。这是什么原因?
那么在内嵌汇编中就有如下注意事项:
嵌入汇编时,除了汇编语言的指令不能省略,可选参数与保留列表都可以省略
当省略保留列表时,对应的“ : ”可忽略
当省略可选参数时,寄存器前使用 % 作为前缀
当有可选参数时,寄存器前使用 %% 作为前缀
在学习了上述的一系列原理后,我们写出如下代码:
9-2.c
#include
int main()
{
char* s = "D.T.Software\n";
int l = 13;
printf("main begin\n");
asm volatile(
"movl $4, %%eax\n"
"movl $1, %%ebx\n"
"movl %0, %%ecx\n"
"movl %1, %%edx\n"
"int $0x80 \n"
:
: "r"(s), "r"(l)
: "eax", "ebx", "ecx", "edx"
);
asm volatile(
"movl $1, %eax\n"
"movl $42, %ebx\n"
"int $0x80 \n"
);
printf("main end\n");
return 0;
}
可以看出,我们使用内嵌汇编打印出了"D.T.Software\n"
; 并且,在第二个汇编代码块中,进行系统调用调用了sys_exit 函数,直接退出进程运行了,所以第二个printf("main end\n");
并没有执行。
当然我们也可以使用以下命令查看最近一次的一个进程退出时的退出状态码:
很明显,退出状态码是42,正好与我们程序中写的一样。
本文参考狄泰软件学院相关课程
想学习的可以加狄泰软件学院群,
群聊号码:199546072
学习探讨加个人(可以免费帮忙下载CSDN资源):
qq:1126137994
微信:liu1126137994