tools / elf-inspector header · sections · symbols32 + 64-bitLE + BE readelf -a ./app

ELF Inspector

Everything readelf -a tells you, laid out so you can actually find it — plus the verdicts you usually have to derive by hand: is it PIE, is it stripped, is RELRO full, which shared libraries does it need.

ELF file parsed locally · never uploaded
Drop an ELF binary here or click to choose a file — executables, .so libraries, object files and core dumps

What this tool reports

An ELF file is a container with three parallel descriptions of the same bytes: the section header table (how the linker sees the file), the program header table (how the kernel and ld.so see the file), and the dynamic section (what the loader must do at runtime). Confusing the three is the root cause of most "it works on my machine" incidents, so the inspector keeps them in separate tabs rather than flattening them into one list.

The header view reports e_ident (class, endianness, OS ABI), e_type (relocatable, executable, shared object / PIE, core), e_machine (the target instruction set), the entry point and the offsets of both tables. The section view lists every section with its flags — A allocate, W writable, X executable, T TLS, C compressed — which is how you spot a build that ended up with an executable .data. The segment view is what actually matters at run time: PT_LOAD mappings, PT_INTERP (the dynamic loader path, which is what tells you glibc versus musl), PT_GNU_STACK (whether the stack is executable) and PT_GNU_RELRO (read-only relocations after relocation processing).

The symbols tab reads both .symtab and .dynsym. If a binary has no .symtab it was stripped, which is normal for release builds and expected for anything shipped by a distribution — the file still runs, you simply lose local symbols. The inspector marks this explicitly instead of leaving you to notice the absence. Mangled C++ and Rust names are demangled on a best-effort basis: it is a compact decoder for the common Itanium ABI subset and Rust's legacy scheme, not a byte-exact replacement for c++filt, and the UI says so.

The dynamic tab is where dependency problems are usually solved. DT_NEEDED is the exact list of shared objects the loader will try to open, DT_SONAME is the name the library registers, and DT_RPATH / DT_RUNPATH decide which extra directories get searched. Note the difference: RPATH is consulted before LD_LIBRARY_PATH, RUNPATH after it — which is why two binaries with apparently identical paths behave differently. If either entry exists it also shows up in the hardening summary, because a baked-in absolute search path is both a portability problem and a privilege-escalation vector for setuid binaries.

Reading the security summary

The summary tab derives five properties that are covered by every Linux hardening checklist, and states the rule behind each one so you can verify it with the real tool:

  • PIE — e_type == ET_DYN and either the DF_1_PIE flag in DT_FLAGS_1 or a PT_INTERP segment. The second condition matters: a shared library is also ET_DYN, so type alone cannot distinguish the two.
  • RELRO — full when PT_GNU_RELRO is present and the binary binds now (DT_BIND_NOW, DT_FLAGS & DF_BIND_NOW or DF_1_NOW); partial when only the segment is there, which leaves the GOT writable until the first call.
  • NX — read from PT_GNU_STACK. If the segment is missing the answer is reported as unknown rather than "enabled", because older linkers omitted it and the kernel's fallback is not something a static analysis can assert.
  • Stack canary — presence of a __stack_chk_fail reference. That is evidence the compiler was invoked with stack protection, not proof that every function is protected; functions without an array or alloca get no canary at all.
  • Static linking — no PT_INTERP and no DT_NEEDED. Useful when a vendor swears a build is static while shipping a dynamically linked binary with a musl interpreter.

When the file is not ELF

Dropping a Mach-O, PE, ar archive, gzip stream or Java class file produces an explicit identification rather than a parse error. This is a common real-world mistake — downloading a macOS build on a Linux host, or a .deb instead of the binary inside it — and naming the actual format is usually faster than any error message the loader would have produced.