llvm-symbolizer - 將位址轉換為原始碼位置

概要

llvm-symbolizer [選項] [位址…]

描述

llvm-symbolizer 從命令列讀取輸入名稱和位址,並將對應的原始碼位置列印到標準輸出。它也可以透過 --filter-markup 來符號化包含 符號器標記 的日誌。位址可以指定為數字或符號名稱。

如果命令列上未指定位址,它會從標準輸入讀取位址。如果命令列上未指定輸入名稱,但有位址,則第一個位址值會被視為輸入名稱。如果輸入值無法識別,它會報告找不到原始碼資訊。

輸入名稱可以與位址一起在標準輸入或作為命令列上的位置引數指定。預設情況下,輸入名稱被解釋為物件檔案路徑。但是,在名稱前加上 BUILDID: 表示它是一個十六進制組建 ID 而不是路徑。這將查找對應的偵錯二進制檔案。為了保持一致性,在名稱前加上 FILE: 明確表示它是一個物件檔案路徑(預設)。

位置引數或標準輸入值可以加上 “DATA” 或 “CODE” 前綴,以指示位址應分別符號化為資料或可執行程式碼。如果兩者都未指定,則假定為 “CODE”。DATA 符號化為位址和符號大小,而不是行號。

llvm-symbolizer 在從命令列解析選項後,從環境變數 LLVM_SYMBOLIZER_OPTS 解析選項。LLVM_SYMBOLIZER_OPTS 主要用於在 llvm-symbolizer 被另一個程式或執行時期調用時補充命令列選項。

範例

以下所有範例都使用以下兩個原始碼檔案作為輸入。它們混合使用 C 風格和 C++ 風格的連結,以說明這些名稱的列印方式有何不同(請參閱 --demangle)。

// test.h
extern "C" inline int foz() {
  return 1234;
}
// test.cpp
#include "test.h"
int bar=42;

int foo() {
  return bar;
}

int baz() {
  volatile int k = 42;
  return foz() + k;
}

int main() {
  return foo() + baz();
}

這些檔案的建置方式如下

$ clang -g test.cpp -o test.elf
$ clang -g -O2 test.cpp -o inlined.elf

範例 1 - 命令列上的位址和物件

$ llvm-symbolizer --obj=test.elf 0x4004d0 0x400490
foz
/tmp/test.h:1:0

baz()
/tmp/test.cpp:11:0

範例 2 - 標準輸入上的位址

$ cat addr.txt
0x4004a0
0x400490
0x4004d0
$ llvm-symbolizer --obj=test.elf < addr.txt
main
/tmp/test.cpp:15:0

baz()
/tmp/test.cpp:11:0

foz
/tmp/./test.h:1:0

範例 3 - 使用位址指定的物件

$ llvm-symbolizer "test.elf 0x400490" "FILE:inlined.elf 0x400480"
baz()
/tmp/test.cpp:11:0

foo()
/tmp/test.cpp:8:10

$ cat addr2.txt
FILE:test.elf 0x4004a0
inlined.elf 0x400480

$ llvm-symbolizer < addr2.txt
main
/tmp/test.cpp:15:0

foo()
/tmp/test.cpp:8:10

範例 4 - BUILDID 和 FILE 前綴

$ llvm-symbolizer "FILE:test.elf 0x400490" "DATA BUILDID:123456789abcdef 0x601028"
baz()
/tmp/test.cpp:11:0

bar
6295592 4

$ cat addr3.txt
FILE:test.elf 0x400490
DATA BUILDID:123456789abcdef 0x601028

$ llvm-symbolizer < addr3.txt
baz()
/tmp/test.cpp:11:0

bar
6295592 4

範例 5 - CODE 和 DATA 前綴

$ llvm-symbolizer --obj=test.elf "CODE 0x400490" "DATA 0x601028"
baz()
/tmp/test.cpp:11:0

bar
6295592 4

$ cat addr4.txt
CODE test.elf 0x4004a0
DATA inlined.elf 0x601028

$ llvm-symbolizer < addr4.txt
main
/tmp/test.cpp:15:0

bar
6295592 4

範例 6 - 路徑風格選項

此範例使用與上述相同的原始碼檔案,但原始碼檔案的完整路徑為 /tmp/foo/test.cpp,並按如下方式編譯。第一種情況顯示預設的絕對路徑,第二種情況顯示 –basenames,第三種情況顯示 –relativenames。

$ pwd
/tmp
$ clang -g foo/test.cpp -o test.elf
$ llvm-symbolizer --obj=test.elf 0x4004a0
main
/tmp/foo/test.cpp:15:0
$ llvm-symbolizer --obj=test.elf 0x4004a0 --basenames
main
test.cpp:15:0
$ llvm-symbolizer --obj=test.elf 0x4004a0 --relativenames
main
foo/test.cpp:15:0

範例 7 - 作為符號名稱的位址

$ llvm-symbolizer --obj=test.elf main
main
/tmp/test.cpp:14:0
$ llvm-symbolizer --obj=test.elf "CODE foz"
foz
/tmp/test.h:1:0

