Skip to content

Inspecting Object Files

This page contains some tips I've found helpful occasionally when inspecting various attributes of binary files.

Determining the Interpreter

All programs on Linux have an interpreter, that is, a program responsible for executing the program itself. For most dynamically-linked executables, this is ld, the GNU dynamic linker. The filesystem path of the program's interpreter is stored in the .interp section in ELF binaries, and can be extracted using readelf:

  [~]$ readelf -p .interp /bin/ls

  String dump of section '.interp':
    [     0]  /lib64/ld-linux-x86-64.so.2

So for the executable ELF file /bin/ls, the interpreter on my system is /lib64/ld-linux-x86-64.so.2.

Disassembling a Single Function

Sometimes, it's useful to examine the generated assembly of your program. To do this, first dump the symbol table to get a list of all the symbols in the binary. Symbols are labelled references to interesting locations in the file containing code or data. There is a rough mapping between functions and global data in a C program to symbols in an ELF symbol table. Note that there are different flags to dump the static or dynamic symbol tables::

  [~]$ objdump -t <staticExecutable>
  [~]$ objdump -T <dynamicExecutable>

Note also that objdump is a part of your compiler toolchain, so in a cross environment, where arm-none-eabi is my compiler toolchain, I would use arm-none-eabi-objdump.

To obtain the assembly for a symbol:

  [~]$ objdump --disassemble=<symbolName> <staticExecutable>

Listing Shared Object Dependencies

For native ELF files, this can be done using ldd:

[edtwardy@hackbook]$ file /usr/bin/ls
/usr/bin/ls: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, for GNU/Linux 3.7.0, stripped
[edtwardy@hackbook]$ ldd /usr/bin/ls
    linux-vdso.so.1 (0x0000ffff97318000)
    libc.so.6 => /usr/lib64/libc.so.6 (0x0000ffff970c0000)
    /lib/ld-linux-aarch64.so.1 => /lib64/ld-linux-aarch64.so.1 (0x0000ffff972e0000)

However, for ELF files compiled for non-native targets, ldd may simply report that the ELF file is "not a dynamic executable". Instead, we can use readelf:

[edtwardy@hackbook]$ file bazel-bin/init
bazel-bin/init: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /nix/store/8jjax8ch07g90axrx64ncfrqkpahn2wn-glibc-armv7l-unknown-linux-gnueabihf-2.38-27/lib/ld-linux-armhf.so.3, for GNU/Linux 3.10.0, not stripped
[edtwardy@hackbook]$ readelf -d bazel-bin/init

Dynamic section at offset 0xeac contains 31 entries:
  Tag        Type                         Name/Value
 0x00000001 (NEEDED)                     Shared library: [libstdc++.so.6]
 0x00000001 (NEEDED)                     Shared library: [libm.so.6]
 0x00000001 (NEEDED)                     Shared library: [libgcc_s.so.1]
 0x00000001 (NEEDED)                     Shared library: [libc.so.6]
 0x0000001d (RUNPATH)                    Library runpath: [/nix/store/8jjax8ch07g90axrx64ncfrqkpahn2wn-glibc-armv7l-unknown-linux-gnueabihf-2.38-27/lib:/nix/store/p9ainll0v4yrf6y89ks17wapwl4hyb07-armv7l-unknown-linux-gnueabihf-gcc-12.3.0-lib/armv7l-unknown-linux-gnueabihf/lib]
 0x0000000c (INIT)                       0x1063c
 0x0000000d (FINI)                       0x10884
 0x00000019 (INIT_ARRAY)                 0x11ea0
 0x0000001b (INIT_ARRAYSZ)               8 (bytes)
 0x0000001a (FINI_ARRAY)                 0x11ea8
 0x0000001c (FINI_ARRAYSZ)               4 (bytes)
 0x00000004 (HASH)                       0x101e8
 0x6ffffef5 (GNU_HASH)                   0x1022c
 0x00000005 (STRTAB)                     0x10320
 0x00000006 (SYMTAB)                     0x10260
 0x0000000a (STRSZ)                      571 (bytes)
 0x0000000b (SYMENT)                     16 (bytes)
 0x00000015 (DEBUG)                      0x0
 0x00000003 (PLTGOT)                     0x11fcc
 0x00000002 (PLTRELSZ)                   48 (bytes)
 0x00000014 (PLTREL)                     REL
 0x00000017 (JMPREL)                     0x1060c
 0x00000011 (REL)                        0x105f4
 0x00000012 (RELSZ)                      24 (bytes)
 0x00000013 (RELENT)                     8 (bytes)
 0x0000001e (FLAGS)                      BIND_NOW
 0x6ffffffb (FLAGS_1)                    Flags: NOW
 0x6ffffffe (VERNEED)                    0x10574
 0x6fffffff (VERNEEDNUM)                 3
 0x6ffffff0 (VERSYM)                     0x1055c
 0x00000000 (NULL)                       0x0

Mutating ELF Files

The NixOS folks (of all people--no surprise) have an interesting project for performing various mutations on ELF files, called patchelf.

Inspecting Section Headers

Sometimes it's helpful to take a look at the section headers in an ELF file-- for example, to inspect the load memory addresss (LMA) of your .text section, etc. This can be done with objdump. Note that unlike some other tools, objdump works on ELF files compiled for any architecture. And, of course, it works on both dynamic and static executables.

[edtwardy@hackbook]$ objdump -h bazel-bin/app/hello-world

bazel-bin/app/hello-world:     file format elf32-littlearm

Sections:
Idx Name          Size      VMA       LMA       File off  Algn
  0 .isr_vector   00000298  08000000  08000000  00001000  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  1 .text         00001cb4  08000298  08000298  00001298  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .startup_copro_fw.Reset_Handler 00000050  08001f4c  08001f4c  00002f4c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  3 .rodata       00000048  08001f9c  08001f9c  00002f9c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .ARM.extab    00000000  08001fe4  08001fe4  00002fe4  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  5 .ARM          00000008  08001fe4  08001fe4  00002fe4  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  6 .preinit_array 00000000  08001fec  08001fec  00003558  2**0
                  CONTENTS, ALLOC, LOAD, DATA
  7 .init_array   00000008  08001fec  08001fec  00002fec  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  8 .fini_array   00000004  08001ff4  08001ff4  00002ff4  2**2
                  CONTENTS, ALLOC, LOAD, DATA
  9 .data         00000558  20000000  08001ff8  00003000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
 10 .bss          0000034c  20000558  08002550  00003558  2**2
                  ALLOC
 11 ._user_heap_stack 00000604  200008a4  08002550  000038a4  2**0
                  ALLOC
 12 .ARM.attributes 0000002e  00000000  00000000  00003558  2**0
                  CONTENTS, READONLY
 13 .comment      00000012  00000000  00000000  00003586  2**0
                  CONTENTS, READONLY