Bug 22747 - ld.so --library-path doesn't work with DT_RPATH
Summary: ld.so --library-path doesn't work with DT_RPATH
Status: NEW
Alias: None
Product: glibc
Classification: Unclassified
Component: dynamic-link (show other bugs)
Version: 2.27
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2018-01-25 16:24 UTC by H.J. Lu
Modified: 2018-02-14 19:48 UTC (History)
1 user (show)

See Also:
Host:
Target: i686
Build:
Last reconfirmed:
fweimer: security-


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description H.J. Lu 2018-01-25 16:24:42 UTC
On i686, the following steps show that ld.so --library-path doesn't
always work:

1. Checkout release/2.26/master and build glibc for i686.
2. Checkout release/2.25/master and configure glibc for i686
with

 --prefix=/usr --without-cvs --without-selinux --target=i686-linux --build=i686-linux --host=i686-linux --enable-check-abi --enable-hardcoded-path-in-tests

"--enable-hardcoded-path-in-tests" is the key.

3. Build glibc 2.25 and run "make check".
4. Run test binaries in glibc 2.25 with ld.so from glibc 2.26:

(gdb) r --library-path /export/build/gnu/glibc-32bit-test-2/build-i686-linux:/export/build/gnu/glibc-32bit-test-2/build-i686-linux/nptl:. /export/build/gnu/glibc-32bit-test/build-i686-linux/string/tst-strlen 
Starting program: /export/build/gnu/glibc-32bit-test-2/build-i686-linux/elf/ld-linux.so.2 --library-path /export/build/gnu/glibc-32bit-test-2/build-i686-linux:/export/build/gnu/glibc-32bit-test-2/build-i686-linux/nptl:. /export/build/gnu/glibc-32bit-test/build-i686-linux/string/tst-strlen

Program received signal SIGSEGV, Segmentation fault.
0xffffffff in ?? ()
(gdb) bt
#0  0xffffffff in ?? ()
#1  0xf7f37dda in _dl_vdso_vsym (
    name=name@entry=0xf7f74762 "__vdso_clock_gettime", 
    vers=vers@entry=0xffffcf50) at ../sysdeps/unix/sysv/linux/dl-vdso.c:40
#2  0xf7e2e2fc in __vdso_platform_setup ()
    at ../sysdeps/unix/sysv/linux/i386/init-first.c:40
#3  _init (argc=1, argv=0xffffd010, envp=0xffffd018) at ../csu/init-first.c:81
#4  0xf7fe67bd in call_init (l=0xf7fd0de0, argc=argc@entry=1, 
    argv=argv@entry=0xffffd010, env=0xffffd018) at dl-init.c:58
#5  0xf7fe6907 in call_init (env=0xffffd018, argv=0xffffd010, argc=1, 
    l=<optimized out>) at dl-init.c:30
#6  _dl_init (main_map=<optimized out>, argc=1, argv=0xffffd010, 
    env=0xffffd018) at dl-init.c:120
#7  0xf7fd7b5f in _dl_start_user ()
   from /export/build/gnu/glibc-32bit-test-2/build-i686-linux/elf/ld-linux.so.2
(gdb) r --library-path /export/build/gnu/glibc-32bit-test-2/build-i686-linux:/export/build/gnu/glibc-32bit-test-2/build-i686-linux/nptl:. /export/build/gnu/glibc-32bit-test/build-i686-linux/elf/sprof
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /export/build/gnu/glibc-32bit-test-2/build-i686-linux/elf/ld-linux.so.2 --library-path /export/build/gnu/glibc-32bit-test-2/build-i686-linux:/export/build/gnu/glibc-32bit-test-2/build-i686-linux/nptl:. /export/build/gnu/glibc-32bit-test/build-i686-linux/elf/sprof
Try `sprof --help' or `sprof --usage' for more information.
[Inferior 1 (process 221985) exited with code 01]
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.26-25.0.fc27.i686
(gdb) 

The differences are

[hjl@gnu-skx-1 glibc]$ readelf -l /export/build/gnu/glibc-32bit-test/build-i686linux/elf/sprof

Elf file type is EXEC (Executable file)
Entry point 0x8049f02
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R   0x4
  INTERP         0x000154 0x08048154 0x08048154 0x00013 0x00013 R   0x1
      [Requesting program interpreter: /lib/ld-linux.so.2]
  LOAD           0x000000 0x08048000 0x08048000 0x04070 0x04070 R E 0x1000
  LOAD           0x004ef4 0x0804def4 0x0804def4 0x00204 0x002a0 RW  0x1000
  DYNAMIC        0x004f04 0x0804df04 0x0804df04 0x000f8 0x000f8 RW  0x4
  NOTE           0x000168 0x08048168 0x08048168 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x003cdc 0x0804bcdc 0x0804bcdc 0x0008c 0x0008c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x004ef4 0x0804def4 0x0804def4 0x0010c 0x0010c R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .data.rel.ro .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .data.rel.ro .dynamic .got 
[hjl@gnu-skx-1 glibc]$ readelf -l /export/build/gnu/glibc-32bit-test/build-i686-linux/string/tst-strlen

