Segmentation fault when running a packaged prebuilt binary

I’m very new to the Nix ecosystem, and so I’m struggling to figure out how to create a derivation that packages a prebuilt binary from anacierdem/libdragon-docker’s GitHub releases using fetchzip for personal use. Whenever I try to run the binary produced by nix-build it fails, complaining about Segmentation fault (core dumped). The following is the expression in question:

{ stdenv, fetchzip }:
let
  version = "12.0.4";
in
stdenv.mkDerivation {
  pname = "libdragon-docker";
  inherit version;

  src = fetchzip {
    url = "https://github.com/anacierdem/libdragon-docker/releases/download/v${version}/libdragon-linux-x86_64.tar.gz";
    sha256 = "sha256-R02xvxncx6e2QawPKNlPm9yAPQYUtMRGomksEv7bEnI=";
  };

  installPhase = ''
    mkdir -p $out/bin
    cp libdragon $out/bin
    chmod +x $out/bin/libdragon
  '';
}

The expected result is an error produced by the binary itself: No action provided. This result can be achieved by running the binary manually after downloading it from the releases page. I’ve also noticed that the produced binary is a few MBs smaller than if I were to manually download the binary.

On an unrelated note, I’m only packaging the binary because I couldn’t figure out how to build it from source. The following fails to package during a call to the pack npm script, which is supposed to produce a binary for the build platform:

packaging source
{
  buildNpmPackage,
  fetchFromGitHub,
  nodejs_23,
}:

buildNpmPackage rec {
  pname = "libdragon-docker";
  version = "12.0.4";

  src = fetchFromGitHub {
    owner = "anacierdem";
    repo = "libdragon-docker";
    rev = "v${version}";
    sha256 = "0jcf32knsvbs9grfhig0jl7laawjy1r05970hq5xvlbsd2lrna5i";
  };

  npmDepsHash = "sha256-N/p5uexuGoNryx27+nNyDU4z3VkDT2x1FRis2OdA9Js=";

  nodejs = nodejs_23;

  buildPhase = ''
    runHook preBuild
    npm run pack ${version}
    runHook postBuild
  '';
}

Is this a fault in how I’ve written the expression, or is this an upstream issue?

1 Like

NixOS cannot execute binaries built for fhs distros, since its linker is not in the standard path - deliberately so, because linkers can be incompatible, and this can cause binaries to fail in deployment; nix kind of exists because this way of publishing software is inherently flawed.

This wiki page lists all your options: Packaging/Binaries - NixOS Wiki

That, or you package the package from source properly. Hard to say without digging into your build, but it’s most likely just not a trivial package. If you don’t share the exact errors I can’t help much :wink:

1 Like

Riiight, this explains other errors I’ve encountered. It’s just taking some time for me to wrap my head around NixOS works. I guess I have some more reading to do :sweat:

So I’ve run the following sequence of commands on a live boot of the NixOS Minimal ISO image in an attempt to rule out my install configuration as a cause:

git clone https://github.com/anacierdem/libdragon-docker
cd libdragon-docker
nix-shell -p nodejs_23
npm install
npm run pack

The output of this is the same as the error I was getting on my own system:

$ npm run pack
> libdragon@12.0.4 pack
> node pack.mjs

