ziglang / zig

General-purpose programming language and toolchain for maintaining robust, optimal, and reusable software.
https://ziglang.org
MIT License
35.07k stars 2.56k forks source link

Segmentation fault by default due to a wrong dynamic linker on KissLinux #21963

Open Ogromny opened 1 week ago

Ogromny commented 1 week ago

Zig Version

0.13.0

Steps to Reproduce and Observed Behavior

On KissLinux:

main.zig


const c = @cImport({
@cInclude("stdio.h");
});

pub fn main() !void { _ = c.puts("hello"); }


>build.zig
```zig
const std = @import("std");

pub fn build(b: *std.Build) void {
    const exe = b.addExecutable(.{
        .name = "hello",
        .root_source_file = b.path("main.zig"),
        .target = b.host,
        .optimize = .Debug,
    });
    exe.linkLibC();
    b.installArtifact(exe);
}

image

gdb output image

After digging a little bit I realized that the linking stage was failing but still outputs the executable: image

By taking the exact build command and removing the listen=- argument and adding --dynamic-linker /usr/lib/ld-musl-x86_64.so.1 the executable get successfully linked and works as expected image

image

What can be the cause of this issue ?

Expected Behavior

The executable gets linked successfully

alexrp commented 1 week ago

Please try this with a nightly build from https://ziglang.org/download as there have been multiple changes to std.Target.DynamicLinker in master.

Ogromny commented 1 week ago

Please try this with a nightly build from https://ziglang.org/download as there have been multiple changes to std.Target.DynamicLinker in master.

Unfortunately I get the same issue with the latest nightly build image

BratishkaErik commented 5 days ago

I guess it's similar reason as in https://github.com/ziglang/zig/issues/21941#issuecomment-2481272623 :

Yes, because it checks /usr/bin/env or its interpreter (/usr/bin/coreutils, /bin/sh) to find out libc, and if you compile it statically, it would assume that whole system is static -> musl needed.

In kiss-chroot-24.06.14-2:

$ file usr/bin/env
usr/bin/env: symbolic link to busybox

$ file usr/bin/busybox
usr/bin/busybox: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

$ file usr/bin/x86_64-pc-linux-musl-gcc
usr/bin/x86_64-pc-linux-musl-gcc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, stripped

So if you start zig build-exe --show-builtin:

pub const abi = std.Target.Abi.musl;
// ...
pub const os = std.Target.Os{
    .tag = .linux,
    .version_range = .{ .linux = .{
        .range = .{
            .min = .{
                .major = 6,
                .minor = 11,
                .patch = 5,
            },
            .max = .{
                .major = 6,
                .minor = 11,
                .patch = 5,
            },
        },
        .glibc = .{
            .major = 2,
            .minor = 28,
            .patch = 0,
        },
    }},
};
pub const target: std.Target = .{
    .cpu = cpu,
    .os = os,
    .abi = abi,
    .ofmt = object_format,
    .dynamic_linker = std.Target.DynamicLinker.none,
};

But if I replace env with x86_64-pc-linux-musl-gcc, this part changes:

pub const target: std.Target = .{
    .cpu = cpu,
    .os = os,
    .abi = abi,
    .ofmt = object_format,
    .dynamic_linker = std.Target.DynamicLinker.init("/lib/ld-musl-x86_64.so.1"),
};

I assume result above is what you want?

Ogromny commented 5 days ago

I guess it's similar reason as in #21941 (comment) :

Yes, because it checks /usr/bin/env or its interpreter (/usr/bin/coreutils, /bin/sh) to find out libc, and if you compile it statically, it would assume that whole system is static -> musl needed.

In kiss-chroot-24.06.14-2:

$ file usr/bin/env
usr/bin/env: symbolic link to busybox

$ file usr/bin/busybox
usr/bin/busybox: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, stripped

$ file usr/bin/x86_64-pc-linux-musl-gcc
usr/bin/x86_64-pc-linux-musl-gcc: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-x86_64.so.1, stripped

So if you start zig build-exe --show-builtin:

pub const abi = std.Target.Abi.musl;
// ...
pub const os = std.Target.Os{
    .tag = .linux,
    .version_range = .{ .linux = .{
        .range = .{
            .min = .{
                .major = 6,
                .minor = 11,
                .patch = 5,
            },
            .max = .{
                .major = 6,
                .minor = 11,
                .patch = 5,
            },
        },
        .glibc = .{
            .major = 2,
            .minor = 28,
            .patch = 0,
        },
    }},
};
pub const target: std.Target = .{
    .cpu = cpu,
    .os = os,
    .abi = abi,
    .ofmt = object_format,
    .dynamic_linker = std.Target.DynamicLinker.none,
};

But if I replace env with x86_64-pc-linux-musl-gcc, this part changes:

pub const target: std.Target = .{
    .cpu = cpu,
    .os = os,
    .abi = abi,
    .ofmt = object_format,
    .dynamic_linker = std.Target.DynamicLinker.init("/lib/ld-musl-x86_64.so.1"),
};

I assume result above is what you want?

Yes, this is exactly what I want, a workaround for this issue is to simply manually set the dynamic_linker as you shown in the build.zig like this:

const std = @import("std");

pub fn build(b: *std.Build) void {
    b.host.query.dynamic_linker = std.Target.DynamicLinker.init("/usr/lib/ld-musl-x86_64.so.1");
    const exe = b.addExecutable(.{
        .name = "hello",
        .root_source_file = b.path("main.zig"),
        .target = b.host,
        .optimize = .Debug,
    });
    exe.linkLibC();
    b.installArtifact(exe);
}
BratishkaErik commented 5 days ago

Yes, this is exactly what I want, a workaround for this issue is to simply manually set the dynamic_linker as you shown in the build.zig like this:

As an easier alternative, you can pass zig build -Ddynamic-linker=/usr/lib/... in projects that use b.standardTargetOptions in their build.zig, same way as you passed --dynamic-linker /usr/lib/ld-musl-x86_64.so.1 in zig build-exe