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

zig cc -Wl,--version: unsupported linker arg: --version #15549

Closed
motiejus opened this issue May 2, 2023 · 9 comments
Closed

zig cc -Wl,--version: unsupported linker arg: --version #15549

motiejus opened this issue May 2, 2023 · 9 comments
Labels
zig cc Zig as a drop-in C compiler feature
Milestone

Comments

@motiejus
Copy link
Contributor

motiejus commented May 2, 2023

zig cc -Wl,--version is not understood by the linker:

clang-16

$ clang-16 -Wl,--version
GNU ld (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.

zig-cc

$ zig cc -Wl,--version
error: unsupported linker arg: --version 

meson uses it to determine the linker. Example when configuring glib:

$ CC="zig cc" CXX="zig c++" meson setup   .. --prefix=/tmp
The Meson build system
Version: 0.61.2
Source dir: /home/motiejus/x/glib-2.69.3
Build dir: /home/motiejus/x/glib-2.69.3/build
Build type: native build
Project name: glib
Project version: 2.69.3

../meson.build:1:0: ERROR: Unable to detect linker for compiler "zig cc -Wl,--version"
stdout: 
stderr: error: unsupported linker arg: --version


A full log can be found at /home/motiejus/x/glib-2.69.3/build/meson-logs/meson-log.txt

I did not find a reasonable way to add such flag to src/main.zig and src/Compilation.zig. The best current way to do it is here:

zig/src/Compilation.zig

Lines 3942 to 3948 in 2892347

// When all these flags are true, it means that the entire purpose of
// this compilation is to perform a single zig cc operation. This means
// that we could "tail call" clang by doing an execve, and any use of
// the caching system would actually be problematic since the user is
// presumably doing their own caching by using dep file flags.
if (std.process.can_execv and direct_o and
comp.disable_c_depfile and comp.clang_passthrough_mode)

However, this whole function implies a single c_object is passed, which is obviously not the case here.

@motiejus motiejus changed the title zig cc: zig cc -Wl,--version: unsupported linker arg: --version May 2, 2023
motiejus added a commit to motiejus/hermetic_cc_toolchain that referenced this issue May 2, 2023
@vazub
Copy link

vazub commented May 17, 2023

Just encountered similar problem while trying to use zig cc as drop-in replacement with meson-based build. Found no way to override this yet, so I assume this means a no-go for meson use cases, unless flag gets supported on zig cc side.

@andrewrk andrewrk added the zig cc Zig as a drop-in C compiler feature label May 17, 2023
@andrewrk andrewrk added this to the 0.11.0 milestone May 17, 2023
@andrewrk
Copy link
Member

Here's what I think it should do:

$ zig cc -Wl,--version
zig ld 0.11.0-dev.3198+ad20236e9

It will be up to Meson to decide what to do with this information.

@matteo-briani
Copy link
Contributor

I am hitting the same problem with cmake; trying to use zig as drop in replacement for C/C++.
Cmake generates the shorter argument -v and the failing command becomes:

zig cc -Wl,-v

@matteo-briani
Copy link
Contributor

I would like to contribute to this issue, but I am new to Zig and its compiler, so I take it as an opportunity to dig deeper into Zig if someone is willing to guide me.

Me digging the code

To my understanding, the execution of zig cc -Wl,--version does not find a match for --version.
This triggers the execution of this final else statement and thus call fatal.

zig/src/main.zig

Lines 2207 to 2217 in 128fd7d

} else if (mem.startsWith(u8, arg, "/version:")) {
var split_it = mem.splitBackwardsScalar(u8, arg, ':');
const version_arg = split_it.first();
version = std.SemanticVersion.parse(version_arg) catch |err| {
fatal("unable to parse /version '{s}': {s}", .{ arg, @errorName(err) });
};
have_version = true;
} else {
fatal("unsupported linker arg: {s}", .{arg});
}
}

A very naive approach that I tried is to add another else if block that should match the --version word.
So, right before the final else, I added this piece of code:

else if (mem.eql(u8, arg, "--version")) {
    try std.io.getStdOut().writeAll("zig ld " ++ build_options.version ++ "\n");
}

However, this has two effects:

  • print to stdout zig ld 0.11.0-dev.3735+a72d634b7
  • continue the execution of the program and also add to stdout
