Build go code within a nix shell

Hello everyone,

I’ve been advocating for Nix at my workplace for several months, and one of my colleagues has really embraced it. He’s a Go developer and has been using Nix to set up its home-manager profile based on ECPHP / Devs profile · GitLab and have some ephemeral development environments per project that include all necessary dependencies and tools, using direnv. The operating system we use is Ubuntu, not NixOS.

However, we’ve encountered a snag. Today, he tried to compile a small Go project, and although the compilation succeeded, he was unable to run the resulting binary:

$ go build
$ ./main
exec: failed to execute process ./main: the file exist and is executable. Check the interpreter or linker.

The default devShell defined in the project’s flake is trivial and just include go in the dependencies, nothing else.

I would greatly appreciate any insights or suggestions on how to resolve this issue but also understand it. Has anyone experienced similar problems with Go binaries in Nix? Any advice on troubleshooting or adjusting the configuration to ensure the binaries run correctly?

Thank you in advance for your help!

It’s my understanding that this means there was some problem with dynamic loading for the executable.

On NixOS, it’s typical to run into this error when executing a binary that was built on another system. Or when it’s linked against shared libraries built on another system.

I’d be curious what ldd ./main outputs.

For resolving this… I’d guess there’s some kind of library that should be added to the shell; or there’s some kind of mix of Nix and non-Nix compilers/libraries going on.

We’ve been investigating since yesterday about the issue, without much luck.

I made a quick reproducer here: Go in a Nix Shell · GitHub

Running this code on my own NixOS laptop but also on my own Amazon Workspace (based on RedHat) works pretty fine. I don’t think the issue is related to Nix at all since the beginning.

However, running exactly the same steps on his Amazon Workspace (based on Ubuntu) doesn’t work:

❯ git clone https://gist.github.com/drupol/e8db5a7e22611d3aa9cd94ca5acc4f6e
Cloning into 'e8db5a7e22611d3aa9cd94ca5acc4f6e'...
remote: Enumerating objects: 6, done.
remote: Counting objects: 100% (6/6), done.
remote: Compressing objects: 100% (5/5), done.
remote: Total 6 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (6/6), done.

❯ cd e8db5a7e22611d3aa9cd94ca5acc4f6e/
direnv: error /home/<redacted>/projects/e8db5a7e22611d3aa9cd94ca5acc4f6e/.envrc is blocked. Run `direnv allow` to approve its content

❯ direnv allow
direnv: loading ~/projects/e8db5a7e22611d3aa9cd94ca5acc4f6e/.envrc
direnv: using flake
warning: creating lock file '/home/<redacted>/projects/e8db5a7e22611d3aa9cd94ca5acc4f6e/flake.lock': 
• Added input 'nixpkgs':
    'github:nixos/nixpkgs/25865a40d14b3f9cf19f19b924e2ab4069b09588' (2024-05-05)
warning: Git tree '/home/<redacted>/projects/e8db5a7e22611d3aa9cd94ca5acc4f6e' is dirty
direnv: export +AR +AS +CC +CONFIG_SHELL +CXX +HOST_PATH +IN_NIX_SHELL +LD +NIX_BINTOOLS +NIX_BINTOOLS_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu +NIX_BUILD_CORES +NIX_BUILD_TOP +NIX_CC +NIX_CC_WRAPPER_TARGET_HOST_x86_64_unknown_linux_gnu +NIX_CFLAGS_COMPILE +NIX_ENFORCE_NO_NATIVE +NIX_HARDENING_ENABLE +NIX_LDFLAGS +NIX_STORE +NM +OBJCOPY +OBJDUMP +RANLIB +READELF +SIZE +SOURCE_DATE_EPOCH +STRINGS +STRIP +TEMP +TEMPDIR +TMP +TMPDIR +__structuredAttrs +buildInputs +buildPhase +builder +cmakeFlags +configureFlags +depsBuildBuild +depsBuildBuildPropagated +depsBuildTarget +depsBuildTargetPropagated +depsHostHost +depsHostHostPropagated +depsTargetTarget +depsTargetTargetPropagated +doCheck +doInstallCheck +dontAddDisableDepTrack +mesonFlags +name +nativeBuildInputs +out +outputs +patches +phases +preferLocalBuild +propagatedBuildInputs +propagatedNativeBuildInputs +shell +shellHook +stdenv +strictDeps +system ~PATH ~XDG_DATA_DIRS

❯ go get

❯ go build

❯ ./foo 
exec: Failed to execute process './foo': The file exists and is executable. Check the interpreter or linker?

❯ ldd ./foo
	linux-vdso.so.1 (0x00007ffea015e000)
	libresolv.so.2 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/libresolv.so.2 (0x00007ad67b9b6000)
	libpthread.so.0 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/libpthread.so.0 (0x00007ad67b9b1000)
	libc.so.6 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/libc.so.6 (0x00007ad67b7c4000)
	/nix/store/35pq4hr29c3sl79lgfwgsvd9nwzyp4am-glibc-2.39-5/lib/ld-linux-x86-64.so.2 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib64/ld-linux-x86-64.so.2 (0x00007ad67b9c9000)
