Running a Dynamically linked executable from a very picky programm

I need help trying to Package a program that is not in nixpkgs. I packaged It like this:

{ stdenv, lib, bash, perl, bison, flex, tcl, fltk, mesa, libGL, xorg, haskellPackages, cudaPackages, python3, python313Packages }:
    

stdenv.mkDerivation rec {

  pname = "vmd";
  version = "2.0.0";

  src = ./vmd-2.0.0.bin.LINUXAMD64.tar.gz;
  
  unpackPhase = ''
    tar -xvzf $src
    cp -r ${pname}-${version} source
    chmod -R +w source
    cd source
  '';

  nativeBuildInputs = [ bash perl bison flex cudaPackages.cudnn];

  patchPhase = ''
    patchShebangs configure

  '';

 preConfigure = ''

   substituteInPlace configure \
     --replace-fail 'install_bin_dir="usr/local/bin"' 'install_bin_dir="$out/bin"' \
     --replace-fail 'install_library_dir="/usr/local/lib/$install_name"' 'install_library_dir="$out/lib/vmd"'


 '';

  configurePhase = ''
    ./configure LINUX OPENGL FLTK MESA PYTHON NUMPY
  '';  



  dontBuild = true;

# recursion in symlinks solved. still broken bc other reasons.

  installPhase =''
    substituteInPlace src/config.h \
      --replace-fail "/lib/vmd" "$out/lib/vmd" \
      --replace-fail "/usr/local" "$out/local" 
   
    cat src/Makefile

    for f in src/Makefile bin/vmd.sh bin/vmd.csh  ; do
      substituteInPlace "$f" \
        --replace-fail "/usr/local/lib/vmd" "$out/lib/" \
        --replace-fail "/usr" "$out/usr" 
    done     
   
    cat src/Makefile   

    cd src
    make install 

    mkdir -p $out/bin
    mkdir -p $out/lib/vmd

    cd ..
    cp -r LINUXAMD64/* $out/bin
  '';

  propagatedBuildInputs = [
    tcl
    fltk
    mesa
    libGL
    xorg.libX11
    xorg.libXext
    xorg.libXi
    xorg.libXrender
    haskellPackages.OpenGL
    cudaPackages.cudnn
    python3
    python313Packages.numpy
];

  meta = with lib; {
    description = "Just for personal Use, has a complicated license";
    homepage = "https://www.ks.uiuc.edu/Research/vmd/";
    platforms = platforms.unix;
  };  
}

when running ./result/bin/vmd_LINUXAMD64 it says:

Could not start dynamically linked executable: ./result/bin/vmd_LINUXAMD64
NixOS cannot run dynamically linked executables intended for generic
linux environments out of the box.

How can I make it just run and include it to my configuration file?

Try autoPatchelfHook.

1 Like

autoPatchelfHook is fine but shouldn’t usually be required unless:

  • it’s not built from source
  • there’s some dependency on a library at runtime that is somehow not encoded during buildtime but still declared via DT_NEEDED

To rule out the former, are you sure you’re actually building the thing from source and not just copying in the prebuilt binary into $out/bin?

It’s actually okay to not build it from source (though it does preclude patching the program) - but in that case you might as well not waste the CPU cycles of configurePhase and buildPhase.

You also missed the pre/post-phase hooks, so some of that code isn’t even running :slight_smile:

You’ll need to correct anything that ends in Phase that you manually set.

There’s also other issues I see, but those are only really issues if you want to submit it to nixpkgs. You don’t have to do so.

You were completely right. The preConfigure was not running. I updated it so the redundant steps are no more in the file. Also tried autoPatchelfHook because as you pointed it was also not running from source.

Some of the libraries could be automatically patched, others had to be added to the file. I set some to be ignored because I could not get them right. At the end my default.nix looked like this:

{ stdenv, lib, bash, perl, bison, flex, tcl, fltk, mesa, libGL, xorg, haskellPackages, cudaPackages, python3, python313Packages, pango, dbus, autoPatchelfHook }:
    

stdenv.mkDerivation rec {

  pname = "vmd";
  version = "2.0.0";

  src = ./vmd-2.0.0.bin.LINUXAMD64.tar.gz;
  
  unpackPhase = ''
    tar -xvzf $src
    cd ${pname}-${version}
  '';

  nativeBuildInputs = [ 
    autoPatchelfHook 
  ];
   
  autoPatchelfIgnoreMissingDeps = [
    "liboptixu.so.6.5.0"
    "liboptix.so.6.5.0"
    "libxkbcommon.so.0"
    "libgtk-3.so.0"
    "libgdk-3.so.0"
    "libatk-1.0.so.0"
    "libgdk_pixbuf-2.0.so.0"
    "libhdf5.so.101"
    "libhdf5_hl.so.100"
  ];

  buildInputs = [
    bash
    perl 
    bison 
    flex 
    cudaPackages.cudnn
    haskellPackages.gi-pangocairo
    pango
    dbus
  ];

  patchPhase = ''
    patchShebangs configure
  '';

 preConfigure = ''

   substituteInPlace configure \
     --replace-fail '"/usr/local/bin"' '"$out/bin"' \
     --replace-fail '"/usr/local/lib/$install_name"' '"$out/lib/vmd"' \

 '';

  configurePhase = ''
    runHook preConfigure

    ./configure LINUX OPENGL FLTK MESA PYTHON NUMPY
    
  '';  



  dontBuild = true;


  installPhase =''
    substituteInPlace src/Makefile \
      --replace-fail "/lib/vmd" "$out/lib/" 

    substituteInPlace bin/vmd.csh \
      --replace-fail "/bin" "$out/bin/"
   
    cd src
    make install 

    mkdir -p $out/bin

    cd ..
    cp -r LINUXAMD64/* $out/bin

    cp -r bin/* $out/bin
  '';

  propagatedBuildInputs = [
    tcl
    fltk
    mesa
    libGL
    xorg.libX11
    xorg.libXext
    xorg.libXi
    xorg.libXrender
    haskellPackages.OpenGL
    cudaPackages.cudnn
    python3
    python313Packages.numpy
];

  meta = with lib; {
    description = "VMD installation following the README";
    homepage = "https://www.ks.uiuc.edu/Research/vmd/";
    platforms = platforms.unix;
  };  
}

Sadly it still does not run and I find myself in another loose end.

./result/bin/vmd_LINUXAMD64: error while loading shared libraries: liboptixu.so.6.5.0: cannot open shared object file: No such file or directory

Any suggestions how to go forwards?

You’re still missing runHook postConfigure, same with preInstall postInstall, and *Unpack. And if you’re using autoPatchelfHook then don’t bother configuring or most of the install phase… did you read my prior comment?

Well of course this will cause problems when the program needs this file :wink:
You need to find the correct packages and add them to buildInputs. nix-locate (from nix-index package) may help with this, but it will only work for FOSS libraries.

3 Likes

You should be able to build from source. While I am not registered on that website, they do mention that they also distribute source code here: https://www.ks.uiuc.edu/Development/Download/download.cgi?PackageName=VMD

From the list of dependencies you listed under autoPatchelfIgnoreMissingDeps, I think you can get far by adding [ gtk3 gdk-pixbuf hdf5 ] to your buildInputs

Then again I don’t know how you’re getting optix packaged alongside it…

You may have to do as they say, and just try running the software through CentOS stream (RHEL but free, basically) with distrobox create -n vmd -i quay.io/toolbx-images/centos-toolbox…

2 Likes

Got it to work:

{ stdenv, lib, bash, perl, tcl, fltk, mesa, libGL, xorg, haskellPackages, cudaPackages, python3, python313Packages, autoPatchelfHook, gtk3, gdk-pixbuf, hdf5 }:
    

stdenv.mkDerivation rec {

  pname = "vmd";
  version = "2.0.0";

  src = ./${pname}-${version}.bin.LINUXAMD64.tar.gz;
  

  nativeBuildInputs = [ 
    autoPatchelfHook 
  ];
   
  autoPatchelfIgnoreMissingDeps = [ "*" ];

  buildInputs = [
    bash
    perl
  ];

  patchPhase = ''
    patchShebangs configure
  '';

 preConfigure = ''

   substituteInPlace configure \
     --replace-fail "/usr/local/bin" "$out/bin" \
     --replace-fail '"/usr/local/lib/$install_name"' '"$out/lib/$install_name"' \
   
 '';

  configurePhase = ''
    runHook preConfigure
    
    ./configure LINUXAMD64 
    ./configure

    export PREFIX=$out
    export NIX_DEBUG=1

    runHook postConfigure  
  '';  


  dontBuild = true;
  

  installPhase =''
    runHook preInstall
 
     substituteInPlace src/Makefile \
       --replace-fail "/lib/vmd" "$out/lib/vmd" \

    cd src
    make install

    mkdir -p $out/
    cp -r ./* $out/

    runHook Postinstall
 '';

  dontCheckForBrokenSymlinks = true;

  propagatedBuildInputs = [
    tcl
    fltk
    mesa
    libGL
    xorg.libX11
    xorg.libXext
    xorg.libXi
    xorg.libXrender
    haskellPackages.OpenGL
    python3
    python313Packages.numpy
    cudaPackages.cudnn
    cudaPackages.cudnn-frontend
    cudaPackages.cuda_cudart
    gtk3
    gdk-pixbuf
    hdf5
];

  meta = with lib; {
    description = "Visual Molecular Dynamics";
    homepage = "https://www.ks.uiuc.edu/Research/vmd/";
    platforms = platforms.unix;
  };  
}

OK, a few things worth fixing here:

Remove this. If it misses some dependencies, it’s better to know at buildtime then runtime.

As said previously, remove this if you are packaging from binary. You’re just wasting time when building.

1 Like

I’m sorry for not communicating enough. I meant to take the advise and tried It. I just don’t understand most of it and could not get it to work.

I have a lot of respect for you. This may be the first time I post to this Forum, but I have read quite a few articles and you are always there to help people. Its a bummer that with the good advise also this kind of passive aggressiveness is found.

Take the following with a huge grain of salt because I’m not as knowledgeable as you seem to be. In short I did not understand some of the stuff you suggested, even after reading the documentation attached.

  • The libraries still missing are: “libcuda.so.1“, “libnvcuvid.so.1“, “libcudart.so.9.0“. I dont have any Idea where to find them. I actually used your advise with the nix-index and they were not found, as they are probably not FOSS.
  • The code in preConfigure is needed because the configure file tries sets the Makefile to output to /usr/local/bin and /usr/local/lib/$install_name. The README suggest to modify the paths manually if you wish to install somewhere else.
  • The configure Phase was performed as told by the README. I tested not doing it. The src/Makefile does not generate. The export stuff was removed without it causing a failed build.
  • The installPhase needs to still replace hardcoded absolute Paths in the generated Makefile. The rest is the way of installing as per the README.
  • the cd src into make install was again the way described by the readme. Withouut it still builds but I cannot find the correct executable nowhere. When I execute the one I can find I tells me:

ERROR) This probably means that Tcl wasn’t installed properly.
ERROR) Tk startup error: invalid command name “tcl_findLibrary”
ERROR) Unable to locate VMD glycan data path, VMDDIR environment variable not set
Segmentation fault (core dumped) ./vmd_LINUXAMD64

  • At the end are two dangling symlinks because some cuda libraries point to each other like this: libcudnn.so → libcudnn.so.7 → libcudnn.so.7.3.1 leaving two dangling symlinks.

So yeah I followed all your advise as far as I could. Keep in mind before yesterday I did not know what binaries or symlinks were. My only experience in programming is a “introduction to python for biological life science” course I took at uni.

I just need the program to run and do my actual project with it. I’m still open to better the code if that can help someone else. Just don’t be mean, please.

3 Likes

Are you or are you not building from source?

If you’re building from source, you don’t need autoPatchelfHook at all, as the compiler and the fixupPhase will automatically get things right.

If you’re using a precompiled binary, why are you configuring and building?

I haven’t looked at the details of what you’re trying to package, but it’s certainly unusual to be running make in a binary install.

1 Like

From a quick search of nixpkgs, libcuda.so.1 and libnvcuvid.so.1 are actually provided by /run/opengl-driver, so replace [ "*" ] with them.

Sorry about configure confusion, it does look a bit weird to be running configure and make for a binary install.

Actually, looks like someone already packaged this, so you don’t have to!

2 Likes

Thank you!

I wish I knew it a 5 days ago haha. I looked on search.nixos.org and also put vmd on github before trying this out.

Mind to explain your approach on finding those resources?

1 Like

I mostly just do lang:nix <keywords> in github code search to find things. Actually I was looking at the lang:nix libcudart.so.9.0 results since you mentioned it was missing, but lang:nix vmd LINUXAMD64 finds it as well. github code search is honestly one of the most useful “documentation” resources for Nix.

3 Likes

Thanks for explaining what your thought process was here. I didn’t realise we were speaking essentially different languages/jargon, so I see how my response was unfair. I’ve deleted it as it didn’t add to the discussion.

Some background: practically all compileable code is built from some sort of source files - text files that humans can understand (source code) that are compiled into some machine-readable format (binaries). In open-source communities, however, when we say “built from source” we mean that we used the source code and our own tools to build the binary, instead of trusting whatever binaries the people on MyProgram dot com decided to post.

Building the binary from source ourselves confers some benefits; aside from the security benefits of being able to know what source code was used to build it, we also get the convenience of having the binary actually point directly at its dependencies. Oversimplifying and limiting to Linux, normally binaries that depend on some dependency called blah will declare that dependency on blah.so or maybe a specific version blah.so.1.2.3. When you run that binary, a special program called a dynamic loader (ld-linux.so) will check common directories like /usr/lib/ for this .so file. Of course most of these paths will not exist on NixOS, so when we build stuff using stdenv from nixpkgs, we will encode a more specific requirement in the binary, like “hey find the dependency at /nix/store/.../blah.so instead of trying to guess where it is”.

On the other hand, there is autoPatchelfHook - it runs patchelf, a really helpful hack for closed-source software where we are forced to trust MyProgram dot com to send us some program that does what it claims. Since basically nobody (%-wise) uses NixOS (and even if they did, distributing a binary like this would be unhelpful), they are going to assume a generic Linux, where stuff like /usr/lib will exist - and so we have to use this hacky scalpel to cut into the machine-readable code and replace some references to ensure they point into /nix/store/.... Unfortunately this process is not perfect, so once this is done, (rarely) some binaries will no longer run at all. Hence, we consider patchelf a very last resort, when building from source isn’t an option.

Also, if you actually did build a program from source, and you still somehow needed autoPatchelfHook, I would assume there is something really weird with the source code or with your build - so already a red flag to me.

In this case, the VMD people provide a tarball (that .tar.gz file) that contains some kind of source code, as well as the binary program vmd_LINUXAMD64. I verified that it is some binary (ELF executable) by running file on it:

$ file ~/Downloads/vmd-2.0.0/LINUXAMD64/vmd_LINUXAMD64
~/Downloads/vmd-2.0.0/LINUXAMD64/vmd_LINUXAMD64: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=0bc3a63618fd345529bd51dac57adedd63276c71, with debug_info, not stripped, too many notes (256)

I can also inspect this file and see that it was not built using nixpkgs’ stdenv (readelf is from toybox package here):

$ readelf ~/Downloads/vmd-2.0.0/LINUXAMD64/vmd_LINUXAMD64 -d

Dynamic section at offset 0x2dd89a0 contains 67 entries:
  Tag                Type                 Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libGL.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libX11.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [librt.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libXinerama.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libXi.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [liboptixu.so.6.5.0]
 0x0000000000000001 (NEEDED)             Shared library: [liboptix.so.6.5.0]
 0x0000000000000001 (NEEDED)             Shared library: [libpng16.so.16]
 0x0000000000000001 (NEEDED)             Shared library: [libpthread.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libz.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libXft.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libfontconfig.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libfreetype.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libwayland-cursor.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libwayland-client.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libxkbcommon.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libpangocairo-1.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libpango-1.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libgobject-2.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libglib-2.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libcairo.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libdbus-1.so.3]
 0x0000000000000001 (NEEDED)             Shared library: [libgtk-3.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libgdk-3.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libatk-1.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libcairo-gobject.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libgdk_pixbuf-2.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libgio-2.0.so.0]
 0x0000000000000001 (NEEDED)             Shared library: [libXcursor.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libXfixes.so.3]
 0x0000000000000001 (NEEDED)             Shared library: [libXrender.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libm.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libmvec.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libstdc++.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [libgcc_s.so.1]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x0000000000000001 (NEEDED)             Shared library: [ld-linux-x86-64.so.2]
 0x000000000000000f (RPATH)              Library runpath: [/]

This looks a lot different from something that is built with nixpkgs’ stdenv (the hello binary in the hello package, for example):

$ readelf -d result/bin/hello

Dynamic section at offset 0xcbd8 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [/nix/store/fjkx1l5cnskzrqacf08z7i8z17256w0j-glibc-2.42-61/lib]

You can see the library runpath DT_RUNPATH actually mentions /nix/store here.

So, when I see your build code that puts this vmd_LINUXAMD64 file into $out/bin, I think, why did you spend CPU cycles building some other code, discarding it, and afterwards putting this vmd_LINUXAMD64 file that wasn’t built with nix in $out/bin when the file was there from the start? In other words, it’s as if you didn’t build anything from source at all.

The main reason this may matter to you is that you can spend time debugging only one thing (autoPatchelfHook failing) rather than two things (that + all the configure/install phase stuff) and you can therefore cut out most of the configure/install phases since your installPhase essentially becomes

install -Dm555 vmd_LINUXAMD64 $out/bin/vmd

(install is just a command to copy files around, -D creates $out and $out/bin here, then copies vmd_LINUXAMD64 to $out/bin/vmd, and -m 555 just marks the program runnable.)

Ultimately I still believe the code can be a lot shorter here if you don’t want to package the thing from source (which I understand can be a hassle in many cases). And if you do want to package the thing from source, you would need to check where the binary that you build is actually getting put and copy that to $out/bin/vmd (instead of trying to copy over the existing vmd_LINUXAMD64), and then you (hopefully) wouldn’t need autoPatchelfHook.

Feel free to ask for clarification if any point is unclear.

3 Likes