Elf file type is EXEC (Executable file)
Entry point 0x8048efe
There are 9 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  PHDR           0x000034 0x08048034 0x08048034 0x00120 0x00120 R   0x4
  INTERP         0x000154 0x08048154 0x08048154 0x0003e 0x0003e R   0x1
      [Requesting program interpreter: /export/build/gnu/glibc-32bit-test/build-i686-linux/elf/ld.so]
  LOAD           0x000000 0x08048000 0x08048000 0x027d0 0x027d0 R E 0x1000
  LOAD           0x002e98 0x0804be98 0x0804be98 0x001f0 0x00208 RW  0x1000
  DYNAMIC        0x002ee0 0x0804bee0 0x0804bee0 0x000f8 0x000f8 RW  0x4
  NOTE           0x000194 0x08048194 0x08048194 0x00044 0x00044 R   0x4
  GNU_EH_FRAME   0x0021ac 0x0804a1ac 0x0804a1ac 0x0005c 0x0005c R   0x4
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RW  0x10
  GNU_RELRO      0x002e98 0x0804be98 0x0804be98 0x00168 0x00168 R   0x1

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .interp 
   02     .interp .note.ABI-tag .note.gnu.build-id .hash .gnu.hash .dynsym .dynstr .gnu.version .gnu.version_r .rel.dyn .rel.plt .init .plt .plt.got .text .fini .rodata .eh_frame_hdr .eh_frame 
   03     .init_array .fini_array .data.rel.ro .dynamic .got .got.plt .data .bss 
   04     .dynamic 
   05     .note.ABI-tag .note.gnu.build-id 
   06     .eh_frame_hdr 
   07     
   08     .init_array .fini_array .data.rel.ro .dynamic .got 
[hjl@gnu-skx-1 glibc]$ /export/build/gnu/glibc-32bit-test/build-i686-linux/string/tst-strlen
[hjl@gnu-skx-1 glibc]$
Comment 1 Carlos O'Donell 2018-01-25 20:32:55 UTC
If you use --enable-hardcoded-path-in-tests the tests are built with -Wl,-rpath and this embeds the library search path into DT_RPATH/DT_RUNPATH, both of which take precedence over ld.so's --library-path which is equivalent to setting LD_LIBRARY_PATH IIRC.

To prove this you could try using LD_PRELOAD to preload the required libraries and that would certainly force them to be loaded before DT_RPATH/DT_RUNPATH.

Therefore I don't think there is a bug here.

(a) ld.so --library-path is equivalent to LD_LIBRARY_PATH.

(b) If you use DT_RPATH/DT_RUNPATH in a binary, like is done with --enable-hardcoded-path-in-tests, you must use LD_PRELOAD to get a different set of loaded libraries that match the ld.so you're using to run the test.

Does that make sense?
Comment 2 jsm-csl@polyomino.org.uk 2018-01-25 21:01:17 UTC
Only DT_RPATH should take precedence over LD_LIBRARY_PATH, not DT_RUNPATH.  
(So unless --enable-hardcoded-path-in-tests is made to use 
--enable-new-dtags, and I don't know if that would be a good idea or 
not[*], it's incompatible with running an old glibc's test binaries 
against a new glibc - and if that change were made, of course it would 
only help if the old glibc postdated that change.)

[*] I don't know if any tests, using --enable-hardcoded-path-in-tests, 
rely on the executable's RPATH being used to find indirect shared library 
dependencies, which only works with RPATH not RUNPATH.
Comment 3 H.J. Lu 2018-01-25 23:42:43 UTC
--enable-hardcoded-path-in-tests doesn't change the behavior of ld.so
on x86-64.  Only i686 has this issue.  This may be related to:

 INTERP         0x000154 0x08048154 0x08048154 0x0003e 0x0003e R   0x1
      [Requesting program interpreter: /export/build/gnu/glibc-32bit-test/build-i686-linux/elf/ld.so]
Comment 4 Carlos O'Donell 2018-01-26 00:08:04 UTC
(In reply to H.J. Lu from comment #3)
> --enable-hardcoded-path-in-tests doesn't change the behavior of ld.so
> on x86-64.  Only i686 has this issue.  This may be related to:
> 
>  INTERP         0x000154 0x08048154 0x08048154 0x0003e 0x0003e R   0x1
>       [Requesting program interpreter:
> /export/build/gnu/glibc-32bit-test/build-i686-linux/elf/ld.so]

I think this is an x86_64 bug.

Using --enable-hardcoded-paths-in-tests sets PT_INTERP to the newly built ld.so and DT_RPATH to the library paths.

IMO these *should* override --library-paths, which should behave as LD_LIBRARY_PATH.
Comment 5 H.J. Lu 2018-01-26 00:42:30 UTC
x86-64 also crashes.  DT_RPATH overrides --library-path.  ld.so already
overrides DT_INTERP.  Shouldn't ld.so --library-path prepend DT_RPATH?
Comment 6 Carlos O'Donell 2018-02-14 19:48:18 UTC
(In reply to H.J. Lu from comment #5)
> x86-64 also crashes.  DT_RPATH overrides --library-path.  ld.so already
> overrides DT_INTERP.  Shouldn't ld.so --library-path prepend DT_RPATH?

No. --library-path should behave like LD_LIBRARY_PATH IMO.

We could add a new option to override DT_RPATH, or DT_RUNPATH.