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

[WIP] [preview] Single static binary for Nix+deps (bash, coreutils, ...) via ALLVM #1973

Open
dtzWill opened this issue Mar 13, 2018 · 31 comments
Assignees
Labels
feature Feature request or proposal installer

Comments

@dtzWill
Copy link
Member

dtzWill commented Mar 13, 2018

I'm still working on writing up the details, but as a bit of a preview I present to you:

What

Single static binary that contains:

  • all nix executables
  • bash (4.4p19)
  • coreutils
  • gnutar
  • xz
  • bzip2
  • gzip
  • unzip
  • lsof

Organization

Here's the result of running 'tree' on this: https://gist.github.com/dtzWill/cc721df847f4464e4dc57a0ee29f0cb5

Basically every executable entry point is symlink'd to the same binary, which for this I figured might as well be called "nix" :).

Everything not usually included as part of "nix" is in libexec/nix which is also prepended to PATH when invoking utilities. Commit used can be looked up on my nix "fork" (everything generally useful has been submitted upstream, just saying that the commit identifier can be resolved here :)).

All of these tools are included to ensure a quality experience when using Nix, without any dependency on having the right versions of the right tools installed just to evaluate common Nix expressions (such as those in nixpkgs).

How

Two key components (first available now, second I'm working to make available):

  • "Software multiplexing", novel approach for obtaining most benefits of both static and shared libraries, as implemented in the allmux tool from the allvm-tools package.
    Paper will hopefully be published soon[1][2] :). (EDIT: published, see [1] for info and link!)
  • Nix-based build-all-the-things-as-LLVM-bitcode, heavily used for the ALLVM project. "Coming Soon (tm)".

".nix" file

This is incomplete but hopefully gives a bit of transparency and detail about how this was constructed:

https://gist.github.com/321ab77bb5bcde6af74c277c8ad4cb4c

Interested/determined individuals can also inspect the derivation chain with nix commands pointed at the ALLVM cache. GLHF.

Path Info

$ nix path-info --json --store https://cache.allvm.org /nix/store/826wlhflzcwjcb1cn53gn7rqzg6h7i0s-nix-mux-2.0dtz6036_0f4a51e/bin/nix|jq
[
  {
    "path": "/nix/store/826wlhflzcwjcb1cn53gn7rqzg6h7i0s-nix-mux-2.0dtz6036_0f4a51e",
    "narHash": "sha256:09xsri3nhcabf7wjkz0mjr0p9v0vd2325q1lqziz91ik7knl5rsj",
    "narSize": 24456160,
    "references": [
      "/nix/store/826wlhflzcwjcb1cn53gn7rqzg6h7i0s-nix-mux-2.0dtz6036_0f4a51e"
    ],
    "deriver": "/nix/store/l8c7m7w4v81wv5x3iwhgslw9isdxv18r-nix-mux-2.0dtz6036_0f4a51e.drv",
    "signatures": [
      "gravity.cs.illinois.edu-1:ViMgRoj5BGQ0OdqWIGoUEWJ7H8ZJlhuULivJzMS+ZBWuNGjG8tMhgUlQivfE/xQZvfPSpDzvUb7EkxxJ01l8Ag=="
    ],
    "url": "nar/0sp3isv52j9a2qhk9f7siimdm7d54rvjrx9020gfr8g5wipg735a.nar.xz",
    "downloadHash": "sha256:0sp3isv52j9a2qhk9f7siimdm7d54rvjrx9020gfr8g5wipg735a",
    "downloadSize": 5945000
  }
]

That's right! All of this-- Nix and many important utilities-- as a single statically linked binary (even works on WSL (Windows)!!!) compressed (nar.xz) to just under 6MB! Try comparing that to disk usage or memory usage of traditional Nix deployments!

(Improvements are not "Nix's fault"--Nix is great! 👍 All improvements are from an improved/alternative executable file format, semantics) that are not in any way specific to Nix. I just put this together because ❤️ Nix and it's a good demonstration of allmux IMO.

Caveats

(Known) Bugs and Limitations

  • As with all Nix installations, this is not entirely relocatable as it contains references to the expected absolute path (/nix/store/...) it is installed to.
  • (Investigation WIP) AFAICT Nix isn't quite standards-compliant regarding use of fork [3] in ways that make musl sad. I'm still investigating/debugging, will file issue/submit PR once I sort this out :). Anyway this means that very rarely nix-daemon crashes, basically due to heap corruption resulting from doing things post-fork that aren't "allowed".
  • 100% code used by Nix (and all listed utilities) is included and contained in the static binary, however this is not quite enough to freely ship a single-file Nix as it requires various non-code files to be present. This is generally not a big deal but it does mean it can't just be trivially packaged into a tarball/RPM.

[1] Paper available to all, published in OOPSLA'18, DOI link: https://doi.org/10.1145/3276524
[2] Paper itself is built using entirely ALLVM-driven "allexe"s (ALLVM executables), many of which are JIT-compiled because we can ^_^
[3] generally speaking threads + fork is a "bad idea", see this discussion from 2014. Fixing this is a bit difficult- - and if I understand correctly an upcoming POSIX standard may change language making this valid? Not sure.

@dtzWill
Copy link
Member Author

dtzWill commented Mar 13, 2018

cc #1418

@dtzWill
Copy link
Member Author

dtzWill commented Mar 13, 2018

Try it!

Either add the ALLVM cache's public key as described in the musl PR or alternatively instruct Nix to use a local store and ignore signatures, super WIP instructions can be found here, which I'm planning on cleaning up soon (next few days).

@shlevy
Copy link
Member

shlevy commented Mar 13, 2018

Can we get a "why" section here? 😀

@dtzWill
Copy link
Member Author

dtzWill commented Mar 13, 2018

Haha sure:
• Much smaller
• Faster to start, faster execution
• Runs everywhere
• /bin/sh is full bash (!)
• Memory footprint is <= shared library build
• Disk footprint is < shared library build, << static build
• Including deps is important for usability and consistency

And most importantly "for science!" 😁

@bjornfor
Copy link
Contributor

I really like the idea of a static drop-in nix binary. No more impure install script!

@matthewbauer
Copy link
Member

matthewbauer commented Mar 13, 2018

👍 To this!

This would be helpful for a "portable Nix". Right now I have been running Nix inside a Chroot on computers that I don't have root access to. Being able to download a "nix" binary and put it in ~/.local/bin, combined with the new -store option, would mean I would be able to bypass the Chroot.

@dtzWill How hard would this approach be to port to MacOS or other non-Linux OS? It seems like it should be portable but there could be some Linux-specific hack going on somewhere.

@edolstra
Copy link
Member

While having a single-binary Nix executable would be great, it seems unnecessarily complicated to include stuff like coreutils, bash, xz, ... in that executable. Most of those dependencies could be eliminated pretty easily. For example, IIRC, the only reason why we depend on the bash, xz, gunzip, and bzip executables is because corepkgs/unpack-channel.nix references them. But that expression could easily be removed with the unpacking functionality moved into nix-channel. lsof is not used on Linux BTW. Eliminating tar would be a bit trickier, but replacing it with a library implementation would be useful for <nix/fetchurl.nix> { unpack = true; } since it avoids sandbox complications calling an external program.

Random points:

  • The corepkgs files could easily be included in the Nix binary at build time.

  • The Nix daemon should probably handle connections in a thread rather than a forked process. This should be safe now, except that build.cc may not be entirely thread-safe yet. E.g. there are probably some issues with path locking remaining. We should really replace POSIX locks by BSD locks...

@dtzWill
Copy link
Member Author

dtzWill commented Mar 22, 2018

FWIW, thought I'd share latest version:

New store path:

/nix/store/pra6c39mdkxzp2yind0zv9j5lfl6fd3s-nix-mux-2.0dtz6105_40e3c23/bin/nix

GLHF, will respond/follow-up more properly ASAP :).

@shlevy shlevy added the backlog label Apr 1, 2018
@shlevy shlevy self-assigned this Apr 1, 2018
@matthewbauer
Copy link
Member

matthewbauer commented Apr 10, 2018

So, I've figured out you can get "single file" executables through "nix-bundle" (with the help of Arx). You can try this out right now, by running this bundle:

curl https://matthewbauer.us/nix | sh -s -- run -f channel:nixos-unstable hello -c hello

This is from the Musl branch of nix-bundle:

nix-community/nix-bundle#35

You still need unshare unfortunately- but it's fairly small with Musl! Current build is ~10MB.

@dtzWill
Copy link
Member Author

dtzWill commented Apr 10, 2018

FWIW, some commits to help Nix run "from anywhere" ("relocatable") instead of requiring baked-in paths:

  • dtzWill@286f2ae <-- look for Nix-related resources relative to executing binary (Linux-only use of /proc/self/exe at the moment).
  • dtzWill@5c5a942 <-- look for SSL certificate bundle locally as well, although might make sense to only provide this during installation "bootstrap" and fetch updated bundle-- which I think we do elsewhere.

@matthewbauer
Copy link
Member

Ok will look into those. Probably not easy to upstream to Nix?

Anyway here is where the sizes come from:

4.0K    m4hrdmrvslgv37zcq0sjb8s723g3ww6p-startup
32K     fjnbbc0ccfq0jgbwnsr3s23qwi4kivkk-libatomic_ops-7.6.2
36K     ig45ff8dcpa2mki5hkazgkzw7ax6qql4-nix-user-chroot-2c52b5f
76K     asmrprwx87ifq5gr4h5zvgywyzviz898-bzip2-1.0.6.0.1-bin
84K     6675i590yrbj8farqpkhnjdk817ralr3-bzip2-1.0.6.0.1
112K    k968x93fzf2rwd53h1jrr3y22x7v34g0-nix-2.0-man
124K    v6faky88p9fsz9yb7jrba7z8qfl6zmdr-zlib-1.2.11
164K    g5xzsx86ifs1rn70bh6c4a5c4i0pgmfl-xz-5.2.3-bin
168K    x04z6dajjg2wfxgjn28i3fhfy66ws3xv-gzip-1.9
172K    qynjzj7ripccdphbm2zmnw8f7xczfx03-attr-2.4.47
180K    wyc64anccjax02h5frhrzndfygj0ijmp-xz-5.2.3
192K    2sj8iahlxdhhb31rfhhxz976x1124w78-nghttp2-1.24.0-lib
196K    fz1fw0j374nbysq5na7lsww0x7k95csn-acl-2.2.52
212K    j07fjwfd7vba31d4wycdx24c9liid08k-libssh2-1.8.0
260K    y7792qz6s5i3q54v5akslgwlpsh3597y-boehm-gc-7.6.4
284K    44ip9kw5fj9ia0w32nd0jjbkc2b5j40g-libseccomp-2.3.3-lib
392K    l1l251f22a4sf6bc3k2n3jm4kgncd1ic-libsodium-1.0.16
604K    ksi1gm0xdqdd8fw50fhdy9bcnfj7qavs-curl-7.58.0
1.2M    a4r2f8bwzffyf4lwac8b9l3rfs86g365-bash-4.4-p19
1.2M    ds4zypjjcc87wi6q9ylk63phzn06w9va-sqlite-3.22.0
2.1M    2f4m12vb8av447y9p8v3k9a25qyw1ixw-coreutils-8.29
2.3M    vyn2bvlpg514ib0djfi931gjdh0f6c72-libkrb5-1.15.2
2.6M    vj7n9fqx7idzah9awhwvn4ms4923bag6-gcc-7.3.0-lib
3.0M    gzv17y2gylh37x9wx3pwwcirnj0myky3-gnutar-1.30
3.4M    c18if8jr3y7k3wbqr58z9ykgy3i4b8xl-openssl-1.0.2o
3.9M    fm14xzjac6ijsi8r497bb4cmhsmwdjw2-musl-1.1.19
6.9M    ar3p393w31vimbfm9bgmc1yhws1l090x-nix-2.0

Probably still room for improvement but 30MB is pretty good.

@dtzWill
Copy link
Member Author

dtzWill commented Apr 18, 2018

Probably not easy to upstream to Nix?

No, I don't think they're well-suited for upstream, at least not as-is :). Perhaps at a high-level they're reasonable, but stylistically they're a bit of a mismatch too. If folks think either of these make sense for upstreaming please LMK and I'd be happy to put together some PR's and do at least a little reworking.

Those sizes are pretty great! Not quite as small--and at a glance seems to miss 'unzip' which Nix's nix-prefetch-url requires to be available on PATH sometimes. But honestly much better than I would have expected for the unpacked size. Does nix-bundle support a more persistent unpacked copy, to eliminate the somewhat high startup delay?

If only the user namespaces were more widely supported out-of-the-box, hopefully distributions/kernel/etc get this sorted soon. Beyond use for awesomeness like nix-bundle, they're required for Nix's sandbox support--which is essential for Nix use on non-NixOS, the likely target environment for both of our efforts.


Just to be clear, this isn't just a statically-built Nix, it's a statically linked "multicall" binary (like busybox) with Nix and most required runtime utilities (only known exception is 'git', which is supported in ALLVM of course but seems an especially poor fit for this sort of inclusion).

@matthewbauer
Copy link
Member

Does nix-bundle support a more persistent unpacked copy, to eliminate the somewhat high startup delay?

Not currently but I'm looking into it in nix-community/nix-bundle#34

@dtzWill
Copy link
Member Author

dtzWill commented Oct 29, 2018

Few updates on this:


Will be presenting paper on Software Multiplexing at upcoming OOPSLA'18!

Paper PDF: https://wdtz.org/files/oopsla18-allmux-dietz.pdf

It even mentions NixOS/nixpkgs (in passing)! :)

Please contact me with any feedback/comments/questions!
You may also wish to join the mailing list or our IRC channel,
find more information here: https://github.com/allvm/allvm-tools


Recently Nix grew support for using a single binary for all
command-line entry points! 😁

This gets most, nearly all, of the benefits ignoring the inclusion of utilities and
shells needed to provide fetchers or sandbox functionality,
and as @edolstra mentions above these dependencies would best be
handled by using libraries instead of such bundling (multiplexing or
otherwise).

And I agree. Although of course I'll add that in my biased opinion
multiplexing has a lot to offer regardless... starting with
a conveniently automatic and effective solution in the meantime :).

Also there's value in the ability to easily determine how effective such
"manual multiplexing" may be, before sinking time in the effort.
Multiplexing can also be useful across versions of a single piece of
software or .. well, take a look at the paper for examples of how
it can be used for significant memory and disk footprint reductions
and discussion of suitable use cases. :)


Updated multiplexed Nix can be found in my nur-packages repo:

https://github.com/dtzWill/nur-packages

For example, you can install it with nix-env with a command such as:

$ nix-env -I dtz-nur=https://github.com/dtzWill/nur-packages/archive/master.tar.gz -f '<dtz-nur>' -iA pkgs.nix-mux

For reasons outlined earlier I wouldn't recommend using this for the
nix-daemon on important machines as crashes may occur in certain
situations; regardless I use this is my primary nix via my user
profile on all my machines and for various bootstrapping purposes.
(see the unpack script for how that's done, it can be installed
to a prefix outside the nix store which can be awfully useful)

I'd be happy to help as best I can but even so it's probably
best to treat this as a "use at your own risk" situation :).

I have a Dockerfile for unpacking this into a tiny container base image,
think there are a few minor issues that remain but I can publish
that if folks are interested in it anyway :).


The plan is still to make all of this public (and much more),
I've just been busy wrapping up my degree and other things :).

Bug me if you're interested as that helps motivate prioritizing these
efforts, but it will be happening "soon" regardless.


This issue can probably be closed now,
and thanks to @edolstra for letting me use this space
for sharing and discussing this as it is Nix related
but arguably not an issue :).
Since Nix itself is now a single
binary I think that resolves the "issue" aspect.

Thanks for the support and feedback,
working to have more goodness to share soon.

~Will

@edolstra
Copy link
Member

@dtzWill Congrats on the OOPSLA paper :-) Will have a read.

@mboes
Copy link

mboes commented Dec 19, 2018

@dtzWill this is awesome work IMO and very important work, for the reasons outlined in #1418. In fact fixing #1418 in some way is in the critical path for making Bazel play nice with Nix to the point where Bazel can transparently unpack a local Nix, use it to manage system dependencies for build targets, then proceed with the build. See tweag/rules_nixpkgs#54.

I noticed that there is no PR yet. Are you ready at this point to submit a PR upstream?

@tomberek
Copy link
Contributor

tomberek commented Feb 1, 2019

What is needed for this?

@hughjfchen
Copy link

@dtzWill , I've been trying to build a static binary for a haskell package(postgrest), but I'm stuck at the following error when booting-strap:
building '/nix/store/5i4lphsaikc4mjzaklcnwhv033l6cyjb-bootstrap-tools.tar.xz.drv'... warning: unable to download 'https://wdtz.org/files/wjzsj9cmdkc70f78yh072483x8656nci-stdenv-bootstrap-tools-aarch64-unknown-linux-musl/on-server/bootstrap-tools.tar.xz': SSL connect error (35); retrying in 257 ms warning: unable to download 'https://wdtz.org/files/wjzsj9cmdkc70f78yh072483x8656nci-stdenv-bootstrap-tools-aarch64-unknown-linux-musl/on-server/bootstrap-tools.tar.xz': SSL connect error (35); retrying in 649 ms warning: unable to download 'https://wdtz.org/files/wjzsj9cmdkc70f78yh072483x8656nci-stdenv-bootstrap-tools-aarch64-unknown-linux-musl/on-server/bootstrap-tools.tar.xz': SSL connect error (35); retrying in 1022 ms warning: unable to download 'https://wdtz.org/files/wjzsj9cmdkc70f78yh072483x8656nci-stdenv-bootstrap-tools-aarch64-unknown-linux-musl/on-server/bootstrap-tools.tar.xz': SSL connect error (35); retrying in 2096 ms error: unable to download 'https://wdtz.org/files/wjzsj9cmdkc70f78yh072483x8656nci-stdenv-bootstrap-tools-aarch64-unknown-linux-musl/on-server/bootstrap-tools.tar.xz': SSL connect error (35)

I've tried to specify the cipher with curl command line, but no luck:
`curl -v --ciphers "ECDHE-ECDSA-AES256-GCM-SHA384" https://wdtz.org/files/wjzsj9cmdkc70f78yh072483x8656nci-stdenv-bootstrap-tools-aarch64-unknown-linux-musl/on-server/bootstrap-tools.tar.xz

  • Trying 204.152.39.24...
  • TCP_NODELAY set
  • Connected to wdtz.org (204.152.39.24) port 443 (#0)
  • ALPN, offering h2
  • ALPN, offering http/1.1
  • Cipher selection: ECDHE-ECDSA-AES256-GCM-SHA384
  • successfully set certificate verify locations:
  • CAfile: /etc/ssl/certs/ca-certificates.crt
    CApath: /etc/ssl/certs
  • TLSv1.2 (OUT), TLS header, Certificate Status (22):
  • TLSv1.2 (OUT), TLS handshake, Client hello (1):
  • TLSv1.2 (IN), TLS header, Unknown (21):
  • TLSv1.2 (IN), TLS alert, Server hello (2):
  • error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure
  • Curl_http_done: called premature == 1
  • stopped the pause stream!
  • Closing connection 0
    curl: (35) error:14077410:SSL routines:SSL23_GET_SERVER_HELLO:sslv3 alert handshake failure`

Is this a problem of your site? Or a bug of curl?
The version of curl is:
curl 7.52.1 (aarch64-unknown-linux-gnu) libcurl/7.52.1 OpenSSL/1.0.2u zlib/1.2.8 libidn2/0.16 libpsl/0.17.0 (+libidn2/0.16) libssh2/1.7.0 nghttp2/1.18.1 librtmp/2.3 Protocols: dict file ftp ftps gopher http https imap imaps ldap ldaps pop3 pop3s rtmp rtsp scp sftp smb smbs smtp smtps telnet tftp Features: AsynchDNS IDN IPv6 Largefile GSS-API Kerberos SPNEGO NTLM NTLM_WB SSL libz TLS-SRP HTTP2 UnixSockets HTTPS-proxy PSL

@tilpner
Copy link
Member

tilpner commented Mar 18, 2020

@hughjfchen That works with curl 7.68.0 (20.03, OpenSSL 1.1.1d), and 7.65.3 (19.09, OpenSSL 1.1.1d), but not curl 7.64 (19.03, OpenSSL 1.0.2u). Try with a more recent curl and openssl, possibly from a newer nixpkgs.

@hughjfchen
Copy link

@tilpner , Thank you so much! I upgraded my nix to 2.3.3 which uses libcurl 7.65.3 and now it works.

@dtzWill
Copy link
Member Author

dtzWill commented Mar 19, 2020

Sorry for the trouble! And thanks for the investigation and solution, @tilpner!
The machine hosting wdtz.org is NixOS (hooray nixops), I'm not able to investigate this properly immediately (sorry!) but if there's something likely wrong server-side please let me know. I don't believe I intentionally forced a modern cipher set or something but certainly didn't mean to force use of latest nix only.

FWIW because these bits are critical for building a number of configurations I also made them available elsewhere so if I (or my server) disappeared folks might have a chance to proceed anyway.

Copies of bootstrap files I host (from anything that made it into nixpkgs tree) are additionally available here:

https://github.com/dtzWill/nixos-musl-bootstrap-bits .

Hopefully this helps anyone experiencing issues that can't update, or if there are problems in the future.

@hughjfchen
Copy link

hughjfchen commented Mar 20, 2020

@dtzWill , I'd upgraded nix to the newest version and now can download from wdtz.org, but I'm stuck at building the coreutils package. I got an error which says a test-case, test-init.sh, failed with a message: Segmentation fault. And this one, I totally have on idea how to fix it.
Oh,I must say that I'm building a static binary for a haskell program on the ARM platform. Does anyone has any experience to share?

@dtzWill
Copy link
Member Author

dtzWill commented Mar 22, 2020

FWIW, my paper on "Software Multiplexing" was published in OOPSLA'18!

DOI: https://doi.org/10.1145/3276524

I'll update the original post accordingly, forgot I mention it here :).

@dtzWill
Copy link
Member Author

dtzWill commented Mar 31, 2020

Update2: I investigated this a little bit, and AFAICT this should now be resolved!

Test case was using a copy of curl from 17:09, after the new config it seems to work.

Sorry for the trouble, let me know if anything still isn't working for you1

@hughjfchen
Copy link

@dtzWill , thanks for your update. I rerun the build and unfortunately, it still failed at the test-init.sh of the coreutils package with the same error: Segmentation fault. Do you think any other thing we can do with it? Collect some more data to diagnose? Skip the test?

@stale
Copy link

stale bot commented Feb 14, 2021

I marked this as stale due to inactivity. → More info

@stale stale bot added the stale label Feb 14, 2021
@tomberek
Copy link
Contributor

I am still interested.

Is there a way ahead to turn this into a pkgsAllvm or a bundleWithAllvm [ nix coreutils bash xz ] function?

@stale stale bot removed the stale label Feb 14, 2021
@stale
Copy link

stale bot commented Aug 13, 2021

I marked this as stale due to inactivity. → More info

@stale stale bot added the stale label Aug 13, 2021
@tomberek
Copy link
Contributor

still interested

@stale stale bot removed the stale label Aug 27, 2021
@stale
Copy link

stale bot commented Apr 16, 2022

I marked this as stale due to inactivity. → More info

@stale stale bot added the stale label Apr 16, 2022
@GregBowyer
Copy link

still interested

@stale stale bot removed the stale label Apr 26, 2022
@fricklerhandwerk fricklerhandwerk added feature Feature request or proposal installer labels Oct 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature Feature request or proposal installer
Projects
None yet
Development

No branches or pull requests