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 0x117a0x1199, and 0x11b8 correspond to lines 914, and 19 of the callstack.c file, which are the calls to foobar, 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.