在Linux內(nèi)核開(kāi)發(fā)與調(diào)試場(chǎng)景中,你是否遇到過(guò)這些困惑??jī)?nèi)核panic時(shí)打印的pc: ffffffc00801c400究竟對(duì)應(yīng)哪個(gè)函數(shù)?編寫(xiě)模塊時(shí)引用的foo符號(hào)為何提示“未定義”??jī)?yōu)化內(nèi)核時(shí)如何判斷某個(gè)功能是否被編譯進(jìn)去?
答案都藏在kernel-6.1/System.map中——它不是內(nèi)核代碼,卻是連接“機(jī)器地址”與“人類可讀功能”的核心橋梁,是kernel開(kāi)發(fā)者的“調(diào)試字典”與“開(kāi)發(fā)指南針”。
本文將從以下5個(gè)維度,帶你吃透kernel-6.1 System.map的價(jià)值,讓內(nèi)核開(kāi)發(fā)調(diào)試效率翻倍:
1.本質(zhì)定位:System.map是什么?kernel-6.1中的結(jié)構(gòu)如何解讀?
2.核心知識(shí)點(diǎn):符號(hào)類型、地址空間、內(nèi)核段關(guān)聯(lián)的底層邏輯(附kernel-6.1實(shí)例)
3.調(diào)試實(shí)戰(zhàn):查看該文件能解決哪些痛點(diǎn)?(Oops定位、棧回溯等案例)
4.開(kāi)發(fā)意義:對(duì)模塊編寫(xiě)、內(nèi)核裁剪、版本兼容的實(shí)際幫助
5.流程可視化:用流程圖梳理實(shí)戰(zhàn)場(chǎng)景(崩潰調(diào)試、模塊開(kāi)發(fā))
一、System.map本質(zhì):kernel-6.1的“地址-符號(hào)”映射字典
kernel-6.1/System.map是內(nèi)核編譯過(guò)程中由鏈接器ld生成的符號(hào)表文件,核心作用是將內(nèi)核運(yùn)行時(shí)的“虛擬地址”與“可讀符號(hào)(函數(shù)/變量)”建立映射。
它就像內(nèi)核的“身份證系統(tǒng)”——每個(gè)符號(hào)(如函數(shù)die、變量jiffies_64)都有唯一的“地址身份證”,開(kāi)發(fā)者通過(guò)地址查符號(hào),就能快速定位功能歸屬。
1.1生成路徑與核心作用
?默認(rèn)路徑:kernel-6.1編譯后,默認(rèn)存放在kernel-6.1/System.map;
?核心價(jià)值:
?破解“地址黑盒”:將Oops/panic打印的虛擬地址(如ffffffc00801c400)翻譯成可讀符號(hào)(如die);
?驗(yàn)證符號(hào)有效性:判斷模塊引用的符號(hào)是否存在、是否可導(dǎo)出(如T類型符號(hào)可被外部調(diào)用);
?反推內(nèi)核配置:通過(guò)符號(hào)是否存在,判斷功能是否編譯(如smp_send_reschedule存在→開(kāi)啟SMP)。
1.2 kernel-6.1符號(hào)結(jié)構(gòu)解析(一行看懂)
System.map的每一行都遵循固定格式,以kernel-6.1中ffffffc00801c400 T die為例,拆解為3個(gè)關(guān)鍵字段:
|
字段
|
示例值
|
說(shuō)明(結(jié)合kernel-6.1)
|
|
虛擬地址
|
ffffffc00801c400
|
符號(hào)在內(nèi)存中的虛擬地址(ARM64內(nèi)核地址多以ffffffc0開(kāi)頭,用戶空間地址以00000000開(kāi)頭)
|
|
符號(hào)類型
|
T
|
區(qū)分符號(hào)屬性:大寫(xiě)為全局符號(hào)(可被外部模塊引用),小寫(xiě)為局部符號(hào)(僅內(nèi)核內(nèi)部使用)
|
|
符號(hào)名
|
die
|
可讀符號(hào)名(die是kernel-6.1中內(nèi)核崩潰的核心處理函數(shù),定義在kernel/exit.c)
|
1.3 kernel-6.1常見(jiàn)符號(hào)類型對(duì)照表
符號(hào)類型直接反映符號(hào)的“歸屬段”(代碼段/數(shù)據(jù)段)和“可見(jiàn)性”,kernel-6.1中高頻出現(xiàn)的類型如下:
|
符號(hào)類型
|
含義
|
kernel-6.1實(shí)例
|
對(duì)應(yīng)內(nèi)核段
|
|
T
|
全局代碼段符號(hào)(可導(dǎo)出)
|
T _text(內(nèi)核代碼段起始地址)
|
.text(代碼段)
|
|
t
|
局部代碼段符號(hào)(僅內(nèi)部使用)
|
|
.text(代碼段)
|
|
A
|
絕對(duì)符號(hào)(地址編譯時(shí)固定)
|
A PECOFF_FILE_ALIGNMENT(PECOFF對(duì)齊值)
|
絕對(duì)段
|
|
W
|
弱符號(hào)(可被重定義)
|
W calibrate_delay_is_known(延遲校準(zhǔn)標(biāo)志)
|
.data(數(shù)據(jù)段)
|
|
B
|
全局?jǐn)?shù)據(jù)段符號(hào)(已初始化)
|
B jiffies_64(系統(tǒng)滴答計(jì)數(shù)器)
|
.data(數(shù)據(jù)段)
|
二、必須掌握的4個(gè)核心知識(shí)點(diǎn)(結(jié)合kernel-6.1)
看懂System.map不只是“查地址”,更要通過(guò)符號(hào)反推kernel-6.1的內(nèi)存布局、功能模塊、配置狀態(tài)——這才是它的深層價(jià)值。
2.1符號(hào)類型→內(nèi)核段歸屬:快速定位功能區(qū)域
kernel-6.1的內(nèi)存被劃分為多個(gè)“功能段”,符號(hào)類型+地址范圍可直接判斷歸屬,幫你快速定位功能場(chǎng)景:
?代碼段(.text):T/t類型符號(hào)的聚集地,存放內(nèi)核所有執(zhí)行函數(shù),例如:
?T _text(ffffffc008000000):kernel-6.1代碼段起始地址,內(nèi)核啟動(dòng)后第一個(gè)執(zhí)行的代碼段;
?T __irqentry_text_start~T __irqentry_text_end:中斷入口代碼段,包含gic_handle_irq(ARM64 GIC中斷處理函數(shù));
?T vectors(ffffffc008010800):ARM64異常向量表,是內(nèi)核處理中斷、系統(tǒng)調(diào)用、異常的“入口網(wǎng)關(guān)”。

