Build Status Dependabot Status Code Climate Issue Count Test Coverage Inline docs MIT License

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!