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