No version is provided, building cli with the existing version.
Wrote single executable preparation blob to build/sea-prep.blob
/home/nixos/libdragon-docker/node_modules/zx/build/core.cjs:256
          const output = new ProcessOutput({
                         ^

ProcessOutput [Error]: 
    at file:///home/nixos/libdragon-docker/pack.mjs:66:7
    exit code: 1
    at EventEmitter.end (/home/nixos/libdragon-docker/node_modules/zx/build/core.cjs:256:26)
    at EventEmitter.emit (node:events:507:28)
    at ChildProcess.<anonymous> (/home/nixos/libdragon-docker/node_modules/zx/build/vendor-core.cjs:453:16)
    at ChildProcess.emit (node:events:507:28)
    at maybeClose (node:internal/child_process:1101:16)
    at ChildProcess._handle.onexit (node:internal/child_process:305:5) {
  _code: [Getter],
  _signal: [Getter],
  _stdout: [Getter],
  _stderr: [Getter],
  _combined: [Getter],
  _duration: [Getter]
}

As a control, I also did the equivalent on a fresh install of Debian via WSL, and this successfully produced the binary. Could it be that the methods used by this package to produce a build just aren’t compatible with NixOS?

When run like this, absolutely. npm is fond of downloading random binaries from the internet and attempting to run them. This will not work on NixOS. Given it’s calling a third process that’s quite likely here.

During the package build nix prevents downloads, though, and to an extent patches stuff like this. What happens if you use buildNpmPackage instead of your shell?

1 Like

Building the expression from my first post with nix-build -E 'with import <nixpkgs> {}; callPackage ./package.nix {}' results in the same error. For reference, the expression:

{
  buildNpmPackage,
  fetchFromGitHub,
  nodejs_23,
}:

buildNpmPackage rec {
  pname = "libdragon-docker";
  version = "12.0.4";

  src = fetchFromGitHub {
    owner = "anacierdem";
    repo = "libdragon-docker";
    rev = "v${version}";
    sha256 = "0jcf32knsvbs9grfhig0jl7laawjy1r05970hq5xvlbsd2lrna5i";
  };

  npmDepsHash = "sha256-N/p5uexuGoNryx27+nNyDU4z3VkDT2x1FRis2OdA9Js=";

  nodejs = nodejs_23;

  buildPhase = ''
    runHook preBuild
    npm run pack ${version}
    runHook postBuild
  '';
}

this patch fixes your error

diff --git a/pack.mjs b/pack.mjs
index 9aed1c2..3dc3255 100644
--- a/pack.mjs
+++ b/pack.mjs
@@ -63,6 +63,8 @@ if (process.platform === 'win32') {
   await $`codesign --remove-signature ${executablePath}`.nothrow();
 }
 
+await $`chmod +w ${executablePath}`;
+
 await $([
   `npx postject ${executablePath} NODE_SEA_BLOB ${path.join(
     'build',

This is the final derivation I came up with, does print the help message. but… I haven’t tested it further.

{
  fetchFromGitHub,
  fetchNpmDeps,
  nodejs_22,
  npmHooks,
  stdenv,
}:

stdenv.mkDerivation (finalAttrs: {
  pname = "libdragon-docker";
  version = "12.0.4";

  src = fetchFromGitHub {
    owner = "anacierdem";
    repo = "libdragon-docker";
    tag = "v${finalAttrs.version}";
    hash = "sha256-sSibqWh60d0LhuCkAnLwkitFD5XgRejyS3ptbacYjkk=";
  };
  patches = [ ./allow-write.patch ];

  npmDeps = fetchNpmDeps {
    name = "${finalAttrs.pname}-npm-deps-${finalAttrs.version}";
    inherit (finalAttrs) src;
    hash = "sha256-N/p5uexuGoNryx27+nNyDU4z3VkDT2x1FRis2OdA9Js=";
  };

  nativeBuildInputs = [
    nodejs_22
    npmHooks.npmConfigHook
  ];

  dontFixup = true;

  buildPhase = ''
    runHook preBuild
    npm run pack ${finalAttrs.version}
    runHook postBuild
  '';

  installPhase = ''
    runHook preInstall
    mkdir -p $out/bin
    mv build/libdragon-linux $out/bin/libdragon
    runHook postInstall
  '';
})

I used this interactive script, Nix-build-phases: run nix build phases interactively and ran the steps manually one by one, commented out that last part in pack.mjs and ran it from the cli npx postject .. command which gave the error, can’t write to executable, so I added the chmod +w patch.

Then I found the final binary doesn’t need node_modules, nor is it a nodejs package so I moved to stdenv.mkDerivation instead of buildNpmPackage, and followed the nixpkgs unstable manual nodejs npm section.

Encountered one more seg fault with final binary, but it was working in the interactive build steps after buildPhase (script I linked above) and thus I assumed one of the later phases mainly fixupPhase was the culprit, so disabled it.

other minor things

  • using nodejs_22 because the upstream github actions workflows have node version 22
  • tag instead of rev in src fetchFromGithub (optional)
  • hash instead of sha256 (optional)
  • using finalAttrs pattern instead of rec (optional)
2 Likes

Wow, this solution works flawlessly. Thanks for taking the time to test and explain this all, I’ve learnt a lot from this. Marking this as the solution.