gcc调用场景中的各种事物,不仅仅是ld,如果你想证明它的话,这很容易实现(把正确的替换为ld和其他二进制文件,把它们打印出来命令行,然后运行gcc并查看该二进制文件被调用)。
当你使用gcc作为它通过一个C预处理程序汇编,所以你可以做一些很恶心的事情是这样的:
的start.s
//this is a comment
@this is a comment
#define FOO BAR
.globl _start
_start:
mov sp,#0x80000
bl hello
b .
.globl world
world:
bx lr
,并看到更多的是怎么回事的在这里有其他文件
so.h
unsigned int world (unsigned int, unsigned int);
#define FIVE 5
#define SIX 6
so.c
#include "so.h"
unsigned int hello (void)
{
unsigned int a,b,c;
a=FIVE;
b=SIX;
c=world(a,b);
return(c+1);
}
构建
arm-none-eabi-gcc -save-temps -nostdlib -nostartfiles -ffreestanding -O2 start.s so.c -o so.elf
arm-none-eabi-objdump -D so.elf
生产
00008000 <_start>:
8000: e3a0d702 mov sp, #524288 ; 0x80000
8004: eb000001 bl 8010 <hello>
8008: eafffffe b 8008 <_start+0x8>
0000800c <world>:
800c: e12fff1e bx lr
00008010 <hello>:
8010: e92d4010 push {r4, lr}
8014: e3a01006 mov r1, #6
8018: e3a00005 mov r0, #5
801c: ebfffffa bl 800c <world>
8020: e8bd4010 pop {r4, lr}
8024: e2800001 add r0, r0, #1
8028: e12fff1e bx lr
这里是一个非常简单的项目是so.i后的预处理器肚里,并得到了包括文件,并替换定义
# 1 "so.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "so.c"
# 1 "so.h" 1
unsigned int world (unsigned int, unsigned int);
# 4 "so.c" 2
unsigned int hello (void)
{
unsigned int a,b,c;
a=5;
b=6;
c=world(a,b);
return(c+1);
}
然后gcc调用实际编译器(其节目名不是GCC)
产生so.s
.cpu arm7tdmi
.eabi_attribute 20, 1
.eabi_attribute 21, 1
.eabi_attribute 23, 3
.eabi_attribute 24, 1
.eabi_attribute 25, 1
.eabi_attribute 26, 1
.eabi_attribute 30, 2
.eabi_attribute 34, 0
.eabi_attribute 18, 4
.file "so.c"
.text
.align 2
.global hello
.syntax unified
.arm
.fpu softvfp
.type hello, %function
hello:
@ Function supports interworking.
@ args = 0, pretend = 0, frame = 0
@ frame_needed = 0, uses_anonymous_args = 0
push {r4, lr}
mov r1, #6
mov r0, #5
bl world
pop {r4, lr}
add r0, r0, #1
bx lr
.size hello, .-hello
.ident "GCC: (GNU) 6.3.0"
,然后将其馈送到汇编器,使so.o,然后连接器调用把这些成这样。elf
现在你可以直接进行大部分调用,这并不意味着这些程序有他们调用的其他程序,gcc仍然调用一个或多个程序来实际编译。
arm-none-eabi-as start.s -o start.o
arm-none-eabi-gcc -O2 -S so.c
arm-none-eabi-as so.s -o so.o
arm-none-eabi-ld start.o so.o -o so.elf
arm-none-eabi-objdump -D so.elf
使用-S用gcc确实觉得有点不对劲给出相同的结果
00008000 <_start>:
8000: e3a0d702 mov sp, #524288 ; 0x80000
8004: eb000001 bl 8010 <hello>
8008: eafffffe b 8008 <_start+0x8>
0000800c <world>:
800c: e12fff1e bx lr
00008010 <hello>:
8010: e92d4010 push {r4, lr}
8014: e3a01006 mov r1, #6
8018: e3a00005 mov r0, #5
801c: ebfffffa bl 800c <world>
8020: e8bd4010 pop {r4, lr}
8024: e2800001 add r0, r0, #1
8028: e12fff1e bx lr
,使用它像这样反而感觉 更自然
arm-none-eabi-gcc -O2 -c so.c -o so.o
现在有一个链接我们没有提供哪个工具链具有默认的脚本,我们可以控制它,并且取决于它的目标应该是什么。
我不高兴地看到,新的/当前版本的as容忍C评论,等等......以前没有用过这种方式,必须是最新版本的新东西。
因此,术语“工具链”是一些链接在一起的工具,一个链接到下一个顺序。
并非所有编译器都采用汇编语言步骤。有些编译为中间代码,然后有另一个工具,将编译器特定的中间代码转换为汇编语言,然后调用一些汇编程序(gcc的中间代码位于编译步骤的表内部,其中clang/llvm可以让它编译为该代码然后从那里转到汇编语言以获取其中一个目标)。一些编译器直接转到机器代码,而不是停在汇编语言。这可能就是其中之一,因为它在那里而爬山,事情就会发生。就像纯粹用汇编语言编写操作系统一样。对于任何像样大小的项目和一个可以支持它的工具,您将拥有一个链接器和一个汇编器,这是您为支持新目标而制作的第一个工具。处理器(芯片或ip或两者)供应商将有一个汇编程序,然后还有其他工具可用。尝试使用汇编语言手动编译上面的简单C程序,然后再次尝试,而不使用汇编语言BYHAND,只使用机器代码。你会发现使用汇编语言作为中间步骤对于编译器开发人员来说更加理智,除了这种方式永远这样做以外,这也是继续这样做的一个很好的理由。
,如果你徜徉在GNU工具链目录你使用你会发现像CC1
./libexec/gcc/arm-none-eabi/6.3.0/cc1 --help
The following options are specific to just the language Ada:
None found. Use --help=Ada to show *all* the options supported by the Ada front-end.
The following options are specific to just the language AdaSCIL:
None found. Use --help=AdaSCIL to show *all* the options supported by the AdaSCIL front-end.
The following options are specific to just the language AdaWhy:
None found. Use --help=AdaWhy to show *all* the options supported by the AdaWhy front-end.
The following options are specific to just the language C:
None found. Use --help=C to show *all* the options supported by the C front-end.
The following options are specific to just the language C++:
-Wplacement-new
-Wplacement-new= 0xffffffff
The following options are specific to just the language Fortran:
程序现在,如果你运行的程序CC1对您保存在-save-临时工的so.i文件,你可以得到汇编语言文件。
你也许可以继续挖掘到目录或GNU工具的来源,找到更多的好东西......
注意这个问题之前已经多次在这里以各种方式要求在计算器。
另外请注意main()并不是特别的,正如我已经演示的那样。它可能是一些编译器,但我可以制作不需要该函数名称的程序。
'gcc'本身代表“[GNU编译器集合](https://gcc.gnu.org/)”(注意它没有以任何方式提及C,这是正确的,C编译器是隐藏在'cc'下,而C++在'g ++'下面,'gcc'中有更多的编译器)。所以它实际上就是如何调用所有其他工具来生成最终的二进制可执行文件,这是一个非常聪明的工具,知道哪些选项用于所有工具(如目标平台),哪些属于编译器或链接器。 – Ped7g