Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Link to libraries through absolute paths? #24844

Open
lheckemann opened this issue Apr 12, 2017 · 73 comments
Open

Link to libraries through absolute paths? #24844

lheckemann opened this issue Apr 12, 2017 · 73 comments
Labels
1.severity: mass-rebuild This PR causes a large number of packages to rebuild 9.needs: community feedback significant Novel ideas, large API changes, notable refactorings, issues with RFC potential, etc.

Comments

@lheckemann
Copy link
Member

lheckemann commented Apr 12, 2017

Issue description

echo's dynamic section currently looks like this...

 0x0000000000000001 (NEEDED)             Shared library: [libacl.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libattr.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [librt.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib:/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib:/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib]
<snip>

Because we don't have any caching as far as I understand, this means that when echo is run, a whole lot of fairly superfluous system calls are made:

$ strace -e open,stat echo hello
<snip>
open("/run/opengl-driver/lib/tls/x86_64/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/run/opengl-driver/lib/tls/x86_64", 0x7ffddc7f01d0) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver/lib/tls/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/run/opengl-driver/lib/tls", 0x7ffddc7f01d0) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver/lib/x86_64/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/run/opengl-driver/lib/x86_64", 0x7ffddc7f01d0) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver/lib/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/run/opengl-driver/lib", {st_mode=S_IFDIR|0555, st_size=4096, ...}) = 0
open("/run/opengl-driver-32/lib/tls/x86_64/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/run/opengl-driver-32/lib/tls/x86_64", 0x7ffddc7f01d0) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver-32/lib/tls/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/run/opengl-driver-32/lib/tls", 0x7ffddc7f01d0) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver-32/lib/x86_64/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/run/opengl-driver-32/lib/x86_64", 0x7ffddc7f01d0) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver-32/lib/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/run/opengl-driver-32/lib", {st_mode=S_IFDIR|0555, st_size=4096, ...}) = 0
open("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/tls/x86_64/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/tls/x86_64", 0x7ffddc7f01d0) = -1 ENOENT (No such file or directory)
open("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/tls/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/tls", 0x7ffddc7f01d0) = -1 ENOENT (No such file or directory)
open("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/x86_64/libacl.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/x86_64", 0x7ffddc7f01d0) = -1 ENOENT (No such file or directory)
open("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/libacl.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/run/opengl-driver/lib/libattr.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver-32/lib/libattr.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/libattr.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/tls/x86_64/libattr.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/tls/x86_64", 0x7ffddc7f01a0) = -1 ENOENT (No such file or directory)
open("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/tls/libattr.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/tls", 0x7ffddc7f01a0) = -1 ENOENT (No such file or directory)
open("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/x86_64/libattr.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/x86_64", 0x7ffddc7f01a0) = -1 ENOENT (No such file or directory)
open("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/libattr.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/run/opengl-driver/lib/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver-32/lib/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib/tls/x86_64/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib/tls/x86_64", 0x7ffddc7f0170) = -1 ENOENT (No such file or directory)
open("/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib/tls/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib/tls", 0x7ffddc7f0170) = -1 ENOENT (No such file or directory)
open("/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib/x86_64/librt.so.1", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
stat("/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib/x86_64", 0x7ffddc7f0170) = -1 ENOENT (No such file or directory)
open("/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib/librt.so.1", O_RDONLY|O_CLOEXEC) = 3
open("/run/opengl-driver/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver-32/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib/libpthread.so.0", O_RDONLY|O_CLOEXEC) = 3
open("/run/opengl-driver/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/run/opengl-driver-32/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/v0wcqsb6vpljx13vw8q60dvldf5pffma-acl-2.2.52/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/gij6mgj1vixf7qcyb13h5aa5y15r2xxd-attr-2.4.47/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
open("/nix/store/vn6fkjnfps37wa82ri4mwszwvnnan6sk-glibc-2.25/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
<snip>

This issue is more pronounced for programs that link to lots of libraries, as the linker will go through the RPATH for each library linked to. As far as I understand it, the DT_NEEDED entries in the dynamic relocation section could contain absolute paths, which would reduce the amount of searching necessary. Additionally, this ties the library reference more closely to the derivation's build inputs.

Any thoughts? I haven't got any hard data on the performance impact this has (though the fact that most distros use the ld.so.cache mechanism to speed up loading suggests that it is relevant at least in some contexts), or any idea what other consequences of using absolute paths would be, but having executable loading be O(n²) for the number of referenced store components just somewhat bothers me.

Technical details

  • System: 17.03.899.c87abfae79 (Gorilla)
  • Nix version: nix-env (Nix) 1.11.8
  • Nixpkgs version: "17.03.899.c87abfae79"
@Mic92
Copy link
Member

Mic92 commented Apr 12, 2017

Is it unambiguous, which library should be linked? Does ELF even allow this?

@abbradar
Copy link
Member

Another possible issue is if LD_PRELOAD and LD_LIBRARY_PATH would still work with those binaries. With that all cleared however it seems a definite win to me!

@lheckemann
Copy link
Member Author

Does ELF even allow this?

http://www.skyfree.org/linux/references/ELF_Format.pdf not a canonical source as far as I can tell, but the "Shared Object Dependencies" section suggests so.

Another possible issue is if LD_PRELOAD and LD_LIBRARY_PATH would still work with those binaries.

I don't think LD_LIBRARY_PATH would still work for replacing the libraries that would then be linked through absolute pathnames (but is that desirable? The only case where I think it would be useful is the OpenGL drivers). I think LD_PRELOAD would (based on the fact that the libraries specified there are the first to be loaded before any others), but again I'm not sure.

@dezgeg
Copy link
Contributor

dezgeg commented Apr 12, 2017

Yes, LD_LIBRARY_PATH won't be searched if the DT_NEEDED entry contains a slash (_dl_map_object in glibc sources). So the current libGL driver selection breaks.

Hopefully we have that converted to use libglvnd some day. Maybe @edolstra knows other places where LD_LIBRARY_PATH is used and/or needed.

@dezgeg
Copy link
Contributor

dezgeg commented Apr 12, 2017

I whipped up a patch to patchelf to implement this in case anybody wants to try to see what breaks: https://github.com/dezgeg/patchelf/tree/abs

@abbradar
Copy link
Member

abbradar commented Apr 12, 2017

One example that I can remember is scanner drivers (sane). For example on my system:

LD_LIBRARY_PATH=/nix/store/4m8l0zzdyl5lynsdx5gjml07zplph9gl-sane-config/lib/sane:/run/opengl-driver/lib:/run/opengl-driver-32/lib

I have a libglvnd branch in progress but wanted to wait some more until Fedora ships its support.

@dezgeg
Copy link
Contributor

dezgeg commented Apr 12, 2017

But are the sane plugin .so:s referenced by DT_NEEDED and not dlopen():ed? I don't think why dlopen() with LD_LIBRARY_PATH wouldn't work anymore.

@abbradar
Copy link
Member

abbradar commented Apr 12, 2017

@dezgeg True! That leaves us with libGL (libglvnd to the rescue but AMD proprietary drivers don't support it currently), libEGL, libGLES and others (some of those are still not supported by libglvnd, I don't remember exactly which, but they are planned) and, last but not least, cases when people want to force their library paths. Not sure about latter but it definitely could happen.

@vcunat vcunat added 1.severity: mass-rebuild This PR causes a large number of packages to rebuild 9.needs: community feedback labels Apr 12, 2017
@vcunat
Copy link
Member

vcunat commented Apr 12, 2017

Very interesting. I didn't know the loader can be so easily convinced to absolute path searches (without changing sonames). It would really be a significant change to basic nix(pkgs) principles, maybe worth of an RFC before we switch (but I'd favor more investigation+testing before RFC).

libGL: we could patch that selectively – either converting it to non-absolute or removing the R(UN)PATH item completely.

Looking at the comment about DT_RUNPATH, I wonder what would happen if two instances of the same library are attempted to be loaded into a single process (same sonames but different /nix/store paths). For example, libGL may often have been linked against a different instance of libc... EDIT: now I see this part has been used all the time, if I understand it correctly.

@dezgeg
Copy link
Contributor

dezgeg commented Apr 12, 2017

If I read the logic in glibc correctly, yes, loading libraries (by both DT_NEEDED or dlopen()) that have same sonames but different /nix/store paths will load both of them wheras the current RUNPATH-based approach would load only the first copy.

@Profpatsch
Copy link
Member

Profpatsch commented Apr 14, 2017

philip@katara ~/c/n/cabal2nix (master)> strace -e trace=file result/bin/hoogle 2>&1 | grep "open.*so.*= 3.*" | wc -l
128
philip@katara ~/c/n/cabal2nix (master)> strace -e trace=file result/bin/hoogle 2>&1 | grep "open.*so.*= -1.*" | wc -l
8829
philip@katara ~/c/n/cabal2nix (master)> strace -e trace=file result/bin/purs  2>&1 | grep "open.*so.*= 3.*" | wc -l
166
philip@katara ~/c/n/cabal2nix (master)> strace -e trace=file result/bin/purs 2>&1 | grep "open.*so.*= -1.*" | wc -l
14615

That’s the current ratio of .so stat hits to misses for hoogle and the purescript compiler. hoogle has a startup time delay of 2s and purs of >4s on my SSD-based system. When compiled statically, the startup of both programs is instantaneous. cc @peti

@Profpatsch
Copy link
Member

Profpatsch | Ah, I understand now!                                           
Profpatsch | The problem with rpath.                                         
Profpatsch | So you have one rpath entry for each haskell Dependency.        
Profpatsch | And it tries to find foobar.so                                  
   dtzWill | and looks through all the things                                
Profpatsch | It will linearly stat *every* rpath entry until it finds the so.
Profpatsch | which means n^2                                                 
Profpatsch | Or probably (n^2)/2 in the mean case.                           
    hodapp | Still O(n^2) :P                                                 
Profpatsch | purs has 168 deps                                               
Profpatsch | -5 for the normal .so’s the Haskell runtime needs               
Profpatsch | 163^2 = 26.5k                                                   
Profpatsch |  /2 and we arrive at the 13k misses I measured before.          
Profpatsch | Science!                                                        
   dtzWill | ٩(^ᴗ^)۶                                                         
    hodapp | hah                                                             

@lheckemann
Copy link
Member Author

lheckemann commented Apr 14, 2017

Yeah, exactly. Although I'm not sure your statistics for hits are correct though — It's possible that all of the open calls result in the FD 3 but not guaranteed I think, so rather than grep "open.*so.*= 3.*" you'd probably want to grep "open.*so.*= [^-].*" so that every non-negative return value is considered a success.

@copumpkin
Copy link
Member

On Darwin, all of our Nix library references are absolute by default, even though we do use rpath in some places. Just FYI for "prior art" 😄

@vcunat
Copy link
Member

vcunat commented Apr 14, 2017

No! Now I can't patent the technique ;-)

@Profpatsch
Copy link
Member

I wrote a WIP patch for haskellPackages, using @dezgeg’s patch for patchelf.
Profpatsch@93cfe6e

It fails because of broken ELF binaries, though. Probably a bug in the patchelf .dynstr size handling.

@lheckemann
Copy link
Member Author

lheckemann commented Apr 22, 2017

FWIW this would break this hack for changing freetype behaviour — at least in its current form. It should of course still work using LD_PRELOAD (which may be a better idea in general).

@lheckemann
Copy link
Member Author

The primary approach for implementing this should probably be making libraries and executables link with the absolute paths from the get-go rather than patching them post-hoc, right? Does anyone know how this would be done?

@Profpatsch
Copy link
Member

Profpatsch commented Jun 24, 2017

Does anyone know how this would be done?

I experimented with that a few days in April; the GNU linker describes how it will skip search heuristics if it finds absolute paths in the ELF header.
Now comes the fun part: the GNU linker also does compile time linking and (as far as I could see) they do not provide the ability to link in absolute paths. There simply are no options that do that. I think I never facepalmed so hard before.

@vcunat
Copy link
Member

vcunat commented Jun 24, 2017

One advantage for a fixup-phase approach: it should work even on binary packages. (Though I personally don't consider that a significant pro.)

@lheckemann
Copy link
Member Author

It's certainly more versatile, but it still feels wrong: why compile something the wrong way then fix it rather than compiling it right in the first place?

@vcunat
Copy link
Member

vcunat commented Jun 24, 2017

I understand that sentiment, but if our default linker doesn't allow it, we'd have to patch the linker to add this (optional) functionality, I guess, and that feels rather cumbersome to maintain. (not mentioning the additional complications if someone wants to use a different linker, etc.) Still, it's possible the other option won't turn out easy either.

@dtzWill
Copy link
Member

dtzWill commented Jun 24, 2017

As for doing it w/o fixup, I think we can replace -lfoo with /path/to/libfoo.so in the ld wrapper?

Shouldn't be too much trouble since we already have misc support for basically gathering all the "-L" paths and locating each library anyway.

FWIW a quick look into this particular issue leads to these relevant discussions:

catkin discussion
binutils issue about intentionally removing/"fixing" support for -l:/path/to/libfoo.so

Doing this with a fixup sounds good to me as well, maybe give both a try?

EDIT: Absolute path for libc.so has problems because it's really a script. Can special-case this, but might happen elsewhere. IIRC libc++.so does this sort of thing, at least optionally. This suggests a fixup might be easier?

(Err I suppose this "only" happened because I also removed all "-L" switches.)

@ben0x539
Copy link
Contributor

I don't think passing absolute paths to ld actually makes those paths show up in the generated binary?

@dtzWill
Copy link
Member

dtzWill commented Jun 24, 2017

@ben0x539 seems to for me? Here's a quick test:

with import <nixpkgs> {};

rec {
  foo = stdenv.mkDerivation {
    name = "libfoo";

    buildCommand = ''
      echo "int foo() { return 5; }" >> foo.c

      cc foo.c -o libfoo.so -shared

      mkdir -p $out/lib
      mv libfoo.so $out/lib
    '';
  };

  main = stdenv.mkDerivation {
    name = "abspath-test";

    buildInputs = [ foo ];

    buildCommand = ''
      cat >>main.c <<EOF
      #include <stdio.h>
      int foo();
      int main() {
        printf("foo: %d\n", foo());
        return 0;
      }
      EOF

      cc main.c -o main -lfoo

      mkdir -p $out/bin
      mv main $out/bin/
    '';
  };

  main-abs = stdenv.mkDerivation {
    name = "abspath-test";

    buildInputs = [ foo ];

    buildCommand = ''
      cat >>main.c <<EOF
      #include <stdio.h>
      int foo();
      int main() {
        printf("foo: %d\n", foo());
        return 0;
      }
      EOF

      cc main.c -o main ${foo}/lib/libfoo.so

      mkdir -p $out/bin
      mv main $out/bin/
    '';
  };

}
$ nix-build -A main -o main
$ nix-build -A main-abs -o main-abs
$ readelf -d main/bin/main

Dynamic section at offset 0xde8 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libfoo.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [/nix/store/nirdkvlp2wim11mh0fj0bbpmdq6m5rg4-abspath-test/lib64:/nix/store/nirdkvlp2wim11mh0fj0bbpmdq6m5rg4-abspath-test/lib:/nix/store/my9f454r82wy6jv3x7bvvpx6hffcahsj-gcc-5.4.0/libexec/gcc/x86_64-unknown-linux-gnu/5.4.0:/nix/store/h6np4yvv6j7qcp3ikh4gcgj49zgjsb7n-libfoo/lib:/nix/store/7crrmih8c52r8fbnqb933dxrsp44md93-glibc-2.25/lib:/nix/store/89bjrnjb8c9vvhzb92fz832x6fwg3kbj-gcc-5.4.0-lib/lib]
 0x000000000000000c (INIT)               0x4006c8
 0x000000000000000d (FINI)               0x4008a4
 0x0000000000000019 (INIT_ARRAY)         0x600dd0
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x600dd8
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x0000000000000004 (HASH)               0x4002e0
 0x0000000000000005 (STRTAB)             0x400410
 0x0000000000000006 (SYMTAB)             0x400320
 0x000000000000000a (STRSZ)              528 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x600fc8
 0x0000000000000007 (RELA)               0x400668
 0x0000000000000008 (RELASZ)             96 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000000000001e (FLAGS)              BIND_NOW
 0x000000006ffffffb (FLAGS_1)            Flags: NOW
 0x000000006ffffffe (VERNEED)            0x400638
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x400620
 0x0000000000000000 (NULL)               0x0

$ readelf -d main-abs/bin/main

Dynamic section at offset 0xde8 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [/nix/store/h6np4yvv6j7qcp3ikh4gcgj49zgjsb7n-libfoo/lib/libfoo.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [/nix/store/2x61jy86dxzzyp88vah6hk1s0556bx1z-abspath-test/lib64:/nix/store/2x61jy86dxzzyp88vah6hk1s0556bx1z-abspath-test/lib:/nix/store/my9f454r82wy6jv3x7bvvpx6hffcahsj-gcc-5.4.0/libexec/gcc/x86_64-unknown-linux-gnu/5.4.0:/nix/store/h6np4yvv6j7qcp3ikh4gcgj49zgjsb7n-libfoo/lib:/nix/store/7crrmih8c52r8fbnqb933dxrsp44md93-glibc-2.25/lib:/nix/store/89bjrnjb8c9vvhzb92fz832x6fwg3kbj-gcc-5.4.0-lib/lib]
 0x000000000000000c (INIT)               0x400700
 0x000000000000000d (FINI)               0x4008e4
 0x0000000000000019 (INIT_ARRAY)         0x600dd0
 0x000000000000001b (INIT_ARRAYSZ)       8 (bytes)
 0x000000000000001a (FINI_ARRAY)         0x600dd8
 0x000000000000001c (FINI_ARRAYSZ)       8 (bytes)
 0x0000000000000004 (HASH)               0x4002e0
 0x0000000000000005 (STRTAB)             0x400410
 0x0000000000000006 (SYMTAB)             0x400320
 0x000000000000000a (STRSZ)              583 (bytes)
 0x000000000000000b (SYMENT)             24 (bytes)
 0x0000000000000015 (DEBUG)              0x0
 0x0000000000000003 (PLTGOT)             0x600fc8
 0x0000000000000007 (RELA)               0x4006a0
 0x0000000000000008 (RELASZ)             96 (bytes)
 0x0000000000000009 (RELAENT)            24 (bytes)
 0x000000000000001e (FLAGS)              BIND_NOW
 0x000000006ffffffb (FLAGS_1)            Flags: NOW
 0x000000006ffffffe (VERNEED)            0x400670
 0x000000006fffffff (VERNEEDNUM)         1
 0x000000006ffffff0 (VERSYM)             0x400658
 0x0000000000000000 (NULL)               0x0

@dtzWill
Copy link
Member

dtzWill commented Jun 24, 2017

Exploring the idea of doing this by modifying ld-wrapper in this branch.

YMMV, let's see if this gets an absolute-path-linked gcc :).

I'd feel better if we had a fixup phase regardless, to either catch and abort if things aren't absolute or to handle cases missed for whatever reason.

Currently I'm handling libc.so-style things by preserving the "-L" flags so that the libraries it mentions (libc.so.6, libc_nonshared.a, and the dynamic linker) are successfully resolved. This could be cleaned up by the fixup phase?

Or maybe just doing it all in the fixup phase would be simpler? (Especially if a fixup phase is used anyway...?)

Update: branch currently gets mostly through gcc, but dies when it attempts to link against a shared object when -static is given. I'm not sure how "worth" it is to re-implement the library search behavior like this, or if there are likely other flags that cause problems.

Solving this with a fixup has the appeal of being straightforward to implement, and as @vcunat mentioned would work on pre-compiled binaries as well.

@ben0x539
Copy link
Contributor

ben0x539 commented Jun 24, 2017 via email

@Profpatsch
Copy link
Member

but if our default linker doesn't allow it, we'd have to patch the linker to add this (optional) functionality, I guess, and that feels rather cumbersome to maintain.

Somebody else should take a look at it first, I might have overlooked a flag with ld(1); of course every linker probably has a different option for that. So we should at least add a check that emits a warning in fixup phase if some ELF paths are not absolute.

@stale

This comment has been minimized.

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jun 2, 2020
@lheckemann
Copy link
Member Author

no, bad bot

@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jun 3, 2020
@ehmry
Copy link
Contributor

ehmry commented Jun 11, 2020

Why not apply @dezgeg 's --make-needed-absolute patch to patchelf?

@lheckemann
Copy link
Member Author

iirc it had some bug that either crashed patchelf itself or caused the resulting executables to crash (can't remember which). That said, that is my preferred approach — the patch just needs fixing, which is what I'll have a look at doing (see #45105) unless someone else beats me to it.

@ehmry
Copy link
Contributor

ehmry commented Oct 27, 2020

I'm working with a small loader without support for RPATH so I've tried patching Clang to resolve libraries to absolute paths before linking, but LLD somehow unresolves them to filenames. Even if it did work, patching during fixupPhase is still probably the most robust solution because there tools can distinguish between temporary paths and permanent store paths.

EDIT: This behavior is stupid easy to patch in LLVM LLD: ehmry/llvm-project@796dbd9

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/genodepkgs-extending-nixpkgs-nixos-to-genode/8779/2

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/lots-of-libraries-cant-be-found/11670/2

@ehmry
Copy link
Contributor

ehmry commented Feb 24, 2021

BTW, replacing short library names with full paths across nixpkgs isn't feasible until this is fixed: NixOS/patchelf#244.

@stale
Copy link

stale bot commented Aug 28, 2021

I marked this as stale due to inactivity. → More info

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Aug 28, 2021
@mohe2015
Copy link
Contributor

Definitely not stale.

@tomberek
Copy link
Contributor

Potential: https://guix.gnu.org/en/blog/2021/taming-the-stat-storm-with-a-loader-cache/

@stale stale bot removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Aug 30, 2021
@Mathnerd314
Copy link
Contributor

I like the absolute paths solution better because the loader caches duplicate dependency information every time they are included. But the loader cache is probably less effort to maintain than a new ELF format. Maybe the solution is to make a new loader cache format that allows recursive loading.

@lheckemann
Copy link
Member Author

NixOS/patchelf#357 implements this

@Profpatsch
Copy link
Member

If this lands it would be huge.

@stale stale bot added the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Jul 31, 2022
@PanAeon
Copy link
Contributor

PanAeon commented Sep 13, 2022

still relevant

@ehmry ehmry removed the 2.status: stale https://github.com/NixOS/nixpkgs/blob/master/.github/STALE-BOT.md label Sep 13, 2022
@haampie
Copy link

haampie commented Sep 22, 2022

See also https://stoppels.ch/2022/08/04/stop-searching-for-shared-libraries.html for a simple(r) solution

@vcunat
Copy link
Member

vcunat commented Sep 22, 2022

Using absolute paths in sonames was discussed above. I'm personally not convinced that we should use that (by default); I fear it would be much more trouble than worth.

@Mathnerd314
Copy link
Contributor

My system has a slow HD and it takes forever to open applications. I'm pretty sure it's due to this issue. So IMO it's worth the trouble to figure out some solution to the "stat storm".

@lheckemann
Copy link
Member Author

@Mathnerd314 I think we all agree that it would be great to fix it. It's just tricky :/

@haampie
Copy link

haampie commented Nov 4, 2022

In the Spack package manager we now enable this feature under a config flag.

@nixos-discourse
Copy link

This issue has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/tweag-nix-dev-update-40/23480/3

@Qyriad Qyriad added the significant Novel ideas, large API changes, notable refactorings, issues with RFC potential, etc. label Jun 27, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
1.severity: mass-rebuild This PR causes a large number of packages to rebuild 9.needs: community feedback significant Novel ideas, large API changes, notable refactorings, issues with RFC potential, etc.
Projects
None yet
Development

No branches or pull requests