我正在尝试为我的自定义ISA生成程序集,类似于MIPS。我已经阅读过使用mips-gcc交叉编译器从C程序生成MIPS asm然后将其转换为我们所需的asm,但后来我无法清楚地了解它。有人请向我解释程序以及如何实现这一目标。
谢谢!!!
答案 0 :(得分:2)
我假设你在谈论二进制翻译。
以此代码为例
unsigned int fun ( unsigned int a, unsigned int b, unsigned int c )
{
return((a+b)&c);
}
假设我想使用mips C编译器,因为我没有为我的处理器编译器(在这种情况下,我将使用ARM仅用于演示目的,我们当然有ARM编译器)。 MIPS到ARM也不是一对一的事情,但对于这个简单的例子,它可以正常工作。因为ARM不使用延迟槽,所以我们要求gcc不要这样做,我们编译这个
mips-elf-gcc -O2 -c -fno-delayed-branch fun.c -o fun.o
mips-elf-objdump -D fun.o
我当然是在作弊,你会想要链接到某个地址,我正在快速剪切并拆解该对象。您的翻译器首先阅读MIPS二进制文件,您可能或可能不希望从入口点开始预扫描指令,按照所有路径的执行顺序并记录所有分支目的地。还要隔离哪些字节是指令并假设哪些不是数据。但是,正弦mips是固定的指令长度(不是真的,有一个可以混合的16位版本)你也可以采取从一开始的方法开始并拆解所有它,你将最终拆卸数据和创建额外的工作。
所以我让gnu为我这个例子反汇编这个
00000000 <fun>:
0: 00851021 addu v0,a0,a1
4: 00461024 and v0,v0,a2
8: 03e00008 jr ra
c: 00000000 nop
一次只需一条指令,您的翻译工具就会分开指令(反汇编)
0: 00851021 addu v0,a0,a1
00000000100001010001000000100001 0x00851021
000000 00100 00101 00010 00000 100001
000000 sssss ttttt ddddd 00000 100001 addu rd,rs,rt
r4 r5 r2
以
结尾addu r2 = r4 + r5
因此我们将其直接转换为等效的
add r2,r4,r5
第二条指令
4: 00461024 and v0,v0,a2
00000000010001100001000000100100
000000 00010 00110 00010 00000 100100
000000 sssss ttttt ddddd 00000 100100 and rd,rs,rt
r2 r6 r2
and r2 = r2 & r6
臂
and r2,r2,r6
第三条指令
8: 03e00008 jr ra
000000 11111 000000000000000 001000
000000 sssss 000000000000000 001000 jr rs
r31
jr r31 or return from function
手臂
bx lr
第四条指令
c: 00000000 nop
臂
nop
因此,对于这四个mips指令,我们有一个直接静态二进制转换为arm
add r2,r4,r5
and r2,r2,r6
bx lr
nop
现在你可以做两件事之一,你可以在每条指令上加上标签,然后是if / when 有一个分支,您可以分支该标签,或清洁,您可以预览所有代码 并寻找分支目的地,只标记那些。使用mips地址构建标签,以便更容易跟踪。
L00000000: add r2,r4,r5
L00000004: and r2,r2,r6
L00000008: bx lr
L0000000C: nop
在这种情况下,我创建了arm汇编语言,我会将其输入
arm-none-eabi-as fun.s -o fun.o
arm-none-eabi-objdump -D fun.o
fun.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <L00000000>:
0: e0842005 add r2, r4, r5
00000004 <L00000004>:
4: e0022006 and r2, r2, r6
00000008 <L00000008>:
8: e12fff1e bx lr
0000000c <L0000000C>:
c: e1a00000 nop ; (mov r0, r0)
我们不需要关心名为fun的标签,地址是处理器的工作方式以及如何将其编码为指令等,因此您可以使用带有上述地址的标签,它将起作用。当然,如果有任何用于分支的计算地址 目的地,或者甚至加载和存储,然后你必须在你的 翻译。
现在,如果您的新指令集与您用于编译的mips非常相似,那么您不需要进行汇编,只需将机器代码转换为机器代码,即可在一对一翻译中进行细微更改,如果它不是一对一的机器代码翻译,那么你需要创建一个伪装配器,它允许你处理标签和一对一或许多mips指令。
因为我这么远,如果你试图使用静态二进制转换来说明从MIPS转换为ARM实际上是相关的切线,你不应该在目标端使用汇编语言。使用C或您喜欢的高级语言。这使得它对任何指令集都更具可移植性(如果您正在进行此练习,则可能需要不再为另一个指令集执行此操作)。最重要的是,虽然你允许C编译器优化器为目标处理删除死代码,但是当你开始处理处理器标志时会直接转换,这样就会产生大量必须处理的死代码。
所以而不是
L00000000: add r2,r4,r5
L00000004: and r2,r2,r6
L00000008: bx lr
L0000000C: nop
如果我的目标有一个优化的C编译器,但它不是一个mips目标而且我只有一个mips二进制文件我正在翻译我没有原始的源代码然后转换为类似
...
unsigned int r2;
...
unsigned int r3;
unsigned int r4;
unsigned int r5;
unsigned int r6;
...
unsigned int PC;
switch(PC)
{
...
case L00000000:
L00000000:
r2=r4+r5;
r2=r2&r6;
PC=0x00000008;
break;
...
}
使用此开关附近的一些代码来管理函数调用的进入和退出。当然,还有很多我没有描述的工作。
我知道使用链接是不好的形式,但Graham Toal写了一个静态二进制翻译HOWTO,你可以谷歌搜索并希望找到http://www.gtoal.com/sbt/,希望它仍然存在。
当然如果你的指令集与mips相当接近,那你为什么不修改binutils让它编码你的指令或修改gcc让它产生汇编语言的变化?
答案 1 :(得分:1)
@dwelch已经做了相当广泛的工作来回答你的问题,但我想我会添加一点,因为我安装了MIPS交叉编译器,我认为值得向您展示如何直接从C源生成汇编
这是test.c:
int main()
{
return -7;
}
要使用我的交叉编译器生成程序集,我会使用:mips-elf-gcc-3.4.6 test.c -S
其中-S
是生成程序集的标志。
这会创建一个名为test.s的文件,如下所示:
.file 1 "test.c"
.text
.align 2
.globl main
.ent main
main:
.frame $fp,8,$31 # vars= 0, regs= 1/0, args= 0, gp= 0
.mask 0x40000000,-8
.fmask 0x00000000,0
addiu $sp,$sp,-8
sw $fp,0($sp)
move $fp,$sp
li $2,-7 # 0xfffffffffffffff9
move $sp,$fp
lw $fp,0($sp)
addiu $sp,$sp,8
j $31
.end main
为了获得额外的奖励,我们可以加快编译器的优化,以便使用mips-elf-gcc-3.4.6 test.c -S -O3
进行更高效的汇编。这看起来像:
.file 1 "test.c"
.set nobopt
.text
.align 2
.globl main
.ent main
main:
.frame $sp,0,$31 # vars= 0, regs= 0/0, args= 0, gp= 0
.mask 0x00000000,0
.fmask 0x00000000,0
.set noreorder
.set nomacro
j $31
li $2,-7 # 0xfffffffffffffff9
.set macro
.set reorder
.end main