Linux 符號和來源
本文說明 WinDbg 如何支援標準 Linux 符號和來源。 Linux 上的偵錯支援需要 WinDbg 1.2402.24001.0 版或更新版本。
DebugInfoD 符號伺服器
Window 調試程式會使用 DebugInfoD 標準來自動下載適用於 Linux 的組建成品。 相較下,DebugInfoD 是Microsoft符號伺服器和來源伺服器技術的組合。 它允許根據組建標識碼自動下載三種成品類型(可執行檔(ELF)、偵錯資訊(DWARF)和原始程式碼(程序代碼)。Linux 的各種散發套件現在會裝載自己的 DebugInfoD 伺服器,以提供一些成品類型。 各種 DebugInfoD 伺服器會列在 ELFUTILS https://debuginfod.elfutils.org。
以下是 DebugInfoD 的一般資訊:
標籤 DebugInfoD*
可以指向一或多個 DebugInfoD 伺服器,且每個伺服器 URL 的格式為 https://domain.com
,並以 分隔 *
。 伺服器會依來源路徑中所列的順序進行搜尋,而且會從第一個相符 URL 擷取檔案。
例如,您可以設定如下的符號路徑。
.sympath+ DebugInfoD*https://debuginfod.elfutils.org
!sym noisy
使用 命令來顯示符號載入的相關信息。 如需詳細資訊,請參閱 !sym。
來源路徑命令 (.srcpath, .lsrcpath (設定來源路徑)支援透過 DebugInfoD*
標籤從DebugInfoD 伺服器擷取檔案,以便擷取原始程式碼成品。 例如,您可以設定來源路徑,如下所示。
.srcpath+ DebugInfoD*https://debuginfod.elfutils.org
如需詳細資訊,請參閱 原始程式碼延伸存取。
DWARF 符號
DWARF 是廣泛使用且標準化的偵錯數據格式。 DWARF 最初是與可執行檔和可連結格式 (ELF) 一起設計,但與物件檔格式無關。 如需詳細資訊,請參閱 https://en.wikipedia.org/wiki/DWARF 和 版本 5 標準,請參閱 DWARF 第 5 版。
使用物件傾印命令來判斷 DWARF 符號版本。 在此範例中,第 5 版。
bob@BOB:/mnt/c/Users/BOB$ objdump -g DisplayGreeting | grep -A 2 'Compilation Unit @'
Compilation Unit @ offset 0x0:
Length: 0x285c (32-bit)
Version: 5
WinDbg DWARF 支援
WinDbg 支援下列 DWARF 和 ELF 用法。
Linux 使用者模式 - 開啟 Linux ELF 核心傾印 (
-z <core dump>
) 並使用完整的私人 DWARF 符號執行驗屍偵錯和分析。Linux 核心模式 - 開啟 Linux 核心 (ELF VMCORE) 傾印,並使用完整的私人 DWARF 符號執行驗屍偵錯和分析。
Linux 核心模式 - 開啟 Linux 核心壓縮 KDUMP,並使用完整的私人 DWARF 符號進行驗屍偵錯和分析(WinDbg 僅支援 ZLIB 壓縮的 KDUMP 檔案。不支援 LZO 和 Snappy 壓縮 KDUMP。
開啟 ELF 影像 (
-z <ELF image>
) 並檢查內容、反組譯碼等。其他案例 - 瞭解混合 PE/ELF 環境中的 ELF 映像和 DWARF 符號(例如:偵錯 Windows 上載入的開啟記憶體保護區元件。如需詳細資訊,請參閱 開啟記憶體保護區偵錯。)
WinDbg GDBServer Linux 支援
在 Linux 上使用 GNU 調試程式 GDBServer 來支援 WinDbg 連線。 如需 GDBServer 的詳細資訊,請參閱 https://en.wikipedia.org/wiki/Gdbserver。 您可以在您可以在這裡檢視遠端 gdb 偵錯檔的其中一個位置 - https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging
如需有關搭配 WinDbg 使用 GDBServer 和程式代碼逐步解說的詳細資訊,請參閱 Linux 即時遠端進程偵錯。 這裡的範例會使用在 Windows 子系統 Linux 版 (WSL) 下執行的 Ubuntu,但也可以使用其他 Linux 實作。
DWARF 實作
支援內嵌在原始映像中的 DWARF 符號(偵錯二進位檔),或分割成個別的 ELF 映射(偵錯套件)。
為了讓 Linux DWARF 堆疊逐步解說成功,必須能夠找到載入 Linux 程式之任何模組的原始二進位映像。
您可以透過調試程式的同情或符號伺服器找到 DWARF 符號/ELF 映像(透過 GNU 組建識別符哈希根據 .NET Core 編製索引)。
您可以透過 Linux 樣式偵錯套件安裝找到 DWARF 符號。 這類是由符號路徑中名為 .build-id
的目錄所提供。 以下是根據 GNU 組建標識碼哈希的第一個字節所命名的目錄。 在每個這類目錄下,都是名為 <remaining 18 bytes of GNU Build ID hash>
.debug 的檔案。
當調試程式開啟 DWARF 符號時,它會執行初始索引步驟,因為格式本身不包含必要的查閱表格。 對於大型 DWARF 符號集(例如:Linux 核心的私人 DWARF 資訊),這可能需要 10 - 30 秒。
!addsourcemap,可從已知的存放庫/認可自動擷取來源
如果您要偵錯從已知存放庫和認可建置的元件,則有延伸模組 !addsourcemap
、調試程式命令可讓您告訴調試程式特定模組和路徑,您想要從已知 URL 自動擷取來源。 延伸模組的使用方式如下:
!addsourcemap <module> <local spec> <remote spec>
其中:
<module>
是感興趣的模組名稱。
<local spec>
是該模組內要透過URL查閱的來源路徑。 此路徑應該以通配符結尾。
<remote spec>
是要查閱相符 <local spec>
檔案的URL。 此路徑應該以通配符結尾,此通配符將會取代為中通配符 <local spec>
與特定來源路徑的比對方式。
若要設定sourcemap,請使用 lm 確認模組存在(清單載入的模組)。 然後判斷來源的遠端位置。
此範例會將 vmlinux 模組設定為 GitHub 上可用的特定組建。
0:000> !addsourcemap vmlinux /build/linux/* https://raw.githubusercontent.com/torvalds/linux/6e61dde82e8bfe65e8ebbe43da45e615bc529236/
Source map /build/linux/* -> https://raw.githubusercontent.com/torvalds/linux/6e61dde82e8bfe65e8ebbe43da45e615bc529236/ successfully added
發出 sourcemap 命令之後,許多專案會觸發來源載入,例如使用 .reload 命令來回切換畫面格或重載。 之後,就會從 GitHub 自動提取來源。
!sourcemaps
!sourcemaps
使用 命令來列出現有的來源對應。
0:000> !sourcemaps
Source maps for vmlinux.6:
/build/linux/* -> https://raw.githubusercontent.com/torvalds/linux/6e61dde82e8bfe65e8ebbe43da45e615bc529236/
!removesourcemaps
!removesourcemaps
使用 命令移除現有的來源對應。
0:000> !removesourcemaps vmlinux /build/linux/* https://raw.githubusercontent.com/torvalds/linux/6e61dde82e8bfe65e8ebbe43da45e615bc529236/
1 source maps successfully removed
針對 DWARF 符號進行疑難解答
如果您要偵錯 Linux/Android 傾印(或使用 DWARF 符號的其他目標),您可能想要查看符號的原始內容,以了解為什麼局部變數、類型定義或函式定義不正確。 若要這樣做,調試程式有一些內建擴充功能命令,可傾印 DWARF 符號的原始內容。 此外,使用 readelf 和 dumpdwarf 等 Linux 公用程式來顯示符號內部資訊。
readelf 命令
在Linux命令提示字元中使用 readelf 命令,顯示針對在Linux即時遠程進程偵錯中建立之範例 DisplayGreeting 程式所建立的偵錯組建識別碼。 在此範例中,會傳回 aba822dd158b997b09903d4165f3dbfd37f5e5c1 的組建標識符。
Bob@BOB6:/mnt/c/Users/Bob$ readelf -n DisplayGreeting
Displaying notes found in: .note.gnu.property
Owner Data size Description
GNU 0x00000020 NT_GNU_PROPERTY_TYPE_0
Properties: x86 feature: IBT, SHSTK
x86 ISA needed: x86-64-baseline
Displaying notes found in: .note.gnu.build-id
Owner Data size Description
GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring)
Build ID: aba822dd158b997b09903d4165f3dbfd37f5e5c1
Displaying notes found in: .note.ABI-tag
Owner Data size Description
GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag)
OS: Linux, ABI: 3.2.0
Readelf 可與 grep 搭配使用,以傳回符號版本。
readelf --debug-dump=info DisplayGreeting | grep -A 2 'Compilation Unit @'
Compilation Unit @ offset 0x0:
Length: 0x285c (32-bit)
Version: 5
dwarfdump
dwarfdump linux 命令會列印或檢查特定選項所要求的 DWARF 區段。 使用 dwarfdump -h 來檢視許多選項。
bob@BOB6:/mnt/c/Users/BOB$ dwarfdump -h
如需在Ubuntu上使用 dwarfdump 的詳細資訊,請參閱 dwarfdump 。
!diesym
這個調試程式命令會顯示指定之表達式中任何符號的 DIE (或 DIE 子樹)(可以是位址、函式名稱等),並選擇性地指定遞歸層級。 它會找出符號的 DIE(通常是函式,但可能是數據等等...),並執行與在符號上執行 dwarfdump 或 llvm-dwarfdump 類似的 DIE 診斷傾印,並尋找 DIE。
!diesym [options] <expression>
-r#
:以遞歸方式傾印 N 個層級。 一般而言,這是一個,而且只會傾印 DIE 本身。
<expression>
- 尋找 DIE 的位址是由表示式所指定。 它可以是一般十六進位位址 (0x<blah>
) 或可能是另一個唯一的函式名稱。
數據模型的標準評估需要評估它。 使用 dx 命令來驗證模型表達式。 如需使用 dx 命令的詳細資訊,請參閱 dx (顯示除錯程式物件模型表示式)。
0:000> dx DisplayGreeting!GetCppConGreeting
DisplayGreeting!GetCppConGreeting : DisplayGreeting!GetCppConGreeting+0x0 [Type: GetCppConGreeting]
顯示範例 DisplayGreeting 程式 GetCppConGreeting 函式的 DIE 符號資訊。
0:000> !diesym DisplayGreeting!GetCppConGreeting
0x2816: DW_TAG_subprogram [^^^]
DW_AT_external (true)
DW_AT_name 'GetCppConGreeting'
DW_AT_decl_file 1 ('/mnt/c/Users/BOB/DisplayGreeting.cpp')
DW_AT_decl_line 0x7
DW_AT_decl_column 0x6
DW_AT_linkage_name '_Z17GetCppConGreetingPwm'
DW_AT_low_pc 0x11E9
DW_AT_high_pc +0x3c (== 0x1225)
DW_AT_frame_base DW_OP_call_frame_cfa
DW_AT_call_all_tail_calls (true)
使用 -r2 選項可顯示額外的 DIE 符號資訊層級。
0:000> !diesym -r2 DisplayGreeting!GetCppConGreeting
0x2816: DW_TAG_subprogram [^^^]
DW_AT_external (true)
DW_AT_name 'GetCppConGreeting'
DW_AT_decl_file 1 ('/mnt/c/Users/BOB/DisplayGreeting.cpp')
DW_AT_decl_line 0x7
DW_AT_decl_column 0x6
DW_AT_linkage_name '_Z17GetCppConGreetingPwm'
DW_AT_low_pc 0x11E9
DW_AT_high_pc +0x3c (== 0x1225)
DW_AT_frame_base DW_OP_call_frame_cfa
DW_AT_call_all_tail_calls (true)
0x2834: DW_TAG_formal_parameter [^^^]
DW_AT_name 'buffer'
DW_AT_decl_file 1 ('/mnt/c/Users/BOB/DisplayGreeting.cpp')
DW_AT_decl_line 0x7
DW_AT_decl_column 0x21
DW_AT_type (CU + 0x12f7 == 0x12f7)
DW_AT_location DW_OP_fbreg(-40)
!死
!die
將會針對任何 DIE 在 DWARF 偵錯區段中具有選擇性指定遞歸層級的指定位移表示式,顯示 DIE (或 DIE 子樹狀結構)。
!die [-r#] [-t] -m <module base expression> <offset expression>
-r#
:以遞歸方式傾印 N 個層級。
-t
:如果 DIE 位於 .debug_types 中的類型單位內,而不是.debug_info內的編譯單位,您必須指定 -t 參數。
-m <module base expression>
提供 ,提供您要查詢之任何模組的基位址。
<offset expression>
是 DIE 位移的大小。
在 Linux 提示字元中,使用 dwarfdump 搭配 -r 來列印 DWARF 檔案的 .debug_aranges 區段,以找出 DIE 位移。
bob@BOB6:/mnt/c/Users/BOB$ dwarfdump -r DisplayGreeting
.debug_aranges
COMPILE_UNIT<header overall offset = 0x00000000>:
< 0><0x0000000c> DW_TAG_compile_unit
DW_AT_producer GNU C++17 11.4.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection
DW_AT_language DW_LANG_C_plus_plus_14
DW_AT_name DisplayGreeting.cpp
DW_AT_comp_dir /mnt/c/Users/BOB
DW_AT_ranges 0x0000000c
Offset of rnglists entries: 0x0000000c
[ 0] start,end 0x000011e9 0x0000134a
[ 1] start,end 0x0000134a 0x00001368
[ 2] start,end 0x00001368 0x0000137b
[ 3] start,end 0x0000137b 0x0000138d
[ 4] end of list
DW_AT_low_pc 0x00000000
DW_AT_stmt_list 0x00000000
arange starts at 0x000011e9, length of 0x00000161, cu_die_offset = 0x0000000c
arange starts at 0x0000134a, length of 0x0000001e, cu_die_offset = 0x0000000c
arange starts at 0x00001368, length of 0x00000013, cu_die_offset = 0x0000000c
arange starts at 0x0000137b, length of 0x00000012, cu_die_offset = 0x0000000c
請注意 的DW_AT_ranges值 0x0000000c
。 在調試程式中,使用該位移值和 DisplayGreeting 的模組名稱來顯示 DIE 符號資訊。
0:000> !die -m DisplayGreeting 0x0000000c
0xc: DW_TAG_compile_unit [^^^]
DW_AT_producer 'GNU C++17 11.4.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection'
DW_AT_language 0x21
DW_AT_name
DW_AT_comp_dir
DW_AT_ranges
[0x11e9 - 0x134a)
[0x134a - 0x1368)
[0x1368 - 0x137b)
[0x137b - 0x138d)
DW_AT_low_pc 0x0
DW_AT_stmt_list
!dieancestry
!dieancestry
命令的行為類似!die
,不同之處在於它會向包含編譯或類型單位的 DIE 樹狀結構走去,而不是向下樹狀結構。
!dieancestry [-r#] [-t] -m <module base expression> <offset expression>
-r#
:以遞歸方式傾印 N 個層級。
-m <module base expression>
提供 ,提供您要查詢之任何模組的基位址。
<offset expression>
是 DIE 位移的大小。
範例:
0:000> !dieancestry -m DisplayGreeting 0x0000000c
0xc: DW_TAG_compile_unit [^^^]
DW_AT_producer 'GNU C++17 11.4.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection'
DW_AT_language 0x21
DW_AT_name
DW_AT_comp_dir
DW_AT_ranges
[0x11e9 - 0x134a)
[0x134a - 0x1368)
[0x1368 - 0x137b)
[0x137b - 0x138d)
DW_AT_low_pc 0x0
DW_AT_stmt_list
請注意,例如父代或同層級連結,可點選以允許進一步周遊 DWARF 符號樹狀結構。
0:000> !die -r2 -m 0x555555554000 0xc
0xc: DW_TAG_compile_unit [^^^]
DW_AT_producer 'GNU C++17 11.4.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection'
DW_AT_language 0x21
DW_AT_name
DW_AT_comp_dir
DW_AT_ranges
[0x11e9 - 0x134a)
[0x134a - 0x1368)
[0x1368 - 0x137b)
[0x137b - 0x138d)
DW_AT_low_pc 0x0
DW_AT_stmt_list
0x2a: DW_TAG_namespace [^^^]
DW_AT_name 'std'
DW_AT_decl_file 9 ('/usr/include/c++/11/bits/exception_ptr.h')
DW_AT_decl_line 0x116
DW_AT_decl_column 0xb
DW_AT_sibling (CU + 0xf01 == 0xf01)
0xf01: DW_TAG_base_type [^^^]
DW_AT_byte_size 0x1
DW_AT_encoding DW_ATE_boolean (2)
DW_AT_name 'bool'
0xf08: DW_TAG_base_type [^^^]
DW_AT_byte_size 0x8
DW_AT_encoding DW_ATE_unsigned (7)
DW_AT_name 'long unsigned int'
...
並非所有輸出都會顯示。
!dwunwind
!dwunwind
與 PE 影像的 .fnent (顯示函式數據) 有些類似。 它會顯示表示式所指定位址的 DWARF 回溯規則。 它也類似於 readelf --unwind 命令,可在可用時顯示回溯資訊。
!dwunwind <expression>
本範例會顯示 DisplayGreeting 程式中 GetCppConGreeting 函式的回溯規則。
0:000> !dwunwind DisplayGreeting!GetCppConGreeting
DW_FRAME_SAME_VAL: 0('rax'), 1('rdx'), 2('rcx'), 3('rbx'), 4('rsi'), 5('rdi'), 6('rbp'), 7('rsp'), 8('r8'), 9('r9'), 10('r10'), 11('r11'), 12('r12'), 13('r13'), 14('r14'), 15('r15')
0('CFA'): DW_EXPR_OFFSET 7('rsp') + 8
16('<Return Address>'): DW_EXPR_OFFSET 12290('CFA') + -8
這會顯示指令指標緩存器的回溯堆疊。
0:000> !dwunwind @rip
DW_FRAME_SAME_VAL: 0('rax'), 1('rdx'), 2('rcx'), 4('rsi'), 5('rdi'), 7('rsp'), 8('r8'), 9('r9'), 10('r10'), 11('r11'), 14('r14'), 15('r15')
0('CFA'): DW_EXPR_OFFSET 7('rsp') + 208
3('rbx'): DW_EXPR_OFFSET 12290('CFA') + -40
6('rbp'): DW_EXPR_OFFSET 12290('CFA') + -32
12('r12'): DW_EXPR_OFFSET 12290('CFA') + -24
13('r13'): DW_EXPR_OFFSET 12290('CFA') + -16
16('<Return Address>'): DW_EXPR_OFFSET 12290('CFA') + -8
以下是程式計數器範例。
0:000> !dwunwind @pc
DW_FRAME_SAME_VAL: 0('x0'), 1('x1'), 2('x2'), 3('x3'), 4('x4'), 5('x5'), 6('x6'), 7('x7'), 8('x8'), 9('x9'), 10('x10'), 11('x11'), 12('x12'), 13('x13'), 14('x14'), 15('x15'), 16('x16'), 17('x17'), 18('x18'), 31('sp'), 32('pc')
0('CFA'): DW_EXPR_OFFSET 31('sp') + 208
19('x19'): DW_EXPR_OFFSET 1436('CFA') + -192
20('x20'): DW_EXPR_OFFSET 1436('CFA') + -184
21('x21'): DW_EXPR_OFFSET 1436('CFA') + -176
22('x22'): DW_EXPR_OFFSET 1436('CFA') + -168
23('x23'): DW_EXPR_OFFSET 1436('CFA') + -160
24('x24'): DW_EXPR_OFFSET 1436('CFA') + -152
25('x25'): DW_EXPR_OFFSET 1436('CFA') + -144
26('x26'): DW_EXPR_OFFSET 1436('CFA') + -136
27('x27'): DW_EXPR_OFFSET 1436('CFA') + -128
28('x28'): DW_EXPR_OFFSET 1436('CFA') + -120
29('fp'): DW_EXPR_OFFSET 1436('CFA') + -208
30('lr'): DW_EXPR_OFFSET 1436('CFA') + -200
!dietree
在指定的遞歸層級傾印指定模組的 DIE 樹狀結構,類似於在符號上執行 dwarfdump 或 llvm-dwarfdump 並尋找 DIE。
!dietree [OPTIONS] -m <module base> <offset expression>
-r#
:指定遞歸層級
-t
:傾印.debug_types而非.debug_info
包含 DIE 之模組的模組基底必須由 選項提供 -m <expression>
。
<offset expression>
是 DIE 位移的大小。
範例:
0:000> !dietree -m DisplayGreeting 0x0000000c
0xc: DW_TAG_compile_unit [^^^]
DW_AT_producer 'GNU C++17 11.4.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection'
DW_AT_language 0x21
DW_AT_name
DW_AT_comp_dir
DW_AT_ranges
[0x11e9 - 0x134a)
[0x134a - 0x1368)
[0x1368 - 0x137b)
[0x137b - 0x138d)
DW_AT_low_pc 0x0
DW_AT_stmt_list
使用 -r2 選項在 dietree 中顯示其他值。
0:000> !dietree -r2 -m DisplayGreeting 0x0000000c
0xc: DW_TAG_compile_unit [^^^]
DW_AT_producer 'GNU C++17 11.4.0 -mtune=generic -march=x86-64 -g -fasynchronous-unwind-tables -fstack-protector-strong -fstack-clash-protection -fcf-protection'
DW_AT_language 0x21
DW_AT_name
DW_AT_comp_dir
DW_AT_ranges
[0x11e9 - 0x134a)
[0x134a - 0x1368)
[0x1368 - 0x137b)
[0x137b - 0x138d)
DW_AT_low_pc 0x0
DW_AT_stmt_list
0x2a: DW_TAG_namespace [^^^]
DW_AT_name 'std'
DW_AT_decl_file 9 ('/usr/include/c++/11/bits/exception_ptr.h')
DW_AT_decl_line 0x116
DW_AT_decl_column 0xb
DW_AT_sibling (CU + 0xf01 == 0xf01)
0xf01: DW_TAG_base_type [^^^]
DW_AT_byte_size 0x1
DW_AT_encoding DW_ATE_boolean (2)
DW_AT_name 'bool'
0xf08: DW_TAG_base_type [^^^]
DW_AT_byte_size 0x8
DW_AT_encoding DW_ATE_unsigned (7)
DW_AT_name 'long unsigned int'
0xf0f: DW_TAG_base_type [^^^]
DW_AT_byte_size 0x1
DW_AT_encoding DW_ATE_unsigned_char (8)
DW_AT_name 'unsigned char'
...
並非所有輸出都會顯示。 請注意,例如同層級連結,可點選以允許進一步周遊 DWARF 符號樹狀結構。
!dielocal
找出名為 「name」 之局部變數的 DIE,並執行與在符號上執行 dwarfdump 或 llvm-dwarfdump 類似的 DIE 診斷傾印,並尋找 DIE。
!dielocal [options] <name>
-r#
:以遞歸方式傾印 N 個層級。 一般而言,這是一個,而且只會傾印 DIE 本身。
<name>
:名為 “name” 的局部變數。
範例:
0:000> !dielocal greeting
0x2806: DW_TAG_variable [^^^]
DW_AT_name 'greeting'
DW_AT_decl_file 1 ('/mnt/c/Users/BOB/DisplayGreeting.cpp')
DW_AT_decl_line 0xf
DW_AT_decl_column 0x1d
DW_AT_type (CU + 0xb18 == 0xb18)
DW_AT_location DW_OP_fbreg(-240)