Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native support for Apple Silicon #105026

Merged
merged 71 commits into from
May 16, 2021
Merged

Conversation

thefloweringash
Copy link
Member

@thefloweringash thefloweringash commented Nov 26, 2020

Motivation for this change

Native support for Apple's new CPU architecture.

Included here is a cross compiled bootstrap tools, and native stdenv based on them. Since there are no open source components for Big Sur, this relies almost entirely on the Apple SDK for Big Sur. It also includes helper utilities written by myself, for the purpose of generating ad-hoc signatures (sigtool) and rewriting framework re-exports to fit into nixpkg's frameworks structure (rewrite-tbd).

Presently very few packages build. In a number of cases the upstream projects do not have support, and we'll likely to have wait or backport fixes. Failing packages I've noticed include python, neovim, and ctags.

I've added a review for some of the points I'd like help with.

For signing, it looks like upstream cctools-port has support based on ldid. I made my implementation as part of investigating the requirements on the dtk, and before I was aware of an existing one. I'm not really attached to either implementation. The best possible outcome would be that Apple releases an open source version with signing but I don't know if that's a reasonable expectation.

Pre merge tests: https://gist.github.com/thefloweringash/62d7b3f3e71cdc97c92fdcd320a492ad. These check the darwin bootstrap tools and resulting stdenv for x86_64-darwin and aarch64-darwin.

Related pull requests:

Some of these are included (in some form) here, but once these are merged this branch can be rebased on them and become more manageable.

Current Known Blockers
  • Trusted bootstrap tools
  • Evaluation errors
Things done
  • Tested using sandboxing (nix.useSandbox on NixOS, or option sandbox in nix.conf on non-NixOS linux)
  • Built on platform(s)
    • NixOS
    • macOS
    • other Linux distributions
  • Tested via one or more NixOS test(s) if existing and applicable for the change (look inside nixos/tests)
  • Tested compilation of all pkgs that depend on this change using nix-shell -p nixpkgs-review --run "nixpkgs-review wip"
  • Tested execution of all binary files (usually in ./result/bin/)
  • Determined the impact on package closure size (by running nix path-info -S before and after)
  • Ensured that relevant documentation is up to date
  • Fits CONTRIBUTING.md.

@ofborg ofborg bot added the 6.topic: stdenv Standard environment label Nov 26, 2020
pkgs/build-support/bintools-wrapper/default.nix Outdated Show resolved Hide resolved
${extraBefore+"${extraBefore[@]}"} \
${params+"${params[@]}"} \
${extraAfter+"${extraAfter[@]}"}

if [ -e "@out@/nix-support/post-link-hook" ]; then
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a hook to sign everything the linker produces, without putting explicit signing references in the general linker wrapper.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does this interact with stripping binaries? Don't signatures cover the whole binaries? (I am just driving by and reading through the comments)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any modification of the binary, including strip and install_name_tool will cause the signature to be invalid. However, upstream's versions of these tools recreate a valid signature. To be like upstream, I've also wrapped strip and install_name_tool to recreate valid signatures. One of the todo list: add a similar wrapper for remove-references-to, which is valid to call on a binary. This is probably the correct solution to #105026 (comment). The current version will have broken this again (sorry @bobrik!).

Initially I used a fixup phase hook, which generally worked, but ran into a few cases that needed extra work. meson, for example, calls install_name_tool internally on binaries that are then executed to generate caches in some package. I initially patched meson to call codesign again, but I think wrapping is more correct. I also had to be very carefully to the signing hook run last in the fixup phase, which seems to run before postFixup.

