summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/ci.yml13
-rw-r--r--Makefile.am4
-rw-r--r--README.md (renamed from README)23
-rw-r--r--flake.lock32
-rw-r--r--flake.nix43
-rw-r--r--patchelf.16
-rw-r--r--patchelf.spec.in2
-rw-r--r--release.nix14
-rw-r--r--src/patchelf.cc55
-rw-r--r--tests/Makefile.am4
-rwxr-xr-xtests/force-rpath.sh39
-rwxr-xr-xtests/output-flag.sh38
12 files changed, 215 insertions, 58 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..a4c6829
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,13 @@
+name: "CI"
+on:
+ pull_request:
+ push:
+jobs:
+ tests:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v2
+ - uses: cachix/install-nix-action@v9
+ with:
+ skip_adding_nixpkgs_channel: true
+ - run: nix-build release.nix
diff --git a/Makefile.am b/Makefile.am
index aa0d513..188f898 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,7 +1,7 @@
SUBDIRS = src tests
-EXTRA_DIST = COPYING README patchelf.spec version $(man1_MANS)
+EXTRA_DIST = COPYING README.md patchelf.spec version $(man1_MANS)
man1_MANS = patchelf.1
-doc_DATA = README
+doc_DATA = README.md
diff --git a/README b/README.md
index 60441ec..cb533a8 100644
--- a/README
+++ b/README.md
@@ -52,22 +52,21 @@ libraries. In particular, it can do the following:
$ patchelf --set-soname libnewname.so.3.4.5 path/to/libmylibrary.so.1.2.3
-INSTALLING
+## INSTALLING
You can download a pre-compiled binary from the releases or compile it by yourself:
- ./bootstrap.sh
- ./configure
- make
- sudo make install
+ ./bootstrap.sh
+ ./configure
+ make
+ sudo make install
-
-AUTHOR
+## AUTHOR
Copyright 2004-2019 Eelco Dolstra <edolstra@gmail.com>.
-LICENSE
+## LICENSE
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -83,12 +82,12 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-HOMEPAGE
+## HOMEPAGE
-http://nixos.org/patchelf.html
+https://nixos.org/patchelf.html
-BUGS
+## BUGS
The `strip' command from binutils generated broken executables when
applied to the output of patchelf (if `--set-rpath' or
@@ -97,7 +96,7 @@ This appears to be a bug in binutils
(http://bugs.strategoxt.org/browse/NIXPKGS-85).
-RELEASE HISTORY
+## RELEASE HISTORY
0.10 (March 28, 2019):
diff --git a/flake.lock b/flake.lock
index 965d038..5d516b4 100644
--- a/flake.lock
+++ b/flake.lock
@@ -1,10 +1,28 @@
{
- "nonFlakeRequires": {},
- "requires": {
- "nixpkgs": {
- "contentHash": "sha256-vy2UmXQM66aS/Kn2tCtjt9RwxfBvV+nQVb5tJQFwi8E=",
- "uri": "github:edolstra/nixpkgs/a4d896e89932e873c4117908d558db6210fa3b56"
- }
+ "nodes": {
+ "nixpkgs": {
+ "info": {
+ "lastModified": 1590140420,
+ "narHash": "sha256-ozgGYyqGHoEKvgL00r6G0ht+ODXHuhpfW37IYxAda0A="
+ },
+ "locked": {
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "48723f48ab92381f0afd50143f38e45cf3080405",
+ "type": "github"
+ },
+ "original": {
+ "id": "nixpkgs",
+ "ref": "nixos-20.03",
+ "type": "indirect"
+ }
},
- "version": 1
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 5
}
diff --git a/flake.nix b/flake.nix
index 9d579b9..5ba87db 100644
--- a/flake.nix
+++ b/flake.nix
@@ -1,20 +1,41 @@
{
- name = "patchelf";
-
description = "A tool for modifying ELF executables and libraries";
- requires = [ "nixpkgs" ];
+ inputs.nixpkgs.url = "nixpkgs/nixos-20.03";
- provides = deps: rec {
+ outputs = { self, nixpkgs }:
- hydraJobs = import ./release.nix {
- patchelfSrc = deps.self;
- nixpkgs = deps.nixpkgs;
- };
+ let
+ supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ];
+ forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);
+ in
+
+ rec {
+
+ overlay = final: prev: {
- packages.patchelf = hydraJobs.build.x86_64-linux;
+ patchelf-new = final.stdenv.mkDerivation {
+ name = "patchelf-${hydraJobs.tarball.version}";
+ src = "${hydraJobs.tarball}/tarballs/*.tar.bz2";
+ };
- defaultPackage = packages.patchelf;
+ };
- };
+ hydraJobs = import ./release.nix {
+ patchelfSrc = self;
+ nixpkgs = nixpkgs;
+ };
+
+ checks = forAllSystems (system: {
+ build = hydraJobs.build.${system};
+ });
+
+ defaultPackage = forAllSystems (system:
+ (import nixpkgs {
+ inherit system;
+ overlays = [ self.overlay ];
+ }).patchelf-new
+ );
+
+ };
}
diff --git a/patchelf.1 b/patchelf.1
index 43a5d24..8c998ad 100644
--- a/patchelf.1
+++ b/patchelf.1
@@ -10,6 +10,7 @@ patchelf - Modify ELF files
.I OPTION
.B
.I FILE
+.SM ...
.B
.SH DESCRIPTION
@@ -20,7 +21,7 @@ of executables and change the RPATH of executables and libraries.
.SH OPTIONS
-The single option given operates on a given FILE, editing in place.
+The single option given operates on each FILE, editing in place.
.IP "--page-size SIZE"
Uses the given page size instead of the default.
@@ -82,6 +83,9 @@ option can be given multiple times.
Marks the object that the search for dependencies of this object will ignore any
default library search paths.
+.IP "--output FILE"
+Set the output file name. If not specified, the input will be modified in place.
+
.IP --debug
Prints details of the changes made to the input file.
diff --git a/patchelf.spec.in b/patchelf.spec.in
index d0f326f..5569026 100644
--- a/patchelf.spec.in
+++ b/patchelf.spec.in
@@ -34,5 +34,5 @@ rm -rf $RPM_BUILD_ROOT
%files
%{_bindir}/patchelf
-%doc %{_docdir}/patchelf/README
+%doc %{_docdir}/patchelf/README.md
%{_mandir}/man1/patchelf.1.gz
diff --git a/release.nix b/release.nix
index 075ef8b..06bf91d 100644
--- a/release.nix
+++ b/release.nix
@@ -1,5 +1,5 @@
{ patchelfSrc ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
-, nixpkgs ? builtins.fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-19.03.tar.gz
+, nixpkgs ? builtins.fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-20.03.tar.gz
, officialRelease ? false
}:
@@ -14,13 +14,19 @@ let
tarball =
pkgs.releaseTools.sourceTarball rec {
name = "patchelf-tarball";
- version = builtins.readFile ./version + (if officialRelease then "" else "pre${toString patchelfSrc.revCount or 0}_${patchelfSrc.shortRev or "0000000"}");
+ version = builtins.readFile ./version +
+ (if officialRelease then "" else
+ "." +
+ ((if patchelfSrc ? lastModifiedDate
+ then builtins.substring 0 8 patchelfSrc.lastModifiedDate
+ else toString patchelfSrc.revCount or 0)
+ + "." + patchelfSrc.shortRev));
versionSuffix = ""; # obsolete
src = patchelfSrc;
preAutoconf = "echo ${version} > version";
postDist = ''
- cp README $out/
- echo "doc readme $out/README" >> $out/nix-support/hydra-build-products
+ cp README.md $out/
+ echo "doc readme $out/README.md" >> $out/nix-support/hydra-build-products
'';
};
diff --git a/src/patchelf.cc b/src/patchelf.cc
index 592799d..965686a 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -46,6 +46,8 @@ static bool debugMode = false;
static bool forceRPath = false;
static std::vector<std::string> fileNames;
+static std::string outputFileName;
+static bool alwaysWrite = false;
static int pageSize = PAGESIZE;
typedef std::shared_ptr<std::vector<unsigned char>> FileContents;
@@ -497,7 +499,9 @@ void ElfFile<ElfFileParamNames>::sortShdrs()
static void writeFile(std::string fileName, FileContents contents)
{
- int fd = open(fileName.c_str(), O_TRUNC | O_WRONLY);
+ debug("writing %s\n", fileName.c_str());
+
+ int fd = open(fileName.c_str(), O_CREAT | O_TRUNC | O_WRONLY, 0777);
if (fd == -1)
error("open");
@@ -742,14 +746,13 @@ void ElfFile<ElfFileParamNames>::rewriteSectionsLibrary()
since DYN executables tend to start at virtual address 0, so
rewriteSectionsExecutable() won't work because it doesn't have
any virtual address space to grow downwards into. */
- if (isExecutable) {
- if (startOffset >= startPage) {
- debug("shifting new PT_LOAD segment by %d bytes to work around a Linux kernel bug\n", startOffset - startPage);
- }
+ if (isExecutable && startOffset > startPage) {
+ debug("shifting new PT_LOAD segment by %d bytes to work around a Linux kernel bug\n", startOffset - startPage);
startPage = startOffset;
}
/* Add a segment that maps the replaced sections into memory. */
+ wri(hdr->e_phoff, sizeof(Elf_Ehdr));
phdrs.resize(rdi(hdr->e_phnum) + 1);
wri(hdr->e_phnum, rdi(hdr->e_phnum) + 1);
Elf_Phdr & phdr = phdrs[rdi(hdr->e_phnum) - 1];
@@ -1248,7 +1251,17 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
}
- if (std::string(rpath ? rpath : "") == newRPath) return;
+ if (!forceRPath && dynRPath && !dynRunPath) { /* convert DT_RPATH to DT_RUNPATH */
+ dynRPath->d_tag = DT_RUNPATH;
+ dynRunPath = dynRPath;
+ dynRPath = 0;
+ } else if (forceRPath && dynRunPath) { /* convert DT_RUNPATH to DT_RPATH */
+ dynRunPath->d_tag = DT_RPATH;
+ dynRPath = dynRunPath;
+ dynRunPath = 0;
+ } else if (std::string(rpath ? rpath : "") == newRPath) {
+ return;
+ }
changed = true;
@@ -1262,15 +1275,6 @@ void ElfFile<ElfFileParamNames>::modifyRPath(RPathOp op,
debug("new rpath is '%s'\n", newRPath.c_str());
- if (!forceRPath && dynRPath && !dynRunPath) { /* convert DT_RPATH to DT_RUNPATH */
- dynRPath->d_tag = DT_RUNPATH;
- dynRunPath = dynRPath;
- dynRPath = 0;
- }
-
- if (forceRPath && dynRPath && dynRunPath) { /* convert DT_RUNPATH to DT_RPATH */
- dynRunPath->d_tag = DT_IGNORE;
- }
if (newRPath.size() <= rpathSize) {
strcpy(rpath, newRPath.c_str());
@@ -1560,7 +1564,7 @@ static bool printNeeded = false;
static bool noDefaultLib = false;
template<class ElfFile>
-static void patchElf2(ElfFile && elfFile, std::string fileName)
+static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, std::string fileName)
{
if (printInterpreter)
printf("%s\n", elfFile.getInterpreter().c_str());
@@ -1596,6 +1600,9 @@ static void patchElf2(ElfFile && elfFile, std::string fileName)
if (elfFile.isChanged()){
elfFile.rewriteSections();
writeFile(fileName, elfFile.fileContents);
+ } else if (alwaysWrite) {
+ debug("not modified, but alwaysWrite=true\n");
+ writeFile(fileName, fileContents);
}
}
@@ -1609,11 +1616,12 @@ static void patchElf()
debug("Kernel page size is %u bytes\n", getPageSize());
auto fileContents = readFile(fileName);
+ std::string outputFileName2 = outputFileName.empty() ? fileName : outputFileName;
if (getElfType(fileContents).is32Bit)
- patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed>(fileContents), fileName);
+ patchElf2(ElfFile<Elf32_Ehdr, Elf32_Phdr, Elf32_Shdr, Elf32_Addr, Elf32_Off, Elf32_Dyn, Elf32_Sym, Elf32_Verneed>(fileContents), fileContents, outputFileName2);
else
- patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed>(fileContents), fileName);
+ patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed>(fileContents), fileContents, outputFileName2);
}
}
@@ -1637,9 +1645,10 @@ void showHelp(const std::string & progName)
[--replace-needed LIBRARY NEW_LIBRARY]\n\
[--print-needed]\n\
[--no-default-lib]\n\
+ [--output FILE]\n\
[--debug]\n\
[--version]\n\
- FILENAME\n", progName.c_str());
+ FILENAME...\n", progName.c_str());
}
@@ -1723,6 +1732,11 @@ int mainWrapped(int argc, char * * argv)
neededLibsToReplace[ argv[i+1] ] = argv[i+2];
i += 2;
}
+ else if (arg == "--output") {
+ if (++i == argc) error("missing argument");
+ outputFileName = argv[i];
+ alwaysWrite = true;
+ }
else if (arg == "--debug") {
debugMode = true;
}
@@ -1744,6 +1758,9 @@ int mainWrapped(int argc, char * * argv)
if (fileNames.empty()) error("missing filename");
+ if (!outputFileName.empty() && fileNames.size() != 1)
+ error("--output option only allowed with single input file");
+
patchElf();
return 0;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 32218e8..96339b3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -21,7 +21,9 @@ no_rpath_arch_TESTS = \
src_TESTS = \
plain-fail.sh plain-run.sh shrink-rpath.sh set-interpreter-short.sh \
set-interpreter-long.sh set-rpath.sh no-rpath.sh big-dynstr.sh \
- set-rpath-library.sh soname.sh shrink-rpath-with-allowed-prefixes.sh
+ set-rpath-library.sh soname.sh shrink-rpath-with-allowed-prefixes.sh \
+ force-rpath.sh \
+ output-flag.sh
build_TESTS = \
$(no_rpath_arch_TESTS)
diff --git a/tests/force-rpath.sh b/tests/force-rpath.sh
new file mode 100755
index 0000000..9256905
--- /dev/null
+++ b/tests/force-rpath.sh
@@ -0,0 +1,39 @@
+#! /bin/sh -e
+SCRATCH=scratch/$(basename $0 .sh)
+
+rm -rf ${SCRATCH}
+mkdir -p ${SCRATCH}
+
+SCRATCHFILE=${SCRATCH}/libfoo.so
+cp libfoo.so $SCRATCHFILE
+
+doit() {
+ echo patchelf $*
+ ../src/patchelf $* $SCRATCHFILE
+}
+
+expect() {
+ out=$(echo $(objdump -x $SCRATCHFILE | grep PATH))
+
+ if [ "$out" != "$*" ]; then
+ echo "Expected '$*' but got '$out'"
+ exit 1
+ fi
+}
+
+doit --remove-rpath
+expect
+doit --set-rpath foo
+expect RUNPATH foo
+doit --force-rpath --set-rpath foo
+expect RPATH foo
+doit --force-rpath --set-rpath bar
+expect RPATH bar
+doit --remove-rpath
+expect
+doit --force-rpath --set-rpath foo
+expect RPATH foo
+doit --set-rpath foo
+expect RUNPATH foo
+doit --set-rpath bar
+expect RUNPATH bar
diff --git a/tests/output-flag.sh b/tests/output-flag.sh
new file mode 100755
index 0000000..5da26b5
--- /dev/null
+++ b/tests/output-flag.sh
@@ -0,0 +1,38 @@
+#! /bin/sh -e
+SCRATCH=scratch/$(basename $0 .sh)
+
+rm -rf ${SCRATCH}
+mkdir -p ${SCRATCH}
+mkdir -p ${SCRATCH}/libsA
+mkdir -p ${SCRATCH}/libsB
+
+cp main ${SCRATCH}/
+cp libfoo.so ${SCRATCH}/libsA/
+cp libbar.so ${SCRATCH}/libsB/
+
+oldRPath=$(../src/patchelf --print-rpath ${SCRATCH}/main)
+if test -z "$oldRPath"; then oldRPath="/oops"; fi
+
+../src/patchelf --force-rpath --set-rpath $oldRPath:$(pwd)/${SCRATCH}/libsA:$(pwd)/${SCRATCH}/libsB ${SCRATCH}/main --output ${SCRATCH}/main2
+# make sure it copies even when there is nothing to do (because rpath is already set)
+../src/patchelf --force-rpath --set-rpath $oldRPath:$(pwd)/${SCRATCH}/libsA:$(pwd)/${SCRATCH}/libsB ${SCRATCH}/main2 --output ${SCRATCH}/main3
+
+if test "$(uname)" = FreeBSD; then
+ export LD_LIBRARY_PATH=$(pwd)/${SCRATCH}/libsB
+fi
+
+exitCode=0
+(cd ${SCRATCH} && ./main2) || exitCode=$?
+
+if test "$exitCode" != 46; then
+ echo "bad exit code!"
+ exit 1
+fi
+
+exitCode=0
+(cd ${SCRATCH} && ./main3) || exitCode=$?
+
+if test "$exitCode" != 46; then
+ echo "bad exit code!"
+ exit 1
+fi