?數(shù)據(jù)段(.data):W/B類型符號(hào)所在,存放已初始化的全局變量,例如:
?W calibration_delay_done:延遲校準(zhǔn)完成標(biāo)志,內(nèi)核啟動(dòng)時(shí)用于判斷是否跳過(guò)校準(zhǔn)流程;
?B jiffies_64:系統(tǒng)滴答計(jì)數(shù)器,記錄內(nèi)核運(yùn)行時(shí)間,是定時(shí)器、調(diào)度的核心變量。
?絕對(duì)段:A類型符號(hào),地址編譯時(shí)固定,不隨內(nèi)存布局變化,例如A _kernel_size_le_lo32(kernel-6.1內(nèi)核大小低32位)。
2.2 ARM64地址空間→符號(hào)的“居住區(qū)域”
kernel-6.1(ARM64架構(gòu))的符號(hào)地址主要分兩類,對(duì)應(yīng)Linux內(nèi)核的“地址空間隔離”設(shè)計(jì):
1.內(nèi)核虛擬地址(ffffffc0開(kāi)頭):
?示例:ffffffc00801c400 T die(崩潰處理)、ffffffc008010628 T __entry_text_start(系統(tǒng)調(diào)用入口段起始);
?特點(diǎn):與用戶空間地址(00000000~ffff0000)完全隔離,保障內(nèi)核安全性,僅內(nèi)核態(tài)可訪問(wèn)。
1.早期/特定段地址(00000000開(kāi)頭):
?示例:00000000 A _kernel_flags_le_hi32(內(nèi)核標(biāo)志高32位)、00000000 A __pecoff_data_rawsize(PECOFF數(shù)據(jù)原始大小);
?特點(diǎn):地址編譯時(shí)固定,多用于內(nèi)核早期啟動(dòng)(如EFI stub初始化)或文件格式相關(guān)(PECOFF是Windows可執(zhí)行文件格式,內(nèi)核用于兼容引導(dǎo))。
2.3符號(hào)名→內(nèi)核子系統(tǒng)映射:一眼識(shí)別功能
kernel-6.1的符號(hào)名遵循“功能前綴”規(guī)則,通過(guò)符號(hào)名可直接對(duì)應(yīng)內(nèi)核子系統(tǒng),減少查源碼的時(shí)間:
|
內(nèi)核子系統(tǒng)
|
符號(hào)名前綴/關(guān)鍵詞
|
kernel-6.1實(shí)例
|
功能說(shuō)明
|
|
中斷處理
|
gic_、irq_、do_undef
|
t gic_handle_irq、T do_undefinstr
|
GIC中斷處理、未定義指令異常處理
|
|
進(jìn)程調(diào)度
|
sched_、cpu_switch
|
T cpu_switch_to、t pick_next_task_fair
|
進(jìn)程切換、CFS調(diào)度器選任務(wù)
|
|
內(nèi)存管理
|
pgd_、do_page_fault
|
T pgd_alloc、t do_page_fault
|
頁(yè)表分配、頁(yè)錯(cuò)誤處理
|
|
系統(tǒng)調(diào)用
|
__arm64_sys_
|
T __arm64_sys_mmap、T __arm64_sys_exit
|
ARM64架構(gòu)的mmap/exit系統(tǒng)調(diào)用
|
|
EFI引導(dǎo)
|
__efistub_、efi_
|
A __efistub_primary_entry_offset
|
EFI stub啟動(dòng)入口偏移量
|
2.4符號(hào)存在性→內(nèi)核配置判斷
kernel-6.1中某個(gè)符號(hào)是否存在,直接反映內(nèi)核編譯時(shí)的配置(.config):
?若存在T smp_send_reschedule→開(kāi)啟CONFIG_SMP(對(duì)稱多處理器);
?若存在T __arm64_sys_fanotify_init→開(kāi)啟CONFIG_FANOTIFY(文件系統(tǒng)事件通知);
?若不存在t has_no_fpsimd→開(kāi)啟CONFIG_FPSIMD(ARM64浮點(diǎn)/向量支持)。
這對(duì)內(nèi)核裁剪優(yōu)化非常有用:若不需要SMP功能,編譯時(shí)關(guān)閉CONFIG_SMP,smp_send_reschedule等符號(hào)會(huì)消失,減少內(nèi)核體積。
三、調(diào)試時(shí)關(guān)注System.map:解決4大核心痛點(diǎn)
kernel-6.1調(diào)試中,System.map是“效率工具”——沒(méi)有它,你可能需要花幾小時(shí)猜地址;有了它,幾分鐘就能定位問(wèn)題。
3.1 Oops崩潰定位:從地址到函數(shù)的“一秒翻譯”
內(nèi)核Oops是最常見(jiàn)的調(diào)試場(chǎng)景,例如打印:
Oops:0000000000000005[PCisat ffffffc00801c400
此時(shí)查System.map即可快速定位:
1.打開(kāi)kernel-6.1/System.map,搜索ffffffc00801c400;
2.找到對(duì)應(yīng)行:ffffffc00801c400 T die→確認(rèn)崩潰發(fā)生在die函數(shù);
3.查看die源碼(kernel/exit.c),結(jié)合Oops上下文(如寄存器值、調(diào)用鏈),判斷是“空指針訪問(wèn)”還是“非法內(nèi)存地址”。

流程圖:

3.2內(nèi)核恐慌?;厮荩貉a(bǔ)全函數(shù)調(diào)用鏈
當(dāng)內(nèi)核panic打印?;厮荩?/span>Backtrace)時(shí),會(huì)輸出一串函數(shù)地址,例如:
Backtrace:ffffffc00801c400 → ffffffc00801c680 → ffffffc00801d8e0
通過(guò)System.map翻譯地址:
?ffffffc00801c400 → die(崩潰處理);
?ffffffc00801c680 → arm64_force_sig_fault(強(qiáng)制發(fā)送信號(hào));
?ffffffc00801d8e0 → do_serror(系統(tǒng)錯(cuò)誤處理)。
瞬間補(bǔ)全調(diào)用鏈:do_serror→arm64_force_sig_fault→die,快速定位錯(cuò)誤傳播路徑。

