Preferred nix-friendly way to call runtime dependency from binary

I’m currently trying to write a derivation for Sign in · GitLab. The libexec/sac2c/1.3.3-MijasCosta-552-g630ef/libsac2c_p.so binary has calls to /usr/sbin/cc (cc is a runtime dep). I didn’t manage to replace them in the binary. I have contact to the developers and I can ask them make some changes to make the output more nix-friendly. My question is: What would be the recommended (best practice) way to do this? Is calling programs with absolute paths the standard practice? I was told by the devs that they considered PATH dependence (as would happen with e.g. using instead /usr/bin/env cc) undesirable.
Would them defining an environment envariable for cc that I could set be good practice? Should it be for cc directly, or for the binpath containing cc? It seems to me a bit of a hack, cluttering the environment whenever you want to use that package.
Is there perhaps an existing derivation in nixpkgs with a similar situation I could take inspiration from?

I found this derivation: https://github.com/NixOS/nixpkgs/blob/f05284c43101114e89462a1342b79062d16fe986/pkgs/os-specific/linux/ati-drivers/builder.sh where sed seems to work for replacing paths in the binary. I subsequently executed the replacement in some binaries I had missed, but that still didn’t catch all occurrences of usr/sbin/cc.

I’m not sure what the “best” approach here is, but some decent options would be passing in the path at build time (a configure flag, perhaps), or using an environment variable and creating a wrapper that sets that variable.

1 Like

I realized that actually there actually was build-time configuration that allowed me to set the cc path. Replacing it in the binary was perhaps a red herring (although I haven’t checked if it runs that far yet), in any case, it caused a segfault.

The segfaults you observed are probably because the new path didn’t have exactly the same length as the original path. While replacing strings by arbitrary other strings in the source code is generally fine, in compiled binaries the lengths have to exactly match.

How come patchelf works so nicely to patch the rpath whereas hard-coded executable paths have to be literally replaced (which doesn’t work if they differ in length, due to segfaults)? Could something like the rpath mechanism be used for patching the executable paths as well, if defined in a certain way in the source? I don’t know very much about C, but it is at least conceivable

rpath lives in an ELF header, and this is safe to resize because references to later in the file are relative to the start of various sections in the file, and patchelf knows how to update the ELF file appropriately. In general, you can’t arbitratily change the lengths of strings in code or data sections of a binary though, as sed does, because code and other things in the file are expecting things to be at specific offsets. You would need to update all references, but this would amount to recompiling the code to do properly.