連結腳本(a linker script)
當連結器合併你的目的檔時,它必須決定每樣東西去哪:哪些輸入節區被併進哪些輸出節區、每個輸出節區從哪個位址開始、它們在最終檔案裡出現的順序。對一般作業系統上的一般程式,連結器使用一個合理的內建預設,而你從不需要去想它。連結腳本是覆蓋那個預設的明確配方——一個文字檔,精確告訴連結器如何在記憶體中安排程式的節區與區段。
連結腳本繞著兩個主要概念組織。SECTIONS 命令列出要建立的輸出節區,並為每一個指明要把哪些輸入節區蒐集進去、放在什麼位址——例如「把所有輸入 .text 節區放進一個從位址 0x8000000 開始的輸出 .text 節區」。一個特殊符號,寫成一個點('.')的位置計數器,在腳本逐一安放東西時追蹤目前位址,你可以對它賦值以留下間隙或對齊到邊界。可選的 MEMORY 命令描述可用的實體區域(它們的起始位址與大小),這在裸機與嵌入式系統上極其重要,那裡你必須把程式碼放進快閃記憶體、把資料放進特定 RAM 範圍。腳本也能定義符號(如某區域的開頭與結尾),讓你的程式碼之後能參照。
它在一般應用程式設計之外最重要。在嵌入式微控制器上沒有作業系統替你安排記憶體,所以連結腳本是必要的:它把中斷向量表放在重置位址、把程式碼放進快閃記憶體、為堆疊與變數保留 RAM——沒有它晶片甚至無法開機。誠實的提醒是:對日常的 Linux 或 macOS 程式你幾乎從不寫一個,而為一個有作業系統的系統手寫一個容易出現微妙的錯誤;工具鏈提供的預設腳本是精心調校過的。關於節區如何變成載入器所映射之區段,請見有關節區與區段配置的相關欄位。
/* 微小的連結腳本片段 */ SECTIONS { . = 0x8000000; /* 設定位置計數器(起始位址) */ .text : { *(.text*) } /* 把所有輸入 .text 蒐集進輸出 .text */ .data : { *(.data*) } _end = .; /* 在目前位址定義一個符號 */ }
SECTIONS 區塊把輸入節區對應到選定位址的輸出節區;那個點是位置計數器,_end 變成你的程式碼可用的符號。
對 Linux/macOS 上有作業系統的程式你很少寫連結腳本——工具鏈的預設是精心調校的。它在裸機/嵌入式目標上不可或缺,那裡它必須放好向量表、把程式碼放進快閃記憶體、把資料放進 RAM,否則裝置無法開機。