可執行檔案格式
現代 Linux 系統採用 ELF (Executable and Linkable Format)。查看檔案類型:
$ file hello.ko
hello.ko: ELF 32-bit LSB relocatable, ARM, version 1 (SYSV), not stripped
三大核心區段
| 區段 | 內容 | size 輸出欄位 | 佔檔案空間 |
| .text (代碼段) | 機器指令、唯讀資料 | text | 是 |
| .data (資料段) | 已初始化的全域/靜態變數 | data | 是 |
| .bss (BSS段) | 未初始化或初始為 0 的全域/靜態變數 | bss | 否(僅記錄大小) |
size 指令輸出範例
$ size hello.ko
text data bss dec hex filename
224 300 0 524 20c hello.ko
a.out 記憶體映射佈局(由低至高)
+------------------------+
| Stack (堆疊) | 高位址,向下增長,區域變數、參數、返回位址
| |
| Heap (堆) | 向上增長,malloc/new 動態分配
| |
| Unmapped Hole | 保護區,捕捉空指標存取
+------------------------+
| .bss (BSS段) | 零初始化,緊接 .data 之後
+------------------------+
| .data (資料段) | 已初始化全域/靜態變數
+------------------------+
| .text (代碼段) | 指令碼,唯讀,可共享
+------------------------+
| ELF Header | 魔數、程式進入點、區段表位置
+------------------------+ 低位址(通常 0x08048000 或隨機化基址)
驗證實驗:Linux 核心模組
// hello.c
#include <linux/init.h>
#include <linux/module.h>
MODULE_LICENSE("GPL");
static int __init hello_init(void)
{
printk(KERN_WARNING "Hello world!\n");
return 0;
}
static void __exit hello_exit(void)
{
printk(KERN_WARNING "Goodbye!\n");
}
module_init(hello_init);
module_exit(hello_exit);
編譯後 size 變化觀察
| 程式碼變更 | text | data | bss | 說明 |
| 原始 | 224 | 300 | 0 | 基準 |
加 int i; (未初始化全域) | 224 | 300 | 8 | bss +8 bytes (2個int) |
加 int m = 2; (非零初始化) | 224 | 308 | 8 | data +8 bytes |
加 static int j; (未初始化靜態) | 224 | 308 | 8 | bss 不變(編譯器最佳化?) |
函數內大陣列 int arr[1000]; | 224 | 308 | 8 | 區域變數不在可執行檔,運行時棧上分配 |
關鍵結論
- 區域變數不存在可執行檔中,運行時於堆疊分配
- 全域/靜態變數:未初始化或為 0 → .bss;非零初始化 → .data
.bss 不佔檔案空間,載入器根據記錄大小在記憶體中劃分並歸零
- 使用者進程虛擬位址空間最低端(通常 64KB)故意不映射,用於捕捉空指標解參考