patchelf.rb
Implements features of NixOS/patchelf in pure Ruby.
Installation
Available on RubyGems.org!
$ gem install patchelf
Usage
$ patchelf.rb
# Usage: patchelf.rb <commands> FILENAME [OUTPUT_FILE]
# --print-interpreter, --pi Show interpreter's name.
# --print-needed, --pn Show needed libraries specified in DT_NEEDED.
# --print-runpath, --pr Show the path specified in DT_RUNPATH.
# --print-soname, --ps Show soname specified in DT_SONAME.
# --set-interpreter, --interp INTERP
# Set interpreter's name.
# --set-needed, --needed LIB1,LIB2,LIB3
# Set needed libraries, this will remove all existent needed libraries.
# --add-needed LIB Append a new needed library.
# --remove-needed LIB Remove a needed library.
# --replace-needed LIB1,LIB2 Replace needed library LIB1 as LIB2.
# --set-runpath, --runpath PATH
# Set the path of runpath.
# --force-rpath According to the ld.so docs, DT_RPATH is obsolete,
# patchelf.rb will always try to get/set DT_RUNPATH first.
# Use this option to force every operations related to runpath (e.g. --runpath)
# to consider 'DT_RPATH' instead of 'DT_RUNPATH'.
# --set-soname, --so SONAME Set name of a shared library.
# --version Show current gem's version.
Display information
$ patchelf.rb --print-interpreter --print-needed /bin/ls
# interpreter: /lib64/ld-linux-x86-64.so.2
# needed: libselinux.so.1 libc.so.6
Change the dynamic loader (interpreter)
# $ patchelf.rb --interp NEW_INTERP input.elf output.elf
$ patchelf.rb --interp /lib/AAAA.so /bin/ls ls.patch
$ file ls.patch
# ls.patch: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/AAAA.so, for GNU/Linux 3.2.0, BuildID[sha1]=9567f9a28e66f4d7ec4baf31cfbf68d0410f0ae6, stripped
Modify dependency libraries
Add
$ patchelf.rb --add-needed libnew.so /bin/ls ls.patch
Remove
$ patchelf.rb --remove-needed libc.so.6 /bin/ls ls.patch
Replace
$ patchelf.rb --replace-needed libc.so.6,libcnew.so.6 /bin/ls ls.patch
$ readelf -d ls.patch | grep NEEDED
# 0x0000000000000001 (NEEDED) Shared library: [libselinux.so.1]
# 0x0000000000000001 (NEEDED) Shared library: [libcnew.so.6]
Set directly
$ patchelf.rb --needed a.so,b.so,c.so /bin/ls ls.patch
$ readelf -d ls.patch | grep NEEDED
# 0x0000000000000001 (NEEDED) Shared library: [a.so]
# 0x0000000000000001 (NEEDED) Shared library: [b.so]
# 0x0000000000000001 (NEEDED) Shared library: [c.so]
Set RUNPATH of an executable
$ patchelf.rb --runpath . /bin/ls ls.patch
$ readelf -d ls.patch | grep RUNPATH
# 0x000000000000001d (RUNPATH) Library runpath: [.]
Change SONAME of a shared library
$ patchelf.rb --so libc.so.217 /lib/x86_64-linux-gnu/libc.so.6 libc.patch
$ readelf -d libc.patch | grep SONAME
# 0x000000000000000e (SONAME) Library soname: [libc.so.217]
As Ruby library
require 'patchelf'
patcher = PatchELF::Patcher.new('/bin/ls')
patcher.interpreter
#=> "/lib64/ld-linux-x86-64.so.2"
patcher.interpreter = '/lib/AAAA.so.2'
patcher.interpreter
#=> "/lib/AAAA.so.2"
patcher.save('ls.patch')
# $ file ls.patch
# ls.patch: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/AAAA.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=9567f9a28e66f4d7ec4baf31cfbf68d0410f0ae6, stripped
Environment
patchelf.rb is implemented in pure Ruby, so it should work in all environments include Linux, macOS, and Windows!