Keyboard Shortcuts

Ctrl+K
Open search
Esc
Close search / dialog
↑
Back to top

Symbol Table

15 minintermediateModule 5: SymbolsContinues from section-vs-segment

Why This Matters

Symbols are how ELF files name their functions and variables. When you call printf(), the linker resolves that name to an address by looking it up in a symbol table. Without symbols, there is no way to reference code or data across compilation units.

Understanding symbol tables lets you debug linking errors, inspect binary contents with nm, and understand how shared libraries export their interfaces.

A Simple Model

A symbol table is a lookup dictionary. Each entry maps a name (like "main" or "printf") to:

The names themselves live in a separate string table (.strtab). Each symbol entry holds an offset into that string table.

The Actual Detail

ELF has two symbol tables:

TableSectionPurposeUsed By
.symtabSHT_SYMTABFull symbol table (all symbols)Static linker, debugger
.dynsymSHT_DYNSYMDynamic symbols (exported/imported only)Dynamic linker (ld.so)

Each entry is an Elf64_Sym structure (24 bytes on 64-bit):

FieldTypeSizeDescription
st_nameElf64_Word4 bytesOffset into string table
st_infounsigned char1 byteBinding (high 4 bits) + Type (low 4 bits)
st_otherunsigned char1 byteVisibility (low 2 bits)
st_shndxElf64_Half2 bytesSection index (0 = undefined)
st_valueElf64_Addr8 bytesSymbol value (address or offset)
st_sizeElf64_Xword8 bytesSymbol size (0 if unknown)

The st_info field packs two values: binding and type. Use ELF64_ST_BIND(info) to extract binding, ELF64_ST_TYPE(info) for type.

A Real Example

$ nm -n /bin/ls | head -20
                 w __cxa_finalize
                 w __gmon_start__
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 w __cxa_atexit
                 U __errno_location
                 U __fpending
                 U __freading
                 U __malloc_hook
                 U __memcpy_chk
                 U __stack_chk_fail
                 U abort
                 U access
                 U backtrace
                 00000000000030a0 T .annobin_... 
                 0000000000003120 T _start
                 0000000000003150 T .annobin_...
                 0000000000003190 T __do_global_dtors_aux

nm -n sorts by address. U means "undefined" (imported from another library). T means "in text section" (defined here).

The .dynsym is a subset — only symbols needed for dynamic linking:

$ readelf -sW /bin/ls | grep FUNC | head -10
     1: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __ctype_b_loc
     2: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND __errno_location
     5: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND free
     8: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND malloc
    15: 0000000000000000     0 FUNC    GLOBAL DEFAULT  UND printf
    3: 0000000000003120   108 FUNC    GLOBAL DEFAULT   13 _start
    4: 0000000000003190   186 FUNC    GLOBAL DEFAULT   13 __do_global_dtors_aux
    6: 0000000000003260  1162 FUNC    GLOBAL DEFAULT   13 main

Try It

# See all symbols in your program
nm -n ./my_program

# Only defined symbols (not imports)
nm --defined-only ./my_program

# Demangle C++ names
nm -C ./my_program_cpp

# See the raw symbol table structure
readelf -sW ./my_program

Check Your Understanding

What is the difference between .symtab and .dynsym?

Apply It

If a symbol has st_shndx = SHN_UNDEF (0), what does that mean?

Connect

Each symbol has a binding (local, global, weak) and a visibility. These determine how the linker resolves symbols across object files — that is what the next two concepts cover.

Recognize the Output

Which type of symbol does this readelf output represent?

    8: 0000000000000000     0 FUNC    WEAK   DEFAULT  UND __cxa_atexit

Recognize the Output

What does this symbol table entry tell you?

   42: 0000000000401130    23 FUNC    GLOBAL DEFAULT  13 main
← Swipe to navigate →