LLVM-SYMBOLIZER(1)

πŸ“¦
version 16.0.6 Β· 3 versions
, in ubuntu noble

πŸ’– NAME πŸ’–

llvm-symbolizer β€” bestie, it converts addresses into source code locations. That's the whole vibe. πŸ‘‘


✨ SYNOPSIS ✨

llvm-symbolizer [options] [addresses…]

πŸ¦‹ DESCRIPTION πŸ¦‹

Okay so here's the tea, bestie: llvm-symbolizer reads input names and addresses straight off the command line and prints the matching source code locations to standard output. It can also symbolize logs that contain Symbolizer Markup, but only if you hit it with --filter-markup. Lowkey iconic. ✨

If you don't hand it any address on the command line, it just reads the addresses from standard input instead. And if you didn't give it an input name on the command line but you did give it addresses β€” or honestly if at any point it just doesn't recognize an input value β€” it simply echoes that input to the output. No drama, no cap. 🫢

Input names can come along with the addresses, either on standard input or as positional arguments on the command line. By default, input names get interpreted as object file paths. But here's the glow-up: if you prefix a name with BUILDID:, that says "hey this is a hex build ID, not a path," and it'll go look up the corresponding debug binary for you. And for consistency, prefixing a name with FILE: explicitly states that it's an object file path β€” which is literally the default anyway. πŸ’…

A positional argument or standard input value can also be preceded by DATA or CODE to say whether that address should be symbolized as data or as executable code. If you don't specify either one, CODE is just assumed, ngl. And heads up: DATA gets symbolized as address and symbol size rather than line number. πŸŽ€

Also! llvm-symbolizer parses options from the environment variable LLVM_SYMBOLIZER_OPTS after it parses options from the command line. This is mainly useful for supplementing the command-line options when llvm-symbolizer is being invoked by another program or runtime. iykyk. 🌸


πŸ”₯ EXAMPLES πŸ”₯

Every single example below uses these two source files as input. They mix C-style and C++-style linkage on purpose, to show how those names get printed differently β€” peep --demangle for that. 😌

// 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();
}

And these files get built like this:

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

🌈 Example 1 β€” addresses and object on command-line

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

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

🌈 Example 2 β€” addresses on standard input

$ 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

🌈 Example 3 β€” object specified with address

$ 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

🌈 Example 4 β€” BUILDID and FILE prefixes

$ 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

🌈 Example 5 β€” CODE and DATA prefixes

$ 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

🌈 Example 6 β€” path-style options

This one uses the same source file as above, but the source file's full path is /tmp/foo/test.cpp, and it gets compiled like so. The first case shows the default absolute path, the second shows --basenames, and the third shows --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

🌈 OPTIONS 🌈

okay bestie, buckle UP, this is the flag menu and it is long. every single one of them is here, no skips, no shortcuts, i promise. πŸ’–

  • --adjust-vma <offset> β€” adds that offset to object file addresses while it's doing lookups. so it's basically "pretend the object got relocated by this much" energy. ✨
  • --basenames, -s β€” prints just the file's name, no directories, instead of the full absolute path. short and sweet, lowkey iconic. πŸŽ€
  • --build-id β€” looks up the object using the build ID you give it, written as a hex string. mutually exclusive with --obj, so pick a lane bestie, you can't have both. πŸ’…
  • --color [=<always|auto|never>] β€” tells it whether to use color in --filter-markup mode. defaults to auto, which sniffs out whether standard output can even do color. and if you just write --color with nothing else? that's the same as --color=always. slay. πŸ¦‹
  • --debug-file-directory <path> β€” hands it a path to a directory that has a .build-id subdirectory, so it can go hunting for debug info for stripped binaries. pass this more than once and it searches them in the order given. 🧚
  • --debuginfod, --no-debuginfod β€” whether to try debuginfod lookups for debug binaries or not. unless you say otherwise, debuginfod is only on if libcurl was compiled in (LLVM_ENABLE_CURL) and at least one server URL came from the environment variable DEBUGINFOD_URLS. 🌸
  • --demangle, -C β€” prints demangled function names when the names are mangled. like the mangled name _Z3bazv turns into baz(), but the already-non-mangled foz just gets printed as it is. defaults to true. πŸ’œ
  • --dwp <path> β€” use the DWP file sitting at <path> for any CUs that have split DWARF debug data.
  • --fallback-debug-path <path> β€” when a separate file holds the debug data and it's referenced by a GNU debug link section, use this path as the base for finding that debug data if it can't be found relative to the object. the backup plan, fr fr. 🫢
  • --filter-markup β€” reads from standard input, turns the Symbolizer Markup it finds into human-readable form, and prints the results to standard output. these markup elements are not yet supported, so don't come for me:
    • {{{hexdict}}}
    • {{{dumpfile}}}

