# ELF Inspector

> Open an ELF binary in your browser: header, program headers, sections, symbols, relocations and notes, with hex highlighting. Nothing is uploaded.

- URL: https://bytescope.dev/en/elf-inspector/
- Locale: en · Also: [ja](https://bytescope.dev/ja/elf-inspector.md) · [zh-Hant](https://bytescope.dev/zh-Hant/elf-inspector.md)
- Runs: Your files never leave your browser — all processing is local.
- Requires: Any modern browser (Chrome, Edge, Firefox, Safari), desktop and mobile.
- Updated: 2026-09-08

## What it does

Drop an ELF here — an executable, a `.so`, a kernel module, a `.o`, a CTF binary, a firmware image — and you get what `readelf -a` would tell you, in two panes wired together. On the left the structure tree: the ELF header field by field, the program headers, the section table, `.dynsym` and `.symtab` kept separate, `.dynamic`, every relocation section, the notes and the version definitions and requirements. On the right the file's bytes. Click any row and exactly the bytes it was decoded from light up; click a byte and the innermost structure covering it is revealed and selected.

The summary card answers the questions people actually open a binary to settle, before any table is touched: is it ELF32 or ELF64, which architecture and endianness, EXEC or DYN or REL, where the entry point is, and — where the architecture has decoded `e_flags`, as RISC-V and MIPS both do — what those flags mean. Then the derived facts: statically or dynamically linked and which loader it asks for, stripped or not, PIE or not, the build ID from `.note.gnu.build-id`, and whether `PT_GNU_STACK` carries `PF_X`, which is a real security-relevant fact and is stated plainly rather than left for you to spot in a table.

Nothing is uploaded, and for this audience that is the point rather than a feature. A CTF binary is someone's unsolved challenge and a firmware image is often a customer's property; posting either to a stranger's server to read a section header is a bad trade. The file is read by the tab, handed to a Web Worker, and parsed in memory — there is no upload endpoint, and you can confirm it in DevTools: opening a binary produces no network request at all. Parsing runs in the worker so a 60 MB binary never freezes the tab, and files past 128 MiB are refused with the reason rather than accepted and hung on.

Every name this page prints — machine, type, section type, symbol type, bind and visibility, relocation type, dynamic tag — is GNU binutils `readelf`'s own spelling, because the parser is tested against real `readelf` output rather than against its own expectations. A 1900-symbol binary is ordinary and a 400,000-symbol one exists, so symbol and relocation tables are filtered, sorted and paged where the data lives instead of being rendered row by row into the page.

## Limits

Does not disassemble or decompile code and does not read PE (.exe) or Mach-O files; it shows ELF headers, sections, segments and symbols.

## Machine output

Copy JSON copies an envelope `{ tool, url, generatedAt, userAgentHint, result }`. `tool` is `"elf-inspector"`; `result` is the on-screen ELF structure below. `runpath`, `interpreter`, note `descriptor` text, and dynamic `valueText` may contain absolute paths from the build machine (user names under `/home/...`). The dropped file name, hex-pane bytes, section payload bytes, and disassembly are omitted. Section and symbol names are included (capped).

- `byteLength` (number | null) — File size in bytes. `null` = not supplied.
- `summary` (object) — Summary-card facts: `className`, `endian`, `osAbiName`, `typeName`, `machineName`, `entryHex`, `flagsHex`, `flagsText`, `linkage` (`"dynamic"` | `"static"` | `"none"`, or `null`), `interpreter` (or `null` if absent), `pie` (or `null`), `stripped` (`true` = no `.symtab`; distinct from `.dynsym` still present), `symtabCount`, `dynsymCount`, `buildId` (or `null`), `execStack` (`"exec"` | `"noexec"` | `"absent"`, or `null`), `needed` (DT_NEEDED names), `soname` (or `null`), `runpath` (DT_RUNPATH or DT_RPATH string, or `null`; may be a build-machine path), `sectionCount`, `segmentCount`, `relocationCount`, `noteCount`, `debugSections` (names only).
- `header` (object) — ELF header as shown: class/endian/ABI/type/machine numbers and names, `entryHex`, `phoff` / `shoff` as decimal strings, flags, table sizes, and `fields` (`name` + `display` only; raw `value` bytes are omitted).
- `segments` (object) — Program headers in file order, capped at 200. `rows`, `truncated`, `totalCount` (real count). Each row matches the on-screen columns plus `interpreter` when that segment is PT_INTERP.
- `sections` (object) — Section header table in file order, capped at 200. Names and sizes only — no section contents. `totalCount` is the real section count.
- `dynamic` (object) — `.dynamic` entries in file order, capped at 200. Each: `tagName`, `tagHex`, `valueText`, `name` (resolved NEEDED/SONAME/RPATH/RUNPATH string, or `null`).
- `notes` (object) — Note records in on-screen order, capped at 200. `descriptor` is the interpreted text shown (build-id hex, printable comment, or hex preview) — not the raw descriptor bytes.
- `symbols` (object | null) — `null` = symbol rows were not supplied (paged table not copied). When present: rows capped at 200 from `.symtab` then `.dynsym` in table order; `truncated`; `totalCount` is the real symbol count across those tables, not the copied length. Empty `rows` with `totalCount: 0` = parsed, no symbol rows.
- `versionDefs` (object) — GNU version definitions, capped at 200.
- `versionNeeds` (object) — GNU version needs, capped at 200.
- `warnings` (array of strings) — Parser warnings shown on screen, in parse order.

## FAQ

### Is my binary uploaded anywhere?

No. The file is read in this tab, transferred to a Web Worker, and parsed there. This page makes no network request while you use it, so you can leave DevTools open on the Network tab and watch it stay empty. That matters here more than on most tools: the binaries people bring to an ELF viewer are unreleased firmware, a customer's build, or a CTF challenge that other people are still working on.

### Is this a disassembler or a decompiler?

Neither, and it does not pretend to be. It reads ELF metadata: it can tell you that `.text` is 41 KiB of executable PROGBITS starting at file offset 0x1000, that `puts` comes from `libc.so.6` through a `R_X86_64_JUMP_SLOT` relocation, and that the entry point is at 0x1140 — and it cannot tell you what any of those instructions do. For that you want objdump, Ghidra, Binary Ninja or IDA, and none of them run in a browser tab.

### Which architectures does it read?

All of them, because ELF's structure does not change with the target. x86-64, i386, ARM, AArch64, RISC-V, MIPS in both byte orders, PowerPC, S390, SPARC, Xtensa, AVR and the rest decode the same way; only the relocation type names and the meaning of `e_flags` are architecture-specific, and those come from a per-machine table. A machine the table does not know still parses in full and reports its raw `e_machine` number instead of inventing a name.

### How large a binary can it open?

128 MiB. The whole file sits in the tab's memory to be parsed, and an object is built per symbol and per relocation on top of that, so peak usage is a multiple of the file size. Past this ceiling a tab is killed mid-parse, which to you looks like the page hanging, so the tool refuses the file and says so instead. Anything that large is usually an unstripped debug build — run `readelf` or `objdump` on it locally, or strip the debug sections first.

### It says the binary is stripped. Where did the symbols go?

A stripped binary has no `.symtab`, which is where local and static function names live; `strip` removes that section outright. What survives is `.dynsym`, the dynamic symbol table, because the loader needs it to resolve imports and exports at run time — so you still see which libraries and which functions the binary calls, and the names of anything it exports, but not the names of its own internal functions. The summary card says which of the two you are looking at and how many symbols each holds.

### The file is damaged and it still showed me something. Is that a bug?

It is the design. A truncated download, a hand-patched header, a section table that points past the end of the file — all of these are normal inputs for CTF work, and a viewer that answers a mangled ELF with a blank error page is useless exactly when you need it most. Whole-file problems (no ELF magic, an impossible class or byte order) still stop the parse, but a bad section, symbol or relocation degrades to a warning, and every warning is shown on the page instead of being swallowed.

## Related tools

- [APK Analyzer](https://bytescope.dev/en/apk-analyzer.md) — Pull the native .so libraries out of an APK, then read them here.
- [Hex Editor](https://bytescope.dev/en/hex-editor.md) — Open the raw bytes of any file with the same hex pane.
- [File Type Identifier](https://bytescope.dev/en/file-type.md) — Not sure it is an ELF at all? Identify a file by its magic bytes.