範例 8 - 沒有行對應的位址(與零行關聯的位址)的 --skip-line-zero 輸出

// test.c
int foo = 0;
int x = 1234;
int main() {
  if (x)
    return foo;
  else
    return x;
}

這些檔案的建置方式如下

$ clang -g -O2 -S test.c -o test.s
$ llvm-mc -filetype=obj -triple=x86_64-unknown-linux  test.s -o test.o
$ llvm-symbolizer --obj=test.o --skip-line-zero 0xa
main
/tmp/test.c:5:7 (approximate)

選項

--adjust-vma <offset>

在執行查找時,將指定的偏移量新增至物件檔案位址。這可以用於執行查找,就好像物件已由偏移量重新定位一樣。

--skip-line-zero

如果位址沒有關聯的行號,請使用行表目前序列中的最後一個行號。此類行在輸出中標記為「近似」,因為它們可能具有誤導性。

--basenames, -s

僅列印檔案的名稱,不包含任何目錄,而不是絕對路徑。

--build-id

使用給定的組建 ID 查找物件,組建 ID 指定為十六進制字串。與 --obj 互斥。

--color [=<always|auto|never>]

指定是否在 --filter-markup 模式下使用顏色。預設為 auto,它會偵測標準輸出是否支援顏色。單獨指定 --color 等同於 --color=always

--debug-file-directory <path>

提供包含 .build-id 子目錄的目錄路徑,以搜尋已剝離二進制檔案的偵錯資訊。將按照給定的順序搜尋此引數的多個實例。

--debuginfod, --no-debuginfod

是否嘗試 debuginfod 查找偵錯二進制檔案。除非另有指定,否則僅當 libcurl 已編譯到 (LLVM_ENABLE_CURL) 中,並且環境變數 DEBUGINFOD_URLS 提供了至少一個伺服器 URL 時,才會啟用 debuginfod。

--demangle, -C

如果函數名稱已損壞,則列印還原損壞的函數名稱(例如,損壞的名稱 _Z3bazv 變為 baz(),而非損壞的名稱 foz 則按原樣列印)。預設為 true。

--dwp <path>

對於任何具有分割 DWARF 偵錯資料的 CU,請使用 <path> 中指定的 DWP 檔案。

--fallback-debug-path <path>

當單獨的檔案包含偵錯資料,並由 GNU 偵錯連結區段引用時,如果無法相對於物件找到偵錯資料,請使用指定的路徑作為查找偵錯資料的基礎。

--filter-markup

從標準輸入讀取,將包含的 符號器標記 轉換為人類可讀的形式,並將結果列印到標準輸出。以下標記元素尚不受支援

  • {{{hexdict}}}

  • {{{dumpfile}}}

{{{bt}}} 回溯元素使用以下語法報告框架

#<編號>[.<內聯>] <位址> <函數> <檔案>:<行號>:<列號> (<模組>+<相對 位址>)

<內聯> 為內聯到與 <編號> 對應的呼叫者中的呼叫提供框架編號。內聯呼叫編號從 1 開始,並從被呼叫者增加到呼叫者。

<位址> 是函數呼叫指令內的位址。該位址可能不是指令的開始。<相對 位址> 是載入到該位址的 <模組> 中的對應虛擬偏移量。

--functions [=<none|short|linkage>], -f

指定列印函數名稱的方式(省略函數名稱、列印簡短函數名稱或分別列印完整連結名稱)。預設為 linkage

--help, -h

顯示此命令的說明和用法。

--inlining, --inlines, -i

如果原始碼位置位於內聯函數中,則列印所有內聯框架。這是預設值。

--no-inlines

不要列印內聯框架。

--no-demangle

不要列印還原損壞的函數名稱。

--obj <path>, --exe, -e

要符號化的物件檔案路徑。如果指定 -,則直接從標準輸入流讀取物件。與 --build-id 互斥。

--output-style <LLVM|GNU|JSON>

指定偏好的輸出樣式。預設為 LLVM。當輸出樣式設定為 GNU 時,該工具遵循 GNU 的 addr2line 的樣式。LLVM 樣式的差異在於

  • 不列印原始碼位置的欄。

  • 在位址的報告後,不新增空行。

  • 當未顯示內聯框架時,不會將內聯函數的名稱替換為最頂層呼叫者的名稱。

  • 當位址的偵錯資料鑑別器非零時,會列印它。產生鑑別器的一種方法是使用 clang 的 -fdebug-info-for-profiling 進行編譯。

JSON 樣式以 JSON 格式提供機器可讀的輸出。如果位址是

透過 stdin 提供,則輸出 JSON 將是一系列單獨的物件。否則,所有結果都將包含在單個陣列中。

$ llvm-symbolizer --obj=inlined.elf 0x4004be 0x400486 -p
baz() at /tmp/test.cpp:11:18
 (inlined by) main at /tmp/test.cpp:15:0

foo() at /tmp/test.cpp:6:3

$ llvm-symbolizer --output-style=LLVM --obj=inlined.elf 0x4004be 0x400486 -p --no-inlines
main at /tmp/test.cpp:11:18

