Help understanding linking in NixOS

TLDR: Executable file fails to run. ldd says “Missing Library”. Add Library path to LD_LIBRARY_PATH. ldd says “Library found”. Executable still fails to run.

Hello,

I’m facing a problem in NixOS that’s baffling me (not that I’m too smart :)). I’m unable to run an executable. The executable is aapt2 which is part of an android SDK downloaded by android-studio. aapt2 is a standard part of the android/gradle build chain but it fails to run on my machine. I do a file on the aapt2 binary and it’s an ELF 64 executable so that’s fine, but then ldd reveals a missing libz. I find the current version of libz and add it to LD_LIBRARY_PATH. The interpreter can now see libz but it still fails to run the executable.

Here’s a session if that helps find problems-.

[~]
$cd /home/zx/Android/Sdk/build-tools/27.0.3/ 

# Try to run aapt2                                                                                                         
[~/Android/Sdk/build-tools/27.0.3]
$./aapt2                                                                                                                                                  
zsh: no such file or directory: ./aapt2

# File command says it's 64 bit. Check if dependencies met. 
[127][~/Android/Sdk/build-tools/27.0.3]
$ldd ./aapt2                                                                                                                                              
	linux-vdso.so.1 (0x00007ffe367b9000)
	libc++.so => /home/zx/Android/Sdk/build-tools/27.0.3/./lib64/libc++.so (0x00007f504f2d5000)
	libz.so.1 => not found
	libpthread.so.0 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libpthread.so.0 (0x00007f504f2b4000)
	libm.so.6 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libm.so.6 (0x00007f504f11e000)
	libgcc_s.so.1 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libgcc_s.so.1 (0x00007f504ef08000)
	libc.so.6 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libc.so.6 (0x00007f504ed52000)
	/lib64/ld-linux-x86-64.so.2 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib64/ld-linux-x86-64.so.2 (0x00007f504f60c000)
	librt.so.1 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/librt.so.1 (0x00007f504ed46000)
	libdl.so.2 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libdl.so.2 (0x00007f504ed41000)

# Check what kind of file the missing dependency is. 
[~/Android/Sdk/build-tools/27.0.3]
$ZLIB_PATH=$(dirname $(readlink -e /run/current-system/sw/lib/libz.so.1.2.11))                                                                            
[~/Android/Sdk/build-tools/27.0.3]
$file $ZLIB_PATH/libz.so.1.2.11                                                                                                                           
/nix/store/f8zs7mknva4rdx7zxr6j54y0igh3pras-zlib-1.2.11/lib/libz.so.1.2.11: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, not stripped

# Add missing dependency to LD_LIBRARY_PATH
[~/Android/Sdk/build-tools/27.0.3]
$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ZLIB_PATH                                  

# see if the missing dependency is recognized                                                                      
[~/Android/Sdk/build-tools/27.0.3]
$ldd ./aapt2                                                                                                                                              
	linux-vdso.so.1 (0x00007ffd2152d000)
	libc++.so => /home/zx/Android/Sdk/build-tools/27.0.3/./lib64/libc++.so (0x00007fc5ef21e000)
	libz.so.1 => /nix/store/f8zs7mknva4rdx7zxr6j54y0igh3pras-zlib-1.2.11/lib/libz.so.1 (0x00007fc5ef1ff000)
	libpthread.so.0 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libpthread.so.0 (0x00007fc5ef1de000)
	libm.so.6 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libm.so.6 (0x00007fc5ef048000)
	libgcc_s.so.1 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libgcc_s.so.1 (0x00007fc5eee32000)
	libc.so.6 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libc.so.6 (0x00007fc5eec7c000)
	/lib64/ld-linux-x86-64.so.2 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib64/ld-linux-x86-64.so.2 (0x00007fc5ef555000)
	librt.so.1 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/librt.so.1 (0x00007fc5eec70000)
	libdl.so.2 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib/libdl.so.2 (0x00007fc5eec6b000)

# try to run again. 
[~/Android/Sdk/build-tools/27.0.3]
$./aapt2                                                                                                                                                  
zsh: no such file or directory: ./aapt2



Could somebody give me pointers on how to go about solving my problem? I have what most here would call an intermediate level of understanding of how linking/loading et al work.

Bumping, I hope it’s not considered bad manners here.

You need to change the interpreter. Run patchelf --print-interpreter ./aapt2 and I believe it will point to something like /lib64/ld-linux-x86-64.so.2 which does not exists in NixOS (that’s my you see no such file or directory from shell).

You could run patchelf --set-interpreter $(nix-build "<nixpkgs>" -A glibc --add-root ~/.cache/gcroots/glibc --indirect --no-out-link)/lib/ld-linux-x86-64.so.2 ./aapt2 to set an available interpreter. ( the --add-root ... --indirect option add a gcroot to prevent the glibc from being garbage-collected)

Will try when on a computer but isn’t that the 3rd last line in the ldd output? I gathered ldd printed the interpret we too

libc.so.6 is found in LD_LIBRARY_PATH or rpath, so that’s printed by ldd, but the interpreter is set in another section.

(I’m not an expert, correct me if I’m wrong about the ELF format)

I was talking about the third last line:

 /lib64/ld-linux-x86-64.so.2 => /nix/store/kksyrix1bpklvgkmvngcv0q9nh8hn2fl-glibc-2.27/lib64/ld-linux-x86-64.so.2 (0x00007f504f60c000)

This is what threw me off.

You were current in your diagnosis and prescription though. Thank you!

EDIT: I wonder what would it take for the environment to not require me to set LD_LIBRARY_PATH to include zlib- I’d love for zlib to be found automatically. Even after setting the right interpreter it fails to find it.

Ah, then I don’t have a very good explanation now, I still have so much to learn :stuck_out_tongue:

Any way, you could append rpath in the ELF file so the dynamic linker will find libraries under these paths. You could use patchelf --set-rpath "$(patchelf --print-rpath ./aapt2):$(dirname $(readlink -e /run/current-system/sw/lib/libz.so.1.2.11))" ./aapt2

1 Like

I have written two blog posts that elaborately explain the concepts of patching binaries so that they can be used in NixOS:

Android, in particular, is quite a challenge to make work properly in NixOS, for a variety of reasons.

3 Likes