---
title: 'GCC 編譯、Makefile'
disqus: kyleAlien
---
GCC 編譯、Makefile
===
## OverView of Content
這裡會使用 gcc 編譯 .c 檔案,並寫一個簡單的 Makefile
[TOC]
## 安裝 gcc
1. 當前環境使用 Ubutun 20 版本
```shell=
# install gcc from apt
sudo apt install gcc
```
> 
2. 寫一個簡單的 test.c 檔案,動態申請記憶體空間
```c=
// test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define PAGE_SIZE 4096
#define MAX_SIZE 100*PAGE_SIZE
int main() {
char *buf = (char*)malloc(MAX_SIZE);
memset(buf, 0, MAX_SIZE);
printf("buffer address =0x%p\n", buf);
free(buf);
return 0;
}
```
## 編譯
編譯分為 4 個步驟
1. 預編譯
2. 編譯
3. 彙編
4. 鏈結
### 預編譯
* 透過 gcc 使用以下指令,預編譯會先編譯檔案中定義好的 #define
```shell=
# -E 是預編譯
# -o 是輸出檔案
aarch64-linux-gnu-gcc -E test.c -o test.i
```
* 預編譯結果
```c=
// test.i
extern void *malloc (size_t __size) __attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__mallo c__))
__attribute__ ((__alloc_size__ (1))) ;
... 省略部分
int main() {
char *buf = (char*)malloc(100*4096); // 替換為 定義好的數字
memset(buf, 0, 100*4096); // 替換為 定義好的數字
printf("buffer address =0x%p\n", buf);
free(buf);
return 0;
}
```
### 編譯
* 在這個步驟中會去 **檢查語法是否錯誤,並 ++產生==彙編碼==++**,透過 gcc 使用以下指令,預編譯會先編譯檔案中定義好的 #define
```shell=
# -S 是編譯
# -o 是輸出檔案
aarch64-linux-gnu-gcc -S test.i -o test.s
```
* 編譯結果 (結果是彙編碼)
```cmake=
.arch armv8-a
.file "test.c"
.text
.section .rodata
.align 3
.LC0:
.string "buffer address =0x%p\n"
.text
.align 2
.global main
.type main, %function
main:
.LFB6:
.cfi_startproc
stp x29, x30, [sp, -32]!
.cfi_def_cfa_offset 32
.cfi_offset 29, -32
.cfi_offset 30, -24
mov x29, sp
mov x0, 16384
movk x0, 0x6, lsl 16
bl malloc
str x0, [sp, 24]
mov x2, 16384
movk x2, 0x6, lsl 16
mov w1, 0
ldr x0, [sp, 24]
bl memset
ldr x1, [sp, 24]
adrp x0, .LC0
add x0, x0, :lo12:.LC0
bl printf
ldr x0, [sp, 24]
bl free
mov w0, 0
ldp x29, x30, [sp], 32
.cfi_restore 30
.cfi_restore 29
.cfi_def_cfa_offset 0
ret
.cfi_endproc
.LFE6:
.size main, .-main
.ident "GCC: (Ubuntu 11.2.0-7ubuntu2) 11.2.0"
.section .note.GNU-stack,"",@progbits
```
:::danger
* 若語法錯誤會在 gcc`編譯` 查出
> 
:::
### 彙編
* 將上個步驟的彙編文件轉為 2 進制檔案
```shell=
# -c 是彙編
# -o 是輸出檔案
aarch64-linux-gnu-gcc -c test.s -o test.o
```
* 編譯結果 (結果是 2 進制)
> 
### 鏈結
* 鏈結主要是在鏈結其他 Library,像是我們使用的 `printf`、`memset`、`free` 都是透過 include 拉進來,而在鏈結的階段,它就會連接這些函式庫
```shell=
# -o 是輸出檔案
aarch64-linux-gnu-gcc test.o -o test
```
* Linux 内核的 Library 分為 **兩大類**,^1^ **動態庫 (.so 文件)**,^2^ **靜態庫 (.a 文件)**
GCC 在連結時預設先使用 動態庫,若動態庫不存在才會使用靜態庫
```shell=
# -o 是輸出檔案
# --static 使用靜態庫
aarch64-linux-gnu-gcc test.o -o test --static
```
:::success
* ARM64 GCC 交叉編譯靜態 Library 為 `libc.a`,動態 Libray 為 `libc.so`
> 
:::
## Makefile
透過 make 工具幫我們編譯出我們需要的檔案
### 安裝 make 工具
* 當前環境使用 Ubutun 20 版本
```shell=
sudo apt install make
```
> 
### Makefile 指令格式
```shell=
TARGET: Prerequisites
Commands
```
1. TARGET:目標生成的文件
2. Prerequisites:目標所依賴的所有文件 (可多個)
3. Commands:當 Prerequisites 有任何一個比 TARGET 新時都會觸發到 Command 命令的執行,具體命令是依照使用者需求所設置 (eg. 調用 shell, gcc, java 等等...)
:::warning
* Commands 前必須要有一個 Tab 空格
:::
### 編寫 Makefile 腳本
1. 創建 makefile 檔案
```shell=
touch makefile
```
2. 撰寫 Makefile 檔案
```shell=
## makefile 腳本
## 以下四個都是變數
cc = aarch64-linux-gnu-gcc
prom = test
obj = test.o
CFLAGS = -static
## 條件滿足 obj 就執行以下指令
$(prom) : $(obj)
$(cc) -o $(prom) $(obj) $(CFLAGS)
## 條件滿足 obj 就執行以下指令
%.o : %.c
$(cc) -c $< -o $@
## 清除指定檔案
clean:
rm -rf $(obj) $(prom)
```
3. 執行 make 指令
```shell=
## 在該目錄下執行 make
make
```
> 
4. 之後可以腳本內的 clean 清除產生的檔案
```shell=
## 執行腳本中的 clean 命令
make clean
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `Linux 基礎`