Contained in bytes 0x40 to 0x7F.
If the number of sections is greater than or equal to SHN_LORESERVE (0xff00), e_shnum has the value SHN_UNDEF (0) and the actual number of section header table entries is contained in the sh_size field of the section header at index 0 (otherwise, the sh_size member of the initial entry contains 0).
There are also other magic sections detailed in Figure 4-7: Special Section Indexes.
Sections with sh_type == SHT_STRTAB are called string tables.
They hold a null separated array of strings.
Such sections are used by other sections when string names are to be used. The using section says:
  • which string table they are using
  • what is the index on the target string table where the string starts
So for example, we could have a string table containing:
Data: \0 a b c \0 d e f \0
Index: 0 1 2 3  4 5 6 7  8
The first byte must be a 0. TODO rationale?
And if another section wants to use the string d e f, they have to point to index 5 of this section (letter d).
Notable string table sections:
  • .shstrtab
  • .strtab
Section type: sh_type == SHT_SYMTAB.
Common name: "symbol table".
First the we note that:
  • sh_link = 5
  • sh_info = 6
For SHT_SYMTAB sections, those numbers mean that:
  • strings that give symbol names are in section 5, .strtab
  • the relocation data is in section 6, .rela.text
A good high level tool to disassemble that section is:
nm hello_world.o
which gives:
0000000000000000 T _start
0000000000000000 d hello_world
000000000000000d a hello_world_len
This is however a high level view that omits some types of symbols and in which the symbol types . A more detailed disassembly can be obtained with:
readelf -s hello_world.o
which gives:
Symbol table '.symtab' contains 7 entries:
   Num:    Value          Size Type    Bind   Vis      Ndx Name
     0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND
     1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS hello_world.asm
     2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
     3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2
     4: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT    1 hello_world
     5: 000000000000000d     0 NOTYPE  LOCAL  DEFAULT  ABS hello_world_len
     6: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT    2 _start
The binary format of the table is documented at www.sco.com/developers/gabi/2003-12-17/ch4.symtab.html
The data is:
readelf -x .symtab hello_world.o
which gives:
Hex dump of section '.symtab':
  0x00000000 00000000 00000000 00000000 00000000 ................
  0x00000010 00000000 00000000 01000000 0400f1ff ................
  0x00000020 00000000 00000000 00000000 00000000 ................
  0x00000030 00000000 03000100 00000000 00000000 ................
  0x00000040 00000000 00000000 00000000 03000200 ................
  0x00000050 00000000 00000000 00000000 00000000 ................
  0x00000060 11000000 00000100 00000000 00000000 ................
  0x00000070 00000000 00000000 1d000000 0000f1ff ................
  0x00000080 0d000000 00000000 00000000 00000000 ................
  0x00000090 2d000000 10000200 00000000 00000000 -...............
  0x000000a0 00000000 00000000                   ........
The entries are of type:
typedef struct {
    Elf64_Word  st_name;
    unsigned char   st_info;
    unsigned char   st_other;
    Elf64_Half  st_shndx;
    Elf64_Addr  st_value;
    Elf64_Xword st_size;
} Elf64_Sym;
Like in the section table, the first entry is magical and set to a fixed meaningless values.
Entry 1 has ELF64_R_TYPE == STT_FILE. ELF64_R_TYPE is continued inside of st_info.
  • 10 8: st_name = 01000000 = character 1 in the .strtab, which until the following \0 makes hello_world.asm
    This piece of information file may be used by the linker to decide on which segment sections go: e.g. in ld linker script we write:
    segment_name :
    {
        file(section)
    }
    to pick a section from a given file.
    Most of the time however, we will just dump all sections with a given name together with:
    segment_name :
    {
        *(section)
    }
  • 10 12: st_info = 04
    Bits 0-3 = ELF64_R_TYPE = Type = 4 = STT_FILE: the main purpose of this entry is to use st_name to indicate the name of the file which generated this object file.
    Bits 4-7 = ELF64_ST_BIND = Binding = 0 = STB_LOCAL. Required value for STT_FILE.
  • 10 13: st_shndx = Symbol Table Section header Index = f1ff = SHN_ABS. Required for STT_FILE.
  • 20 0: st_value = 8x 00: required for value for STT_FILE
  • 20 8: st_size = 8x 00: no allocated size
Now from the readelf, we interpret the others quickly.
There are two such entries, one pointing to .data and the other to .text (section indexes 1 and 2).
Num:    Value          Size Type    Bind   Vis      Ndx Name
  2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1
  3: 0000000000000000     0 SECTION LOCAL  DEFAULT    2