@@ -23,7 +23,7 @@ fixDarwinDylibNames() {
for fn in "$@"; do
if [ -L "$fn" ]; then continue; fi
echo "$fn: fixing dylib"
int_out=$(install_name_tool -id "$fn" "${flags[@]}" "$fn" 2>&1)
int_out=$(@targetPrefix@install_name_tool -id "$fn" "${flags[@]}" "$fn" 2>&1)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the prefixed tools from bintools are exposed in corresponding variables in the bintools setup hook. Would it be reasonable to do the same for install_name_tool?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, if there is a conventional env var for it, go right ahead!

@@ -18,7 +19,7 @@ let
mv clang-tools-extra-* $sourceRoot/tools/extra
'';

nativeBuildInputs = [ cmake python3 lld ]
nativeBuildInputs = [ cmake python3 buildPackages.llvmPackages_10.lld ]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I pulled the cross compilation changes for clang out as #100574. This PR is now the motivation for that one.

@@ -183,8 +202,13 @@ let

depsBuildBuild = [ buildPackages.stdenv.cc makeWrapper ];

patches = stdenv.lib.optional stdenv.hostPlatform.isDarwin [
./cross-darwin.patch
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be sent upstream. It's harder to make a proper quality patch than it is to make something that works. Is it reasonable to avoid perl here? I think it's only for openssl.

Comment on lines 12 to 21
includeDirs = [
"CommonCrypto" "_types" "architecture" "arpa" "atm" "bank" "bsd" "bsm"
"corecrypto" "corpses" "default_pager" "device" "dispatch" "hfs" "i386"
"iokit" "kern" "libkern" "mach" "mach-o" "mach_debug" "machine" "malloc"
"miscfs" "net" "netinet" "netinet6" "netkey" "nfs" "os" "osfmk" "pexpert"
"platform" "protocols" "pthread" "rpc" "rpcsvc" "secure" "security"
"servers" "sys" "uuid" "vfs" "voucher" "xlocale"
] ++ [
"arm" "xpc"
];
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't open source libSystem, but Apple SDK libSystem. In the upstream sdk all the headers are in a single directory, so I don't know which ones are part of libSystem and which are unrelated. I've approximated it by looking at the output of the current libSystem.

@@ -48,7 +48,9 @@ in lib.init bootStages ++ [
# Prior overrides are surely not valid as packages built with this run on
# a different platform, and so are disabled.
overrides = _: _: {};
extraBuildInputs = [ ]; # Old ones run on wrong platform
extraBuildInputs = [ ] # Old ones run on wrong platform
++ lib.optionals hostPlatform.isDarwin [ buildPackages.targetPackages.darwin.apple_sdk.frameworks.CoreFoundation ]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is something I have found no good solution for, I'm hoping I missed something obvious. This step defines the stdenv that produces targetPackages, so including a dependency package from targetPackages is trivially infinite recursion. In the Darwin stdenv it's easy to build things on a different stage and use them in a later stage, since all platforms are the same. However when cross compiling the adjacent stages are quite rigid. The only way this isn't infinite recursion is by overriding extraBuildInputs = [ ] for stdenvNoCC, and using that to build CoreFoundation.

cp -d ${llvmPackages.llvm.lib}/lib/libLLVM.dylib $out/lib
cp -d ${libffi}/lib/libffi*.dylib $out/lib

mkdir $out/include
cp -rd ${llvmPackages.libcxx}/include/c++ $out/include

# copy .tbd assembly utils
cp -d ${pkgs.darwin.rewrite-tbd}/bin/rewrite-tbd $out/bin
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This increase in bootstrap tools is unfortunate, but supports extracting the sdk almost immediately.

@@ -19,6 +19,7 @@ let lib = import ../../../lib; in lib.makeOverridable (
, setupScript ? ./setup.sh

, extraNativeBuildInputs ? []
, extraNativeBuildInputsPostStrip ? [] # TODO: this is terrible
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Exactly what the comment says. Signing has a dependency on codesign_allocate, which is prefixed with the targetPrefix, so it needs some way to know the prefix. The other files in this list use the variables introduced in the bintools setup hook, for example strip.sh uses $STRIP.

@@ -114,6 +114,22 @@ let
'' + mkExtraBuildCommands cc;
};

cctoolsClangNoLibcxx = wrapCCWith rec {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a lot of duplication here with the lldClang* versions. This is complicated enough without having to maintain it over ~5 different clang versions. Is there a better way?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, it's not easy to get around. We could add some helper function since lld, cctools, and binutils will all need the same 3 stage process I think (if binutils works at all).

@dhess
Copy link
Contributor

dhess commented Nov 26, 2020

Does nixpkgs support anything like Debian's multiarch? I'm wondering if it would be feasible to default to aarch64-darwin but fall back to x86_64-darwin for packages that aren't supported on Apple Silicon yet.

@vikanezrimaya
Copy link
Member

@dhess you could mix and match two Nixpkgs evaluations for x86_64 and aarch64 in one environment, though it needs to be done manually. Something like nixpkgs.buildEnv { buildInputs = [ nixpkgs-x86_64.python nixpkgs.hello ]; }?

@dhess
Copy link
Contributor

dhess commented Nov 27, 2020

@kisik21 Right, that makes sense. Thanks!

stdenv_ =
if stdenv.hostPlatform.isDarwin && (stdenv.hostPlatform != stdenv.buildPlatform)
then overrideCC stdenv buildLlvmTools.cctoolsClangNoCompilerRt
else if stdenv.hostPlatform.useLLVM or false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the useLLVM flag should probably get precedence

stdenv = overrideCC stdenv buildLlvmTools.lldClangNoCompilerRt;
compiler-rt = let
stdenv_ =
if stdenv.hostPlatform.isDarwin && (stdenv.hostPlatform != stdenv.buildPlatform)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should test that Darwin -> Linux cross compilation still works (if it used to at all). I think hostPlatform.isDarwin is okay here, but we need to make sure cctools is only used when we compile to mach-o systems (either darwin or ios).

@@ -114,6 +114,22 @@ let
'' + mkExtraBuildCommands cc;
};

cctoolsClangNoLibcxx = wrapCCWith rec {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, it's not easy to get around. We could add some helper function since lld, cctools, and binutils will all need the same 3 stage process I think (if binutils works at all).

# compiling. It's not very sound. The cross stdenv has:
# extraBuildInputs = [ targetPackages.darwin.apple_sdks.frameworks.CoreFoundation ]
# and uses stdenvNoCC. In order to make this not infinitely recursive, we need to exclude
# this extraBuildInput.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks similar to https://github.com/NixOS/nixpkgs/blob/master/pkgs/top-level/static.nix#L24. We may be able to remove that after this PR (should check that pkgsStatic. still works first)

pname = "libtapi";
version = "1000.10.8"; # determined by looking at VERSION.txt

version = "1100.0.11";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note to myself to verify iOS cross-compilation still works after this

@matthewbauer
Copy link
Member

Quite a few things depend on Rust which doesn't look like it provides bootstrap tools for aarch64-apple-darwin yes?

@matthewbauer
Copy link
Member

Does nixpkgs support anything like Debian's multiarch? I'm wondering if it would be feasible to default to aarch64-darwin but fall back to x86_64-darwin for packages that aren't supported on Apple Silicon yet.

See NixOS/nix#4310 for some support in Nix. So you can use system = x86_64-darwin or system = aarch64-darwin to nixpkgs

@kloenk
Copy link
Member

kloenk commented Dec 8, 2020

Quite a few things depend on Rust which doesn't look like it provides bootstrap tools for aarch64-apple-darwin yes?

IIRC rust is only yet supported on the nightly build. Stable support should come with the next release.
But not sure about any bootstrap tools.

@thefloweringash
Copy link
Member Author

Quite a few things depend on Rust which doesn't look like it provides bootstrap tools for aarch64-apple-darwin yes?

I think that Go and Python are in a similar situation, either lacking bootstrapping tools or fundamental libraries need updating. There are a lot of things that don't build in this branch. Once we have a stdenv in place there's going to be a lot of work to make things build, but hopefully a most of that work will be done by the various upstreams in the near future.

@matthewbauer
Copy link
Member

@thefloweringash Could you take a look at thefloweringash#2 ? This came up when trying to build aarch64 Nix. Unfortunately it's a mass rebuild.

@thefloweringash
Copy link
Member Author

@thefloweringash Could you take a look at thefloweringash#2 ? This came up when trying to build aarch64 Nix. Unfortunately it's a mass rebuild.

Seems like a good change to make, applied. I'm curious what came up, since I have used this to build a native nix.

@kloenk
Copy link
Member

kloenk commented Dec 8, 2020

If I install nix on my system, than only a native one. Is there any instruction on how to install the native nix? If yes, I would be willing to give fixing stuff a try.

@ofborg ofborg bot added the 2.status: merge conflict This PR has merge conflicts with the target branch label Dec 8, 2020
@matthewbauer
Copy link
Member

I'm curious what came up, since I have used this to build a native nix.

This is nix unstable - although I would think the same would appear in nix 2.3.x. Here is the log:

$ nix build --no-write-lock-file github:matthewbauer/nix\?ref=aarch64-darwin --system aarch64-darwin --override-input nixpkgs github:thefloweringash/nixpkgs\?rev=df5d6050aaab7c6e677c4fe68b7732f0b2b5cf13
...
src/libstore/build/derivation-goal.cc:1747:5: warning: unused label 'fallback' [-Wunused-label]
    fallback:
    ^~~~~~~~~
src/libstore/build/derivation-goal.cc:2976:25: error: 'visit<nix::overloaded<(lambda at src/libstore/build/derivation-goal.cc:2980:17), (lambda at src/libstore/build/derivation-goal.cc:2981:17)>, std::__1::variant<AlreadyRegistered, Perh>
            return std::visit(overloaded {
                        ^
/nix/store/ysa3yq9mgv5sfjniaiw7khbcbl6339r4-libc++-10.0.1/include/c++/v1/variant:1587:26: note: 'visit<nix::overloaded<(lambda at src/libstore/build/derivation-goal.cc:2980:17), (lambda at src/libstore/build/derivation-goal.cc:2981:17)>,>
constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
                         ^
src/libstore/build/derivation-goal.cc:3016:58: error: 'visit<nix::overloaded<(lambda at src/libstore/build/derivation-goal.cc:3017:13), (lambda at src/libstore/build/derivation-goal.cc:3021:13)>, std::__1::variant<AlreadyRegistered, Perh>
        std::optional<StorePathSet> referencesOpt = std::visit(overloaded {
                                                         ^
/nix/store/ysa3yq9mgv5sfjniaiw7khbcbl6339r4-libc++-10.0.1/include/c++/v1/variant:1587:26: note: 'visit<nix::overloaded<(lambda at src/libstore/build/derivation-goal.cc:3017:13), (lambda at src/libstore/build/derivation-goal.cc:3021:13)>,>
constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
                         ^
src/libstore/build/derivation-goal.cc:3193:24: warning: binding dereferenced null pointer to reference has undefined behavior [-Wnull-dereference]
                return *(ValidPathInfo*)0;
                       ^~~~~~~~~~~~~~~~~~
src/libstore/build/derivation-goal.cc:3141:38: error: 'visit<nix::overloaded<(lambda at src/libstore/build/derivation-goal.cc:3142:13), (lambda at src/libstore/build/derivation-goal.cc:3161:13), (lambda at src/libstore/build/derivation-g>
        ValidPathInfo newInfo = std::visit(overloaded {
                                     ^
/nix/store/ysa3yq9mgv5sfjniaiw7khbcbl6339r4-libc++-10.0.1/include/c++/v1/variant:1587:26: note: 'visit<nix::overloaded<(lambda at src/libstore/build/derivation-goal.cc:3142:13), (lambda at src/libstore/build/derivation-goal.cc:3161:13), >
constexpr decltype(auto) visit(_Visitor&& __visitor, _Vs&&... __vs) {
                         ^
2 warnings and 3 errors generated.

nix build --no-write-lock-file github:matthewbauer/nix\?ref=aarch64-darwin --system aarch64-darwin --override-input nixpkgs github:thefloweringash/nixpkgs\?rev=5738b751bbf1d454ab9b83e77c3dc55cdc5acd0f will still fail , but it gets a little bit farther.

@matthewbauer
Copy link
Member

@thefloweringash See NixOS/nix#4334 for changes I had to make for Nix to work.

@@ -0,0 +1,212 @@
diff --git a/Makefile b/Makefile
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we look into upstreaming this?

@matthewbauer
Copy link
Member

Anyone know of aarch64-apple-darwin bootstrap tools for GHC or Rustc? Can't find much although it looks like some people have a compiler working.

@kloenk
Copy link
Member

kloenk commented Dec 16, 2020

Note that no native stable build has been produced yet, so you will need to use the beta or nightly channel until Rust 1.49 is released.

Source: rust-lang/rust#73908 (comment)

So we have to decide if we want to package a nightly rust for now, or wait until the release of rust 1.49.

@prusnak
Copy link
Member

prusnak commented Dec 16, 2020

So we have to decide if we want to package a nightly rust for now, or wait until the release of rust 1.49.

It seems this is due on December 31st 2020, so ~2 weeks from now.

@bobrik
Copy link
Contributor

bobrik commented Dec 24, 2020

Go is similar: the only version that supports running on aarch64-darwin is go1.16beta1. Plus currently it's bootstrapped from go1.4 (or go1.8 for aarch64-linux). When building Go packages I get the error much earlier, with ctags:

In file included from main.c:62:
/nix/store/0h6ph92qdxpj0p9w22jd3bmkx9lp8mh6-libSystem-11.0.0/include/dirent.h:80:2: error: use of undeclared identifier 'unused'
        __unused long   __padding; /* (__dd_rewind space left for bincompat) */
        ^
/nix/store/0h6ph92qdxpj0p9w22jd3bmkx9lp8mh6-libSystem-11.0.0/include/sys/cdefs.h:161:40: note: expanded from macro '__unused'
#define __unused        __attribute__((__unused__))
                                       ^
./general.h:60:37: note: expanded from macro '__unused__'
# define __unused__  __attribute__((unused))
                                    ^
1 error generated.

One interesting thing I noticed while playing with Nix on M1 is that aarch64 binaries are larger:

  • libev:
$ file /nix/store/*-libev-4.33/lib/libev.4.dylib
/nix/store/2s50h5xdrsz1dh29ksbd8xgqh2v854aa-libev-4.33/lib/libev.4.dylib: Mach-O 64-bit dynamically linked shared library arm64
/nix/store/clzf2x1bdwqygyb7blwdw7yvvf91b6qa-libev-4.33/lib/libev.4.dylib: Mach-O 64-bit dynamically linked shared library arm64
/nix/store/dfn5325pqdis2g3raw059wi5zxnlfmw0-libev-4.33/lib/libev.4.dylib: Mach-O 64-bit dynamically linked shared library x86_64
/nix/store/zlqnnp63xhnyz253g4f23kj5va6g4qiw-libev-4.33/lib/libev.4.dylib: Mach-O 64-bit dynamically linked shared library arm64

$ ls -l /nix/store/*-libev-4.33/lib/libev.4.dylib
-r-xr-xr-x  1 ivan  admin  75904 Dec 31  1969 /nix/store/2s50h5xdrsz1dh29ksbd8xgqh2v854aa-libev-4.33/lib/libev.4.dylib
-r-xr-xr-x  1 ivan  admin  75904 Dec 31  1969 /nix/store/clzf2x1bdwqygyb7blwdw7yvvf91b6qa-libev-4.33/lib/libev.4.dylib
-r-xr-xr-x  1 ivan  admin  49408 Dec 31  1969 /nix/store/dfn5325pqdis2g3raw059wi5zxnlfmw0-libev-4.33/lib/libev.4.dylib
-r-xr-xr-x  1 ivan  admin  75904 Dec 31  1969 /nix/store/zlqnnp63xhnyz253g4f23kj5va6g4qiw-libev-4.33/lib/libev.4.dylib
  • jq:
$ file /nix/store/*-jq-1.6-bin/bin/jq
/nix/store/hcl24glm0i9rzva0f96r4yagmj2n2w1k-jq-1.6-bin/bin/jq: Mach-O 64-bit executable x86_64
/nix/store/qhbxcrsp370mff7rbmzvk2l2f01mpgnx-jq-1.6-bin/bin/jq: Mach-O 64-bit executable arm64

$ ls -l /nix/store/*-jq-1.6-bin/bin/jq
-r-xr-xr-x  1 ivan  admin  25148 Dec 31  1969 /nix/store/hcl24glm0i9rzva0f96r4yagmj2n2w1k-jq-1.6-bin/bin/jq
-r-xr-xr-x  1 ivan  admin  55600 Dec 31  1969 /nix/store/qhbxcrsp370mff7rbmzvk2l2f01mpgnx-jq-1.6-bin/bin/jq

@hexagonal-sun
Copy link
Contributor

One interesting thing I noticed while playing with Nix on M1 is that aarch64 binaries are larger:

I would say that this is to be expected. AArch64 has fixed-length 32-bit instructions. X86_64 has variable length, therefore expect the code density to be higher in x86_64 code.

@bobrik
Copy link
Contributor

bobrik commented Dec 24, 2020

The difference is not as dramatic for jq on Debian Buster, and there x86_64 is larger, not smaller:

$ dpkg-deb -x jq_1.6-2.1_amd64.deb jq-amd64
$ dpkg-deb -x jq_1.6-2.1_arm64.deb jq-arm64

$ file jq-*/usr/bin/jq
jq-amd64/usr/bin/jq: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=4b72c533655b521c35df428ceb747ac8cd199646, for GNU/Linux 3.2.0, stripped
jq-arm64/usr/bin/jq: ELF 64-bit LSB pie executable, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=e7bdf1cd578540b4b3e71470d647f0f7be0dc247, for GNU/Linux 3.7.0, stripped

$ ls -l jq-*/usr/bin/jq
-rwxr-xr-x 1 ivan ivan 30712 Dec 10 08:24 jq-amd64/usr/bin/jq
-rwxr-xr-x 1 ivan ivan 26552 Dec 10 08:24 jq-arm64/usr/bin/jq

@prusnak
Copy link
Member

prusnak commented Dec 24, 2020

The difference is not as dramatic for jq on Debian Buster, and there x86_64 is larger, not smaller:

Debian uses GCC, Darwin uses Clang. I was able to find an issue, where this is discussed: android/ndk#21 - It says that GCC defaults to -Bsymbolic when linking, affecting both the size and speed of the resulting binaries.

@bobrik
Copy link
Contributor

bobrik commented Dec 24, 2020

@prusnak in my initial comment I compare aarch64-darwin vs x86_64-darwin, aren't they both compiled with clang?

Could it be that clang version is at play here? I have 7.1.0 for x86_64 and 10.0.1 for aarch64 in my /nix/store:

$ file /nix/store/*-clang-*/bin/clang | fgrep Mach
/nix/store/xkwl96h8ls93ggcm3xm02qij7vm7bww5-clang-7.1.0/bin/clang:                           Mach-O 64-bit executable x86_64
/nix/store/zpvpxvmly0nw4yhj10hjqcj8mbppjwrq-clang-10.0.1/bin/clang:                          Mach-O 64-bit executable arm64

Here's my test:

$ cat hello.c
#include <stdio.h>

int main() {
  printf("howdy, fellas\n");
  return 0;
}

With clang 10.0.1 from aarch64:

$ ~/projects/nix/result/bin/nix-env --file . --install --attr clang_10
$ cc -o hello.nix_aarch64 hello.c

With clang 7.1.0 from x86_64:

$ arch -x86_64 nix-env --install --attr nixpkgs.clang_7
$ arch -x86_64 cc -o hello.nix_x86_64 hello.c

And comparing the resulting binaries:

$ file hello.nix_*
hello.nix_aarch64: Mach-O 64-bit executable arm64
hello.nix_x86_64:  Mach-O 64-bit executable x86_64
$ ls -l hello.nix_*
-rwxr-xr-x  1 ivan  wheel  49958 Dec 24 12:24 hello.nix_aarch64
-rwxr-xr-x  1 ivan  wheel  12556 Dec 24 12:26 hello.nix_x86_64

Compiling x86_64 with clang10 produces the same size as clang7, which means it's not the clang version by itself.

Looking at the output of size -m I can see that it's likely page size related (4k x86_64 vs 16k aarch64):

$ size -m hello.nix_aarch64
Segment __PAGEZERO: 4294967296
Segment __TEXT: 16384
	Section __text: 56
	Section __stubs: 12
	Section __stub_helper: 36
	Section __cstring: 15
	Section __unwind_info: 72
	total 191
Segment __DATA_CONST: 16384
	Section __got: 8
	total 8
Segment __DATA: 16384
	Section __la_symbol_ptr: 8
	Section __data: 8
	total 16
Segment __LINKEDIT: 16384
total 4295032832
$ size -m hello.nix_x86_64
Segment __PAGEZERO: 4294967296
Segment __TEXT: 4096
	Section __text: 42
	Section __stubs: 6
	Section __stub_helper: 26
	Section __cstring: 15
	Section __unwind_info: 72
	total 161
Segment __DATA_CONST: 4096
	Section __got: 8
	total 8
Segment __DATA: 4096
	Section __la_symbol_ptr: 8
	Section __data: 8
	total 16
Segment __LINKEDIT: 4096
total 4294983680

The difference goes away as binaries themself get larger. This makes sense, since on tiny binaries pages are sparsely populated, while in larger ones they are mostly full. Here's git for example:

$ ls -l /nix/store/*-git-2.29.2/bin/git
-r-xr-xr-x  1 ivan  admin  3195680 Dec 31  1969 /nix/store/8c5lmv9ydl1h24q3q3swdz8k3kjpa8ai-git-2.29.2/bin/git
-r-xr-xr-x  1 ivan  admin  3197216 Dec 31  1969 /nix/store/g19kz8hz4b5wxgpibvmcdf996qmkrcz3-git-2.29.2/bin/git
-r-xr-xr-x  1 ivan  admin  3318612 Dec 31  1969 /nix/store/zqbi6byni1x9dszwkfipyh9qq54m4sib-git-2.29.2/bin/git

$ file /nix/store/*-git-2.29.2/bin/git
/nix/store/8c5lmv9ydl1h24q3q3swdz8k3kjpa8ai-git-2.29.2/bin/git: Mach-O 64-bit executable arm64
/nix/store/g19kz8hz4b5wxgpibvmcdf996qmkrcz3-git-2.29.2/bin/git: Mach-O 64-bit executable arm64
/nix/store/zqbi6byni1x9dszwkfipyh9qq54m4sib-git-2.29.2/bin/git: Mach-O 64-bit executable x86_64

Compressing tiny binaries brings them closer together as you discard mostly empty page aligned space:

$ cat hello.x86_64 | zstd | wc -c
     789
$ cat hello.aarch64 | zstd | wc -c
    1075

@bobrik
Copy link
Contributor

bobrik commented Dec 25, 2020

I was getting this error for mcedit built natively on aarch64-darwin:

$ mcedit --version
Killed: 9

Debugging it with lldb didn't show the symptom:

$ lldb mcedit
(lldb) target create "mcedit"
Current executable set to 'mcedit' (arm64).
(lldb) process launch -- --version
warning: (arm64) /nix/store/w43lfkhi442vs6d9bwr3dmg28vfjc0iv-mc-4.8.25/bin/mc(0x0000000100000000) address 0x0000000100000000 maps to more than one section: mc.__TEXT and mc.__TEXT
warning: (arm64) /nix/store/w43lfkhi442vs6d9bwr3dmg28vfjc0iv-mc-4.8.25/bin/mc(0x0000000100000000) address 0x00000001000e0000 maps to more than one section: mc.__DATA and mc.__DATA
Process 19950 launched: '/Users/ivan/.nix-profile/bin/mcedit' (arm64)
warning: (arm64) /Users/ivan/.nix-profile/bin/mcedit address 0x0000000100000000 maps to more than one section: mcedit.__TEXT and mc.__TEXT
warning: (arm64) /Users/ivan/.nix-profile/bin/mcedit address 0x00000001000e0000 maps to more than one section: mcedit.__DATA and mc.__DATA
GNU Midnight Commander 4.8.25
Built with GLib 2.66.2
Built with S-Lang 2.3.2 with terminfo database
With builtin Editor
With subshell support as default
With support for background operations
With mouse support on xterm
With support for X11 events
With internationalization support
With multiple codepages support
Virtual File Systems:
 cpiofs, tarfs, sfs, extfs, ftpfs, sftpfs, fish, smbfs
Data types:
 char: 8; int: 32; long: 64; void *: 64; size_t: 64; off_t: 64;
Process 19950 exited with status = 0 (0x00000000)

Turns out the signature was corrupted:

$ codesign --verify --verbose $(which mcedit)
/Users/ivan/.nix-profile/bin/mcedit: invalid signature (code or signature have been modified)
In architecture: arm64

It doesn't happen with x86_64 binary that runs under rosetta, because in that case there's no signature to break:

$ codesign --verify --verbose /nix/store/7r77c3n2ni62wzavdwaaba8fb1l30f74-mc-4.8.25/bin/mcedit
/nix/store/7r77c3n2ni62wzavdwaaba8fb1l30f74-mc-4.8.25/bin/mcedit: code object is not signed at all
In architecture: x86_64

I was able to fix this by not running postFixup:

  postFixup = ''
    # remove unwanted build-dependency references
    sed -i -e "s!PKG_CONFIG_PATH=''${PKG_CONFIG_PATH}!PKG_CONFIG_PATH=$(echo "$PKG_CONFIG_PATH" | sed -e 's/./0/g')!" $out/bin/mc
  '';

The same can be achieved by moving this step from postFixup to preFixup, but it seems like there should be a safeguard for this error, especially since it's a non-trivial thing to debug.

@prusnak
Copy link
Member

prusnak commented May 17, 2021

How do I switch native builds from x86_64-darwin to aarch64-darwin?

I am trying the following but it seems it's building x86_64 binaries:

nix-shell --argstr system aarch64-darwin -I nixpkgs=/Users/xxx/nixos/nixpkgs -p hello

@vcunat
Copy link
Member

vcunat commented May 17, 2021

I'm not sure how it's for nix-shell... but for nix-build and nix build this parametrization works. Anyway, you probably want to switch the default by setting system in nix.conf.

@vcunat
Copy link
Member

vcunat commented May 17, 2021

And you probably want to choose nixpkgs commit that has enough binaries (even though experimental), e.g. 5be0234 https://hydra.nixos.org/eval/1666126

@jaen
Copy link
Contributor

jaen commented May 17, 2021

I kind of understood how to use it from this branch before the merge, but not sure what to do now? Do we have to wait for the PR updating this to use the bootstrapped tools, before we can use the channel? (sorry if that's something obvious)

@vcunat
Copy link
Member

vcunat commented May 18, 2021

Yes, the merged state isn't usable yet (for aarch64-darwin), so I expect you want to use e.g. commit 5be0234.

@jaen
Copy link
Contributor

jaen commented May 18, 2021

@vcunat right, I understood that part from your previous message, sorry if I was being unclear. What I've meant is how can I track when staging (or master) becomes usable? Is there any meta-issue? Will this issue be mentioned in the tools update PR?

@vcunat
Copy link
Member

vcunat commented May 18, 2021

Ah, OK. Someone will surely put a comment into this thread.

BTW, the job for new bootstrapping tools is here: https://hydra.nixos.org/build/143059349 (might even finish today)

@jonringer
Copy link
Contributor

@thefloweringash The darwin bootstrap job has finished https://hydra.nixos.org/build/143059349

@thefloweringash
Copy link
Member Author

@thefloweringash The darwin bootstrap job has finished https://hydra.nixos.org/build/143059349

Great! Could i get someone with the ability to write to tarballs.nixos.org to upload them there? Then we can reference them from the stdenv.

@jonringer
Copy link
Contributor

cc @grahamc

@grahamc
Copy link
Member

grahamc commented May 18, 2021

Uploaded to the tarball mirrors under stdenv-darwin/aarch64/20acd4c4f14040485f40e55c0a76c186aa8ca4f3/:

bootstrap-tools.cpio.bz2 https://tarballs.nixos.org/stdenv-darwin/aarch64/20acd4c4f14040485f40e55c0a76c186aa8ca4f3/bootstrap-tools.cpio.bz2 bed128fe32c7cdb540ab54b3b7e615ac5adb7322472e52733575aa31a61843ec  -
bzip2 https://tarballs.nixos.org/stdenv-darwin/aarch64/20acd4c4f14040485f40e55c0a76c186aa8ca4f3/bzip2 8ab10f0ea5403d87c7126a172dd561a940f67a0336f71352e671f3bb2313eab0  -
cpio https://tarballs.nixos.org/stdenv-darwin/aarch64/20acd4c4f14040485f40e55c0a76c186aa8ca4f3/cpio 9b4ba53c68142ba58478fdb92e85268909dd006466f019cbb2620c23554c5edd  -
mkdir https://tarballs.nixos.org/stdenv-darwin/aarch64/20acd4c4f14040485f40e55c0a76c186aa8ca4f3/mkdir 9314d372d5e0267208aa6b4cdad80f57ab0933c0a807dd406215dbcb2b216658  -
sh https://tarballs.nixos.org/stdenv-darwin/aarch64/20acd4c4f14040485f40e55c0a76c186aa8ca4f3/sh b4bb70d824de3c7c95d3d187479d092fda9fcec1a963a1a38ea6d0dcc978588c  -

These came from /nix/store/riw6lnfrz2qklbf06rbzx11h66sd8xws-stdenv-bootstrap-tools-aarch64-apple-darwin which came from https://hydra.nixos.org/build/143059349#tabs-buildinputs

@vcunat
Copy link
Member

vcunat commented May 20, 2021

@jaen: the staging-next branch is now getting somewhat usable, I think, though most jobs still don't have binaries (it takes time).

@domenkozar
Copy link
Member

It built ~16k packages succesfully while ~3k are failing.

@domenkozar
Copy link
Member

domenkozar commented May 31, 2021

21.05 and trunk now build packages for aarch64-darwin (about 16k build compared to 24k for x86_64)

  • support for building Nix is being worked on by @kloenk
  • I'll test & prepare documentation on how to get started once Nix is supported

@domenkozar
Copy link
Member

I'll post updates to #95903

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/nix-macos-monthly/12330/4

@dotkrnl
Copy link
Contributor

dotkrnl commented Jun 15, 2021

FYI, for anyone who is interested in migrating all packages to aarch64-darwin, there is an instruction at LnL7/nix-darwin#334 .

@nixos-discourse
Copy link

This pull request has been mentioned on NixOS Discourse. There might be relevant details there:

https://discourse.nixos.org/t/how-to-install-nix-on-macos-with-m1/13791/1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.