编译工具链的使用:gcc/clang/arm-linux-gnuebihf-gcc


前言

编译原理第一次实验,目的是熟悉编译工具


一、GCC编译器的使用

本关任务:用gcc编译器,指定合适的命令行选项,编译出符合要求的二进制可执行代码。

GCC的命令行选项

GCC(GNU Compiler Collection,GNU编译器套件)是由GNU开发的编程语言编译器。GNU编译器套件包括C、C++、 Objective-C、 Fortran、Java、Ada和Go语言前端,也包括了这些语言的库(如libstdc++,libgcj等。)

C++是GNU针对C++语言的编译器。

常用的命令行选项有:

-E 只执行预处理
-c 编译或汇编源文件,不执行链接
-S 完成编译但不执行汇编,产生汇编文件
-o file 指定输出的文件为file。如果未指定该选项,在Linux下生成的可执行代码将被命名为缺省值a.out。指定输出文件名时,应使用缺省的后缀:预处理后:.i; 汇编代码:.s;目标代码.o等。
-DSOMETHING 宏定义,即预处理语句“#define SOMETHING”中的宏SOMETHING。用法 -D ifdef的条件。
-I 指定头文件的搜索路径
-O 批定优化级别,如:-O2,-O3等。编译系统设计赛在决赛阶段将用-O2选项对测试用例在目标平台上进行编译优化,用优化的可执行代码的执行时间/你的编译器生成的汇编代码在目标平台上汇编后生成的可执行代码运行时间*100 所得的值作为你在该测试用例上的性能得分。

编程要求

有以下源程序和.h文件:
def-test.c
alibaba.c
alibaba.h

//def-test.c
#include <stdio.h>  
#include "alibaba.h"
int main(void)  
{  
   printf( "Instructor: Hello, I am your instuctor, please introduce yourself.\n");  
   #ifdef BILIBILI  
   printf("BILIBILI: My name is Bili, 先生お久しぶりです!\n");  
   #endif  
   alibaba();  
   return 0;  
}  
//alibaba.h
#ifndef __ALIBABA__  
#define __ALIBABA__  
void alibaba(void);  
#endif    
//alibaba.c
#include <stdio.h>  
#include "alibaba.h"  
void alibaba(void)  
{  
   int y = 24;  
   printf("Alibaba: My name is Alibaba, I am %d years old.\n", y);  
   #ifdef BILIBILI  
   printf("Alibaba: Hey Bili, 你娃最近赚钱没得?\n");  
   #endif  
}   
#!/bin/bash

# 请用 gcc 编译器,连编def-test.c, alibaba.c,生成def-test二进制可执行代码。通过在编译命令行加适当的参数,
# 使生成的程序能形成Alibaba和Bili两个人的对话:
gcc def-test.c alibaba.c -o def-test -D BILIBILI

二、CLANG编译器的使用

Clang是一个用C++编写、基于LLVM、发布于LLVM BSD许可证下的编译器,它与GCC高度兼容,并在某些方面超越了gcc。

Clang命令行编译选项

Clang的命令行编译选项基本继承的gcc的大部分编译选项,如:-S, -o, -O, -c等。它也有一些gcc不支持的编译选项,比如交叉编译,通过指定-target参数,可以在X86的平台将C源程序“翻译”成其它平台下的汇编代码或二进制代码。

比如,下列编译指令将helloworld.c编译成armv7架构,linux操作系统,遵循gnueabihf嵌入式应用程序二进制接口,并支持arm硬浮点的汇编代码:

clang -S -target armv7-linux-gnueabihf helloworld.c -o helloword.s

编程要求

 int bar(){  
    int a = 10*10;  //这里在编译时可以直接计算出100这个值,这叫做“常数折叠”  
    int b = 20;     //这个变量没有用到,可以在代码中删除,这叫做“死代码删除”
    if (a>0){       //因为a一定大于0,所以判断条件和else语句都可以去掉  
        return a+1; //这里可以在编译器就计算出是101  
    }  
    else{  
        return a-1;  
    }  
}
int main() {  
    int a = bar();      //这里可以直接换成 a=101  
    putint(a);  
    putch(10);  
}  
 #!/bin/bash

# 请用 用clang编译器把bar.c“翻译”成优化的(优化级别O2)armv7架构,linux系统,符合gnueabihf嵌入式二进制接口规则,
# 并支持arm硬浮点的汇编代码(程序中并没有浮点数)。汇编代码文件名为bar.clang.arm.s
# 请使用恰当的编译选项以完成上述任务:
clang -S -O2 -target armv7-linux-gnueabihf bar.c -o bar.clang.arm.s

