A Python implementation of the Go nm symbol table utility, supporting multiple binary file formats.
- Multiple Binary Formats: Full support for ELF, Mach-O (32/64-bit), PE (Windows), COFF, and AR archives
- Symbol Table Extraction: Extracts and displays symbols from compiled binaries
- Flexible Sorting: Sort symbols by address, name, size, or original order
- Archive Support: Process symbols from archive members (
.afiles) - Cross-platform: Works on Linux, macOS, and Windows
pip install -e .Display symbols from a binary file:
pynm myprogrampynm [options] file...
Options:
-n Sort by address (numeric) - shorthand for -sort address
-size Print symbol size in decimal between address and type
-sort ORDER Sort output: 'address', 'name' (default), 'size', or 'none'
-type Print symbol type after the name
Sort symbols by address:
pynm -n myprogramShow symbol sizes:
pynm -size myprogramSort by size (largest first):
pynm -sort size myprogramShow all information:
pynm -n -size -type myprogramProcess multiple files:
pynm prog1 prog2 prog3The tool outputs symbol type codes compatible with the standard nm utility:
| Code | Meaning |
|---|---|
| U | Undefined (referenced but not defined) |
| T | Text (code) - external |
| t | Text (code) - local |
| R | Read-only data - external |
| r | Read-only data - local |
| B | BSS (uninitialized data) - external |
| b | BSS (uninitialized data) - local |
| D | Initialized data - external |
| d | Initialized data - local |
| A | Absolute address |
| V | Weak external symbol |
| v | Weak local symbol |
| C | Common (uninitialized data) |
| I/i | Indirect symbols |
| ? | Unknown |
reader.py: Core binary file parsing logic supporting all formatssymbols.py: Data classes forSymbolandEntryrepresentationscli.py: Command-line argument parsing__main__.py: Entry point and output formatting
- Reads symbol table from
.symtabsection - Extracts names from
.strtabstring table - Supports 32-bit and 64-bit formats
- Handles both little-endian and big-endian
- Reads LC_SYMTAB load command
- Extracts symbol names from string table
- Supports 32-bit and 64-bit variants
- Reads COFF symbol table
- Handles string table for long symbol names
- Maps storage classes to symbol types
- Reads COFF header and symbol table
- Supports various storage classes
- Section-aware symbol classification
- Parses POSIX AR archive format
- Extracts and parses contained binaries
- Supports members in any supported format
You can compile pynm into a standalone executable using PyInstaller. This creates a single binary that doesn't require Python to be installed.
Install the development dependencies:
pip install -e ".[dev]"./build.shOptions:
--clean- Remove previous builds before building--debug- Enable verbose debug output
./build.sh --clean --debugbuild.batOptions:
--clean- Remove previous builds before building--debug- Enable verbose debug output
build.bat --clean --debugYou can also build manually using PyInstaller:
# One-file executable (default)
pyinstaller pynm.spec
# Or use command-line options
pyinstaller --onefile --name pynm pynm/__main__.pyThe built executable will be in the dist/ directory:
- Linux/macOS:
dist/pynm - Windows:
dist\pynm.exe
The standalone executable works the same as the Python module:
# Using the executable
./dist/pynm myprogram
# Or on Windows
dist\pynm.exe myprogrampytest tests/ -vThe project includes 33 tests covering:
- Format detection for all supported formats
- Symbol extraction from real binaries
- CLI argument parsing
- Integration tests with actual Go and C binaries
pynm/
├── __init__.py # Package initialization
├── __main__.py # Entry point and output formatting
├── cli.py # Command-line argument parsing
├── reader.py # Binary format parsing
└── symbols.py # Data classes
tests/
├── conftest.py # Test fixtures
├── test_cli.py # CLI parsing tests
├── test_integration.py # Integration tests
├── test_reader.py # Reader tests
└── test_symbols.py # Data class tests
- Python: 3.10+
- Dependencies: None (only standard library)
- Symbol sizes are not extracted from Mach-O, PE, or COFF formats (always 0)
- This is a limitation of these formats which don't always store size information
- Weak symbols and indirect symbols may not be fully distinguished in all formats
When adding support for new formats or fixing bugs:
- Add comprehensive docstrings to all public methods
- Update the test suite with format detection and parsing tests
- Ensure all existing tests continue to pass
- Use the
_extract_string()helper for string table lookups to reduce code duplication
- All source files include module and function docstrings
- Type hints are used throughout
- The
Readerclass implements the context manager protocol for proper resource management - String extraction is centralized in the
_extract_string()helper method
The Reader class should always be used with a context manager to ensure proper cleanup:
with Reader("binary") as reader:
entries = reader.entries()
# Process entries
# File is automatically closed(Add your license information here)