patchelf
Patchelf is a small utility for modifying existing ELF executables and libraries. It can change the dynamic linker (interpreter) of executables and adjust the RPATH/RUNPATH, which specifies directories where the dynamic linker should look for shared libraries. The current version is 0.17.2.4. The project has an active development cycle, often maintaining multiple parallel release branches for bug fixes and new features, with frequent backports.
Warnings
- gotcha The `patchelf` package on PyPI provides the command-line utility, not a direct Python API for ELF manipulation. Interaction with ELF files should be done by executing the `patchelf` binary via `subprocess.run()`.
- gotcha The project actively maintains multiple release branches (e.g., 0.15.x, 0.17.x, 0.18.x). Bug fixes and minor features are often backported to older maintenance branches, meaning a seemingly 'older' minor version might contain recent critical fixes.
- gotcha Directly modifying ELF executables is a low-level and potentially destructive operation. Incorrect usage can corrupt binaries, leading to crashes, unexpected behavior, or rendering them unexecutable.
- breaking Several critical out-of-bounds read/write vulnerabilities in functions like `modifySoname` and `modifyRPath` have been fixed in recent maintenance and feature releases. Older versions are susceptible to silent corruption or crashes.
Install
-
pip install patchelf
Imports
- CLI tool
import subprocess subprocess.run(['patchelf', '--print-rpath', '/path/to/executable'], check=True)
Quickstart
import subprocess
import os
# Create a dummy executable for demonstration (requires clang/gcc)
# In a real scenario, you'd use an existing ELF binary.
with open('my_test_program.c', 'w') as f:
f.write('int main() { return 0; }')
subprocess.run(['gcc', '-o', 'my_executable', 'my_test_program.c'], check=True)
print("Original RPATH:")
try:
result = subprocess.run(['patchelf', '--print-rpath', './my_executable'], capture_output=True, text=True, check=True)
print(result.stdout.strip())
except subprocess.CalledProcessError as e:
if 'has no RPATH' in e.stderr:
print('No RPATH found.')
else:
raise
print("\nSetting RPATH to '$ORIGIN/lib'")
subprocess.run(['patchelf', '--set-rpath', '$ORIGIN/lib', './my_executable'], check=True)
print("\nNew RPATH:")
result = subprocess.run(['patchelf', '--print-rpath', './my_executable'], capture_output=True, text=True, check=True)
print(result.stdout.strip())
# Clean up dummy files
os.remove('my_test_program.c')
os.remove('my_executable')