Safe Penetration Testing Labs · intermediate · ~15 min

Count global symbols in an ELF .symtab

Walk an ELF64 symbol table and count the entries with global binding.

Overview

Iterate the buffer 24 bytes at a time, read st_info at offset 4, count entries where (st_info >> 4) == 1.

Why it matters

Symbol counts are the first signal you get from a stripped vs. un-stripped binary.

Lesson

Why this matters

Every reverse-engineering tool — nm, objdump, readelf, Ghidra, radare2 — starts by reading the symbol table out of an ELF binary. The on-disk layout is fixed: 24-byte entries, the binding lives in the top nibble of st_info.

We don't need a full ELF reader to make use of .symtab — we just need to count entries with a property.

What an Elf64_Sym looks like

offset  size  field
0       4     st_name
4       1     st_info    <- top 4 bits = binding, low 4 bits = type
5       1     st_other
6       2     st_shndx
8       8     st_value
16      8     st_size

Bindings:

  • 0 LOCAL
  • 1 GLOBAL ← we count these
  • 2 WEAK

So binding = st_info >> 4.

Your job

Implement int count_global_symbols(const uint8_t *buf, size_t n) where buf is the raw .symtab bytes and n is its length. Return the number of GLOBAL-binding entries, or -1 if n is not a multiple of 24 (an incomplete entry is a corrupt symtab).

NULL buf with n == 0 → return 0 (empty is fine). NULL buf with n > 0 → return -1.

Common mistakes

  • Mixing up the binding and the type. Type is the low nibble; binding is the high nibble.
  • Forgetting that n must be a multiple of 24.
  • Walking with a struct * pointer cast — alignment-unsafe on some hosts. Just index buf[i*24 + 4].

What this is NOT

  • A name resolver. We never look at .strtab here.
  • A relocation walker. .rela.dyn is a separate module.

Summary

24-byte stride. High nibble of byte 4. Count when it equals 1.

Practice with these exercises