foo() at /tmp/test.cpp:6:3

$ llvm-symbolizer --output-style=GNU --obj=inlined.elf 0x4004be 0x400486 -p --no-inlines
baz() at /tmp/test.cpp:11
foo() at /tmp/test.cpp:6

$ clang -g -fdebug-info-for-profiling test.cpp -o profiling.elf
$ llvm-symbolizer --output-style=GNU --obj=profiling.elf 0x401167 -p --no-inlines
main at /tmp/test.cpp:15 (discriminator 2)

$ llvm-symbolizer --output-style=JSON --obj=inlined.elf 0x4004be 0x400486 -p
[
  {
    "Address": "0x4004be",
    "ModuleName": "inlined.elf",
    "Symbol": [
      {
        "Column": 18,
        "Discriminator": 0,
        "FileName": "/tmp/test.cpp",
        "FunctionName": "baz()",
        "Line": 11,
        "StartAddress": "0x4004be",
        "StartFileName": "/tmp/test.cpp",
        "StartLine": 9
      },
      {
        "Column": 0,
        "Discriminator": 0,
        "FileName": "/tmp/test.cpp",
        "FunctionName": "main",
        "Line": 15,
        "StartAddress": "0x4004be",
        "StartFileName": "/tmp/test.cpp",
        "StartLine": 14
      }
    ]
  },
  {
    "Address": "0x400486",
    "ModuleName": "inlined.elf",
    "Symbol": [
      {
        "Column": 3,
        "Discriminator": 0,
        "FileName": "/tmp/test.cpp",
        "FunctionName": "foo()",
        "Line": 6,
        "StartAddress": "0x400486",
        "StartFileName": "/tmp/test.cpp",
        "StartLine": 5
      }
    ]
  }
]
--pretty-print, -p

列印人類可讀的輸出。如果指定了 --inlining,則封閉範圍會以 (inlined by) 作為前綴。對於 JSON 輸出,此選項將導致 JSON 縮排並在新行上分割。否則,JSON 輸出將以緊湊形式列印。

$ llvm-symbolizer --obj=inlined.elf 0x4004be --inlining --pretty-print
baz() at /tmp/test.cpp:11:18
 (inlined by) main at /tmp/test.cpp:15:0
--print-address, --addresses, -a

在原始碼位置之前列印位址。預設為 false。

$ llvm-symbolizer --obj=inlined.elf --print-address 0x4004be
0x4004be
baz()
/tmp/test.cpp:11:18
main
/tmp/test.cpp:15:0

$ llvm-symbolizer --obj=inlined.elf 0x4004be --pretty-print --print-address
0x4004be: baz() at /tmp/test.cpp:11:18
 (inlined by) main at /tmp/test.cpp:15:0
--print-source-context-lines <N>

為每個符號化的位址列印 N 行原始碼上下文。

$ llvm-symbolizer --obj=test.elf 0x400490 --print-source-context-lines=3
baz()
/tmp/test.cpp:11:0
10  :   volatile int k = 42;
11 >:   return foz() + k;
12  : }
--relativenames

列印相對於編譯目錄的檔案路徑,而不是絕對路徑。如果編譯器的命令列包含完整路徑,則這將與預設值相同。

--verbose

列印詳細的位址、行和欄資訊。

$ llvm-symbolizer --obj=inlined.elf --verbose 0x4004be
baz()
  Filename: /tmp/test.cpp
  Function start filename: /tmp/test.cpp
  Function start line: 9
  Function start address: 0x4004b6
  Line: 11
  Column: 18
main
  Filename: /tmp/test.cpp
  Function start filename: /tmp/test.cpp
  Function start line: 14
  Function start address: 0x4004b0
  Line: 15
  Column: 18
--version, -v

列印工具的版本資訊。

@<FILE>

從回應檔案 <FILE> 讀取命令列選項。

WINDOWS/PDB 特定選項

--dia

使用 Windows DIA SDK 進行符號化。如果找不到 DIA SDK,llvm-symbolizer 將回退到原生實作。

MACH-O 特定選項

--default-arch <arch>

如果二進制檔案包含多個架構的物件檔案(例如,它是 Mach-O 通用二進制檔案),則符號化給定架構的物件檔案。您也可以透過在輸入中寫入 binary_name:arch_name 來指定架構(請參閱以下範例)。如果未以任何一種方式指定架構,則不會符號化該位址。預設為空字串。

$ cat addr.txt
/tmp/mach_universal_binary:i386 0x1f84
/tmp/mach_universal_binary:x86_64 0x100000f24

$ llvm-symbolizer < addr.txt
_main
/tmp/source_i386.cc:8

_main
/tmp/source_x86_64.cc:8
--dsym-hint <path/to/file.dSYM>

如果二進制檔案的偵錯資訊不存在於預設位置,請在此選項提供的 .dSYM 路徑中查找偵錯資訊。此標誌可以多次使用。

結束狀態

llvm-symbolizer 返回 0。其他結束代碼表示內部程式錯誤。

參見

llvm-addr2line(1)