GNU ld (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.

This secondary effect is produced when hitting line 2272

zig/src/main.zig

Lines 2267 to 2273 in 128fd7d

.preprocessor => {
output_mode = .Obj;
// An error message is generated when there is more than 1 C source file.
if (c_source_files.items.len != 1) {
// For example `zig cc` and no args should print the "no input files" message.
return process.exit(try clangMain(arena, all_args));
}

Questions

I read the comment #15549 (comment) but i do not understand why this

zig/src/Compilation.zig

Lines 3942 to 3948 in 2892347

// When all these flags are true, it means that the entire purpose of
// this compilation is to perform a single zig cc operation. This means
// that we could "tail call" clang by doing an execve, and any use of
// the caching system would actually be problematic since the user is
// presumably doing their own caching by using dep file flags.
if (std.process.can_execv and direct_o and
comp.disable_c_depfile and comp.clang_passthrough_mode)

should be the place to act to fix the issue.

May I have some help to clear my mind?
Since it's my first look at the code I am missing the big picture (and many details as well :D ).

@motiejus
Copy link
Contributor Author

Questions

I read the comment #15549 (comment) but i do not understand why this

zig/src/Compilation.zig

Lines 3942 to 3948 in 2892347

// When all these flags are true, it means that the entire purpose of
// this compilation is to perform a single zig cc operation. This means
// that we could "tail call" clang by doing an execve, and any use of
// the caching system would actually be problematic since the user is
// presumably doing their own caching by using dep file flags.
if (std.process.can_execv and direct_o and
comp.disable_c_depfile and comp.clang_passthrough_mode)

should be the place to act to fix the issue.

My original intent was to execve to the underlying system-specific lld. So the output of zig cc -Wl,--version would be equivalent to this:

$ zig ld.lld --version
LLD 16.0.1 (compatible with GNU linkers)

May I have some help to clear my mind? Since it's my first look at the code I am missing the big picture (and many details as well :D ).

What Andrew is proposing is much easier to implement, since you don't need to invoke the underlying linker.

continue the execution of the program and also add to stdout

I believe this should not be necessary. Just exit after printing the version.

@matteo-briani
Copy link
Contributor

I see it now, thanks for the clarification!
If the goal is to print the version and exit, I would gladly open a PR and make my contribution.

Shall I add the -v option as well? I discovered this because I stumbled upon the same problem using zig cc -Wl,-v

@matteo-briani
Copy link
Contributor

matteo-briani commented Jun 23, 2023

I implemented the change, but then, while using it, I realized that it is more complicated than anticipated.

Simple printing the version and exit does not work since it will cause an early exit on any invocation of zig cc or zig c++ including the -Wl,-v argument.

On the other end, printing the ld version and continue the program execution will behave correctly, but it will print on stdout both zig ld <version> and the linker version output.

What would be the correct behaviour?
Would it be correct to intercept the "-Wl,-v", print the zig ld <version> to stdout and then remove the "-v" from the linker argument before passing the control to clangMain(arena, all_args) ? Sounds very hackish though.
Am I missing something else?

@motiejus
Copy link
Contributor Author

I implemented the change, but then, while using it, I realized that it is more complicated than anticipated.

Simple printing the version and exit does not work since it will cause an early exit on any invocation of zig cc or zig c++ including the -Wl,-v argument.

On the other end, printing the ld version and continue the program execution will behave correctly, but it will print on stdout both zig ld <version> and the linker version output.

What would be the correct behaviour?

We should follow the lead on what gcc/clang do:

motiejus@mtwork:/code/hermetic_cc_toolchain/test/c$ clang-16 -fuse-ld=lld -Wl,--version main.c -o main
Ubuntu LLD 16.0.6 (compatible with GNU linkers)
motiejus@mtwork:/code/hermetic_cc_toolchain/test/c$ clang-16  -Wl,--version main.c -o main
GNU ld (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.
motiejus@mtwork:/code/hermetic_cc_toolchain/test/c$ gcc  -Wl,--version main.c -o main
collect2 version 11.3.0
/usr/bin/ld -plugin /usr/lib/gcc/x86_64-linux-gnu/11/liblto_plugin.so -plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper -plugin-opt=-fresolution=/tmp/ccjuwBPu.res -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc -plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker /lib64/ld-linux-x86-64.so.2 -pie -z now -z relro -o main /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crti.o /usr/lib/gcc/x86_64-linux-gnu/11/crtbeginS.o -L/usr/lib/gcc/x86_64-linux-gnu/11 -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu -L/usr/lib/gcc/x86_64-linux-gnu/11/../../../../lib -L/lib/x86_64-linux-gnu -L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/11/../../.. --version /tmp/cc24Zva2.o -lgcc --push-state --as-needed -lgcc_s --pop-state -lc -lgcc --push-state --as-needed -lgcc_s --pop-state /usr/lib/gcc/x86_64-linux-gnu/11/crtendS.o /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/crtn.o
GNU ld (GNU Binutils for Ubuntu) 2.38
Copyright (C) 2022 Free Software Foundation, Inc.
This program is free software; you may redistribute it under the terms of
the GNU General Public License version 3 or (at your option) a later version.
This program has absolutely no warranty.

It does not create a file:

motiejus@mtwork:/code/hermetic_cc_toolchain/test/c$ ls -lh ./main
ls: cannot access './main': No such file or directory

Does that help?

@matteo-briani
Copy link
Contributor

matteo-briani commented Jun 23, 2023

Yes! Actually thanks to your example I found out what was bugging me: -v and --versionare two completely different arguments.
-v stands for --verbose but I was keep reading it as --version.

I got even more confused because the verbose argument also print the version of the linker, and so I wasn't spotting my mistake.

Well, I guess I can revisit my PR, remove the -v argument, implement the --version behavior and open a different issue for the --verbose/-v unsupported linker arg.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
zig cc Zig as a drop-in C compiler feature
Projects
None yet
Development

No branches or pull requests

4 participants