龙空技术网

C语言及AT&T汇编格式改写《现代X86汇编语言程序设计》范例(一)

C语言与汇编 142

前言:

今天朋友们对“c语言程序开发范例宝典”大体比较讲究,姐妹们都想要剖析一些“c语言程序开发范例宝典”的相关内容。那么小编在网摘上收集了一些有关“c语言程序开发范例宝典””的相关文章,希望同学们能喜欢,看官们一起来了解一下吧!

《现代X86汇编语言程序设计》的示范程序在windows操作系统“Core”CPU的操作环境下,采用C++语言嵌套intel风格的MASN汇编语言进行编写,本文拟在Linux操作系统、“Celeron N2840”CPU的环境下,使用C语言嵌套AT&T汇编格式,对书中第一个示范程序ch02_01(中文版第18页)进行改写。改写的程序中变量名称、变量值、程序结构与示范程序一致,其余内容均属原创。文中主要涉及的知识包括:1、如何在C语言中嵌套汇编语言;2、如何使用gdb调试工具;3、C语言中对变量的定义和赋值存放在哪些硬件空间中。程序实现的功能是对变量a、b、c、d进行赋值,并输出a+b+c-d的计算值。

一、在同一文件夹内创建.c、.asm、Makefile文件

$touch ch02_01.c

$touch ch02_01_01.asm

$touch Makefile

说明:书中的汇编程序文件名为ch02_01.asm,若采用这个文件名,在编译时c程序与汇编程序都会产生同名的目标文件ch02_01.o,导致编译失败。因此汇编程序与C程序不能同名。

二、编辑ch02_01.c文件如下:

#include<stdio.h>

#include<inttypes.h> //该头文件的作用是进行转换进制,如果没有转换进制,汇编引用时会被认为是十六进制数

int64_t IntegerAddSub_(int64_t a,int64_t b,int64_t c,int64_t d); //定义即将引用的汇编函数IntegerAddSub_

int main(void)

{

int64_t a;

int64_t b;

int64_t c;

int64_t d;

int result; //由于IntegerAddSub_函数已经转化为十进制,因此result变量不需在转进制

a=10;

b=20;

c=30;

d=18;

result=IntegerAddSub_(a,b,c,d); //引起汇编函数

printf("test1\n %ld\n %ld\n %ld\n %ld\n result=%" PRIi64 "\n",a,b,c,d,result);

//PRIi64两边有空格,表示打印64位有符号数据,i表示有符号,本案例也可换为%ld

a=101;

b=34;

c=5; //原书案例中此数为负数,本例直接采用负数会出现计算错误,因时间关系,以后在继续研究如何采用负值计算

d=18;

result=IntegerAddSub_(a,b,c,d); //再次引用汇编函数

printf("test2\n %ld\n %ld\n %ld\n %ld\n result=%" PRIi64 "\n",a,b,c,d,result);

return 0;

}

说明:书中的示范程序可以对有符号的负数进行计算,由于时间关系,这次改写尚不能满足负数计算,暂时只能进行无符号的正整数计算。

三、编辑ch02_01_01.asm文件(以下先展示错误代码,错误代码由书中代码直接改写,后面再说明如何调试、修正)

.text //以下为代码段

.global IntegerAddSub_ //定义标签IntegerAddSub_,该标签处的代码将被C程序作为函数引用

IntegerAddSub_: //标签入口

movq %rcx,%rax //将寄存器rcx的值赋予rax,意味着第一个变量a的值在rcx中

addq %rdx,%rax //将寄存器rdx与rax的值相加,并将和值存入rax中,意味着第二个变量b存放在rdx中

addq %r8d,%rax //同上

subq %r9d,%rax //将寄存器rax的值减去r9d中,计算值存入rax

ret

四、编辑Makefile文件

ch02_01:ch02_01_01.o ch02_01.c //表示可执行程序ch02_01由ch02_01_01.o和ch02_01.c共同形成

gcc ch02_01_01.o ch02_01.c -o ch02_01 //用gcc编译、链接ch02_01_01.o和ch02_01.c,形成可执行文件ch02,主要开头缩进要用“TAB”键,不能用空格键。

ch02_01_01.o:ch02_01_01.asm //表示目标文件ch02_01_01由汇编程序ch02_01_01.asm形成

as ch02_01_01.asm -o ch02_01_01.o //用as编译汇编程序ch02_01_01.asm,形成目标文件ch02_01_01.o

五、编译、运行可执行文件

$make

$./ch02_01

六、调试

步骤五中程序可以正常通过编译,但是执行可执行文件后产生错误的计算结果,用gdb调试,查看错误原因。

$gdb ./ch02_01

(gdb)break main //在C程序main 函数处打断

(gdb)run //运行程序,运行至main函数入口处会被中断

(gdb)disas //显示汇编代码

显示的汇编代码如下:

push %rbp //在为变量提供地址前,将rbp的地址压栈保存

mov %rsp,%rbp //将rsp的地址赋值于rbp

sub $0x30,%rsp

movq $0xa,-0x8(%rbp) //rbp地址偏移-0x8的位置处存放数值0xa,即十进制10

movq $0x14,-0x10(%rbp) //rbp地址偏移-0x10的位置处存放数值0x14,即十进制20

movq $0x1e,-0x18(%rbp) //rbp地址偏移-0x18的位置处存放数值0x1e,即十进制30

movq $0x12,-0x20(%rbp) //rbp地址偏移-0x20的位置处存放数值0x12,即十进制18

mov -0x20(%rbp),%rcx //将rbp地址偏移-0x20处的数据赋值于rcx

mov -0x18(%rbp),%rdx //将rbp地址偏移-0x18处的数据赋值于rdx

mov -0x10(%rbp),%rsi //将rbp地址偏移-0x10处的数据赋值于rsi

mov -0x8(%rbp),%rax //将rbp地址偏移-0x8处的数据赋值于rax

mov %rax,%rdi

call ...<IntegerAddSub_> //调用IntegerAddSub_函数

说明:由上可知变量a、b、c、d的赋值分别存放在寄存器rax、rsi、rdx、rcx中,变量赋值位置与书中代码展示的分别位于ecx、edx、r8d、r9d不同,原因可能是因为使用的CPU不同,所以CPU指令集不同。

七、修正汇编程序如下

.text

.global IntegerAddSub_

IntegerAddSub_:

addq %rsi,%rax

addq %rdx,%rax

subq %rcx,%rax

ret

八、编译、运行

$make

$./ch02_01

最终产生正确计算结果。

标签: #c语言程序开发范例宝典