summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.travis.yml7
-rw-r--r--Makefile.am4
-rw-r--r--README.md (renamed from README)42
-rw-r--r--configure.ac2
-rw-r--r--flake.lock28
-rw-r--r--flake.nix41
-rw-r--r--patchelf.18
-rw-r--r--patchelf.spec.in4
-rw-r--r--release.nix67
-rw-r--r--src/patchelf.cc112
-rw-r--r--tests/Makefile.am4
-rwxr-xr-xtests/force-rpath.sh39
-rwxr-xr-xtests/output-flag.sh38
13 files changed, 308 insertions, 88 deletions
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..e112de4
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,7 @@
+language: cpp
+dist: xenial
+script:
+ - ./bootstrap.sh
+ - ./configure
+ - make
+ - cd tests && make check
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 406cefd..cb533a8 100644
--- a/README
+++ b/README.md
@@ -1,4 +1,4 @@
-PatchELF is a simple utility for modifing existing ELF executables and
+PatchELF is a simple utility for modifying existing ELF executables and
libraries. In particular, it can do the following:
* Change the dynamic loader ("ELF interpreter") of executables:
@@ -47,13 +47,26 @@ libraries. In particular, it can do the following:
This option can be give multiple times.
+* Change SONAME of a dynamic library:
-AUTHOR
+ $ patchelf --set-soname libnewname.so.3.4.5 path/to/libmylibrary.so.1.2.3
-Copyright 2004-2016 Eelco Dolstra <eelco.dolstra@logicblox.com>.
+## INSTALLING
-LICENSE
+You can download a pre-compiled binary from the releases or compile it by yourself:
+
+ ./bootstrap.sh
+ ./configure
+ make
+ sudo make install
+
+## AUTHOR
+
+Copyright 2004-2019 Eelco Dolstra <edolstra@gmail.com>.
+
+
+## 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
@@ -69,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
@@ -83,7 +96,19 @@ This appears to be a bug in binutils
(http://bugs.strategoxt.org/browse/NIXPKGS-85).
-RELEASE HISTORY
+## RELEASE HISTORY
+
+0.10 (March 28, 2019):
+
+* Many bug fixes. Please refer to the Git commit log:
+
+ https://github.com/NixOS/patchelf/commits/master
+
+ This release has contributions from Adam Trhoň, Benjamin Hipple,
+ Bernardo Ramos, Bjørn Forsman, Domen Kožar, Eelco Dolstra, Ezra
+ Cooper, Felipe Sateler, Jakub Wilk, James Le Cuirot, Karl Millar,
+ Linus Heckemann, Nathaniel J. Smith, Richard Purdie, Stanislav
+ Markevich and Tuomas Tynkkynen.
0.9 (February 29, 2016):
@@ -155,4 +180,3 @@ RELEASE HISTORY
0.1 (October 11, 2005):
* Initial release.
-
diff --git a/configure.ac b/configure.ac
index d08a006..c2e5e98 100644
--- a/configure.ac
+++ b/configure.ac
@@ -13,7 +13,7 @@ AC_ARG_WITH([page-size],
)
if test "$PAGESIZE" = auto; then
- if type -p getconf &>/dev/null; then
+ if command -v getconf >/dev/null; then
PAGESIZE=$(getconf PAGESIZE || getconf PAGE_SIZE)
fi
if test "$PAGESIZE" = auto -o -z "$PAGESIZE"; then
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..5d516b4
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,28 @@
+{
+ "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"
+ }
+ },
+ "root": {
+ "inputs": {
+ "nixpkgs": "nixpkgs"
+ }
+ }
+ },
+ "root": "root",
+ "version": 5
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..5ba87db
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,41 @@
+{
+ description = "A tool for modifying ELF executables and libraries";
+
+ inputs.nixpkgs.url = "nixpkgs/nixos-20.03";
+
+ outputs = { self, nixpkgs }:
+
+ let
+ supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ];
+ forAllSystems = f: nixpkgs.lib.genAttrs supportedSystems (system: f system);
+ in
+
+ rec {
+
+ overlay = final: prev: {
+
+ patchelf-new = final.stdenv.mkDerivation {
+ name = "patchelf-${hydraJobs.tarball.version}";
+ src = "${hydraJobs.tarball}/tarballs/*.tar.bz2";
+ };
+
+ };
+
+ 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 ecf4217..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.
@@ -75,13 +76,16 @@ Replaces a declared dependency on a dynamic library with another one (DT_NEEDED)
This option can be give multiple times.
.IP "--remove-needed LIBRARY"
-Removes a declared depency on LIBRARY (DT_NEEDED entry). This
+Removes a declared dependency on LIBRARY (DT_NEEDED entry). This
option can be given multiple times.
.IP "--no-default-lib"
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 a361554..5569026 100644
--- a/patchelf.spec.in
+++ b/patchelf.spec.in
@@ -12,7 +12,7 @@ Prefix: /usr
%description
-PatchELF is a simple utility for modifing existing ELF executables and
+PatchELF is a simple utility for modifying existing ELF executables and
libraries. It can change the dynamic loader ("ELF interpreter") of
executables and change the RPATH of executables and libraries.
@@ -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 1b6c67c..2cf9181 100644
--- a/release.nix
+++ b/release.nix
@@ -1,10 +1,11 @@
{ patchelfSrc ? { outPath = ./.; revCount = 1234; shortRev = "abcdef"; }
+, nixpkgs ? builtins.fetchTarball https://github.com/NixOS/nixpkgs-channels/archive/nixos-19.03.tar.gz
, officialRelease ? false
}:
let
- pkgs = import <nixpkgs> { };
+ pkgs = import nixpkgs { system = builtins.currentSystem or "x86_64-linux"; };
jobs = rec {
@@ -13,13 +14,19 @@ let
tarball =
pkgs.releaseTools.sourceTarball rec {
name = "patchelf-tarball";
- version = builtins.readFile ./version + (if officialRelease then "" else "pre${toString patchelfSrc.revCount}_${patchelfSrc.shortRev}");
+ 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
'';
};
@@ -32,39 +39,27 @@ let
};
- build = pkgs.lib.genAttrs [ "x86_64-linux" "i686-linux" /* "x86_64-freebsd" "i686-freebsd" "x86_64-darwin" "i686-solaris" "i686-cygwin" */ ] (system:
+ build = pkgs.lib.genAttrs [ "x86_64-linux" "i686-linux" "aarch64-linux" /* "x86_64-freebsd" "i686-freebsd" "x86_64-darwin" "i686-solaris" "i686-cygwin" */ ] (system:
- with import <nixpkgs> { inherit system; };
+ with import nixpkgs { inherit system; };
releaseTools.nixBuild {
name = "patchelf";
src = tarball;
doCheck = !stdenv.isDarwin && system != "i686-cygwin" && system != "i686-solaris";
buildInputs = lib.optionals stdenv.isLinux [ acl attr ];
- isReproducible = true;
+ isReproducible = system != "aarch64-linux"; # ARM machines are still on Nix 1.11
});
+ /*
+ rpm_fedora27x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora27x86_64);
- rpm_fedora20i386 = makeRPM_i686 (diskImages: diskImages.fedora20i386);
- rpm_fedora20x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora20x86_64);
- rpm_fedora21i386 = makeRPM_i686 (diskImages: diskImages.fedora21i386);
- rpm_fedora21x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora21x86_64);
- rpm_fedora23i386 = makeRPM_i686 (diskImages: diskImages.fedora23i386);
- rpm_fedora23x86_64 = makeRPM_x86_64 (diskImages: diskImages.fedora23x86_64);
+ deb_debian9i386 = makeDeb_i686 (diskImages: diskImages.debian9i386);
+ deb_debian9x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian9x86_64);
- deb_debian7i386 = makeDeb_i686 (diskImages: diskImages.debian7i386);
- deb_debian7x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian7x86_64);
- deb_debian8i386 = makeDeb_i686 (diskImages: diskImages.debian8i386);
- deb_debian8x86_64 = makeDeb_x86_64 (diskImages: diskImages.debian8x86_64);
-
- deb_ubuntu1404i386 = makeDeb_i686 (diskImages: diskImages.ubuntu1404i386);
- deb_ubuntu1404x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu1404x86_64);
- deb_ubuntu1410i386 = makeDeb_i686 (diskImages: diskImages.ubuntu1410i386);
- deb_ubuntu1410x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu1410x86_64);
- deb_ubuntu1504i386 = makeDeb_i686 (diskImages: diskImages.ubuntu1504i386);
- deb_ubuntu1504x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu1504x86_64);
- deb_ubuntu1510i386 = makeDeb_i686 (diskImages: diskImages.ubuntu1510i386);
- deb_ubuntu1510x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu1510x86_64);
+ deb_ubuntu1804i386 = makeDeb_i686 (diskImages: diskImages.ubuntu1804i386);
+ deb_ubuntu1804x86_64 = makeDeb_x86_64 (diskImages: diskImages.ubuntu1804x86_64);
+ */
release = pkgs.releaseTools.aggregate
@@ -73,15 +68,13 @@ let
[ tarball
build.x86_64-linux
build.i686-linux
- #build.x86_64-freebsd
- #build.i686-freebsd
- #build.x86_64-darwin
- rpm_fedora23i386
- rpm_fedora23x86_64
- deb_debian8i386
- deb_debian8x86_64
- deb_ubuntu1510i386
- deb_ubuntu1510x86_64
+ /*
+ rpm_fedora27x86_64
+ deb_debian9i386
+ deb_debian9x86_64
+ deb_ubuntu1804i386
+ deb_ubuntu1804x86_64
+ */
];
meta.description = "Release-critical builds";
};
@@ -95,7 +88,7 @@ let
makeRPM =
system: diskImageFun:
- with import <nixpkgs> { inherit system; };
+ with import nixpkgs { inherit system; };
releaseTools.rpmBuild rec {
name = "patchelf-rpm";
@@ -111,7 +104,7 @@ let
makeDeb =
system: diskImageFun:
- with import <nixpkgs> { inherit system; };
+ with import nixpkgs { inherit system; };
releaseTools.debBuild {
name = "patchelf-deb";
diff --git a/src/patchelf.cc b/src/patchelf.cc
index e9d7ea5..cbf420d 100644
--- a/src/patchelf.cc
+++ b/src/patchelf.cc
@@ -24,6 +24,7 @@
#include <memory>
#include <sstream>
#include <limits>
+#include <stdexcept>
#include <cstdlib>
#include <cstdio>
@@ -44,7 +45,9 @@ static bool debugMode = false;
static bool forceRPath = false;
-static std::string fileName;
+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;
@@ -134,8 +137,11 @@ private:
ElfFile * elfFile;
bool operator ()(const Elf_Phdr & x, const Elf_Phdr & y)
{
- if (x.p_type == PT_PHDR) return true;
+ // A PHDR comes before everything else.
if (y.p_type == PT_PHDR) return false;
+ if (x.p_type == PT_PHDR) return true;
+
+ // Sort non-PHDRs by address.
return elfFile->rdi(x.p_paddr) < elfFile->rdi(y.p_paddr);
}
};
@@ -328,7 +334,12 @@ static FileContents readFile(std::string fileName,
int fd = open(fileName.c_str(), O_RDONLY);
if (fd == -1) throw SysError(fmt("opening '", fileName, "'"));
- if ((size_t) read(fd, contents->data(), size) != size)
+ size_t bytesRead = 0;
+ ssize_t portion;
+ while ((portion = read(fd, contents->data() + bytesRead, size - bytesRead)) > 0)
+ bytesRead += portion;
+
+ if (bytesRead != size)
throw SysError(fmt("reading '", fileName, "'"));
close(fd);
@@ -393,10 +404,13 @@ ElfFile<ElfFileParamNames>::ElfFile(FileContents fileContents)
error("wrong ELF type");
if ((size_t) (rdi(hdr->e_phoff) + rdi(hdr->e_phnum) * rdi(hdr->e_phentsize)) > fileContents->size())
- error("missing program headers");
+ error("program header table out of bounds");
+
+ if (rdi(hdr->e_shnum) == 0)
+ error("no section headers. The input file is probably a statically linked, self-decompressing binary");
if ((size_t) (rdi(hdr->e_shoff) + rdi(hdr->e_shnum) * rdi(hdr->e_shentsize)) > fileContents->size())
- error("missing section headers");
+ error("section header table out of bounds");
if (rdi(hdr->e_phentsize) != sizeof(Elf_Phdr))
error("program headers have wrong size");
@@ -463,7 +477,7 @@ void ElfFile<ElfFileParamNames>::sortShdrs()
/* Sort the sections by offset. */
CompShdr comp;
comp.elfFile = this;
- sort(shdrs.begin(), shdrs.end(), comp);
+ sort(shdrs.begin() + 1, shdrs.end(), comp);
/* Restore the sh_link mappings. */
for (unsigned int i = 1; i < rdi(hdr->e_shnum); ++i)
@@ -485,11 +499,18 @@ 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");
- if (write(fd, contents->data(), contents->size()) != (off_t) contents->size())
+ size_t bytesWritten = 0;
+ ssize_t portion;
+ while ((portion = write(fd, contents->data() + bytesWritten, contents->size() - bytesWritten)) > 0)
+ bytesWritten += portion;
+
+ if (bytesWritten != contents->size())
error("write");
if (close(fd) != 0)
@@ -558,8 +579,12 @@ template<ElfFileParams>
Elf_Shdr & ElfFile<ElfFileParamNames>::findSection(const SectionName & sectionName)
{
Elf_Shdr * shdr = findSection2(sectionName);
- if (!shdr)
- error("cannot find section '" + sectionName + "'");
+ if (!shdr) {
+ std::string extraMsg = "";
+ if (sectionName == ".interp" || sectionName == ".dynamic" || sectionName == ".dynstr")
+ extraMsg = ". The input file is most likely statically linked";
+ error("cannot find section '" + sectionName + "'" + extraMsg);
+ }
return *shdr;
}
@@ -621,7 +646,8 @@ void ElfFile<ElfFileParamNames>::writeReplacedSections(Elf_Off & curOff,
for (auto & i : replacedSections) {
std::string sectionName = i.first;
Elf_Shdr & shdr = findSection(sectionName);
- memset(contents + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size));
+ if (shdr.sh_type != SHT_NOBITS)
+ memset(contents + rdi(shdr.sh_offset), 'X', rdi(shdr.sh_size));
}
for (auto & i : replacedSections) {
@@ -1231,7 +1257,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;
@@ -1245,15 +1281,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());
@@ -1461,6 +1488,7 @@ void ElfFile<ElfFileParamNames>::addNeeded(const std::set<std::string> & libs)
wri(newDyn.d_tag, DT_NEEDED);
wri(newDyn.d_un.d_val, j);
setSubstr(newDynamic, i * sizeof(Elf_Dyn), std::string((char *) &newDyn, sizeof(Elf_Dyn)));
+ i++;
}
changed = true;
@@ -1542,7 +1570,7 @@ static bool printNeeded = false;
static bool noDefaultLib = false;
template<class ElfFile>
-static void patchElf2(ElfFile && elfFile)
+static void patchElf2(ElfFile && elfFile, const FileContents & fileContents, std::string fileName)
{
if (printInterpreter)
printf("%s\n", elfFile.getInterpreter().c_str());
@@ -1578,23 +1606,29 @@ static void patchElf2(ElfFile && elfFile)
if (elfFile.isChanged()){
elfFile.rewriteSections();
writeFile(fileName, elfFile.fileContents);
+ } else if (alwaysWrite) {
+ debug("not modified, but alwaysWrite=true\n");
+ writeFile(fileName, fileContents);
}
}
static void patchElf()
{
- if (!printInterpreter && !printRPath && !printSoname && !printNeeded)
- debug("patching ELF file '%s'\n", fileName.c_str());
+ for (auto fileName : fileNames) {
+ if (!printInterpreter && !printRPath && !printSoname && !printNeeded)
+ debug("patching ELF file '%s'\n", fileName.c_str());
- debug("Kernel page size is %u bytes\n", getPageSize());
+ debug("Kernel page size is %u bytes\n", getPageSize());
- auto fileContents = readFile(fileName);
+ 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));
- else
- patchElf2(ElfFile<Elf64_Ehdr, Elf64_Phdr, Elf64_Shdr, Elf64_Addr, Elf64_Off, Elf64_Dyn, Elf64_Sym, Elf64_Verneed>(fileContents));
+ if (getElfType(fileContents).is32Bit)
+ 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), fileContents, outputFileName2);
+ }
}
@@ -1617,9 +1651,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());
}
@@ -1703,6 +1738,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;
}
@@ -1717,11 +1757,15 @@ int mainWrapped(int argc, char * * argv)
printf(PACKAGE_STRING "\n");
return 0;
}
- else break;
+ else {
+ fileNames.push_back(arg);
+ }
}
- if (i == argc) error("missing filename");
- fileName = argv[i];
+ if (fileNames.empty()) error("missing filename");
+
+ if (!outputFileName.empty() && fileNames.size() != 1)
+ error("--output option only allowed with single input file");
patchElf();
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