三、交叉编译器arm-linux-gnueabihf-gcc和qemu-arm虚拟机的使用

交叉编译器arm-linux-gnueabihf-gcc

arm-linux-gnueabihf-gcc是Linaro旗下的交叉编译器,其功能与gcc相当,区别在于它可以将C源程序编译成arm汇编代码或二进制可执行程序。由于本实验的要完成的编译器只需将SysY2022(C的子集)源程序编译成arm汇编代码,出于排错、学习或者性能比较的需 要,我们可能需要比较自己的编译器生成的arm汇编代码,与编译工具生成的arm汇编代码有多大区别。所以,熟悉这款交叉编译器是有意义的。当然,这一工作也可以直接在目标平台(即arm架构的树莓派)上直接用gcc -S来完成,但当arm资源有限时,用唾手可得的X86 Linux服务器来完成这件工作就显得十分有意义。

arm-linux-gnueabihf-gcc跟gcc的用法十分类似,主要命令行编译选项有:

-S 只生成汇编代码
-O 优化选项,如-O2,-O3等
-march= 设置具体的arm型号,如armv8,armv7等,编译系统设计赛的目标平台指定为armv7。
-mfpu= 设置浮点运算单元,比如vfp,vfpv3,vfpv4,neon,neon-vfpv4等。
-mfloat-abi= 在编译带有浮点参数的函数时采用的接口中规范,只有三个选项:
-mfloat-abi=soft
-mfloat-abi=softfp
-mfloat-abi=hard

"soft"选项:表明不使用FPU硬件,而是使用GCC的整数算术运算来模拟浮点运算。

"softfp"选项:表明要使用FPU硬件来做浮点运算,只是,函数的参数传递到整数寄存器(r0-r3)中,然后再传递到FPU中。

"hard"选项:表明要使用FPU硬件来做浮点运算,并且,函数的参数直接传递到FPU的寄存器(s0、d0)中。

上述-mfpu选项的设置,要看目标平台具体支持哪此选项,在收到树莓派后,可以通过在命令行键入:
cat /proc/cpuinfo
查看cpu的特性,根据赛事组委会提供的树莓派型号信息,该CPU应具有以下特性:
processor: 0-3 (共4个)
model name: ARMv7 Processor rev 3 (v7l)
Features: half thumb fastmult vfp edsp neon vfpv3 tls vfpv4 idiva idivt vfpd32 lpae evtstrm crc32
即支持vpf,vpfv3,vfpv4,vfpd32,neon等特性。其中neon是ARM的Advanced SIMD(单指令多数据)技术的俗称,一个128位的neon寄存器可以被分割为4个32位寄存器(即可装入4个int或单精度浮点型float),在作数组运算或矩阵运算时,可以考虑这种机制以提高性能(每次取4个地址连续的int或float进行运算).

qemu-arm虚拟机

在arm资源缺乏时,可以在X86架构的linux服务器上用qemu-arm虚拟机来运行arm的可执行代码,以检查测试用例是否被正确编译。
使用的方法是:
qemu-arm -L /usr/arm-linux-gnueabihf/ arm可执行代码
在educoder的命令行上,可以用环境变量$ARM代替arm运行时库的路径,但在在脚本文件中,请直接指定arm运行时库路径。

编程要求

//iplusf.c
extern void putfloat(float a);  
extern float getfloat();  
extern int getint();  
extern void putch(int a);
int main(){  
    int a;  
    float b;  
    a = getint();  
    b = getfloat();  
    putfloat(a + b);  
    putch(10);  
    return 0;  
}   
 #!/bin/bash

# 用arm-linux-gnueabihf-gcc 将iplusf.c编译成arm汇编代码iplusf.arm.s
arm-linux-gnueabihf-gcc -S -o iplusf.arm.s iplusf.c
# 再次用arm-linux-gnueabihf-gcc 汇编iplusf.arm.s,同时连接SysY2022的运行时库sylib.a,生成arm的可执行代码iplusf.arm
arm-linux-gnueabihf-gcc iplusf.arm.s sylib.a -o iplusf.arm
# 用qemu-arm运行iplusf.arm
qemu-arm -L /usr/arm-linux-gnueabihf/ ./iplusf.arm

总结

开始你的任务吧,祝你成功!

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值