diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 430cf21b..b42f9679 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,18 +6,18 @@ 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@v30 - 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 @@ -28,3 +28,87 @@ jobs: mkdir build && cd build ../configure --with-asan --with-ubsan make -j$(nproc) check + + glibc: + strategy: + fail-fast: false + matrix: + glibc: ["2_17", "2_27", "2_28", "2_31", "2_34", "2_35", "2_39"] + platform: ["x86_64", "ppc64le", "aarch64", "s390x"] + include: + - glibc: "2_17" + platform: "i686" + - glibc: "2_27" + platform: "i686" + - glibc: "2_27" + platform: "armv7l" + - glibc: "2_31" + platform: "armv7l" + - glibc: "2_35" + platform: "armv7l" + - glibc: "2_39" + platform: "armv7l" + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - name: Set up QEMU + if: matrix.platform != 'x86_64' + uses: docker/setup-qemu-action@v3 + - run: | + cat < build.sh + set -e + set -x + + if [ "${{ matrix.glibc }}" == "2_27" ]; then + apt-get update + apt-get -y install software-properties-common + add-apt-repository -y ppa:ubuntu-toolchain-r/test + apt-get update + apt-get -y install automake g++-11 make + update-alternatives --install /usr/bin/c++ c++ $(which g++-11) 100 + update-alternatives --install /usr/bin/cc cc $(which gcc-11) 100 + elif [ "${{ matrix.glibc }}" == "2_31" ] || [ "${{ matrix.glibc }}" == "2_35" ] || [ "${{ matrix.glibc }}" == "2_39" ]; then + apt-get update + apt-get -y install automake g++ make + fi + + c++ --version + ld --version + autoconf --version + ./bootstrap.sh + mkdir build && cd build + ../configure + make -j$(nproc) check || (cat tests/test-suite.log; exit 1) + EOF + + if [ "${{ matrix.platform }}" == "x86_64" ]; then + DOCKER_PLATFORM=amd64 + elif [ "${{ matrix.platform }}" == "i686" ]; then + DOCKER_PLATFORM=386 + elif [ "${{ matrix.platform }}" == "aarch64" ]; then + DOCKER_PLATFORM=arm64/v8 + elif [ "${{ matrix.platform }}" == "armv7l" ]; then + DOCKER_PLATFORM=arm/v7 + else + DOCKER_PLATFORM=${{ matrix.platform }} + fi + + if [ "${{ matrix.glibc }}" == "2_17" ]; then + IMAGE=quay.io/pypa/manylinux2014_${{ matrix.platform }}:latest + elif [ "${{ matrix.glibc }}" == "2_27" ]; then + IMAGE=ubuntu:18.04 + elif [ "${{ matrix.glibc }}" == "2_28" ]; then + IMAGE=quay.io/pypa/manylinux_${{ matrix.glibc }}_${{ matrix.platform }}:latest + elif [ "${{ matrix.glibc }}" == "2_31" ]; then + IMAGE=ubuntu:20.04 + elif [ "${{ matrix.glibc }}" == "2_34" ]; then + IMAGE=quay.io/pypa/manylinux_${{ matrix.glibc }}_${{ matrix.platform }}:latest + elif [ "${{ matrix.glibc }}" == "2_35" ]; then + IMAGE=ubuntu:22.04 + elif [ "${{ matrix.glibc }}" == "2_39" ]; then + IMAGE=ubuntu:24.04 + else + exit 1 + fi + + docker run --platform linux/${DOCKER_PLATFORM} -v $(pwd):/gha ${IMAGE} sh -ec "cd /gha && bash ./build.sh" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index c7ed3372..88015d93 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -13,16 +13,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@v30 - 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 +30,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@v30 - 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 +47,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@v4 with: - name: patchelf + name: patchelf-windows path: dist - name: Show binaries run: dir .\\dist @@ -71,11 +71,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@v4 with: - name: patchelf + name: patchelf-tarball path: dist - name: Build binaries env: @@ -100,7 +110,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 +119,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 +131,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@v4 with: - name: patchelf + pattern: patchelf-* + merge-multiple: true path: dist - name: Upload binaries to release uses: svenstaro/upload-release-action@v2 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/completions/zsh/_patchelf b/completions/zsh/_patchelf index c674549a..a448f14a 100644 --- a/completions/zsh/_patchelf +++ b/completions/zsh/_patchelf @@ -1,22 +1,45 @@ #compdef patchelf +_get_dep() { + # preparm have fewer checks, as they can't be the so to replace + if [[ -f $words[2] ]] { # check if arg #1 is a elf file and not a so + local deps=($(patchelf --print-needed $words[2] 2>/dev/null)) # discard error + if [[ $? -ne 0 ]] || [[ $#deps -eq 0 ]] { # if no dependency or not a elf + _files # fallback to _files + } else { + _values "LIBS" $deps # else use dependencies of the file as candidates + } + # postparm need to check + } elif [[ -f $words[-1] ]] && [[ -z ${words[-1]:e} ]] { # check arg #-1, as most people do like this + local deps=($(patchelf --print-needed $words[-1] 2>/dev/null)) + if [[ $? -ne 0 ]] || [[ $#deps -eq 0 ]] { + _files + } else { + _values "LIBS" $deps + } + } else { + _files + } +} + local options=( - '--page-size[Uses the given page size]:SIZE' - '--set-interpreter[Change the dynamic loader of executable]:INTERPRETER:dynamic loader:_files' + '--page-size[Uses the given page size]:SIZE:' + '--set-interpreter[Change the dynamic loader of executable]:INTERPRETER:_files' '(- : *)--print-interpreter[Prints the ELF interpreter of the executable]' '(- : *)--print-os-abi[Prints the OS ABI of the executable]' '--set-os-abi[Changes the OS ABI of the executable]:ABI:(none sysv hpux netbsd gnu linux solaris aix irix freebsd tru64 modesto openbsd arm_aeabi arm standalone)' '(- : *)--print-soname[Prints DT_SONAME entry of .dynamic section]' - '--set-soname[Sets DT_SONAME entry of a library to SONAME]:SONAME' + '--set-soname[Sets DT_SONAME entry of a library to SONAME]:SONAME:' '--set-rpath[Change the DT_RUNPATH of the executable or library to RUNPATH]:RUNPATH:_dirs' + '--add-rpath[Add RUNPATH to the existing DT_RUNPATH of the executable or library.]:RUNPATH:_dirs' '--remove-rpath[Removes the DT_RPATH or DT_RUNPATH entry of the executable or library]' '--shrink-rpath[Remove from the DT_RUNPATH or DT_RPATH all directories that do not contain a library referenced by DT_NEEDED fields of the executable or library]' - '--allowed-rpath-prefixes[Combined with the "--shrink-rpath" option, this can be used for further rpath tuning]:PREFIXES' + '--allowed-rpath-prefixes[Combined with the "--shrink-rpath" option, this can be used for further rpath tuning]:PREFIXES:' '(- : *)--print-rpath[Prints the DT_RUNPATH or DT_RPATH for an executable or library]' '--force-rpath[Forces the use of the obsolete DT_RPATH in the file instead of DT_RUNPATH]' - '--add-needed[Adds a declared dependency on a dynamic library]:LIBRARY' - '*--replace-needed[Replaces a declared dependency on a dynamic library with another one]:a declared dependency:LIB_ORIG:another declared dependency:LIB_NEW' - '--remove-needed[Removes a declared dependency on LIBRARY]:LIBRARY' + '--add-needed[Adds a declared dependency on a dynamic library]:LIBRARY:_files' + '*--replace-needed[Replaces a declared dependency on a dynamic library with another one]:LIB_ORIG:_get_dep:LIB_NEW:_files' + '--remove-needed[Removes a declared dependency on LIBRARY]:LIBRARY:_get_dep' '(- : *)--print-needed[Prints all DT_NEEDED entries of the executable]' '--no-default-lib[Marks the object so that the search for dependencies of this object will ignore any default library search paths]' '--no-sort[Do not sort program headers or section headers]' @@ -24,7 +47,7 @@ local options=( '(- : *)--print-execstack[Prints the state of the executable flag of the GNU_STACK program header, if present]' '--clear-execstack[Clears the executable flag of the GNU_STACK program header, or adds a new header]' '--set-execstack[Sets the executable flag of the GNU_STACK program header, or adds a new header]' - '--rename-dynamic-symbols[Renames dynamic symbols]:NAME_MAP_FILE' + '--rename-dynamic-symbols[Renames dynamic symbols]:NAME_MAP_FILE:_files' '--output[Set the output file name]:FILE:_files' '--debug[Prints details of the changes made to the input file]' '--version[Shows the version of patchelf]' diff --git a/flake.lock b/flake.lock index 1e068201..eda6a5f7 100644 --- a/flake.lock +++ b/flake.lock @@ -2,17 +2,18 @@ "nodes": { "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": { diff --git a/flake.nix b/flake.nix index 471a31b9..9bbe09d9 100644 --- a/flake.nix +++ b/flake.nix @@ -1,12 +1,12 @@ { 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 }: let - supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" ]; + supportedSystems = [ "x86_64-linux" "i686-linux" "aarch64-linux" "riscv64-linux" ]; forAllSystems = nixpkgs.lib.genAttrs supportedSystems; version = nixpkgs.lib.removeSuffix "\n" (builtins.readFile ./version); @@ -36,6 +36,11 @@ versionSuffix = ""; # obsolete src = self; 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 @@ -76,10 +81,12 @@ [ self.hydraJobs.tarball self.hydraJobs.build.x86_64-linux self.hydraJobs.build.i686-linux - # FIXME: add aarch64 emulation to our github action... + # FIXME: add aarch64/riscv64 emulation to our github action... #self.hydraJobs.build.aarch64-linux + #self.hydraJobs.build.riscv64-linux self.hydraJobs.build-sanitized.x86_64-linux #self.hydraJobs.build-sanitized.aarch64-linux + #self.hydraJobs.build-sanitized.riscv64-linux self.hydraJobs.build-sanitized.i686-linux self.hydraJobs.build-sanitized-clang.x86_64-linux ]; diff --git a/patchelf.1 b/patchelf.1 index 6a8a94e1..7bb94f7f 100644 --- a/patchelf.1 +++ b/patchelf.1 @@ -131,6 +131,15 @@ old_name new_name Symbol names do not contain version specifier that are also shown in the output of the nm -D command from binutils. So instead of the name write@GLIBC_2.2.5 it is just write. +.IP "--no-clobber-old-sections" +Do not clobber old section values. + +patchelf defaults to overwriting replaced header sections with garbage to ensure they are not +used accidentally. This option allows to opt out of that behavior, so that binaries that attempt +to read their own headers from a fixed offset (e.g. Firefox) continue working. + +Use sparingly and with caution. + .IP "--output FILE" Set the output file name. If not specified, the input will be modified in place. diff --git a/src/elf.h b/src/elf.h index 920e6891..89fc8021 100644 --- a/src/elf.h +++ b/src/elf.h @@ -1,5 +1,5 @@ /* This file defines standard ELF types, structures, and macros. - Copyright (C) 1995-2022 Free Software Foundation, Inc. + Copyright (C) 1995-2023 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or @@ -210,7 +210,7 @@ typedef struct #define EM_68HC12 53 /* Motorola M68HC12 */ #define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator */ #define EM_PCP 55 /* Siemens PCP */ -#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NCPU 56 /* Sony nCPU embedded RISC */ #define EM_NDR1 57 /* Denso NDR1 microprocessor */ #define EM_STARCORE 58 /* Motorola Start*Core processor */ #define EM_ME16 59 /* Toyota ME16 processor */ @@ -559,7 +559,7 @@ typedef struct /* Possible bitmasks for si_flags. */ #define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ -#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-through symbol for translator */ #define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ #define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy loaded */ @@ -728,6 +728,7 @@ typedef struct #define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ #define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ #define PT_GNU_PROPERTY 0x6474e553 /* GNU property */ +#define PT_GNU_SFRAME 0x6474e554 /* SFrame segment. */ #define PT_LOSUNW 0x6ffffffa #define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ #define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ @@ -1223,6 +1224,9 @@ typedef struct #define AT_HWCAP2 26 /* More machine-dependent hints about processor capabilities. */ +#define AT_RSEQ_FEATURE_SIZE 27 /* rseq supported feature size. */ +#define AT_RSEQ_ALIGN 28 /* rseq allocation alignment. */ + #define AT_EXECFN 31 /* Filename of executable. */ /* Pointer to the global system page used for system calls and other @@ -3998,8 +4002,11 @@ enum #define R_RISCV_SET32 56 #define R_RISCV_32_PCREL 57 #define R_RISCV_IRELATIVE 58 +#define R_RISCV_PLT32 59 +#define R_RISCV_SET_ULEB128 60 +#define R_RISCV_SUB_ULEB128 61 -#define R_RISCV_NUM 59 +#define R_RISCV_NUM 62 /* RISC-V specific values for the st_other field. */ #define STO_RISCV_VARIANT_CC 0x80 /* Function uses variant calling @@ -4159,6 +4166,55 @@ enum #define R_LARCH_GNU_VTINHERIT 57 #define R_LARCH_GNU_VTENTRY 58 +/* reserved 59-63 */ + +#define R_LARCH_B16 64 +#define R_LARCH_B21 65 +#define R_LARCH_B26 66 +#define R_LARCH_ABS_HI20 67 +#define R_LARCH_ABS_LO12 68 +#define R_LARCH_ABS64_LO20 69 +#define R_LARCH_ABS64_HI12 70 +#define R_LARCH_PCALA_HI20 71 +#define R_LARCH_PCALA_LO12 72 +#define R_LARCH_PCALA64_LO20 73 +#define R_LARCH_PCALA64_HI12 74 +#define R_LARCH_GOT_PC_HI20 75 +#define R_LARCH_GOT_PC_LO12 76 +#define R_LARCH_GOT64_PC_LO20 77 +#define R_LARCH_GOT64_PC_HI12 78 +#define R_LARCH_GOT_HI20 79 +#define R_LARCH_GOT_LO12 80 +#define R_LARCH_GOT64_LO20 81 +#define R_LARCH_GOT64_HI12 82 +#define R_LARCH_TLS_LE_HI20 83 +#define R_LARCH_TLS_LE_LO12 84 +#define R_LARCH_TLS_LE64_LO20 85 +#define R_LARCH_TLS_LE64_HI12 86 +#define R_LARCH_TLS_IE_PC_HI20 87 +#define R_LARCH_TLS_IE_PC_LO12 88 +#define R_LARCH_TLS_IE64_PC_LO20 89 +#define R_LARCH_TLS_IE64_PC_HI12 90 +#define R_LARCH_TLS_IE_HI20 91 +#define R_LARCH_TLS_IE_LO12 92 +#define R_LARCH_TLS_IE64_LO20 93 +#define R_LARCH_TLS_IE64_HI12 94 +#define R_LARCH_TLS_LD_PC_HI20 95 +#define R_LARCH_TLS_LD_HI20 96 +#define R_LARCH_TLS_GD_PC_HI20 97 +#define R_LARCH_TLS_GD_HI20 98 +#define R_LARCH_32_PCREL 99 +#define R_LARCH_RELAX 100 + +/* ARC specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_ARC_MACH_MSK 0x000000ff +#define EF_ARC_OSABI_MSK 0x00000f00 +#define EF_ARC_ALL_MSK (EF_ARC_MACH_MSK | EF_ARC_OSABI_MSK) + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARC_ATTRIBUTES (SHT_LOPROC + 1) /* ARC attributes section. */ /* ARCompact/ARCv2 specific relocs. */ #define R_ARC_NONE 0x0 @@ -4166,7 +4222,7 @@ enum #define R_ARC_16 0x2 #define R_ARC_24 0x3 #define R_ARC_32 0x4 -#define R_ARC_B26 0x5 + #define R_ARC_B22_PCREL 0x6 #define R_ARC_H30 0x7 #define R_ARC_N8 0x8 @@ -4206,16 +4262,23 @@ enum #define R_ARC_SECTOFF_ME_2 0x2A #define R_ARC_SECTOFF_1 0x2B #define R_ARC_SECTOFF_2 0x2C +#define R_ARC_SDA_12 0x2D +#define R_ARC_SDA16_ST2 0x30 +#define R_ARC_32_PCREL 0x31 #define R_ARC_PC32 0x32 #define R_ARC_GOTPC32 0x33 #define R_ARC_PLT32 0x34 #define R_ARC_COPY 0x35 #define R_ARC_GLOB_DAT 0x36 -#define R_ARC_JUMP_SLOT 0x37 +#define R_ARC_JMP_SLOT 0x37 #define R_ARC_RELATIVE 0x38 #define R_ARC_GOTOFF 0x39 #define R_ARC_GOTPC 0x3A #define R_ARC_GOT32 0x3B +#define R_ARC_S21W_PCREL_PLT 0x3C +#define R_ARC_S25H_PCREL_PLT 0x3D + +#define R_ARC_JLI_SECTOFF 0x3F #define R_ARC_TLS_DTPMOD 0x42 #define R_ARC_TLS_DTPOFF 0x43 @@ -4224,9 +4287,12 @@ enum #define R_ARC_TLS_GD_LD 0x46 #define R_ARC_TLS_GD_CALL 0x47 #define R_ARC_TLS_IE_GOT 0x48 -#define R_ARC_TLS_DTPOFF_S9 0x4a -#define R_ARC_TLS_LE_S9 0x4a -#define R_ARC_TLS_LE_32 0x4b +#define R_ARC_TLS_DTPOFF_S9 0x49 +#define R_ARC_TLS_LE_S9 0x4A +#define R_ARC_TLS_LE_32 0x4B +#define R_ARC_S25W_PCREL_PLT 0x4C +#define R_ARC_S21H_PCREL_PLT 0x4D +#define R_ARC_NPS_CMEM16 0x4E /* OpenRISC 1000 specific relocs. */ #define R_OR1K_NONE 0 diff --git a/src/patchelf.cc b/src/patchelf.cc index 82b4b46c..35a5dc1c 100644 --- a/src/patchelf.cc +++ b/src/patchelf.cc @@ -58,6 +58,7 @@ static bool debugMode = false; static bool forceRPath = false; +static bool clobberOldSections = true; static std::vector fileNames; static std::string outputFileName; @@ -366,6 +367,7 @@ unsigned int ElfFile::getPageSize() const noexcept // requirements. There is no authoritative list of these values. The // current list is extracted from GNU gold's source code (abi_pagesize). switch (rdi(hdr()->e_machine)) { + case EM_ALPHA: case EM_IA_64: case EM_MIPS: case EM_PPC: @@ -664,14 +666,16 @@ template void ElfFile::writeReplacedSections(Elf_Off & curOff, Elf_Addr startAddr, Elf_Off startOffset) { - /* Overwrite the old section contents with 'Z's. Do this - *before* writing the new section contents (below) to prevent - clobbering previously written new section contents. */ - for (auto & i : replacedSections) { - const std::string & sectionName = i.first; - const Elf_Shdr & shdr = findSectionHeader(sectionName); - if (rdi(shdr.sh_type) != SHT_NOBITS) - memset(fileContents->data() + rdi(shdr.sh_offset), 'Z', rdi(shdr.sh_size)); + if (clobberOldSections) { + /* Overwrite the old section contents with 'Z's. Do this + *before* writing the new section contents (below) to prevent + clobbering previously written new section contents. */ + for (auto & i : replacedSections) { + const std::string & sectionName = i.first; + const Elf_Shdr & shdr = findSectionHeader(sectionName); + if (rdi(shdr.sh_type) != SHT_NOBITS) + memset(fileContents->data() + rdi(shdr.sh_offset), 'Z', rdi(shdr.sh_size)); + } } std::set noted_phdrs = {}; @@ -843,7 +847,7 @@ void ElfFile::rewriteSectionsLibrary() neededSpace += headerTableSpace; debug("needed space is %d\n", neededSpace); - Elf_Off startOffset = roundUp(fileContents->size(), getPageSize()); + Elf_Off startOffset = roundUp(fileContents->size(), alignStartPage); // In older version of binutils (2.30), readelf would check if the dynamic // section segment is strictly smaller than the file (and not same size). @@ -879,7 +883,7 @@ void ElfFile::rewriteSectionsLibrary() rdi(lastSeg.p_type) == PT_LOAD && rdi(lastSeg.p_flags) == (PF_R | PF_W) && rdi(lastSeg.p_align) == alignStartPage) { - auto segEnd = roundUp(rdi(lastSeg.p_offset) + rdi(lastSeg.p_memsz), getPageSize()); + auto segEnd = roundUp(rdi(lastSeg.p_offset) + rdi(lastSeg.p_memsz), alignStartPage); if (segEnd == startOffset) { auto newSz = startOffset + neededSpace - rdi(lastSeg.p_offset); wri(lastSeg.p_filesz, wri(lastSeg.p_memsz, newSz)); @@ -898,6 +902,7 @@ void ElfFile::rewriteSectionsLibrary() wri(phdr.p_filesz, wri(phdr.p_memsz, neededSpace)); wri(phdr.p_flags, PF_R | PF_W); wri(phdr.p_align, alignStartPage); + assert(startPage % alignStartPage == startOffset % alignStartPage); } normalizeNoteSegments(); @@ -1454,6 +1459,11 @@ void ElfFile::modifySoname(sonameMode op, const std::string & template void ElfFile::setInterpreter(const std::string & newInterpreter) { + if (getInterpreter() == newInterpreter) { + debug("given interpreter is already set\n"); + return; + } + std::string & section = replaceSection(".interp", newInterpreter.size() + 1); setSubstr(section, 0, newInterpreter + '\0'); changed = true; @@ -2069,7 +2079,7 @@ void ElfFile::rebuildGnuHashTable(span strTab, span> tmp(dst.begin(), dst.end()); for (size_t i = 0; i < tmp.size(); ++i) dst[old2new[i]] = tmp[i]; }; @@ -2505,6 +2515,7 @@ static void showHelp(const std::string & progName) [--clear-execstack]\n\ [--set-execstack]\n\ [--rename-dynamic-symbols NAME_MAP_FILE]\tRenames dynamic symbols. The map file should contain two symbols (old_name new_name) per line\n\ + [--no-clobber-old-sections]\t\tDo not clobber old section values - only use when the binary expects to find section info at the old location.\n\ [--output FILE]\n\ [--debug]\n\ [--version]\n\ @@ -2661,6 +2672,9 @@ static int mainWrapped(int argc, char * * argv) symbolsToRename[*symbolsToRenameKeys.insert(from).first] = to; } } + else if (arg == "--no-clobber-old-sections") { + clobberOldSections = false; + } else if (arg == "--help" || arg == "-h" ) { showHelp(argv[0]); return 0; @@ -2689,6 +2703,11 @@ static int mainWrapped(int argc, char * * argv) int main(int argc, char * * argv) { +#ifdef __OpenBSD__ + if (pledge("stdio rpath wpath cpath", NULL) == -1) + error("pledge"); +#endif + try { return mainWrapped(argc, argv); } catch (std::exception & e) { diff --git a/tests/Makefile.am b/tests/Makefile.am index 8bbded7a..b08929f1 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -3,6 +3,7 @@ LIBS = check_PROGRAMS = simple-pie simple simple-execstack main too-many-strtab main-scoped big-dynstr no-rpath contiguous-note-sections no_rpath_arch_TESTS = \ + no-rpath-alpha.sh \ no-rpath-amd64.sh \ no-rpath-armel.sh \ no-rpath-armhf.sh \ @@ -51,7 +52,8 @@ src_TESTS = \ overlapping-segments-after-rounding.sh \ shared-rpath.sh \ short-first-segment.sh \ - empty-note.sh + empty-note.sh \ + set-interpreter-same.sh build_TESTS = \ $(no_rpath_arch_TESTS) @@ -164,13 +166,14 @@ many_syms_main_SOURCES = many-syms-main.c many_syms_main_LDFLAGS = $(LDFLAGS_local) many_syms_main_LDADD = -lmany-syms $(AM_LDADD) many_syms_main_DEPENDENCIES = libmany-syms.so -many_syms_main_CFLAGS = -pie +many_syms_main_CFLAGS = -pie -fPIE libmany_syms_so_SOURCES = many-syms.c libmany_syms_so_LDFLAGS = $(LDFLAGS_sharedlib) no_rpath_SOURCES = no-rpath.c # no -fpic for no-rpath.o no_rpath_CFLAGS = +no_rpath_LDFLAGS = contiguous_note_sections_SOURCES = contiguous-note-sections.s contiguous-note-sections.ld contiguous_note_sections_LDFLAGS = -nostdlib -T $(srcdir)/contiguous-note-sections.ld diff --git a/tests/no-rpath-prebuild/no-rpath-alpha b/tests/no-rpath-prebuild/no-rpath-alpha new file mode 100755 index 00000000..99c32473 Binary files /dev/null and b/tests/no-rpath-prebuild/no-rpath-alpha differ diff --git a/tests/replace-add-needed.sh b/tests/replace-add-needed.sh index 701cb953..1282f4f8 100755 --- a/tests/replace-add-needed.sh +++ b/tests/replace-add-needed.sh @@ -11,7 +11,15 @@ cp libbar.so "${SCRATCH}"/ cd "${SCRATCH}" -libcldd=$(ldd ./simple | awk '/ => / { print $3 }' | grep -E "(libc(-[0-9.]*)*.so|ld-musl)") +# QEMU & ldd are not playing well together in certain cases +if ldd ./simple >/dev/null 2>&1; then + libcldd=$(ldd ./simple | awk '/ => / { print $3 }' | grep -E "(libc(-[0-9.]*)*.so|ld-musl)") +elif [ -f /lib64/libc.so.6 ]; then + libcldd=/lib64/libc.so.6 +else + echo "ldd ./simple failed" + exit 1 +fi # We have to set the soname on these libraries ${PATCHELF} --set-soname libbar.so ./libbar.so diff --git a/tests/set-interpreter-long.sh b/tests/set-interpreter-long.sh index f1e0d2f9..bb692aeb 100755 --- a/tests/set-interpreter-long.sh +++ b/tests/set-interpreter-long.sh @@ -6,7 +6,8 @@ SCRATCH=scratch/$(basename "$0" .sh) oldInterpreter=$(../src/patchelf --print-interpreter ./simple) echo "current interpreter is $oldInterpreter" -if test "$(uname)" = Linux; then +# QEMU & ldd/ld.so are not playing well together in certain cases +if test "$(uname)" = Linux && ldd ./simple >/dev/null 2>&1; then echo "running with explicit interpreter..." "$oldInterpreter" ./simple fi @@ -28,7 +29,7 @@ echo "running with new interpreter..." ln -s "$oldInterpreter" "$newInterpreter" "${SCRATCH}"/simple -if test "$(uname)" = Linux; then +if test "$(uname)" = Linux && ldd ./simple >/dev/null 2>&1; then echo "running with explicit interpreter..." "$oldInterpreter" "${SCRATCH}/simple" fi diff --git a/tests/set-interpreter-same.sh b/tests/set-interpreter-same.sh new file mode 100755 index 00000000..b174628e --- /dev/null +++ b/tests/set-interpreter-same.sh @@ -0,0 +1,56 @@ +#! /bin/sh -e +SCRATCH=scratch/$(basename "$0" .sh) + +./simple + +curInterpreter=$(../src/patchelf --print-interpreter ./simple) +echo "current interpreter is $curInterpreter" + +rm -rf "${SCRATCH}" +mkdir -p "${SCRATCH}" + +cp simple "${SCRATCH}"/ + +echo "set the same interpreter as the current one" +before_checksum=$(sha256sum "${SCRATCH}/simple") +../src/patchelf --set-interpreter "${curInterpreter}" "${SCRATCH}/simple" +after_checksum=$(sha256sum "${SCRATCH}/simple") + +if [ "$before_checksum" != "$after_checksum" ]; then + echo "--set-interpreter should be NOP, but the file has been changed." + exit 1 +fi + +"${SCRATCH}/simple" + +dummyInterpreter="aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +echo "set the dummy interpreter" +before_checksum=$(sha256sum "${SCRATCH}/simple") +../src/patchelf --set-interpreter "${dummyInterpreter}" "${SCRATCH}/simple" +after_checksum=$(sha256sum "${SCRATCH}/simple") + +if [ "$before_checksum" = "$after_checksum" ]; then + echo "--set-interpreter should be run, but the file has not been changed." + exit 1 +fi + +if "${SCRATCH}/simple"; then + echo "simple works, but it shouldn't" + exit 1 +fi + +echo "set the same interpreter as the current one" +before_checksum=$(sha256sum "${SCRATCH}/simple") +../src/patchelf --set-interpreter "${dummyInterpreter}" "${SCRATCH}/simple" +after_checksum=$(sha256sum "${SCRATCH}/simple") + +if [ "$before_checksum" != "$after_checksum" ]; then + echo "--set-interpreter should be NOP, but the file has been changed." + exit 1 +fi + +if "${SCRATCH}/simple"; then + echo "simple works, but it shouldn't" + exit 1 +fi diff --git a/tests/short-first-segment.sh b/tests/short-first-segment.sh index 07019fc4..7a11345f 100755 --- a/tests/short-first-segment.sh +++ b/tests/short-first-segment.sh @@ -6,13 +6,13 @@ READELF=${READELF:-readelf} EXEC_NAME="short-first-segment" -if ! gzip --version >/dev/null; then - echo "skipping test: gzip not found" +if test "$(uname -m)" != amd64 || test "$(uname)" != Linux; then + echo "skipping test: amd64 Linux required" exit 77 fi -if test "$(uname -i)" != x86_64 || test "$(uname)" != Linux; then - echo "skipping test: not supported on x86_64 Linux" +if ! command -v gzip >/dev/null; then + echo "skipping test: gzip not found" exit 77 fi