C语言及AT
这次改写程序是迄今为止最让我感到困难的一次,主要原因在于一方面masm汇编伪代码与gas汇编伪代码差别巨大,另一方面缺乏gas完整的参考手册,从网络搜寻的信息错漏较多,因此需要从网络多个信息中相互比对,加上自己的一些猜测进行试验。当最终通过编译并成功运行时,反倒让自己有些惊讶了。
本篇涉及的新内容包括:1、数据段、未初始化数据段、代码段的定义;2、数组元素的定义;3、跳转指令jl、jge;4、标签首地址赋值于寄存器;5、五种内存寻址方式。
程序运行达到的效果:在汇编语言数据段中定义数组,以4种不同的内存寻址方式读取数组的元素,并打印数组元素,最后打印数组元素的和值。
一、gas伪代码预备知识
.data 定义可读写数据段
.bss 定义未初始化数据段
.comm symbol,length 在.bss段内定义普通符号symbol,预留length个字节
.lcomm symbol,length 在.bss段内定义本地普通符号symbol
.text 定义可执行代码段
.extern 定义允许外部引用的符号,经定义的符号可以被其他段识别
.global或.globl 定义全局符号,经定义的符号可以被外部程序引用、识别
.byte 定义1字节整型,8位的数据或数组
.short 定义2字节整型,16位的数据或数组
.word 定义2字节整型,16位的数据或数组
.hword 定义2字节整型,16位的数据或数组
.2byte 定义2字节整型,16位的数据或数组
.int 定义4字节整型,32位的数据或数组
.4byte 定义4字节整型,32位的数据或数组
.long 定义4字节整型,32位的数据或数组 或 8字节整型,64位数据或数组
.quad 定义8字节整型,64位数据或数组
.octa 定义16字节整型,128位数据或数组
.uleb128 定义变长无符号整型,最长16个字节,128位
.sleb128 定义变长有符号整型,最长16个字节,128位
.single 定义单精度浮点,4字节,32位
.float 定义单精度浮点,4字节,32位
.double 定义双精度浮点,8字节,64位
.ascii 定义无'\0'结束的字符串
.asciz 定义自动以'\0'结束的字符串
二、编辑ch02_06_01.c文件内容如下:
#include<stdio.h>
extern long NumFibVals_,FibValsSum_; /*定义2个外部引用的汇编函数,NumFibVals_记录数组元素个数,FibValsSum_记录数组元素求和*/
extern long MemoryAddressing_(long i,long* v1,long* v2,long* v3,long* v4); /*定义外部引用汇编函数,该函数包含5个变量,i用于计数,判断是否超出元素个数,v1-v4用4种寻址方式传输元素值*/
int main(void)
{
FibValsSum_=0; /*元素求和初始化为0*/
for (long i=-1;i<NumFibVals_+1;i++) /*i从-1开始,逐次增加,并判断是否超出元素个数,超出时跳出循环*/
{
long v1=-1,v2=-1,v3=-1,v4=-1; /*初始化v1-v4=-1,用于判断是否有效读取元素值*/
long rc=MemoryAddressing_(i,&v1,&v2,&v3,&v4); /*调用汇编函数MemoryAddressing_,并将返回值传送给rc*/
printf("i=%ld rc=%ld v1=%ld v2=%ld v3=%ld v4=%ld,\n",i,rc,v1,v2,v3,v4);
}
printf("FibValsSum_=%ld,\n",FibValsSum_); /*结束循环后最终打印元素和值*/
return 0;
}
三、编辑汇编代码ch02_06_02.asm文件如下:
/*定义4个全局标号,FibVals为数组的首地址,NumFibVals_为数组元素个数,FibValsSum_为元素值求和,MemoryAddressing_为代码段入口*/
.global FibVals
.global NumFibVals_
.global FibValsSum_
.global MemoryAddressing_
/*.data定义数据段*/
.data
/*.quad定义元素为64位8字节整型的数组,FibVals标签是数组的首地址*/
FibVals: .quad 0,1,1,2,3,5,8,13
.quad 21,34,55,89,144,233,377,610,987,1597
/*NumFibVals_的首地址减去FibVals的首地址,为数组的地址长度,由于数组每个元素长8个字节,除以8就等于元素的个数*/
NumFibVals_: .quad (NumFibVals_ - FibVals)/8
.extern NumFibVals_ /*声明为全局可访问,经声明后其他段可调用NumFibVals_所在的地址*/
/*.bss定义未初始化的数据段,./comm FibValsSum_,64定义普通符号FibValsSum_,预留8个字节,由于本程序都是按64位编写,8个字节正好是1个quad的宽度*/
.bss
.comm FibValsSum_,8
.extern FibValSum_
.text
MemoryAddressing_:
cmp $0,%rdi /*c语言第1个变量i传递到rdi,比较rdi和0*/
jl InvalidIndex /*i<0时跳至InvalidIndex标签处,此时MemoryAddressing_返回值为0,v1-v4保持初始值-1*/
cmp (NumFibVals_),%rdi /*比较i与数组元素个数大小*/
jge InvalidIndex /*i>=元素个数时跳至InvalidIndex标签处,此时MemoryAddressing_返回值为0,v1-v4保持初始值-1*/
movq %rdi,%r9 /*将i值保存到r9备份*/
/*第一种内存寻址方式:基址寄存器*/
movq $FibVals,%r11 /*将FibVals的首地址传递到r11,$标签表示标签处的首地址*/
shlq $3,%rdi /*rdi的值左移3位,即i*(2^3)=i*8,由于数组元素宽度是8个字节,因此每读取下一个元素的地址都要移动8个字节*/
addq %rdi,%r11 /*r11=r11+i*8,即FibVals的首地址移动i*8个字节,这样就移动到第i个元素的地址*/
movq (%r11),%rax /*将第i个元素地址存储的数值传递给rax,(%r11)表示用基址寄存器来表示内存地址*/
movq %rax,(%rsi) /*将rax的值传递给第2个变量v1*/
/*第二种内存寻址方式:基址寄存器+变址寄存器*/
movq $FibVals,%r11
movq %r9,%rdi /*将备份的i值传递回rdi*/
shlq $3,%rdi
movq (%r11,%rdi),%rax /*(%r11,%rdi)用基址寄存器+变址寄存器的方式表示内存地址,内存地址=r11+rdi*/
movq %rax,(%rdx) /*将rax的值传递个第3个变量v2*/
/*第三种内存寻址方式:基址寄存器+变址寄存器*比例因子*/
movq $FibVals,%r11
movq %r9,%rdi
movq (%r11,%rdi,8),%rax /*(%r11,%rdi,8)用基址寄存器+变址寄存器*比例因子的方式表示内存地址,内存地址=r11+rdi*8 */
movq %rax,(%rcx) /*将rax的值传递给第4个变量v3*/
/*第四种内存寻址方式:基址寄存器+变址寄存器*比例因子+位移值*/
subq $42,%r11 /*为了下面演示通过位移值表示内存地址,此次先将FibVals的首地址减去42*/
movq %r9,%rdi
movq 42(%r11,%rdi,8),%rax /*42(%r11,%rdi,8)用基址寄存器+变址寄存器*比例因子+位移值的方式表示内存地址,内存地址=r11+rdi*8+42*/
movq %rax,(%r8) /*将rax的值传递给第5个变量v4*/
/*第五种内存寻址方式:相对rip寻址*/
addq %rax,(FibValsSum_) /*相对rip寻址,将rax的值,即目前数组元素的值累加到FibValsSum_的预留地址内*/
movq $1,%rax /*正确读取元素,返回值1*/
ret
InvalidIndex:
xor %rax,%rax /*未读取数组元素,返回值0*/
ret
.end
四、编辑Makefile文件内容如下:
ch02_06_0102:ch02_06_01.c ch02_06_02.o
gcc -no-pie ch02_06_02.o ch02_06_01.c -o ch02_06_0102
ch02_06_02.o:ch02_06_02.asm
as ch02_06_02.asm -o ch02_06_02.o
五、编译后程序运行结果如下:
i=-1 rc=0 v1=-1 v2=-1 v3=-1 v4=-1,
i=0 rc=1 v1=0 v2=0 v3=0 v4=0,
i=1 rc=1 v1=1 v2=1 v3=1 v4=1,
i=2 rc=1 v1=1 v2=1 v3=1 v4=1,
i=3 rc=1 v1=2 v2=2 v3=2 v4=2,
i=4 rc=1 v1=3 v2=3 v3=3 v4=3,
i=5 rc=1 v1=5 v2=5 v3=5 v4=5,
i=6 rc=1 v1=8 v2=8 v3=8 v4=8,
i=7 rc=1 v1=13 v2=13 v3=13 v4=13,
i=8 rc=1 v1=21 v2=21 v3=21 v4=21,
i=9 rc=1 v1=34 v2=34 v3=34 v4=34,
i=10 rc=1 v1=55 v2=55 v3=55 v4=55,
i=11 rc=1 v1=89 v2=89 v3=89 v4=89,
i=12 rc=1 v1=144 v2=144 v3=144 v4=144,
i=13 rc=1 v1=233 v2=233 v3=233 v4=233,
i=14 rc=1 v1=377 v2=377 v3=377 v4=377,
i=15 rc=1 v1=610 v2=610 v3=610 v4=610,
i=16 rc=1 v1=987 v2=987 v3=987 v4=987,
i=17 rc=1 v1=1597 v2=1597 v3=1597 v4=1597,
i=18 rc=0 v1=-1 v2=-1 v3=-1 v4=-1,
FibValsSum_=4180,