3.3模塊開(kāi)發(fā)符號(hào)驗(yàn)證:避免“未定義引用”
編寫(xiě)kernel-6.1內(nèi)核模塊時(shí),若引用foo函數(shù)卻提示“undefined reference tofoo”,可通過(guò)System.map排查:
1.搜索foo符號(hào):
?若不存在→內(nèi)核未編譯foo對(duì)應(yīng)的功能,需開(kāi)啟相關(guān)配置(如CONFIG_FOO=y);
?若存在但類型為t→foo是局部符號(hào)(僅內(nèi)核內(nèi)部使用),無(wú)法被模塊引用,需修改內(nèi)核源碼將foo導(dǎo)出(添加EXPORT_SYMBOL(foo));
?若存在且類型為T→foo是全局符號(hào),模塊中聲明extern int foo();即可正常編譯。
3.4性能分析地址翻譯:perf采樣結(jié)果落地
用perf record -g采樣內(nèi)核性能時(shí),結(jié)果會(huì)包含大量地址,例如:
Samples:100 of event 'cycles', Event count (approx.):123456ffffffc0080164a4→20%ffffffc0080165d0→15%
通過(guò)System.map翻譯:
?ffffffc0080164a4 T cpu_switch_to(進(jìn)程切換);
?ffffffc0080165d0 T fpsimd_thread_switch(浮點(diǎn)上下文切換)。