TODO what is their purpose?
By default, NASM places a .symtab on the executable as well.
This is only used for debugging. Without the symbols, we are completely blind, and must reverse engineer everything.
You can strip it with objcopy, and the executable will still run. Such executables are called "stripped executables".
This program did not have certain dynamic linking related sections because we linked it minimally with ld.
However, if you compile a C hello world with GCC 8.2:
gcc -o main.out main.c
some other interesting sections would appear.
Only appears in the executable.
Contains information of how the executable should be put into the process virtual memory.
The executable is generated from object files by the linker. The main jobs that the linker does are:
  • determine which sections of the object files will go into which segments of the executable.
    In Binutils, this comes down to parsing a linker script, and dealing with a bunch of defaults.
    You can get the linker script used with ld --verbose, and set a custom one with ld -T.
  • do relocation according to the .rela.text section. This depends on how the multiple sections are put into memory.
readelf -l hello_world.out gives:
Elf file type is EXEC (Executable file)
Entry point 0x4000b0
There are 2 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x00000000000000d7 0x00000000000000d7  R E    200000
  LOAD           0x00000000000000d8 0x00000000006000d8 0x00000000006000d8
                 0x000000000000000d 0x000000000000000d  RW     200000

 Section to Segment mapping:
  Segment Sections...
   00     .text
   01     .data
On the ELF header, e_phoff, e_phnum and e_phentsize told us that there are 2 program headers, which start at 0x40 and are 0x38 bytes long each, so they are:
00000040  01 00 00 00 05 00 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 40 00 00 00 00 00  00 00 40 00 00 00 00 00  |..@.......@.....|
00000060  d7 00 00 00 00 00 00 00  d7 00 00 00 00 00 00 00  |................|
00000070  00 00 20 00 00 00 00 00                           |.. .....        |
and:
00000070                           01 00 00 00 06 00 00 00  |        ........|
00000080  d8 00 00 00 00 00 00 00  d8 00 60 00 00 00 00 00  |..........`.....|
00000090  d8 00 60 00 00 00 00 00  0d 00 00 00 00 00 00 00  |..`.............|
000000a0  0d 00 00 00 00 00 00 00  00 00 20 00 00 00 00 00  |.......... .....|
Structure represented www.sco.com/developers/gabi/2003-12-17/ch5.pheader.html:
typedef struct {
    Elf64_Word  p_type;
    Elf64_Word  p_flags;
    Elf64_Off   p_offset;
    Elf64_Addr  p_vaddr;
    Elf64_Addr  p_paddr;
    Elf64_Xword p_filesz;
    Elf64_Xword p_memsz;
    Elf64_Xword p_align;
} Elf64_Phdr;
Breakdown of the first one:
  • 40 0: p_type = 01 00 00 00 = PT_LOAD: this is a regular segment that will get loaded in memory.
  • 40 4: p_flags = 05 00 00 00 = execute and read permissions. No write: we cannot modify the text segment. A classic way to do this in C is with string literals: stackoverflow.com/a/30662565/895245 This allows kernels to do certain optimizations, like sharing the segment amongst processes.
  • 40 8: p_offset = 8x 00 TODO: what is this? Standard says:
    This member gives the offset from the beginning of the file at which the first byte of the segment resides.
    But it looks like offsets from the beginning of segments, not file?
  • 50 0: p_vaddr = 00 00 40 00 00 00 00 00: initial virtual memory address to load this segment to
  • 50 8: p_paddr = 00 00 40 00 00 00 00 00: unspecified effect. Intended for systems in which physical addressing matters. TODO example?
  • 60 0: p_filesz = d7 00 00 00 00 00 00 00: size that the segment occupies in memory. If smaller than p_memsz, the OS fills it with zeroes to fit when loading the program. This is how BSS data is implemented to save space on executable files. i368 ABI says on PT_LOAD:
    The bytes from the file are mapped to the beginning of the memory segment. If the segment’s memory size (p_memsz) is larger than the file size (p_filesz), the ‘‘extra’’ bytes are defined to hold the value 0 and to follow the segment’s initialized area. The file size may not be larger than the memory size.
  • 60 8: p_memsz = d7 00 00 00 00 00 00 00: size that the segment occupies in memory
  • 70 0: p_align = 00 00 20 00 00 00 00 00: 0 or 1 mean no alignment required. TODO why is this required? Why not just use p_addr directly, and get that right? Docs also say:
    p_vaddr should equal p_offset, modulo p_align
The second segment (.data) is analogous. TODO: why use offset 0x0000d8 and address 0x00000000006000d8? Why not just use 0 and 0x00000000006000d8?
Then the:
 Section to Segment mapping:
section of the readelf tells us that:
  • 0 is the .text segment. Aha, so this is why it is executable, and not writable
  • 1 is the .data segment.
User mode emulation by Ciro Santilli 40 Updated 2025-07-16
User mode emulation refers to the ability of certain emulators to emulate userland code running on top of a specific operating system, usually Linux.
For example, QEMU allows you to run a variety of userland ELF programs directly on it, without an underlying Linux kernel running.
User mode emulation is achieved by implementing system calls and special filesystems such as /dev manually on the emulator one by one.
The general tradeoff is that simulation is less acurate as it may lack certain highly advanced kernel functionality you haven't implemented yet. But it is much easier to run executables with it, and you don't have to wait for boot to finish before running, you just run executables directly from the command line.
Matrix multiplication by Ciro Santilli 40 Updated 2025-07-16
Since a matrix can be seen as a linear map , the product of two matrices can be seen as the composition of two linear maps:
One cool thing about linear functions is that we can easily pre-calculate this product only once to obtain a new matrix, and so we don't have to do both multiplications separately each time.
Notable mentions:
Other notable people that are likely also awesome but Ciro has less familiarity with their contributions:
Terminal emulator by Ciro Santilli 40 Updated 2025-07-16
Once upon a time young Ciro Santilli spent lots of time evaluating the features of different terimnals. The many windows of Terminator. The pop-uppiness of Guake/Yakuake.
But then one day he met tmux, and he was enlightened
Terminal choice doesn't matter. Just use tmux.
From this we see that there is a convention of naming promoters as protein name + p, e.g. the first gene in E. Coli K-12 MG1655 promoter thrLp encodes protein thrL.
It is also possible to add numbers after the p, e.g. at biocyc.org/ECOLI/NEW-IMAGE?type=OPERON&object=PM0-45989 we see that the protein zur has two promoters:
  • zurp6
  • zurp7
TODO why 6 and 7? There don't appear to be 1, 2, etc.

Pinned article: Introduction to the OurBigBook Project

Welcome to the OurBigBook Project! Our goal is to create the perfect publishing platform for STEM subjects, and get university-level students to write the best free STEM tutorials ever.
Everyone is welcome to create an account and play with the site: ourbigbook.com/go/register. We belive that students themselves can write amazing tutorials, but teachers are welcome too. You can write about anything you want, it doesn't have to be STEM or even educational. Silly test content is very welcome and you won't be penalized in any way. Just keep it legal!
We have two killer features:
  1. topics: topics group articles by different users with the same title, e.g. here is the topic for the "Fundamental Theorem of Calculus" ourbigbook.com/go/topic/fundamental-theorem-of-calculus
    Articles of different users are sorted by upvote within each article page. This feature is a bit like:
    • a Wikipedia where each user can have their own version of each article
    • a Q&A website like Stack Overflow, where multiple people can give their views on a given topic, and the best ones are sorted by upvote. Except you don't need to wait for someone to ask first, and any topic goes, no matter how narrow or broad
    This feature makes it possible for readers to find better explanations of any topic created by other writers. And it allows writers to create an explanation in a place that readers might actually find it.
    Figure 1.
    Screenshot of the "Derivative" topic page
    . View it live at: ourbigbook.com/go/topic/derivative
  2. local editing: you can store all your personal knowledge base content locally in a plaintext markup format that can be edited locally and published either:
    This way you can be sure that even if OurBigBook.com were to go down one day (which we have no plans to do as it is quite cheap to host!), your content will still be perfectly readable as a static site.
    Figure 2.
    You can publish local OurBigBook lightweight markup files to either https://OurBigBook.com or as a static website
    .
    Figure 3.
    Visual Studio Code extension installation
    .
    Figure 4.
    Visual Studio Code extension tree navigation
    .
    Figure 5.
    Web editor
    . You can also edit articles on the Web editor without installing anything locally.
    Video 3.
    Edit locally and publish demo
    . Source. This shows editing OurBigBook Markup and publishing it using the Visual Studio Code extension.
    Video 4.
    OurBigBook Visual Studio Code extension editing and navigation demo
    . Source.
  3. https://raw.githubusercontent.com/ourbigbook/ourbigbook-media/master/feature/x/hilbert-space-arrow.png
  4. Infinitely deep tables of contents:
    Figure 6.
    Dynamic article tree with infinitely deep table of contents
    .
    Descendant pages can also show up as toplevel e.g.: ourbigbook.com/cirosantilli/chordate-subclade
All our software is open source and hosted at: github.com/ourbigbook/ourbigbook
Further documentation can be found at: docs.ourbigbook.com
Feel free to reach our to us for any help or suggestions: docs.ourbigbook.com/#contact