❯

Do you have a clue on what’s going on ?

Thanks for a reproducible example.

Though I’d suggest instead of:

git clone https://gist.github.com/drupol/e8db5a7e22611d3aa9cd94ca5acc4f6e

using git clone <gist> nix-shell-go-linking or so, so it’s friendlier than e8db...4f6e.

FWIW, I could also run this on a NixOS system.

Indeed, using ldd shows this is linked against shared libraries:

$ ldd ./foo
        linux-vdso.so.1 (0x00007ffc4ec93000)
        libresolv.so.2 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/libresolv.so.2 (0x00007f1a7f9f5000)
        libpthread.so.0 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/libpthread.so.0 (0x00007f1a7f9f0000)
        libc.so.6 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/libc.so.6 (0x00007f1a7f803000)
        /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/ld-linux-x86-64.so.2 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib64/ld-linux-x86-64.so.2 (0x00007f1a7fa08000)

(whereas, say, a “hello world” Go program doesn’t).

It’s nicer to be in a situation where you’ve got something that works & something that doesn’t work.

I’d check the output of ldd ./foo, which go, and perhaps stuff like the values of LD_LIBRARY_PATH; and compare/contrast between the system which works & the system which doesn’t.

using git clone <gist> nix-shell-go-linking or so, so it’s friendlier than e8db...4f6e.

Definitely thanks for the tip! I’ll think about it next time I create a new post on the forum.

Here’s some more information from his Amazon Workspace (Ubuntu based):

❯ uname -m
x86_64
❯ type go
go is /nix/store/3h6h9258rjb1idx44aql8m7r43f5pvzl-go-1.22.2/bin/go
❯ cd e8db5a7e22611d3aa9cd94ca5acc4f6e
❯ nix develop
❯ go get
❯ go build
❯ ./foo
bash: ./foo: cannot execute: required file not found
❯ ls -la
total 7300
drwxr-xr-x 4 1686309077 1686200513    4096 May  8 09:28 .
drwxr-xr-x 7 1686309077 1686200513    4096 May  8 09:28 ..
drwxr-xr-x 2 1686309077 1686200513    4096 May  8 09:28 .direnv
-rw-r--r-- 1 1686309077 1686200513       9 May  8 09:28 .envrc
-rw-r--r-- 1 1686309077 1686200513     567 May  8 09:28 flake.lock
-rw-r--r-- 1 1686309077 1686200513     334 May  8 09:28 flake.nix
-rwxr-xr-x 1 1686309077 1686200513 7432119 May  8 11:50 foo
drwxr-xr-x 8 1686309077 1686200513    4096 May  8 11:50 .git
-rw-r--r-- 1 1686309077 1686200513      63 May  8 09:28 go.mod
-rw-r--r-- 1 1686309077 1686200513     169 May  8 09:28 go.sum
-rw-r--r-- 1 1686309077 1686200513     265 May  8 09:28 main.go
❯ ldd ./foo
	linux-vdso.so.1 (0x00007ffff7fc6000)
	libresolv.so.2 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/libresolv.so.2 (0x00007ffff7faf000)
	libpthread.so.0 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/libpthread.so.0 (0x00007ffff7faa000)
	libc.so.6 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib/libc.so.6 (0x00007ffff7dbd000)
	/nix/store/35pq4hr29c3sl79lgfwgsvd9nwzyp4am-glibc-2.39-5/lib/ld-linux-x86-64.so.2 => /nix/store/apab5i73dqa09wx0q27b6fbhd1r18ihl-glibc-2.39-31/lib64/ld-linux-x86-64.so.2 (0x00007ffff7fc8000)

I’m really starting to get curious about his setup. What is this issue: bash: ./foo: cannot execute: required file not found ???

On the other side, on my own Amazon Workspace (RedHat/Fedora based), things are working fine:

Do you have a clue here?

To my understanding, cannot execute: required file not found suggests the interpreter just isn’t there / can’t be executed. I’d double-check /nix/store/35pq4hr29c3sl79lgfwgsvd9nwzyp4am-glibc-2.39-5/lib/ld-linux-x86-64.so.2 is there & that the user has permissions to execute it.

(Though I don’t know how go build could build a binary if those files aren’t there).

e.g. if you have some file bar.sh

#!/bin/nonexist
boop

and chmod +x ./bar.sh this file & try to run it, bash gives the same cannot execute: required file not found error. Whereas fish shell provides The file specified the interpreter '/bin/nonexist', which is not an executable command.

Similarly, using patchelf, running patchelf --set-interpreter /does/not/exist ./foo also gives similar errors: bash says bash: ./foo: cannot execute: required file not found, whereas fish shell says exec: Failed to execute process './foo': The file exists and is executable. Check the interpreter or linker?.

Perhaps using fish shell would provide a more helpful error.

I otherwise tried to reproduce the error with go build and go get on Ubuntu VMs in AWS, and was unable to reproduce it.

1 Like

The Amazon Workspace has been recreated, and the error was gone. Issue solved then.