可快速判斷性能瓶頸在“進(jìn)程切換”,進(jìn)而優(yōu)化調(diào)度策略。
四、對(duì)開(kāi)發(fā)的意義:從“試錯(cuò)”到“精準(zhǔn)”
kernel-6.1/System.map不只是調(diào)試工具,更是kernel開(kāi)發(fā)的“正確性保障”和“效率加速器”。
4.1提升內(nèi)核調(diào)試效率
沒(méi)有System.map時(shí),調(diào)試需通過(guò)addr2line工具(需帶調(diào)試信息的內(nèi)核鏡像vmlinux),且依賴內(nèi)核編譯時(shí)保留調(diào)試符號(hào);有了System.map,直接查地址→符號(hào),無(wú)需額外工具,尤其適合無(wú)調(diào)試信息的release版本內(nèi)核。
4.2保障模塊開(kāi)發(fā)正確性
kernel-6.1模塊開(kāi)發(fā)中,符號(hào)引用錯(cuò)誤是常見(jiàn)問(wèn)題(如引用不存在的符號(hào)、引用局部符號(hào))。System.map可提前驗(yàn)證符號(hào)有效性,避免模塊加載時(shí)因“符號(hào)未定義”被內(nèi)核拒絕(insmod: ERROR: could not insert module xxx.ko: Unknown symbol in module)。
模塊開(kāi)發(fā)流程圖:

4.3輔助內(nèi)核裁剪與優(yōu)化
kernel-6.1支持按需裁剪功能,System.map可驗(yàn)證裁剪效果:
?若不需要EFI引導(dǎo),關(guān)閉CONFIG_EFI后,__efistub_前綴的符號(hào)會(huì)消失,說(shuō)明裁剪成功;
?若不需要浮點(diǎn)支持,關(guān)閉CONFIG_FPSIMD后,fpsimd_前綴的符號(hào)會(huì)消失,減少內(nèi)核體積。
4.4驗(yàn)證版本兼容性
不同內(nèi)核版本(如kernel-6.1與kernel-6.2)的符號(hào)地址可能變化,若模塊硬編碼地址,會(huì)導(dǎo)致加載失敗。通過(guò)對(duì)比System.map:
?若foo符號(hào)在kernel-6.1中地址為ffffffc0080164a4,在kernel-6.2中為ffffffc008016500,說(shuō)明地址偏移,需修改模塊為“符號(hào)引用”而非“地址硬編碼”。
五、總結(jié):System.map是kernel-6.1開(kāi)發(fā)的“基礎(chǔ)設(shè)施”
kernel-6.1/System.map看似是簡(jiǎn)單的“地址-符號(hào)”列表,實(shí)則是內(nèi)核的“功能導(dǎo)航圖”——它連接了機(jī)器可識(shí)別的“地址”與人類可理解的“功能”,解決了調(diào)試中的“地址黑盒”問(wèn)題,保障了開(kāi)發(fā)中的“符號(hào)正確性”。
無(wú)論是內(nèi)核崩潰定位、模塊開(kāi)發(fā),還是性能優(yōu)化、版本兼容,System.map都能幫你從“盲目試錯(cuò)”轉(zhuǎn)向“精準(zhǔn)操作”,是 linux開(kāi)發(fā)者必須掌握的核心工具。
下次遇到內(nèi)核問(wèn)題時(shí),先打開(kāi)System.map——它或許能幫你省下幾小時(shí)的調(diào)試時(shí)間。
-
內(nèi)核
+關(guān)注
關(guān)注
4文章
1474瀏覽量
43088 -
開(kāi)發(fā)調(diào)試
+關(guān)注
關(guān)注
0文章
7瀏覽量
8319
發(fā)布評(píng)論請(qǐng)先 登錄
RK3576 Android 14.0 SDK開(kāi)發(fā)指南(第一集)
RK3576 vs RK3588:為何越來(lái)越多的開(kāi)發(fā)者轉(zhuǎn)向RK3576?
【米爾RK3576開(kāi)發(fā)板免費(fèi)體驗(yàn)】3、移植EtherCAT Igh
【作品合集】米爾RK3576開(kāi)發(fā)板測(cè)評(píng)
【作品合集】靈眸科技EASY EAI Orin Nano(RK3576)開(kāi)發(fā)板測(cè)評(píng)
如何米爾RK3576開(kāi)發(fā)板上移植EtherCAT Igh
新品體驗(yàn) | RK3576開(kāi)發(fā)板
RK3576單板發(fā)布倒計(jì)時(shí):RK3399與RK3576對(duì)比
初次編譯rk3568(rk3576)Linux 6.1內(nèi)核踩坑記錄:從報(bào)錯(cuò)終止到成功解決的完整流程
RK3576音頻調(diào)試全紀(jì)錄
迅為如何在RK3576上部署YOLOv5;基于RK3576構(gòu)建智能門禁系統(tǒng)
(信息量有點(diǎn)大)基于RK3576深入解讀kernel-6.1/System.map:內(nèi)核開(kāi)發(fā)調(diào)試的“地址-功能”導(dǎo)航圖
評(píng)論