I'm working with linux executables and was just wondering how it is that section names are resolved to addresses upon disassembly of an ELF.

For example take some random disassembly output from objdump,

  4002b5:       00 00                   add    %al,(%rax)
  4002b7:       00 67 55                add    %ah,0x55(%rdi)
  4002ba:       61                      (bad)  
  4002bb:       10                      .byte 0x10

Disassembly of section .dynsym:

00000000004002c0 <.dynsym>:
  4002d8:       9b                      fwait
  4002d9:       00 00                   add    %al,(%rax)
  4002db:       00 12                   add    %dl,(%rdx)
  4002ed:       00 00                   add    %al,(%rax)
  4002ef:       00 32                   add    %dh,(%rdx)
  4002f1:       00 00                   add    %al,(%rax)
  4002f3:       00 12                   add    %dl,(%rdx)
  400305:       00 00                   add    %al,(%rax)
  400307:       00 17                   add    %dl,(%rdi)

how has it, if from a heuristic perspective, detected that the section at that address is called dynsm? The same goes for .text, .bss, and even functions listed by objdump, such as 'main'.

How would I reproduce this if I were to write a disassembly tool + executable analyser myself?


  • For ELF binary files, the compiler inserts debug information such as function names, source lines, ... using a debug format called DWARF. The disassembler doesn't guess the name, it reads it from the DWARF info associated to an assembly block/section. check out the documentation : dwarfstd.org. Also, the answer below covers only sections - it is incomplete.
    – yaspr
    Nov 24 '17 at 7:17
  • @yaspr this question is specifically about how to retrieve section information. It is not about debugging or functions. DWARF and debug symbols are not relevant in this particular case.
    – julian
    Nov 24 '17 at 17:03
  • @yaspr could you verify whether this debug information is inserted by default? Reading into DWARF, and the GCC compiler, GCC does not do this by default (gcc -o). Nov 24 '17 at 21:08
  • @SYS_V : "... and even functions listed by objdump...", it is somehow relevant but as I said, your answer is thorough and the DWARF is only complementary with some magic.
    – yaspr
    Nov 24 '17 at 21:45
  • @Marius.pharoe DWARF info are not there by default, but they are very helpful (gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html).
    – yaspr
    Nov 24 '17 at 21:46

Note that the in example in the question, data is being disassembled as code. The .dynsym section holds the dynamic linking symbol table and does not contain code.

  • Information such as the name and the virtual address of the first byte of a section is given in the section header of that section.

  • The section header is a structure stored in the section header table of an ELF binary.

In this case objdump prints the virtual address 00000000004002c0 and the name .dynsym because this is the information stored in the section header for that particular section (technically, section names are strings stored in the section header string table .shstrtab, but this is not so important in this case). If there was no section header for this section or if there was no section header table present in the binary, objdump would not be able to output this information.

There is no need for heuristics unless the section header table is not present in the binary. This is unusual, but has been observed to be the case in certain Linux ELF binaries (viruses) in order to thwart analysis since gdb, objdump, and other Binary File Descriptor-based utilities (thank you Igor Skochinsky for the correction) rely on information in the section header table to parse the binary.

Relevant documentation can be found in the System V ABI (generic) beginning in chapter 4 "Object Files" (page 44), as well as the manual page for ELF.

A section header table contains information describing the file’s sections. Every section has an entry in the table; each entry gives information such as the section name, the section size, and so on.

More specifically:

An object file’s section header table lets one locate all the file’s sections. The section header table is an array of Elf32_Shdr or Elf64_Shdr structures. A section header table index is a subscript into this array. The ELF header’s e_shoff member gives the byte offset from the beginning of the file to the section header table; e_shnum tells how many entries the section header table contains; e_shentsize gives the size in bytes of each entry.


Sections contain all information in an object file, except the ELF header, the program header table, and the section header table. Moreover, object files’ sections satisfy several conditions.

  • Every section in an object file has exactly one section header describing it. Section headers may exist that do not have a section.

  • Each section occupies one contiguous (possibly empty) sequence of bytes within a file.

  • Sections in a file may not overlap. No byte in a file resides in more than one section.

  • An object file may have inactive space. The various headers and the sections might not ‘‘cover’’ every byte in an object file. The contents of the inactive data are unspecified.

Here is a high-level overview of what is involved in locating information contained in an ELF binary's individual section headers. The documentation cited is for ELF32 binaries, but the process is the same with ELF64 binaries.

The location of the section header table is given in the ELF header:

ELF header location

The ELF header is a struct and the offset of the section header table is stored in a member called e_shoff:

ELF header structure and members


$ readelf -h /bin/ls
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x404890
  Start of program headers:          64 (bytes into file)
  Start of section headers:          108288 (bytes into file)   <---------- e_shoff
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         9
  Size of section headers:           64 (bytes)
  Number of section headers:         28
  Section header string table index: 27

The section header table is usually located at the end of the binary following all of the sections:

section header table location in binary

As stated in the documentation and mentioned previously, every section has an entry in the section header table. Each entry is a structure that contains information about the section, including its name and its virtual address when the binary is mapped into memory. The name of the section is stored in a struct member called sh_name, and the address information is stored in a struct member called sh_addr:

section header structure



Retrieving this information is just a matter of correctly parsing these structures within a given binary.

More information can be found here:

  • 1
    Thorough explanation yet incomplete given it only covers sections. The DWARF or debug symbols aren't cited and to be honest, that's where the magic happens.
    – yaspr
    Nov 24 '17 at 7:20
  • 2
    in fact, readelf works fine on files without section headers (unlike BFD-based objdump or gdb)
    – Igor Skochinsky
    Nov 24 '17 at 8:43
  • @IgorSkochinsky Thank you, corrected. If there are any other errors, please let me know.
    – julian
    Nov 24 '17 at 17:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.