the {{{bt}}} backtrace element reports frames with this syntax:

#<number>[.<inline>] <address> <function> <file>:<line>:<col>
(<module>+<relative address>)

<inline> gives you the frame numbers for calls inlined into the caller corresponding to <number>. those inlined call numbers start at 1 and go up from callee to caller. <address> is an address inside the call instruction to the function β€” and heads up, it might not be the start of that instruction. <relative address> is the matching virtual offset in the <module> loaded at that address. 😭 (so much detail, but you asked)

  • --functions [=<none|short|linkage>], -f β€” picks how function names get printed: no function name at all, short function name, or the full linkage name. defaults to linkage. πŸ‘‘
  • --help, -h β€” shows help and usage for this command. the classic.
  • --inlining, --inlines, -i β€” if a source code location is inside an inlined function, it prints all the inlined frames. this is the default, bestie. πŸŽ€
  • --no-inlines β€” doesn't print inlined frames. the opposite vibe.
  • --no-demangle β€” doesn't print demangled function names.
  • --obj <path>, --exe, -e β€” path to the object file you want symbolized. if you pass -, it reads the object straight from the standard input stream. mutually exclusive with --build-id. πŸ”₯
  • --output-style <LLVM|GNU|JSON> β€” sets the preferred output style, defaults to LLVM. when it's GNU, the tool follows GNU's addr2line style. the differences from LLVM style are:
    • it does not print the column of a source code location.
    • it does not add an empty line after the report for an address.
    • it does not replace the name of an inlined function with the name of the topmost caller when inlined frames aren't shown.
    • it prints an address's debug-data discriminator when it's non-zero. one way to produce discriminators is compiling with clang's -fdebug-info-for-profiling.

JSON style gives you machine readable output in JSON. if addresses are supplied via stdin, the output JSON will be a series of individual objects. otherwise, all results will be contained in a single array. πŸ’…

$ 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 β€” prints human readable output. if --inlining is on, the enclosing scope gets prefixed with (inlined by). for JSON output, this option makes the JSON indented and split across new lines β€” otherwise the JSON comes out all compact and squished. πŸ“
$ 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 β€” prints the address before the source code location. defaults to 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> β€” prints N lines of source context for each symbolized address. peak "let me see the receipts" behavior. πŸ”₯
$ 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 β€” prints the file's path relative to the compilation directory instead of the absolute path. but ngl, if the compiler's command-line already had the full path, this ends up being the same as the default. πŸ™ˆ
  • --verbose β€” prints verbose address, line and column information. all the tea. β˜•
$ 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 β€” prints version information for the tool. ✨
  • @<FILE> β€” reads command-line options from a response file <FILE>. fancy.

πŸͺŸ WINDOWS/PDB SPECIFIC OPTIONS πŸͺŸ

  • --dia β€” uses the Windows DIA SDK for symbolization. and if the DIA SDK isn't found? llvm-symbolizer falls back to the native implementation. no drama. πŸ’€

🍎 MACH-O SPECIFIC OPTIONS 🍎

  • --default-arch <arch> β€” if a binary contains object files for multiple architectures (say, it's a Mach-O universal binary), this symbolizes the object file for the architecture you name. you can also specify the architecture by writing binary_name:arch_name in the input (peep the example below). and if the architecture isn't specified either way, the address just won't be symbolized. defaults to empty string. 😀
$ 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> β€” if the debug info for a binary isn't in the default location, go look for it at the .dSYM path you hand over with this option. you can use this flag multiple times, stack 'em up. 🫢

πŸšͺ EXIT STATUS πŸšͺ

llvm-symbolizer returns 0. any other exit code means there's an internal program error. so if you see anything that isn't 0, something went wrong inside and it's not your fault bestie. 😭


πŸ‘€ SEE ALSO πŸ‘€


✍️ AUTHOR ✍️

Maintained by the LLVM Team (https://llvm.org/).


2003-2024, LLVM Project


and that's the whole page, bestie, you made it to the end πŸŽ‰ this one's from the LLVM Project, version 15, dated 2024-04-14 β€” go forth and symbolize, you absolute icon πŸ‘‘πŸ’–

πŸ“¦ where this page comes from