Darwin: missing return value in `void *` function crashes with `Trace/BPT trap: 5`

Hello all,

TLDR: The below MRE in C++ compiles (albeit with a warning) and runs fine when built with host clang on aarch64-darwin, but crashes with Trace/BPT trap: 5 when built with nix. What is happening differently that lets it build and run fine with host clang but crash when built with nix?


I don’t know C-family languages, would love some help wrapping my head around this issue.

After many moons I think I’ve finally sorted out espanso: runtime crash on aarch64-darwin · Issue #247162 · NixOS/nixpkgs · GitHub, which boils down to a void * function that didn’t have an return value.

Part of what took me so long to sort this out is because clang emits a warning but seems to work fine when code violating this is compiled outside of nix (on darwin at least).

For example, an MRE:

// foo.h

#ifndef FOO_H
#define FOO_H

void *a_function(void);

#endif
// foo.cpp

#include "foo.h"

#include <iostream>

void *a_function() {
  std::cerr << "hello from a function" << std::endl;
}

int main() {
  a_function();
  return 0;
}

Seems to work fine in darwin:

$ clang++ foo.cpp -o foo
foo.cpp:7:1: warning: non-void function does not return a value [-Wreturn-type]
}
^
1 warning generated.
$ echo $?
0
$ ./foo
hello from a function
$ echo $?
0

But if I build in nix, it won’t run:

$ cat default.nix
{clangStdenv}:
clangStdenv.mkDerivation {
  name = "foo";
  version = "0.0.1";
  src = ./.;
  buildPhase = ''
    clang++ -o foo ./foo.cpp
  '';
  installPhase = ''
    mkdir -p $out/bin
    cp foo $out/bin/
  '';
}
$
$ nix-build --expr '(import <nixpkgs> {}).callPackage ./. {}'
this derivation will be built:
  /nix/store/m96r3b2fg7m8ajrj8lqv9gxgyrnwlhdg-foo.drv
building '/nix/store/m96r3b2fg7m8ajrj8lqv9gxgyrnwlhdg-foo.drv'...
Running phase: unpackPhase
unpacking source archive /nix/store/ix7hg847sf9ksihji6lb7lsaxvf5qksk-tmp.JsKJ1AKJ9z
source root is tmp.JsKJ1AKJ9z
Running phase: patchPhase
Running phase: updateAutotoolsGnuConfigScriptsPhase
Running phase: configurePhase
no configure script, doing nothing
Running phase: buildPhase
./foo.cpp:7:1: warning: non-void function does not return a value [-Wreturn-type]
}
^
1 warning generated.
Running phase: installPhase
Running phase: fixupPhase
checking for references to /private/tmp/nix-build-foo.drv-0/ in /nix/store/bs2xcdwsyxd1ljd59j0y6vf4w1770vpf-foo...
patching script interpreter paths in /nix/store/bs2xcdwsyxd1ljd59j0y6vf4w1770vpf-foo
stripping (with command strip and flags -S) in  /nix/store/bs2xcdwsyxd1ljd59j0y6vf4w1770vpf-foo/bin
/nix/store/bs2xcdwsyxd1ljd59j0y6vf4w1770vpf-foo
$
$ ./result/bin/foo
hello from a function
Trace/BPT trap: 5

For context, the same code on an (arch) Linux machine errors at runtime:

$ clang++ foo.cpp -o foo
foo.cpp:7:1: warning: non-void function does not return a value [-Wreturn-type]
    7 | }
      | ^
1 warning generated.
$ ./foo 
hello from a function
Illegal instruction (core dumped)

(NB: It runs fine under nix if I resolve the warning by adding return NULL; to the end of a_function.)