1. Overview
The addr2line command is a tool that converts memory addresses into filenames and line numbers. It helps to debug programs by mapping the memory addresses of the instructions or data to the source code files and lines that generated them. This way, we can more easily locate the source of an error or a function call.
In this tutorial, we’ll learn how to use the addr2line command in Linux.
2. The addr2line Command
The addr2line command is a part of the GNU Binutils package, which is a collection of tools for working with binary files. Moreover, the addr2line command can aid with debugging by showing us where in the source code an address is located or generated.
Further, addr2line translates symbol addresses by reading debugging information from the binary file. Notably, symbols reside in a separate file called a symbol table, which addr2line reads to translate addresses.
In particular, a symbol table maps the names of functions and variables to their addresses. One way to acquire a symbol table is to use the readelf command:
$ readelf -s hello
Symbol table '.symtab' contains 67 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000001000 0 SECTION LOCAL DEFAULT 1
2: 0000000000001028 0 SECTION LOCAL DEFAULT 2
3: 0000000000001040 0 SECTION LOCAL DEFAULT 3
...
63: 0000000000201038 0 NOTYPE GLOBAL DEFAULT 23 _end
64: 0000000000201038 0 NOTYPE GLOBAL DEFAULT 23 __bss_start
65: 0000000000201038 0 OBJECT GLOBAL HIDDEN 23 __TMC_END__
66: 0000000000201038 0 NOTYPE GLOBAL DEFAULT ABS _edata
Normally, readelf displays information about ELF format object files. However, the -s option displays the entries in the symbol table section of the file, if it has one.
Now, let’s look at the syntax and usage of addr2line.
3. Basic Syntax and Usage
The addr2line command follows a general syntax:
$ addr2line [options] [addr...]
In the code snippet above, *[*options] are optional flags which modify the behavior of the command. Meanwhile, [addr…] can consist of one or more addresses to convert.
To use addr2line, we’ll provide two essential pieces of information:
- one or more addresses
- binary file that contains the symbols for those addresses
We can specify the name of the binary file with the -e or —exe=[file] option:
$ addr2line -e hello 0x1129
/home/user/hello.c:4
This tells addr2line to use the hello executable file and translate the address 0x1129 into its source code location.
Of course, we can also provide multiple addresses to addr2line at once:
$ addr2line -e hello 0x1135 0x113a
/home/user/hello.c:5
/home/user/hello.c:6
So, this tells addr2line to translate two addresses into their source code locations.
However, if the address is invalid or doesn’t belong to any executable section of the binary file, the command displays two question marks:
$ addr2line -e hello 0x1234
??:0
This means that addr2line couldn’t find any source code location for the address 0x1234.
In addition, there are some other options that can modify the behavior and output of addr2line:
- -a: shows the address before each translated location
- -f: shows the name of the function containing each location
- -i: shows all source locations corresponding to each address
- -p: makes the output more human-readable by using tabs and colors
- -S: shows both symbol names and source code locations for each address
These options can further make the display of our output more readable.
For example, we can also show only the basename of each file path using the -s option:
$ addr2line -s -e hello 0x1135 0x113a
hello.c:5
hello.c:6
As a result, we only see the filename without the /home/user/ prefix.
4. Understanding Call Stack
Basically, a call stack is a data structure that records the sequence of function calls in a program. It helps to understand how a program works and how it reached a certain point or state.
So, let’s suppose we have a C program called callstack.c that prints some messages and calls some functions:
#include <stdio.h>
void foo(void) {
printf("Inside foo\n");
}
void bar(void) {
printf("Inside bar\n");
foo();
}
void baz(void) {
printf("Inside baz\n");
bar();
}
int main(void) {
printf("Inside main\n");
baz();
return 0;
}
Further, we can compile it with debugging information using the -g option of gcc:
$ gcc -g -o callstack.out callstack.c
This produces a binary file called callstack.out that contains the symbol table.
Next, we’ll find out the addresses of these function calls in the binary file using the objdump command:
$ objdump -d -j .text callstack.out | grep "call.*<"
107f: ff 15 53 2f 00 00 call *0x2f53(%rip) # 3fd8 <__libc_start_main@GLIBC_2.34>
1122: e8 19 ff ff ff call 1040 <__cxa_finalize@plt>
1127: e8 64 ff ff ff call 1090 <deregister_tm_clones>
115b: e8 f0 fe ff ff call 1050 <puts@plt>
1175: e8 d6 fe ff ff call 1050 <puts@plt>
117a: e8 ca ff ff ff call 1149 <foo>
1194: e8 b7 fe ff ff call 1050 <puts@plt>
1199: e8 c5 ff ff ff call 1163 <bar>
11b3: e8 98 fe ff ff call 1050 <puts@plt>
11b8: e8 c5 ff ff ff call 1182 <baz>
In the code snippet above, we can see a couple of options following objdump:
- -d: disassembles the executable sections of the binary file
- -j .text: disassembles only the section named .text of the binary file
Also, the grep command searches for a pattern that matches any line that contains the word call followed by any number of characters and then an opening angle bracket.
As a result, the command displays the lines that call other functions in the output. It also shows the addresses of these instructions and the names of the functions they call.
Now, we can use addr2line to find out their source code locations:
$ addr2line -e callstack.out 0x117a 0x1199 0x11b8
/home/abdulhameed/callstack.c:9
/home/abdulhameed/callstack.c:14
/home/abdulhameed/callstack.c:19
The output indicates that the addresses 0x117a, 0x1199, and 0x11b8 correspond to lines 9, 14, and 19 of the callstack.c file, which are the calls to foo, bar, and baz.
5. Conclusion
In this article, we learned how to use the addr2line command in Linux. We understood its purpose, basic syntax and usage, and some of its supported options. Finally, we used an example of a function call stack to better understand the addr2line command.