Skip to content

patchelf --set-rpath outputs a binary with broken .dynsym/.dynstr #244

@delroth

Description

@delroth

Describe the bug

This seems to be one of the root causes behind NixOS/nixpkgs#97407

When running a certain combination of patchelf + strip + patchelf on the upstream released stage2 ghc 8.6.5 binary, the last patchelf invocation (--set-rpath with a long rpath value) ends up generating a broken ELF file which segfaults the dynamic linker.

(gdb) target remote localhost:9000
Remote debugging using localhost:9000
warning: Loadable section ".dynsym" outside of ELF segments
warning: Loadable section ".dynstr" outside of ELF segments
warning: remote target does not support file transfer, attempting to access files from local filesystem.
Reading symbols from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1...
(No debugging symbols found in /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1)
0x00000055008020c0 in _start ()
   from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1
(gdb) c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x0000005500808cf4 in decompose_rpath.isra ()
   from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1
(gdb) bt
#0  0x0000005500808cf4 in decompose_rpath.isra ()
   from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1
#1  0x000000550080901c in _dl_init_paths ()
   from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1
#2  0x0000005500804898 in dl_main ()
   from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1
#3  0x00000055008165b4 in _dl_sysdep_start ()
   from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1
#4  0x00000055008029d4 in _dl_start_final ()
   from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1
#5  0x0000005500802cf0 in _dl_start ()
   from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1
#6  0x00000055008020c8 in _start ()
   from /nix/store/yfa0b4pyywvnspwnlk2nw9id6h6f874x-glibc-2.31/lib/ld-linux-aarch64.so.1

Note that gdb seems to already point to a problem with the binary before execution:

warning: Loadable section ".dynsym" outside of ELF segments
warning: Loadable section ".dynstr" outside of ELF segments

And indeed, readelf seems to agree:

Before:

  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 4] .dynsym           DYNSYM           0000000000400298  00010298
       0000000000007560  0000000000000018   A       5     1     8
  [ 5] .dynstr           STRTAB           00000000004077f8  000177f8
       000000000000c5e1  0000000000000000   A       0     0     1

  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000010000 0x0000000000400000 0x0000000000400000
                 0x00000000001ab134 0x00000000001ab134  R E    0x10000

After:

  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 1] .dynstr           STRTAB           00000000003f0270  00000270
       000000000000c915  0000000000000000   A       0     0     8
  [ 2] .dynsym           DYNSYM           00000000003fcb88  0000cb88
       0000000000007560  0000000000000018   A       1     1     8

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  LOAD           0x0000000000010000 0x0000000000400000 0x0000000000400000
                 0x00000000001ab134 0x00000000001ab134  R E    0x10000

Steps To Reproduce

I attached the input binary, gzipped to make github happy: repro.gz
. To reproduce, run:

src/patchelf --set-rpath '/nix/store/mbd1w6i6b98wbh5pf0ikghs1fz5p24j7-ncurses-6.2-abi5-compat/lib:/nix/store/qsxgr8vk6y8m95r7jf3qxrkz648g8p91-gmp-6.2.0/lib:$ORIGIN/../haskeline-0.7.4.3:$ORIGIN/../stm-2.5.0.0:$ORIGIN/../ghc-8.6.5:$ORIGIN/../terminfo-0.4.1.2:$ORIGIN/../process-1.6.5.0:$ORIGIN/../hpc-0.6.0.3:$ORIGIN/../ghci-8.6.5:$ORIGIN/../transformers-0.5.6.2:$ORIGIN/../template-haskell-2.14.0.0:$ORIGIN/../pretty-1.1.3.6:$ORIGIN/../ghc-heap-8.6.5:$ORIGIN/../ghc-boot-8.6.5:$ORIGIN/../ghc-boot-th-8.6.5:$ORIGIN/../directory-1.3.3.0:$ORIGIN/../unix-2.7.2.2:$ORIGIN/../time-1.8.0.2:$ORIGIN/../filepath-1.4.2.1:$ORIGIN/../binary-0.8.6.0:$ORIGIN/../containers-0.6.0.1:$ORIGIN/../bytestring-0.10.8.2:$ORIGIN/../deepseq-1.4.4.0:$ORIGIN/../array-0.5.3.0:$ORIGIN/../base-4.12.0.0:$ORIGIN/../integer-gmp-1.0.2.0:$ORIGIN/../ghc-prim-0.5.3:$ORIGIN/../rts' repro

Expected behavior

A clear and concise description of what you expected to happen.

patchelf --version output

HEAD

Additional context

For some reason the binaries in question don't crash on a 64k page size system. I suspect this is because there is another tiny LOAD segment (< 0x1000 bytes) which if rounded to 64k does end up mapping the two sections by accident (but not if rounded to 4k). That's pure luck though.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions