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

Musl dynamic linked binary use glibc dynamic linker (not musl) #25178

Closed
bjornfor opened this issue Apr 24, 2017 · 19 comments
Closed

Musl dynamic linked binary use glibc dynamic linker (not musl) #25178

bjornfor opened this issue Apr 24, 2017 · 19 comments
Labels
0.kind: bug Something is broken 6.topic: musl Running or building packages with musl libc

Comments

@bjornfor
Copy link
Contributor

Issue description

musl has it's own dynamic linker, but it seems that builds with Nix/nixpkgs ends up using the glibc linker. Building with musl-gcc on Ubuntu (no Nix) uses the correct (musl) linker.

Steps to reproduce

{ pkgs ? import <nixpkgs> {} }:

with pkgs;

stdenv.mkDerivation rec {
  name = "musl-test";
  buildInputs = [ musl ];
  buildCommand = ''
    mkdir -p "$out/bin"

    cat >> main.c << EOF
    #include <stdio.h>
    int main(int argc, char *argv[])
    {
        printf("Hello Musl world\n");
    }
    EOF

    musl-gcc main.c -o "$out/bin/musl-dynamic-test"
    musl-gcc -static main.c -o "$out/bin/musl-static-test"
  '';
}
$ file $(nix-build test.nix)/bin/musl-dynamic-test
/nix/store/7kvcmxzv8vcwdjpba7cs9hwncdjdgly2-musl-test/bin/musl-dynamic-test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /nix/store/7crrmih8c52r8fbnqb933dxrsp44md93-glibc-2.25/lib/ld-linux-x86-64.so.2, not stripped

In the above output, I expect to see "musl", not "glibc".

Technical details

  • System: NixOS 17.03
@bjornfor bjornfor added the 0.kind: bug Something is broken label Apr 24, 2017
@bjornfor
Copy link
Contributor Author

musl has a .spec file that lists the correct dynamic linker ("/nix/store/...-musl-1.1.15/lib/ld-musl-x86_64.so.1"). Adding NIX_DEBUG=1 to the build shows that someone passes / overrides the -dynamic-linker option to use glibc.

I guess a whole separate stdenv for musl needs to be created to fix this?

@copumpkin
Copy link
Member

I guess a whole separate stdenv for musl needs to be created to fix this?

Yes, I'd expect so. I'd love to see that happen, though. I think someone was talking about that recently on IRC. Was it @cleverca22?

@copumpkin
Copy link
Member

copumpkin commented Apr 24, 2017

Also, normally I'd be a bit concerned about "forking" behavior for such a musl stdenv, but we already support two "libc"s right now: glibc and Darwin's libSystem/libc aggregate, so adding a third doesn't seem ridiculous to me, as long as we can get some hydra jobs in and people commit to keeping it healthy.

@ghost
Copy link

ghost commented Dec 3, 2017

I also have this issue in a much more hacky environment I'm working in.

( I wanted to compile something with musl so that I could get the size into the couple tens of kilobytes range, but it would bloat up to glibc sizes and I managed to confirm that nix was passing a lot of gcc stuff in. Good to know about NIX_DEBUG=1 even if later. )

@ghost
Copy link

ghost commented Dec 3, 2017

A temporary hack that seems to work is to pass the path to your GCC via the REALGCC env var, this is used by musl's musl-gcc wrapper. So you can do something like
REALGCC=$(grep -Eo '".+/gcc"' $(which gcc) | tr -d '"') musl-gcc something.c

I don't know what this will break.

EDIT: (In case it wasn't clear, I meant pass the path to the raw GCC executable and not the wrapper)

@vcunat
Copy link
Member

vcunat commented Dec 3, 2017

First, stdenv (in particular the wrapped compiler inside) has knowledge of the loader it wants to use, so you can't just use our stock glibc stdenv. Second, our wrapper would also need fixes.

With vcunat@bcc0f8045 I can do this

{ pkgs ? import ./. {} }:

with pkgs;
let
  stdenv-musl = overrideCC stdenv gcc-musl;
  gcc-musl = wrapCCWith {
    name = "gcc+musl";
    cc = stdenv.cc.cc;
    libc = musl;
    detectLD = true;
  };
in

stdenv-musl.mkDerivation rec {
  name = "musl-test";
  buildCommand = ''
    mkdir -p "$out/bin"

    cat >> main.c << EOF
    #include <stdio.h>
    int main(int argc, char *argv[])
    {
        printf("Hello Musl world\n");
    }
    EOF

    cc main.c -o "$out/bin/musl-dynamic-test"
    cc -static main.c -o "$out/bin/musl-static-test"
  '';
}

(Beware that wrapCCWith has changed significantly not too long ago.)

/cc @Ericson2314 about ideas on the best way to replace my hack-commit.

@Ericson2314
Copy link
Member

Err aren't we always detecting the loader today, and in your example? I don't see why libc = musl with wrapCCWith already doesn't work.

@vcunat
Copy link
Member

vcunat commented Dec 6, 2017

@Ericson2314: it doesn't work because the wrapper is hardcoded to ld-linux-x86-64.so.2 on x86_64-linux whereas musl uses a different name.

@Ericson2314
Copy link
Member

Oh right. I misread that. I'd just add a targetPlatform.libc == "musl" to the chain.

@vcunat
Copy link
Member

vcunat commented Dec 16, 2017

Ah, so you propose to handle libc change like cross-compilation. I expect that will be more practical when having more dependencies...

@dtzWill
Copy link
Member

dtzWill commented Feb 22, 2018

There's support for musl cross and native now :D, so I think this can be closed?

Also: NixOS/rfcs#23

@Ericson2314
Copy link
Member

Agreed. If the RFC passes, this is fixed for good, if the RFC fails (heaven forbid) this is won't-fix, so either way this is done.

@nh2
Copy link
Contributor

nh2 commented Sep 19, 2018

For readers passing by: You can now easily try musl-linked C programs with nix by using the musl-gcc wrapper that's available in nixpkgs master > 18.03 (e.g. using the commit pinned below):

$ NIX_PATH=nixpkgs=https://github.com/NixOS/nixpkgs/archive/88ae8f7d.tar.gz nix-shell -p musl
[nix-shell:]$ musl-gcc myprogram.c -o myprogram

EDIT: This is wrong, see #25178 (comment) below for how to do it right.

@bjornfor
Copy link
Contributor Author

I'm on NixOS 18.03, but using your pinned nixpkgs example (master > 18.03) should still work, right? However, it does not work for me:

$ NIX_PATH=nixpkgs=https://github.com/NixOS/nixpkgs/archive/88ae8f7d.tar.gz nix-shell -p musl
$ musl-gcc /tmp/hello.c
$ file a.out
a.out: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib/ld-linux-x86-64.so.2, with debug_info, not stripped

IOW, I'm still getting glibc into my musl. :-)

@nh2
Copy link
Contributor

nh2 commented Sep 20, 2018

@bjornfor You are right, a colleague pointed out today too that indeed glibc sneaks in. I now know why:

Looks like even musl-gcc calls plain gcc which includes the nix's stdenv flags which target glibc stuff. Found with strace -fe execve musl-gcc ....

(-static makes those flags to be ignored, so building statically is one workaround.)

The following works, also without -static:

/nix/store/vv4r320p5yd1k01kld62q1lppjxcswhb-gcc-7.3.0/bin/gcc -specs /nix/store/j88hc0jy0h1g350a7n0g2m29yfvvsrrn-musl-1.1.20-dev/lib/musl-gcc.specs -g myprogram.c -lpthread -o myprogram

@bjornfor, try that!

This what musl-gcc eventually calls but with some flags removed that pass libc stuff. The full flags were:

/nix/store/vv4r320p5yd1k01kld62q1lppjxcswhb-gcc-7.3.0/bin/gcc -O2 -D_FORTIFY_SOURCE=2 -fstack-protector-strong --param ssp-buffer-size=4 -fno-strict-overflow -Wformat -Wformat-security -Werror=format-security -fPIC -Wl,-dynamic-linker -Wl,/nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib/ld-linux-x86-64.so.2 -g myprogram.c -lpthread -o myprogram -specs /nix/store/j88hc0jy0h1g350a7n0g2m29yfvvsrrn-musl-1.1.20-dev/lib/musl-gcc.specs -B/nix/store/zk5zj2307zxaq7dx585yia3dn5k4qlsl-gcc-7.3.0-lib/lib -B/nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib/ -idirafter /nix/store/akak0rxhbi4n87z3nx78ipv76frvj841-glibc-2.27-dev/include -idirafter /nix/store/vv4r320p5yd1k01kld62q1lppjxcswhb-gcc-7.3.0/lib/gcc/x86_64-unknown-linux-gnu/7.3.0/include-fixed -B/nix/store/iw94llkj05wgaz268mlzvgx8jkbi1ss0-gcc-wrapper-7.3.0/bin/ -isystem /nix/store/j88hc0jy0h1g350a7n0g2m29yfvvsrrn-musl-1.1.20-dev/include -isystem /nix/store/j88hc0jy0h1g350a7n0g2m29yfvvsrrn-musl-1.1.20-dev/include -Wl,-rpath -Wl,/nix/store/1kf4h2shw37n8n0k0zlmypfv3zmrbd03-shell/lib64 -Wl,-rpath -Wl,/nix/store/1kf4h2shw37n8n0k0zlmypfv3zmrbd03-shell/lib -L/nix/store/mbi77di3ndy5kq3j1a0420pwz5h11ywh-musl-1.1.20/lib -L/nix/store/mbi77di3ndy5kq3j1a0420pwz5h11ywh-musl-1.1.20/lib -L/nix/store/fg4yq8i8wd08xg3fy58l6q73cjy8hjr2-glibc-2.27/lib -L/nix/store/zk5zj2307zxaq7dx585yia3dn5k4qlsl-gcc-7.3.0-lib/lib

@dtzWill do you know what we can do so that these glibc related flags don't leak in via the gcc-wrapper?

@dtzWill
Copy link
Member

dtzWill commented Sep 20, 2018

Honestly I've never used the musl-gcc wrapper much-- it's really a quickfix at best and should never be used in the place of a proper musl-based toolchain. I actually disabled it originally, partially to avoid problems when people used it expecting it to act like what you get with setting crossSystem or localSystem appropriately. I've heard folks say it's useful in a pinch but I'm not sure it's worth debugging if you're running into problems.

The interface for generating a shell from a given 'stdenv' is not as simple as it should be,
but something like the following should get you a 'cc' that builds against musl:

$ nix run nixpkgs.pkgsMusl.stdenv.cc
$ cc test.c -o test-c
$ file /test-c
./test-c: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /nix/store/35mb6imifzx9316y7nknhh6apx1k7a24-musl-1.1.19/lib/ld-musl-x86_64.so.1, with debug_info, not stripped

(-static should work at least for C programs, forget if things provide static versions of the c++ libraries or not)

Same caveats apply as normal re:using Nix-generated compiler wrappers and library dependencies, but this should get you started! Hope this helps!

@nh2
Copy link
Contributor

nh2 commented Sep 20, 2018

I actually disabled it originally

Ah, that's why I couldn't find it in older commits. I thought its presence in newer commits meant that it's a new addition included for that purpose and that I should use it consequently.

@dtzWill
Copy link
Member

dtzWill commented Sep 20, 2018

Oh also keep in mind that for reasons that make no sense to me, 'nix-shell -p' actually unconditionally pulls in the default stdenv!

So, basically, I strongly recommend avoiding nix-shell -p when doing anything related to compilers or you'll run into all kinds of badness. This can be seen with something as simple as nix-shell -p gcc8 and checking cc --version.

(this is because it uses runCommand and "for legacy reasons" but ...well anyway, if you didn't know about this "fun fact" that might be what you're encountering as well)

@domenkozar
Copy link
Member

That's because -p {pkgs} is just templating stdenv.mkDerivation { buildInputs = [ pkgs ... ]; }

flokli added a commit that referenced this issue Nov 13, 2022
```
git log --oneline v251.7..v251.8
ae8b249af4 test: fstab-generator: adjust PATH for fsck
03514a9f64 man: add note that network-generator is not a generator
8c8a423821 condition: Check that subsystem is enabled in ConditionSecurity=tpm2
9243b88b55 test: wait for loop device to be removed
f5c2be99bc test: wait for the lodev to get properly initialized
8cfe979030 test: disable LSan in the ASan env wrapper
db00a62be8 test: introduce a simple environment file for test service
fd082f335e test: lower the # of mpath devices to 16
d17a45340b test: make TEST-64 a bit more ASan friendly
a51cc9e578 test: don't wrap binaries built with ASan
e176dca593 test: drop all LD_PRELOAD-related ASan workarounds
9fba4cdf61 test: set $ASAN_RT_PATH along with $LD_PRELOAD to the ASan runtime DSO
4fbf69fd1b semaphore: remove the Semaphore repositories recursively
6258394c1e test: wrap `ls` and `stat` to make it work w/ sanitizers in specific cases
db14b371df test: create an ASan wrapper for `getent` and `su`
1027d3d633 test: always wrap useradd/userdel when running w/ ASan
65ab7b0950 Revert "Support -D_FORTIFY_SOURCE=3 by using __builtin_dynamic_object_size."
f994276068 test: make TEST-63 more reliable on slower machines
68b4f10f82 test: use PBKDF2 with capped iterations instead of Argon2
1f32ec761c hashmap: use assert_se() to make clang happy
94a25aa6d5 coredump: drop an unused variable
5f09fa4d5e network: drop an unused variable
a29ddb989b machine: drop an unused variable
9a71cd3bf6 sd-journal: drop an unused variable
ae0537f18f ci: reenable validation of GH Actions files
6e92f64ca4 ci: temporarily disable validation of GH Action files
6cd1b11d02 cryptsetup: fix build with -Db_ndebug=true
0ab5e9fe98 test: wrap binaries using systemd DSOs when running w/ ASan
6d4ae5a7cd test: make the virt detection quiet
024ee3def9 test: check for other hypervisors as well
520be40734 test-mountpoint-util: support running on a mount namespace with another mount on /proc
2cd4aed358 test-mountpoint-util: use log_info()
c7b66dbe2a test-mountpoint-util: fix NULL arg to %s
4e49c726ad test: drop redundant log message
b57ef0c672 build(deps): bump meson from 0.63.2 to 0.63.3 in /.github/workflows
8c80564405 build(deps): bump ninja from 1.10.2.3 to 1.10.2.4 in /.github/workflows
70e90da84b build(deps): bump meson from 0.63.1 to 0.63.2 in /.github/workflows
489c00dee5 build(deps): bump meson from 0.63.0 to 0.63.1 in /.github/workflows
08e85ad43d build(deps): bump meson from 0.62.2 to 0.63.0 in /.github/workflows
b0619c9c55 build(deps): bump meson from 0.62.0 to 0.62.2 in /.github/workflows
d982169592 build(deps): bump systemd/mkosi
9d4af5fea1 mkosi: libbpf0 -> libbpf1
3abf9f08f1 mkosi: Switch to Fedora 37
18f9fbab08 mkosi: update to latest commit
5403b727a7 mkosi: Use SourceFileTransfer=mount
9744c04ffd mkosi: Drop kernel-modules-extra from Fedora config
ab2f7a9b9e mkosi: install fdisk for test-loop-block
17acdca99d mkosi: Set ExtraSearchPaths=build/ by default
420e782904 mkosi: update to latest commit
43ef15c752 mkosi: add back packages removed from OpenSUSE build
9a94aa1d88 mkosi: disable isc-dhcp-server again
d1785c462f mkosi: Ensure we build all features/components in mkosi
6712396da3 meson: Downgrade efi-ld warning
66309ee674 ci: Add mold to build tests
86c25ca937 ci: build with clang-15; drop clang-12
28457b030e mkosi: Drop workarounds
abecb21561 mkosi: Update to latest commit
d9eaf39930 mkosi: Update to latest commit
619b36b22c mkosi: Don't use InstallDirectory by default
cdf3fd312a mkosi: Use mkosi.output/ as output directory by default
b8a746e89b mkosi: Add package libfdisk to Ubuntu dependencies (#24211)
0e518f3639 ci: set a timeout for each mkosi stage
5e79cf977c mkosi: Update to latest
edef8edf0b mkosi: Update to latest commit
a0402d3ab6 mkosi: Update to latest commit
081168fa19 mkosi: Build against Fedora rawhide as well
a38a0504ec mkosi: Remove usage of deprecated option names/sections
47404f1802 mkosi: Changes to allow booting with sanitizers in mkosi
db1281e12e mkosi: Update Ubuntu config to 22.04
ca8dc691fe mkosi: Install xxd in images
f12a6945c6 ci: limit which env variables we pass through `sudo`
7e24ac6d77 mkosi: update to latest main
a46ba01e79 mkosi: Update to latest release
7ef1d71895 mkosi: Pull in fix that solves action mirror issue
d3d90ae66b mkosi: Update CI to mkosi 13
9bf797be2c ci: build systemd with clang with -Dmode=release --optimization=2
9e88b3a5e1 ci: bump gcc in the "build test" workflow
dcbc64db61 ci: prefer the distro llvm version if available
ccd81889d4 ci: bump GH Actions to Ubuntu Jammy where applicable
b8fbf21526 kernel-install/90-loaderentry: do not add multiple systemd.machine_id options
fe5e692bfc tests: minor simplification in test-execute
a94fe70bbe tests: make test-execute pass on openSUSE
4a65c1674b firstboot: fix segfault when --locale-messages= is passed without --locale=
c3b22515b9 test: introduce sanity coverage for auxiliary utils
c61e4377d7 udev: add safe guard for setting by-id symlink
2f4fdaaecc udev: drop redundant call of usb_id and assignment of ID_USB_INTERFACE_NUM
491924940f udev: first set properties based on usb subsystem
293c006789 test: further extend systemctl's sanity coverage
f48e6576a2 test: add a couple of sanity tests for systemctl
3d5e379808 test: rename TEST-26-SETENV to TEST-26-SYSTEMCTL
a34afc4197 namespace: Add hidepid/subset support check
2ac138a5b6 coverage: Mark _coverage__exit as noreturn
9952c228a9 parse_hwdb: allow negative value for EVDEV_ABS_ properties
7b6fa1d3e6 test: add a couple of sanity tests for journalctl
cf21555d6d sd-device-monitor: dynamically allocate receive buffer
ee42e84968 man: use the correct 'Markers' property name for marking units
45090f3418 core: fix memleak in GetUnitFileLinks method
7eefd2fbb7 network: forcibly reconfigure all interfaces after sleep
66fa6110ba resolved: fix typo in feature level table
2f8f1d9e4a network: skip to reassign master ifindex if already set
d94f197818 resolved: fix copypasta in resolved varlink API
b61fcaca1b udev: always create device symlinks for USB disks
6fc2f387af man: Add documentation for AssertCredential= (#25178)
c339e8d71b man: document reboot --poweroff exception
91b8491e97 network: allow 0 for table number
3f94f03389 network: Table= also accepts table name
bdd84e82e5 analyze: add --image= + --root= to --help text
23d66a03de meson: Fix build with --optimization=plain
98a45608c4 manager: allow transient units to have drop-ins
228cd82d2c manager: reformat boolean expression in unit_is_pristine()
````
@tomodachi94 tomodachi94 added the 6.topic: musl Running or building packages with musl libc label Oct 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
0.kind: bug Something is broken 6.topic: musl Running or building packages with musl libc
Projects
None yet
Development

No branches or pull requests

8 participants