我为一个类完成了C到MIPS的转换,我想针对程序集进行检查。我听说有一种配置gcc的方法,它可以将C代码转换为MIPS架构而不是x86架构(我的计算机用户使用Intel i5处理器)并打印输出。
在Ubuntu(gcc附带)中运行终端,我用什么命令配置gcc转换为MIPS?我还需要安装什么吗?
编辑: 让我澄清一下。请阅读这个。 我不是在寻找使用哪种编译器,或者人们说“你可以交叉编译,但你应该使用其他没有指示如何设置的东西。”
如果您要发布,至少请参阅说明。 GCC附带Ubuntu。我没有关于如何安装编译器的经验,除了GCC之外,找不到其他任何内容的在线教程并不容易。然后是我需要知道的交叉编译的情况。谢谢。
答案 0 :(得分:26)
GCC 可以为大量架构生成汇编代码,包括MIPS。但是,当GCC本身被编译时,决定了给定GCC实例所针对的架构。您将在Ubuntu系统中找到的预编译二进制文件知道x86(可能是32位和64位模式),但不知道MIPS。
使用与GCC本身将运行的体系结构不同的目标体系结构编译GCC称为准备交叉编译工具链。这是可行的,但需要相当多的文档阅读和耐心;您通常需要首先构建交叉汇编器和交叉链接器(GNU binutils),然后构建交叉GCC本身。
我建议使用buildroot。这是一组脚本和makefile,旨在帮助生成完整的交叉编译工具链和实用程序。在一天结束时,您将获得目标系统的完整操作系统和开发工具。这包括您所追求的交叉编译器。
另一个完全不同的解决方案是使用QEMU。这是用于各种处理器和系统的仿真器,包括MIPS系统。您可以使用它来运行具有MIPS处理器的虚拟机,并在该计算机内安装MIPS操作系统,例如: Debian,Linux发行版。这样,您就可以获得 native GCC(在MIPS系统上运行的GCC并为MIPS生成代码)。
QEMU方式可能更简单;使用交叉编译需要对一些毛茸茸的细节有所了解。无论哪种方式,您都需要大约1 GB的可用磁盘空间。
答案 1 :(得分:5)
这不是配置,你需要一个交叉编译为MIPS的GCC版本。这需要一个特殊的GCC构建,并且相当毛茸茸的设置(构建GCC不适合胆小的人)。
我建议您使用LCC。使用LCC进行交叉编译比使用GCC更容易,并且在当前机器上构建LCC只需几秒钟。
答案 2 :(得分:2)
您需要将源代码下载到binutils和gcc-core,并使用类似../configure --target=mips ...
的内容进行编译。您可能需要选择特定的MIPS目标。然后你可以使用mips-gcc -S
。
答案 3 :(得分:1)
您可以交叉编译GCC,以便生成MIPS代码而不是x86。这是一个很好的学习经历。
如果您想要快速获得结果,您还可以获得具有MIPS支持的预构建GCC。一个是CodeSourcery Lite工具链。它是免费的,适用于许多架构(包括MIPS),他们已经准备好为Linux和Windows使用二进制文件。
http://www.codesourcery.com/sgpp/lite/mips/portal/subscription?@template=lite
答案 4 :(得分:1)
您应该从Ubuntu存储库中安装交叉编译器。存储库中提供了四个GCC MIPS C交叉编译器。根据您的需要选择:
gcc-mips-linux-gnu
-32位big-endian。gcc-mipsel-linux-gnu
-32位little-endian。gcc-mips64-linux-gnuabi64
-64位big-endian。gcc-mips64el-linux-gnuabi64
-64位little-endian。(供Debian用户使用的注意事项:如果您通常希望使用build-essential
软件包安装常规编译器,那么您将有兴趣了解crossbuild-essential-mips
,crossbuild-essential-mipsel
和{ {1}})
在以下示例中,我将假定您选择了32位的低字节序版本(crossbuild-essential-mips64el
)。其他MIPS版本的命令与此类似。
要处理MIPS而不是系统的本机体系结构,请使用sudo apt-get install gcc-mipsel-linux-gnu
命令而不是mipsel-linux-gnu-gcc
。例如,gcc
生成包含MIPS程序集的文件mipsel-linux-gnu-gcc -fverbose-asm -S myprog.c
。
查看MIPS程序集的另一种方法:运行myprog.s
以生成包含调试信息的目标文件mipsel-linux-gnu-gcc -g -c myprog.c
。然后使用myprog.o
查看目标文件的反汇编。例如,如果mipsel-linux-gnu-objdump -d -S myprog.o
是这样的:
myprog.c
如果使用#include <stdio.h>
int main()
{
int a = 1;
int b = 2;
printf("The answer is: %d\n", a + b);
return 0;
}
进行编译,则mipsel-linux-gnu-gcc -g -c myprog.c
将显示如下内容:
mipsel-linux-gnu-objdump -d -S myprog.o
答案 5 :(得分:0)
你应该编译自己的gcc版本,它能够交叉编译。当然这并不容易,因此您可以寻找不同的方法......例如this SDK。
答案 6 :(得分:0)
一次性使用一个小程序或几个函数,不需要在本地安装任何东西。
使用Matt Godbolt的编译器浏览器站点https://godbolt.org/,该站点具有针对各种ISA(包括MIPS和x86-64)和一些其他编译器的GCC和clang。
请注意,默认情况下,编译器资源管理器会过滤指令,以便您可以 just 查看指令,而忽略诸如对齐,节,.globl
等内容。 (对于没有全局/静态数据的函数,这实际上很好,尤其是当您只想使用编译器为您创建示例时。无论如何,如果您不使用任何代码,则默认部分为.text
指令。
大多数需要MIPS asm做作业的人正在使用SPIM或MARS ,通常没有分支延迟插槽。 (与真正的MIPS不同,因此您需要调整编译器,以使不可以在分支无条件运行后利用下一条指令,即使使用了该指令也是如此。)对于GCC,该选项为-fno-delayed-branch
-它将用NOP填充每个延迟时隙,因此代码仍将在真实的MIPS上运行。您可以手动删除所有NOP。
可能还需要进行其他调整,例如MARS可能要求您使用jr $31
而不是j $31
,Tweak mips-gcc output to work with MARS。当然,必须使用MARS's toy system calls来实现I / O代码,而不是对jal
或printf
之类的标准库函数进行std::ostream::operator<<
调用。不过,您可以有用地编译(和手动调整)asm来处理数据,例如将整数相乘或对数组求和或反转。
不幸的是,GCC不能选择使用$a0
之类的寄存器名来代替$r
。对于PowerPC,-mregnames
使用r1
而不是1
,但是MIPS没有类似的选项使用“更多符号”注册表名称。
int maybe_square(int num) {
if (num>0)
return num;
return num * num;
}
On Godbolt 和GCC 5.4 -xc -O3 -march=mips32r2 -Wall -fverbose-asm -fno-delayed-branch
-xc
编译为C,而不是C ++,因为我发现这比在下拉列表中的C和C ++语言之间切换和让站点清除我的源代码更方便。
-fverbose-asm
用目标和源的C变量名称注释汇编。 (在优化的代码中,这通常是临时的,但并非总是如此。)
-O3
启用了全面优化,因为默认的-O0
调试模式对人类来说是一个可怕的混乱。如果您想手工查看代码并查看其如何实现源代码,请始终至少使用-Og
。 How to remove "noise" from GCC/clang assembly output?。如果使用SIMD指令为ISA进行编译,也可以使用-fno-unroll-loops
和-fno-tree-vectorize
。
这使用mul
而不是经典的MIPS mult
+ mflo
,这要归功于-march=
选项告诉GCC我们正在编译更高版本的MIPS ISA,而不是不管默认基线是多少。 (也许是MIPS I,又名R2000,-march=mips1
)
另请参阅MIPS target options上GCC手册的部分。
# gcc 5.4 -O3
square:
blez $4,$L5
nop
move $2,$4 # D.1492, num # retval = num
j $31 # jr $ra = return
nop
$L5:
mul $2,$4,$4 # D.1492, num, num # retval = num * num
j $31 # jr $ra = return
nop
或者使用clang,使用-target mips
告诉它针对MIPS进行编译。 您可以在桌面上执行此操作;与GCC不同,clang通常是在启用了多个后端的情况下构建的。。
从相同的Godbolt链接中,发出铛10.1 -xc -O3 -target mips -Wall -fverbose-asm -fomit-frame-pointer
。默认目标显然是MIPS32或类似clang的目标。另外,clang默认情况下会为MIPS启用帧指针,从而使asm嘈杂。
请注意,它选择制作无分支的asm,将if转换为条件移动,以在原始输入和mul结果之间进行选择。不幸的是clang不支持-fno-delayed-branch
;也许它为相同的选项取了另一个名字,或者也许没有希望。
maybe_square:
slti $1, $4, 1
addiu $2, $zero, 1
movn $2, $4, $1 # conditional move based on $1
jr $ra
mul $2, $2, $4 # in the branch delay slot
在这种情况下,我们可以简单地将mul
放在jr
之前,但是在其他情况下,转换为无分支延迟的asm并非完全不重要。例如在递减计数器 之前在循环计数器上跳转,不能通过将递减放在第一位来撤消它;那会改变意思。
编译器使用寄存器号,而不用打扰名字。为了供人类使用,您通常会希望将其翻译回来。在线上的许多地方都有MIPS寄存器表,这些表显示$ 4 .. $ 7是$ a0 .. $ a3,$ 8 .. $ 15是$ t0 .. $ t7等。例如this one。