diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..142250dc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,26 @@ +# EditorConfig configuration for PatchELF +# http://EditorConfig.org + +# Top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file, UTF-8 charset +[*] +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +charset = utf-8 + +# Match Nix files, set indent to spaces with width of two +[*.nix] +indent_style = space +indent_size = 2 + +# Match C++/C/shell, set indent to spaces with width of four +[*.{hpp,cc,hh,c,h,sh}] +indent_style = space +indent_size = 4 + +# Match diffs, avoid to trim trailing whitespace +[*.{diff,patch}] +trim_trailing_whitespace = false diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 00000000..1392aa88 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Manual nixfmt run +706478750fc8574ed816ef746d3ad3790b2ea66a diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 430cf21b..39eea409 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,23 +1,24 @@ name: "CI" on: pull_request: + merge_group: push: jobs: shellcheck: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - run: shellcheck tests/*.sh nix: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v20 + - uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v31 - run: nix-build -A hydraJobs.release ubuntu: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: get toolchain version run: | c++ --version diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c7ed3372..624eae2e 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -1,6 +1,7 @@ name: Publish on: pull_request: + merge_group: push: branches: - '*' @@ -13,16 +14,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v20 + uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v31 - name: Build tarballs run: | nix build -L .#hydraJobs.tarball install -D ./result/tarballs/*.tar.bz2 ./dist/patchelf-$(cat version).tar.bz2 install -D ./result/tarballs/*.tar.gz ./dist/patchelf-$(cat version).tar.gz - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: patchelf + name: patchelf-tarball path: dist/* build_windows: @@ -30,16 +31,16 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v3 - - uses: cachix/install-nix-action@v20 + uses: actions/checkout@v4 + - uses: cachix/install-nix-action@v31 - name: Build windows executable run: | nix build -L .#patchelf-win32 .#patchelf-win64 install -D ./result/bin/patchelf.exe ./dist/patchelf-win32-$(cat version).exe install -D ./result-1/bin/patchelf.exe ./dist/patchelf-win64-$(cat version).exe - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: patchelf + name: patchelf-windows path: dist/* test_windows: @@ -47,10 +48,10 @@ jobs: needs: [build_windows] runs-on: windows-latest steps: - - uses: actions/checkout@v3 - - uses: actions/download-artifact@v3 + - uses: actions/checkout@v4 + - uses: actions/download-artifact@v5 with: - name: patchelf + name: patchelf-windows path: dist - name: Show binaries run: dir .\\dist @@ -71,11 +72,21 @@ jobs: steps: - name: Set up QEMU if: matrix.platform != 'amd64' - uses: docker/setup-qemu-action@v2 + uses: docker/setup-qemu-action@v3 - - uses: actions/download-artifact@v3 + - name: Set docker arch + run: | + platform=${{ matrix.platform }} + if [[ $platform == arm64v8 ]]; then + platform=arm64 + elif [[ $platform == arm32v7 ]]; then + platform=arm + fi + echo "DOCKER_PLATFORM=$platform" >> $GITHUB_ENV + + - uses: actions/download-artifact@v5 with: - name: patchelf + name: patchelf-tarball path: dist - name: Build binaries env: @@ -100,7 +111,7 @@ jobs: else ENTRYPOINT= fi - docker run -e CXXFLAGS -v $(pwd):/gha ${{ matrix.platform }}/alpine:edge ${ENTRYPOINT} sh -ec "cd /gha && sh ./build.sh" + docker run --platform "$DOCKER_PLATFORM" -e CXXFLAGS -v $(pwd):/gha ${{ matrix.platform }}/alpine:edge ${ENTRYPOINT} sh -ec "cd /gha && sh ./build.sh" - name: Check binaries run: | cat < check.sh @@ -109,10 +120,10 @@ jobs: tar -xf ./dist/patchelf-*-*.tar.gz ./bin/patchelf --version EOF - docker run -v $(pwd):/gha ${{ matrix.platform }}/debian:unstable-slim sh -ec "cd /gha && sh ./check.sh" - - uses: actions/upload-artifact@v3 + docker run --platform "$DOCKER_PLATFORM" -v $(pwd):/gha ${{ matrix.platform }}/debian:unstable-slim sh -ec "cd /gha && sh ./check.sh" + - uses: actions/upload-artifact@v4 with: - name: patchelf + name: patchelf-${{ matrix.platform }} path: dist/* publish: @@ -121,9 +132,10 @@ jobs: if: github.event_name == 'push' && github.repository == 'NixOS/patchelf' && startsWith(github.ref, 'refs/tags/') runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@v3 + - uses: actions/download-artifact@v5 with: - name: patchelf + pattern: patchelf-* + merge-multiple: true path: dist - name: Upload binaries to release uses: svenstaro/upload-release-action@v2 diff --git a/.gitignore b/.gitignore index 423e8eec..df440b64 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ Makefile .deps *.o +CMakeLists.txt.user + /tests/*.log /tests/*.trs /tests/no-rpath diff --git a/.mergify.yml b/.mergify.yml new file mode 100644 index 00000000..442f64ef --- /dev/null +++ b/.mergify.yml @@ -0,0 +1,26 @@ +queue_rules: + - name: default + merge_conditions: + - check-success=Build static musl binaries (amd64) + - check-success=Build static musl binaries (arm32v7) + - check-success=Build static musl binaries (arm64v8) + - check-success=Build static musl binaries (i386) + - check-success=Build static musl binaries (ppc64le) + - check-success=Build static musl binaries (riscv64) + - check-success=Build static musl binaries (s390x) + - check-success=Build tarballs + - check-success=Build windows executable + - check-success=Publish tarballs & binaries + - check-success=Test windows binaries + - check-success=nix + - check-success=shellcheck + - check-success=ubuntu + merge_method: rebase + batch_size: 5 +pull_request_rules: + - name: merge using the merge queue + conditions: + - base=master + - label~=merge-queue|dependencies + actions: + queue: {} diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000..4761582c --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.5) + +project(patchelf) + +file(READ version VERSION) + +include(GNUInstallDirs) + +add_subdirectory(src) +# add_subdirectory(tests) # TODO + +install(FILES patchelf.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1) + +install(FILES README.md DESTINATION ${CMAKE_INSTALL_DOCDIR}) + +install(FILES completions/zsh/_patchelf + DESTINATION ${CMAKE_INSTALL_DATADIR}/zsh/site-functions) diff --git a/COPYING b/COPYING index 94a9ed02..e6000869 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,7 @@ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 - Copyright (C) 2007 Free Software Foundation, Inc. + Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -645,7 +645,7 @@ the "copyright" line and a pointer to where the full notice is found. GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program. If not, see . + along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. @@ -664,11 +664,11 @@ might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see -. +. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read -. +. diff --git a/ChangeLog.md b/ChangeLog.md new file mode 100644 index 00000000..1f408ab1 --- /dev/null +++ b/ChangeLog.md @@ -0,0 +1,185 @@ +## Release History + +## 0.15.2 (August 12, 2025) + +* In the autotools build system for the tests, pass a few more tool env vars in `TESTS_ENVIRONMENT`. + + Some of them are not used yet (at all, or at least not in this backport to the `0.15-maintenance` branch), but at least `STRIP` is. + +## 0.15.1 (August 12, 2025) + +The objective is to prepare a very conservative patch release which can be bumped in Nixpkgs immediately. + +* Backport infra changes from later master (August 2025) +* Backport shellcheck fixes to test suite +* Backport C++ code quality fixes (all of https://github.com/NixOS/patchelf/pull/464 but the final two commits) + +The behavior of this version should be essentially the same as 0.15.0. + +## 0.15.0 (July 16, 2022) + +* Add --add-debug option by @deadw00d in https://github.com/NixOS/patchelf/pull/367 +* Add O_BINARY flag when opening files to allow compilation for Windows by @JagoGyselinck in https://github.com/NixOS/patchelf/pull/372 +* Document --print-needed by @klemensn in https://github.com/NixOS/patchelf/pull/375 +* modifyRPath: return early if new and old rpath are empty by @ehmry in https://github.com/NixOS/patchelf/pull/376 +* Add comment explaining calculation for DT_MIPS_RLD_MAP_REL by @amjoseph-nixpkgs in https://github.com/NixOS/patchelf/pull/379 +* Add --no-sort option by @amjoseph-nixpkgs in https://github.com/NixOS/patchelf/pull/378 +* Handle DT_MIPS_XHASH and .MIPS.xhash by @amjoseph-nixpkgs in https://github.com/NixOS/patchelf/pull/380 + +## 0.14.5 (February 21, 2022) + +* fix faulty version in 0.14.4 + +## 0.14.4 (February 21, 2022) + +* Several test fixes to fix patchelf test suite on openbsd by @klemensn +* Allow multiple modifications in same call by @fzakaria in https://github.com/NixOS/patchelf/pull/361 +* Add support to build with musl by @fzakaria in https://github.com/NixOS/patchelf/pull/362 +* Fix typo: s/folllow/follow/ by @bjornfor in https://github.com/NixOS/patchelf/pull/366 +* mips: fix incorrect polarity on dyn_offset; closes #364 by @a-m-joseph in https://github.com/NixOS/patchelf/pull/365 + +## 0.14.3 (December 05, 2021) + +* this release adds support for static, pre-compiled patchelf binaries + +## 0.14.2 (November 29, 2021) + +* make version number in tarball easier to use for packagers + +## 0.14.1 (November 28, 2021) + +* build fix: add missing include + +## 0.14 (November 27, 2021) + +Changes compared to 0.13: + +* Bug fixes: + - Fix corrupted library names when using --replace-needed multiple times + - Fix setting an empty rpath + - Don't try to parse .dynamic section of type NOBITS + - Fix use-after-free in normalizeNoteSegments + - Correct EINTR handling in writeFile + - MIPS: Adjust PT_MIPS_ABIFLAGS segment and DT_MIPS_RLD_MAP_REL dynamic section if present + - Fix binaries without .gnu.hash section +* Support loongarch architecture +* Remove limits on output file size for elf files +* Allow reading rpath from file +* Requires now C++17 for building + +## 0.13.1 (November 27, 2021) + +* Bug fixes: + - fix setting empty rpath + - use memcpy instead of strcpy to set rpath + - Don't try to parse .dynamic section of type NOBITS + - fix use-after-free in normalizeNoteSegments + - correct EINTR handling in writeFile + - Adjust PT_MIPS_ABIFLAGS segment if present + - Adjust DT_MIPS_RLD_MAP_REL dynamic section entry if present + - fix binaries without .gnu.hash section + +## 0.13 (August 5, 2021) + +* New `--add-rpath` flag. + +* Bug fixes. + +## 0.12 (August 27, 2020) + +* New `--clear-symbol-version` flag. + +* Better support for relocating NOTE sections/segments. + +* Improved the default section alignment choice. + +* Bug fixes. + +## 0.11 (June 9, 2020) + +* New `--output` flag. + +* Some bug fixes. + +## 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) + +* Lots of new features. Please refer to the Git commit log: + + https://github.com/NixOS/patchelf/commits/master + + This release has contributions from Aaron D. Marasco, Adrien + Devresse, Alexandre Pretyman, Changli Gao, Chingis Dugarzhapov, + darealshinji, David Sveningsson, Eelco Dolstra, Felipe Sateler, + Jeremy Sanders, Jonas Kuemmerlin, Thomas Tuegel, Tuomas Tynkkynen, + Vincent Danjean and Vladimír Čunát. + +## 0.8 (January 15, 2014) + +* Fix a segfault caused by certain illegal entries in symbol tables. + +## 0.7 (January 7, 2014) + +* Rewrite section indices in symbol tables. This for instance allows + gdb to show proper backtraces. + +* Added `--remove-needed' option. + +## 0.6 (November 7, 2011) + +* Hacky support for executables created by the Gold linker. + +* Support segments with an alignment of 0 (contributed by Zack + Weinberg). + +* Added a manual page (contributed by Jeremy Sanders + ). + +## 0.5 (November 4, 2009) + +* Various bugfixes. + +* `--force-rpath' now deletes the DT_RUNPATH if it is present. + +## 0.4 (June 4, 2008) + +* Support for growing the RPATH on dynamic libraries. + +* IA-64 support (not tested) and related 64-bit fixes. + +* FreeBSD support. + +* `--set-rpath', `--shrink-rpath' and `--print-rpath' now prefer + DT_RUNPATH over DT_RPATH, which is obsolete. When updating, if both + are present, both are updated. If only DT_RPATH is present, it is + converted to DT_RUNPATH unless `--force-rpath' is specified. If + neither is present, a DT_RUNPATH is added unless `--force-rpath' is + specified, in which case a DT_RPATH is added. + +## 0.3 (May 24, 2007) + +* Support for 64-bit ELF binaries (such as on x86_64-linux). + +* Support for big-endian ELF binaries (such as on powerpc-linux). + +* Various bugfixes. + +## 0.2 (January 15, 2007) + +* Provides a hack to get certain programs (such as the + Belastingaangifte 2005) to work. + +## 0.1 (October 11, 2005) + +* Initial release. diff --git a/README.md b/README.md index 8cef9eb9..0b433771 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +# PatchELF + PatchELF is a simple utility for modifying existing ELF executables and libraries. In particular, it can do the following: @@ -70,7 +72,7 @@ libraries. In particular, it can do the following: ## Compiling and Testing -### Via Autotools +### Via [GNU Autotools](https://www.gnu.org/software/automake/manual/html_node/Autotools-Introduction.html) ```console ./bootstrap.sh ./configure @@ -78,6 +80,27 @@ make make check sudo make install ``` + +### Via [CMake](https://cmake.org/) (and [Ninja](https://ninja-build.org/)) + +```console +mkdir build +cd build +cmake .. -GNinja +ninja all +sudo ninja install +``` + +### Via [Meson](https://mesonbuild.com/) (and [Ninja](https://ninja-build.org/)) + +```console +mkdir build +meson configure build +cd build +ninja all +sudo ninja install +``` + ### Via Nix You can build with Nix in several ways. @@ -86,6 +109,10 @@ You can build with Nix in several ways. 2. You can launch a development environment with `nix develop` and follow the autotools steps above. If you would like to develop with _musl_ try `nix develop .#musl` +## Help and resources + +- Matrix: [#patchelf:nixos.org](https://matrix.to/#/#patchelf:nixos.org) + ## Author Copyright 2004-2019 Eelco Dolstra . diff --git a/configure.ac b/configure.ac index 41299204..fee6302f 100644 --- a/configure.ac +++ b/configure.ac @@ -7,9 +7,11 @@ AC_CONFIG_MACRO_DIR([m4]) AC_CHECK_TOOL([STRIP], [strip]) # Those are only used in tests, hence we gracefully degrate if they are not found. +AC_CHECK_TOOL([NM], [nm], [nm]) AC_CHECK_TOOL([OBJDUMP], [objdump], [objdump]) AC_CHECK_TOOL([OBJCOPY], [objcopy], [objcopy]) AC_CHECK_TOOL([READELF], [readelf], [readelf]) +AC_CHECK_TOOL([STRINGS], [strings], [strings]) AM_PROG_CC_C_O AC_PROG_CXX diff --git a/default.nix b/default.nix index 71d1a80a..00ec5b61 100644 --- a/default.nix +++ b/default.nix @@ -1,3 +1,3 @@ -(import (fetchTarball https://github.com/edolstra/flake-compat/archive/master.tar.gz) { +(import (fetchTarball "https://github.com/edolstra/flake-compat/archive/master.tar.gz") { src = ./.; }).defaultNix diff --git a/flake.lock b/flake.lock index 1e068201..3e2769b8 100644 --- a/flake.lock +++ b/flake.lock @@ -1,22 +1,70 @@ { "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1733312601, + "narHash": "sha256-4pDvzqnegAfRkPwO3wmwBhVi/Sye1mzps0zHWYnP88c=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "205b12d8b7cd4802fbcb8e8ef6a0f1408781a4f9", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "git-hooks-nix": { + "inputs": { + "flake-compat": [], + "gitignore": [], + "nixpkgs": [ + "nixpkgs" + ], + "nixpkgs-stable": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1734279981, + "narHash": "sha256-NdaCraHPp8iYMWzdXAt5Nv6sA3MUzlCiGiR586TCwo0=", + "owner": "cachix", + "repo": "git-hooks.nix", + "rev": "aa9f40c906904ebd83da78e7f328cd8aeaeae785", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "git-hooks.nix", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1672057183, - "narHash": "sha256-GN7/10DNNvs1FPj9tlZA2qgNdFuYKKuS3qlHTqAxasQ=", + "lastModified": 1731763621, + "narHash": "sha256-ddcX4lQL0X05AYkrkV2LMFgGdRvgap7Ho8kgon3iWZk=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b139e44d78c36c69bcbb825b20dbfa51e7738347", + "rev": "c69a9bffbecde46b4b939465422ddc59493d3e4d", "type": "github" }, "original": { - "id": "nixpkgs", + "owner": "NixOS", "ref": "nixpkgs-unstable", - "type": "indirect" + "repo": "nixpkgs", + "type": "github" } }, "root": { "inputs": { + "flake-parts": "flake-parts", + "git-hooks-nix": "git-hooks-nix", "nixpkgs": "nixpkgs" } } diff --git a/flake.nix b/flake.nix index 471a31b9..fb05fc24 100644 --- a/flake.nix +++ b/flake.nix @@ -1,23 +1,101 @@ { description = "A tool for modifying ELF executables and libraries"; - inputs.nixpkgs.url = "nixpkgs/nixpkgs-unstable"; + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - outputs = { self, nixpkgs }: + # dev tooling + inputs.flake-parts.url = "github:hercules-ci/flake-parts"; + inputs.git-hooks-nix.url = "github:cachix/git-hooks.nix"; + # work around https://github.com/NixOS/nix/issues/7730 + inputs.flake-parts.inputs.nixpkgs-lib.follows = "nixpkgs"; + inputs.git-hooks-nix.inputs.nixpkgs.follows = "nixpkgs"; + inputs.git-hooks-nix.inputs.nixpkgs-stable.follows = "nixpkgs"; + # work around 7730 and https://github.com/NixOS/nix/issues/7807 + inputs.git-hooks-nix.inputs.flake-compat.follows = ""; + inputs.git-hooks-nix.inputs.gitignore.follows = ""; + + outputs = + inputs@{ self, nixpkgs, ... }: let - supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ]; - forAllSystems = nixpkgs.lib.genAttrs supportedSystems; + inherit (nixpkgs) lib; + + supportedSystems = [ + "x86_64-linux" + "i686-linux" + "aarch64-linux" + ]; + forAllSystems = lib.genAttrs supportedSystems; - version = nixpkgs.lib.removeSuffix "\n" (builtins.readFile ./version); + version = lib.removeSuffix "\n" (builtins.readFile ./version); pkgs = nixpkgs.legacyPackages.x86_64-linux; + baseSrcFiles = [ + ./COPYING + ./README.md + ./completions + ./patchelf.1 + ./patchelf.spec.in + (lib.fileset.difference ./src ( + lib.fileset.unions [ + ./src/Makefile.am + ./src/CMakeLists.txt + ./src/meson.build + ] + )) + (lib.fileset.difference ./tests ( + lib.fileset.unions [ + ./tests/Makefile.am + #./tests/CMakeLists.txt + #./tests/meson.build + ] + )) + ./version + ]; + + autotoolsSrcFiles = [ + ./Makefile.am + ./src/Makefile.am + ./tests/Makefile.am + ./configure.ac + ./m4 + ]; + + cmakeSrcFiles = [ + ./CMakeLists.txt + ./src/CMakeLists.txt + #./tests/CMakeLists.txt + ]; + + mesonSrcFiles = [ + ./meson.build + ./meson.options + ./src/meson.build + #./tests/meson.build + ]; + + autotoolsSrc = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions (baseSrcFiles ++ autotoolsSrcFiles); + }; + + patchelfFor = + pkgs: + pkgs.callPackage ./package-autotools.nix { + inherit version; + src = autotoolsSrc; + }; - patchelfFor = pkgs: let - # this is only - in pkgs.callPackage ./patchelf.nix { - inherit version; - src = self; + # We don't apply flake-parts to the whole flake so that non-development attributes + # load without fetching any development inputs. + devFlake = inputs.flake-parts.lib.mkFlake { inherit inputs; } { + imports = [ ./maintainers/flake-module.nix ]; + systems = supportedSystems; + perSystem = + { system, ... }: + { + _module.args.pkgs = nixpkgs.legacyPackages.${system}; + }; }; in @@ -29,96 +107,190 @@ }; hydraJobs = { - tarball = - pkgs.releaseTools.sourceTarball rec { - name = "patchelf-tarball"; - inherit version; - versionSuffix = ""; # obsolete - src = self; - preAutoconf = "echo ${version} > version"; - postDist = '' - cp README.md $out/ - echo "doc readme $out/README.md" >> $out/nix-support/hydra-build-products - ''; + tarball = pkgs.releaseTools.sourceTarball rec { + name = "patchelf-tarball"; + inherit version; + src = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions (baseSrcFiles ++ autotoolsSrcFiles ++ cmakeSrcFiles ++ mesonSrcFiles); }; + versionSuffix = ""; # obsolete + preAutoconf = "echo ${version} > version"; + + # portable configure shouldn't have a shebang pointing to the nix store + postConfigure = '' + sed -i '1s|^.*$|#!/bin/sh|' ./configure + ''; + postDist = '' + cp README.md $out/ + echo "doc readme $out/README.md" >> $out/nix-support/hydra-build-products + ''; + }; coverage = (pkgs.releaseTools.coverageAnalysis { name = "patchelf-coverage"; src = self.hydraJobs.tarball; - lcovFilter = ["*/tests/*"]; - }).overrideAttrs (old: { - preCheck = '' - # coverage cflag breaks this target - NIX_CFLAGS_COMPILE=''${NIX_CFLAGS_COMPILE//--coverage} make -C tests phdr-corruption.so - ''; - }); + lcovFilter = [ "*/tests/*" ]; + }).overrideAttrs + (old: { + preCheck = '' + # coverage cflag breaks this target + NIX_CFLAGS_COMPILE=''${NIX_CFLAGS_COMPILE//--coverage} make -C tests phdr-corruption.so + ''; + }); build = forAllSystems (system: self.packages.${system}.patchelf); - build-sanitized = forAllSystems (system: self.packages.${system}.patchelf.overrideAttrs (old: { - configureFlags = [ "--with-asan " "--with-ubsan" ]; - # -Wno-unused-command-line-argument is for clang, which does not like - # our cc wrapper arguments - CFLAGS = "-Werror -Wno-unused-command-line-argument"; - })); + build-sanitized = forAllSystems ( + system: + self.packages.${system}.patchelf.overrideAttrs (old: { + configureFlags = [ + "--with-asan " + "--with-ubsan" + ]; + # -Wno-unused-command-line-argument is for clang, which does not like + # our cc wrapper arguments + CFLAGS = "-Werror -Wno-unused-command-line-argument"; + }) + ); + + build-cmake = forAllSystems (system: self.packages.${system}.patchelf-cmake); + + build-meson = forAllSystems (system: self.packages.${system}.patchelf-meson); # x86_64-linux seems to be only working clangStdenv at the moment - build-sanitized-clang = nixpkgs.lib.genAttrs [ "x86_64-linux" ] (system: self.hydraJobs.build-sanitized.${system}.override { - stdenv = nixpkgs.legacyPackages.${system}.llvmPackages_latest.libcxxStdenv; - }); + build-sanitized-clang = lib.genAttrs [ "x86_64-linux" ] ( + system: + self.hydraJobs.build-sanitized.${system}.override { + stdenv = nixpkgs.legacyPackages.${system}.llvmPackages_latest.libcxxStdenv; + } + ); # To get mingw compiler from hydra cache inherit (self.packages.x86_64-linux) patchelf-win32 patchelf-win64; - release = pkgs.releaseTools.aggregate - { name = "patchelf-${self.hydraJobs.tarball.version}"; - constituents = - [ self.hydraJobs.tarball - self.hydraJobs.build.x86_64-linux - self.hydraJobs.build.i686-linux - # FIXME: add aarch64 emulation to our github action... - #self.hydraJobs.build.aarch64-linux - self.hydraJobs.build-sanitized.x86_64-linux - #self.hydraJobs.build-sanitized.aarch64-linux - self.hydraJobs.build-sanitized.i686-linux - self.hydraJobs.build-sanitized-clang.x86_64-linux - ]; - meta.description = "Release-critical builds"; - }; + release = pkgs.releaseTools.aggregate { + name = "patchelf-${self.hydraJobs.tarball.version}"; + constituents = [ + self.hydraJobs.tarball + self.hydraJobs.build.x86_64-linux + self.hydraJobs.build.i686-linux + self.hydraJobs.build-cmake.x86_64-linux + self.hydraJobs.build-meson.x86_64-linux + # FIXME: add aarch64 emulation to our github action... + #self.hydraJobs.build.aarch64-linux + self.hydraJobs.build-sanitized.x86_64-linux + #self.hydraJobs.build-sanitized.aarch64-linux + self.hydraJobs.build-sanitized.i686-linux + self.hydraJobs.build-sanitized-clang.x86_64-linux + ]; + meta.description = "Release-critical builds"; + }; }; - checks = forAllSystems (system: { - build = self.hydraJobs.build.${system}; - }); - - devShells = forAllSystems (system: { - glibc = self.packages.${system}.patchelf; - default = self.devShells.${system}.glibc; - } // nixpkgs.lib.optionalAttrs (system != "i686-linux") { - musl = self.packages.${system}.patchelf-musl; - }); - - packages = forAllSystems (system: let - pkgs = nixpkgs.legacyPackages.${system}; - in { - patchelf = patchelfFor pkgs; - default = self.packages.${system}.patchelf; - - # This is a good test to see if packages can be cross-compiled. It also - # tests if our testsuite uses target-prefixed executable names. - patchelf-musl-cross = patchelfFor pkgs.pkgsCross.musl64; - patchelf-netbsd-cross = patchelfFor pkgs.pkgsCross.x86_64-netbsd; - - patchelf-win32 = (patchelfFor pkgs.pkgsCross.mingw32).overrideAttrs (old: { - NIX_CFLAGS_COMPILE = "-static"; - }); - patchelf-win64 = (patchelfFor pkgs.pkgsCross.mingwW64).overrideAttrs (old: { - NIX_CFLAGS_COMPILE = "-static"; - }); - } // nixpkgs.lib.optionalAttrs (system != "i686-linux") { - patchelf-musl = patchelfFor nixpkgs.legacyPackages.${system}.pkgsMusl; - }); + checks = forAllSystems ( + system: + { + build = self.hydraJobs.build.${system}; + } + // devFlake.checks.${system} or { } + ); + + devShells = forAllSystems ( + system: + let + mkShell = + patchelf: + patchelf.overrideAttrs ( + old: + let + pkgs = nixpkgs.legacyPackages.${system}; + modular = devFlake.getSystem pkgs.stdenv.buildPlatform.system; + in + { + env = (old.env or { }) // { + _NIX_PRE_COMMIT_HOOKS_CONFIG = "${(pkgs.formats.yaml { }).generate "pre-commit-config.yaml" + modular.pre-commit.settings.rawConfig + }"; + }; + nativeBuildInputs = (old.nativeBuildInputs or [ ]) ++ [ + #pkgs.buildPackages.cmake + #pkgs.buildPackages.meson + #pkgs.buildPackages.ninja + modular.pre-commit.settings.package + (pkgs.buildPackages.writeScriptBin "pre-commit-hooks-install" modular.pre-commit.settings.installationScript) + ]; + } + ); + in + { + glibc = mkShell self.packages.${system}.patchelf; + default = self.devShells.${system}.glibc; + } + // lib.optionalAttrs (system != "i686-linux") { + musl = mkShell self.packages.${system}.patchelf-musl; + } + ); + + packages = forAllSystems ( + system: + let + pkgs = nixpkgs.legacyPackages.${system}; + + patchelfForWindowsStatic = + pkgs: + (pkgs.callPackage ./package-autotools.nix { + inherit version; + src = autotoolsSrc; + # On windows we use win32 threads to get a static binary, + # otherwise `-static` below doesn't work. + stdenv = pkgs.stdenv.override (old: { + cc = old.cc.override (old: { + cc = old.cc.override { + threadsCross = { + model = "win32"; + package = null; + }; + }; + }); + }); + }).overrideAttrs + (old: { + NIX_CFLAGS_COMPILE = "-static"; + }); + in + { + patchelf = patchelfFor pkgs; + default = self.packages.${system}.patchelf; + + patchelf-cmake = pkgs.callPackage ./package-cmake.nix { + inherit version; + src = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions (baseSrcFiles ++ cmakeSrcFiles); + }; + }; + + patchelf-meson = pkgs.callPackage ./package-meson.nix { + inherit version; + src = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions (baseSrcFiles ++ mesonSrcFiles); + }; + }; + + # This is a good test to see if packages can be cross-compiled. It also + # tests if our testsuite uses target-prefixed executable names. + patchelf-musl-cross = patchelfFor pkgs.pkgsCross.musl64; + patchelf-netbsd-cross = patchelfFor pkgs.pkgsCross.x86_64-netbsd; + patchelf-win32 = patchelfForWindowsStatic pkgs.pkgsCross.mingw32; + patchelf-win64 = patchelfForWindowsStatic pkgs.pkgsCross.mingwW64; + } + // lib.optionalAttrs (system != "i686-linux") { + patchelf-musl = patchelfFor nixpkgs.legacyPackages.${system}.pkgsMusl; + } + ); }; } diff --git a/maintainers/flake-module.nix b/maintainers/flake-module.nix new file mode 100644 index 00000000..26eff46c --- /dev/null +++ b/maintainers/flake-module.nix @@ -0,0 +1,96 @@ +{ + lib, + getSystem, + inputs, + ... +}: + +{ + imports = [ + inputs.git-hooks-nix.flakeModule + ]; + + perSystem = + { config, pkgs, ... }: + { + + # https://flake.parts/options/git-hooks-nix#options + pre-commit.settings = { + hooks = { + # Conflicts are usually found by other checks, but not those in docs, + # and potentially other places. + check-merge-conflicts.enable = true; + # built-in check-merge-conflicts seems ineffective against those produced by mergify backports + check-merge-conflicts-2 = { + enable = true; + entry = "${pkgs.writeScript "check-merge-conflicts" '' + #!${pkgs.runtimeShell} + conflicts=false + for file in "$@"; do + if grep --with-filename --line-number -E '^>>>>>>> ' -- "$file"; then + conflicts=true + fi + done + if $conflicts; then + echo "ERROR: found merge/patch conflicts in files" + exit 1 + fi + ''}"; + }; + cmake-format = { + enable = true; + }; + meson-format = + let + meson = pkgs.meson.overrideAttrs { + doCheck = false; + doInstallCheck = false; + patches = [ + (pkgs.fetchpatch { + url = "https://github.com/mesonbuild/meson/commit/38d29b4dd19698d5cad7b599add2a69b243fd88a.patch"; + hash = "sha256-PgPBvGtCISKn1qQQhzBW5XfknUe91i5XGGBcaUK4yeE="; + }) + ]; + }; + in + { + enable = true; + files = "(meson.build|meson.options)$"; + entry = "${pkgs.writeScript "format-meson" '' + #!${pkgs.runtimeShell} + for file in "$@"; do + ${lib.getExe meson} format -ic ${../meson.format} "$file" + done + ''}"; + }; + nixfmt-rfc-style = { + enable = true; + }; + clang-format = { + enable = true; + # https://github.com/cachix/git-hooks.nix/pull/532 + package = pkgs.llvmPackages_latest.clang-tools; + # Not yet formatted + excludes = [ + ''^src/elf.h$'' + ''^src/patchelf.cc$'' + ''^src/patchelf.h$'' + ''^tests/bar.c$'' + ''^tests/foo.c$'' + ''^tests/main.c$'' + ''^tests/no-rpath.c$'' + ''^tests/simple.c$'' + ''^tests/too-many-strtab.c$'' + ''^tests/void.c$'' + ]; + }; + shellcheck = { + enable = true; + }; + }; + }; + }; + + # We'll be pulling from this in the main flake + flake.getSystem = getSystem; +} diff --git a/maintainers/format.sh b/maintainers/format.sh new file mode 100755 index 00000000..b2902e6d --- /dev/null +++ b/maintainers/format.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash + +if ! type -p pre-commit &>/dev/null; then + echo "format.sh: pre-commit not found. Please use \`nix develop -c ./maintainers/format.sh\`."; + exit 1; +fi; +if test -z "$_NIX_PRE_COMMIT_HOOKS_CONFIG"; then + echo "format.sh: _NIX_PRE_COMMIT_HOOKS_CONFIG not set. Please use \`nix develop -c ./maintainers/format.sh\`."; + exit 1; +fi; + +while ! pre-commit run --config "$_NIX_PRE_COMMIT_HOOKS_CONFIG" --all-files; do + if [ "${1:-}" != "--until-stable" ]; then + exit 1 + fi +done diff --git a/meson.build b/meson.build new file mode 100644 index 00000000..82d5bb6f --- /dev/null +++ b/meson.build @@ -0,0 +1,36 @@ +project( + 'patchelf', + 'cpp', + 'c', + version : files('version'), + default_options : { + 'cpp_std' : 'c++17', + 'warning_level' : '2', + }, + meson_version : '>=1.2', +) + +subdir('src') +#subdir('tests') # TODO + +install_man('patchelf.1') + +#specfile = configure_file( +# output : 'patchelf.spec', +# configuration : {'PACKAGE_VERSION' : meson.project_version()}, +#) + +# Commented things out should only be for `meson dist`. Need to +# reimplement for that. +install_data( + 'README.md', + #'COPYING', + #specfile, + #'version', + install_dir : get_option('datadir') / 'doc' / 'patchelf', +) + +install_data( + 'completions/zsh/_patchelf', + install_dir : get_option('datadir') / 'zsh' / 'site-functions', +) diff --git a/meson.format b/meson.format new file mode 100644 index 00000000..4876dd96 --- /dev/null +++ b/meson.format @@ -0,0 +1,7 @@ +indent_by = ' ' +space_array = true +kwargs_force_multiline = false +wide_colon = true +group_arg_value = true +indent_before_comments = ' ' +use_editor_config = true diff --git a/meson.options b/meson.options new file mode 100644 index 00000000..21ae69e7 --- /dev/null +++ b/meson.options @@ -0,0 +1,7 @@ +option( + 'page_size', + type : 'combo', + value : 'auto', + choices : [ 'auto', '4096', '65536', '16384', '8192' ], + description : 'Default page size, or "auto" to detect at runtime', +) diff --git a/package-autotools.nix b/package-autotools.nix new file mode 100644 index 00000000..c0e334fa --- /dev/null +++ b/package-autotools.nix @@ -0,0 +1,13 @@ +{ + stdenv, + autoreconfHook, + version, + src, +}: + +stdenv.mkDerivation { + pname = "patchelf"; + inherit version src; + nativeBuildInputs = [ autoreconfHook ]; + doCheck = true; +} diff --git a/package-cmake.nix b/package-cmake.nix new file mode 100644 index 00000000..de8efe20 --- /dev/null +++ b/package-cmake.nix @@ -0,0 +1,17 @@ +{ + stdenv, + cmake, + ninja, + version, + src, +}: + +stdenv.mkDerivation { + pname = "patchelf"; + inherit version src; + nativeBuildInputs = [ + cmake + ninja + ]; + doCheck = true; +} diff --git a/package-meson.nix b/package-meson.nix new file mode 100644 index 00000000..8e7c4656 --- /dev/null +++ b/package-meson.nix @@ -0,0 +1,17 @@ +{ + stdenv, + meson, + ninja, + version, + src, +}: + +stdenv.mkDerivation { + pname = "patchelf"; + inherit version src; + nativeBuildInputs = [ + meson + ninja + ]; + doCheck = true; +} diff --git a/patchelf.nix b/patchelf.nix deleted file mode 100644 index 6e8e59d3..00000000 --- a/patchelf.nix +++ /dev/null @@ -1,21 +0,0 @@ -{ stdenv, buildPackages, autoreconfHook, version, src, overrideCC }: -let - # on windows we use win32 threads to get a fully static binary - gcc = buildPackages.wrapCC (buildPackages.gcc-unwrapped.override ({ - threadsCross = { - model = "win32"; - package = null; - }; - })); - - stdenv' = if (stdenv.cc.isGNU && stdenv.targetPlatform.isWindows) then - overrideCC stdenv gcc - else - stdenv; -in -stdenv'.mkDerivation { - pname = "patchelf"; - inherit version src; - nativeBuildInputs = [ autoreconfHook ]; - doCheck = true; -} diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000..2e6620ed --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,12 @@ +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS OFF) + +set(PAGESIZE 4096) + +add_executable(${PROJECT_NAME} patchelf.cc elf.h patchelf.h) + +target_compile_definitions( + patchelf PRIVATE PAGESIZE=${PAGESIZE} + PACKAGE_STRING="patchelf ${VERSION_STRING}") + +install(TARGETS patchelf RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 00000000..c6204efc --- /dev/null +++ b/src/meson.build @@ -0,0 +1,18 @@ +# Configure DEFAULT_PAGESIZE via config.h +confdata = configuration_data() +page_size = get_option('page_size') +if page_size != 'auto' + confdata.set_quoted('DEFAULT_PAGESIZE', page_size) +else + # For "auto", leave it undefined so runtime detection happens +endif + +config_h = configure_file(output : 'config.h', configuration : confdata) + +executable( + 'patchelf', + [ 'patchelf.cc', 'patchelf.h', config_h ], + include_directories : include_directories('.'), + cpp_args : [ '-include', meson.current_build_dir() / 'config.h' ], + install : true, +) diff --git a/src/patchelf.cc b/src/patchelf.cc index 82b4b46c..4e8d118a 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -1018,7 +1018,7 @@ void ElfFile::rewriteSectionsExecutable() debug("needed space is %d\n", neededSpace); /* Calculate how many bytes are needed out of the additional pages. */ - size_t extraSpace = neededSpace - startOffset; + size_t extraSpace = neededSpace - startOffset; // Always give one extra page to avoid colliding with segments that start at // unaligned addresses and will be rounded down when loaded unsigned int neededPages = 1 + roundUp(extraSpace, getPageSize()) / getPageSize(); @@ -2095,7 +2095,7 @@ void ElfFile::rebuildGnuHashTable(span strTab, span::rebuildGnuHashTable(span strTab, span(i); - if (static_cast(val) != i) + if (static_cast(val) != i) throw std::runtime_error { "value truncation" }; t = rdi(val); return val; diff --git a/tests/Makefile.am b/tests/Makefile.am index a68d2e23..8bbded7a 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -61,7 +61,7 @@ TESTS = $(src_TESTS) $(build_TESTS) EXTRA_DIST = no-rpath-prebuild $(src_TESTS) no-rpath-prebuild.sh invalid-elf endianness empty-note \ overlapping-segments-after-rounding short-first-segment.gz -TESTS_ENVIRONMENT = PATCHELF_DEBUG=1 STRIP=$(STRIP) OBJDUMP=$(OBJDUMP) READELF=$(READELF) OBJCOPY=$(OBJCOPY) +TESTS_ENVIRONMENT = PATCHELF_DEBUG=1 STRIP=$(STRIP) NM=$(NM) OBJDUMP=$(OBJDUMP) READELF=$(READELF) OBJCOPY=$(OBJCOPY) STRINGS=$(STRINGS) $(no_rpath_arch_TESTS): no-rpath-prebuild.sh @ln -s $< $@