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

macOS: lld lacks code signing for macOS/arm64 #6971

Closed
kubkon opened this issue Nov 4, 2020 · 5 comments · Fixed by #8282
Closed

macOS: lld lacks code signing for macOS/arm64 #6971

kubkon opened this issue Nov 4, 2020 · 5 comments · Fixed by #8282
Labels
arch-aarch64 64-bit ARM os-macos upstream An issue with a third party project that Zig uses.
Milestone

Comments

@kubkon
Copy link
Member

kubkon commented Nov 4, 2020

As reported by @jedisct1 (and confirmed by myself):

macOS/arm64 beginning with Big Sur now requires everything (libraries and executables) to be signed. Or else, they instantly receive a SIGKILL. The system ld automatically signs everything it creates. However, lld currently doesn't.

This is tracker issue for upstream lld implementation of code signing.


EDIT: It is now possible to build Zig without falling back to system linker due to the fact Zig now patches the LLD generated binary and adds the missing code signature. The changes making it possible landed in #7273. As mentioned in the discussion below, this will work 99 out of 100 times; however we rely on the fact that there will be enough padding between the end of the load commands and the beginning of the __text section left by the LLD to be able to insert the LC_CODE_SIGNATURE load command, and this 1 out of 100 times this will not be the case, but Zig will inform of this error and gracefully exit. In such cases, you can either add more code to your binary (yeah, no kidding), or fall back to the system linker. Once LLD adds -headerpad <size> option, even if they don't add code signing, we should be able to always generate a valid code signature.

@kubkon kubkon added os-macos upstream An issue with a third party project that Zig uses. labels Nov 4, 2020
@kubkon kubkon added this to the 0.8.0 milestone Nov 4, 2020
@daurnimator daurnimator changed the title macOS: lld lacks code singing facilities making Zig unusable on Big Sur macOS: lld lacks code signing facilities making Zig unusable on Big Sur Nov 4, 2020
@kubkon
Copy link
Member Author

kubkon commented Nov 4, 2020

Thanks @daurnimator! Although code singing would be something! ;-)

@kubkon kubkon added the arch-aarch64 64-bit ARM label Nov 4, 2020
@kubkon
Copy link
Member Author

kubkon commented Nov 4, 2020

OK, I can't replicate the issue on my x86_64 MBP running Big Sur Public Beta which is a good news wrt to 0.7.0 release. On the other hand, I'm still not sure why there is this additional requirement/limitation on ARM64 Big Sur Public Beta.

@kubkon kubkon changed the title macOS: lld lacks code signing facilities making Zig unusable on Big Sur macOS: lld lacks code signing facilities making Zig unusable on Big Sur, arm64 Nov 4, 2020
@kivikakk
Copy link
Contributor

👀 Zig seems pretty usable on Big Sur arm64 today — I don't think I'm accidentally using the system linker hack, either, as the binary called appears to be ld64.lld:

$ cat hello.zig
const std = @import("std");
pub fn main() void {
    std.debug.print("Hello from {} {}\n", .{ std.builtin.os.tag, std.builtin.arch });
}
$ zig run --verbose-link hello.zig
ld64.lld -error-limit 0 -demangle -dynamic -arch arm64 -macosx_version_min 11.1.0 -sdk_version 11.1.0 -pie -o /Users/kameliya/.cache/zig/o/83ffe50c4e7a24c53c08701f0e40de60/hello -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk /Users/kameliya/.cache/zig/o/83ffe50c4e7a24c53c08701f0e40de60/hello.o /Users/kameliya/.cache/zig/o/caa476c6b81d0829f3d079cc3e94fba2/libcompiler_rt.a -lSystem
Hello from Tag.macos Arch.aarch64
$ 

Not sure if this is news or not, just wanted to share since I'm motivated and happy to see Zig working on macos/aarch64.

@kubkon
Copy link
Member Author

kubkon commented Dec 20, 2020

Hey @kivikakk! I'm glad you're having good experience with Zig on Apple Silicon!

👀 Zig seems pretty usable on Big Sur arm64 today — I don't think I'm accidentally using the system linker hack, either, as the binary called appears to be ld64.lld:

$ cat hello.zig
const std = @import("std");
pub fn main() void {
    std.debug.print("Hello from {} {}\n", .{ std.builtin.os.tag, std.builtin.arch });
}
$ zig run --verbose-link hello.zig
ld64.lld -error-limit 0 -demangle -dynamic -arch arm64 -macosx_version_min 11.1.0 -sdk_version 11.1.0 -pie -o /Users/kameliya/.cache/zig/o/83ffe50c4e7a24c53c08701f0e40de60/hello -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk /Users/kameliya/.cache/zig/o/83ffe50c4e7a24c53c08701f0e40de60/hello.o /Users/kameliya/.cache/zig/o/caa476c6b81d0829f3d079cc3e94fba2/libcompiler_rt.a -lSystem
Hello from Tag.macos Arch.aarch64
$ 

You're right, you are using LLD to do the linking on Apple Silicon. However, it's Zig that does the code signing postmortem, after LLD spits out the linked final binary here:

if (self.code_signature_cmd_index == null) outer: {
    if (target.cpu.arch != .aarch64) break :outer; // This is currently needed only for aarch64 targets.
    const text_segment = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
    const text_section = text_segment.sections.items[self.text_section_index.?];
    const after_last_cmd_offset = self.header.?.sizeofcmds + @sizeOf(macho.mach_header_64);
    const needed_size = @sizeOf(macho.linkedit_data_command) * alloc_num / alloc_den;

    if (needed_size + after_last_cmd_offset > text_section.offset) {
         std.log.err("Unable to extend padding between the end of load commands and start of __text section.", .{});
         std.log.err("Re-run the linker with '-headerpad 0x{x}' option if available, or", .{needed_size});
         std.log.err("fall back to the system linker by exporting 'ZIG_SYSTEM_LINKER_HACK=1'.", .{});
         return error.NotEnoughPadding;
    }

    const linkedit_segment = self.load_commands.items[self.linkedit_segment_cmd_index.?].Segment;
    // TODO This is clunky.
    self.linkedit_segment_next_offset = @intCast(u32, mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, @sizeOf(u64)));
    // Add code signature load command
    self.code_signature_cmd_index = @intCast(u16, self.load_commands.items.len);
    try self.load_commands.append(self.base.allocator, .{
         .LinkeditData = .{
              .cmd = macho.LC_CODE_SIGNATURE,
              .cmdsize = @sizeOf(macho.linkedit_data_command),
              .dataoff = 0,
              .datasize = 0,
        },
    });

    // Pad out space for code signature
    try self.writeCodeSignaturePadding();
    // Write updated load commands and the header
    try self.writeLoadCommands();
    try self.writeHeader();
    // Generate adhoc code signature
    try self.writeCodeSignature();
    }
}

Sadly, LLD itself doesn't have code signing mechanism in place yet, so we've decided to patch up the binary ourselves in the pipeline. This will work 99 out of 100 times; however we rely on the fact that there will be enough padding between the end of the load commands and the beginning of the __text section left by the LLD to be able to insert the LC_CODE_SIGNATURE load command, and this 1 out of 100 times this will not be the case, but Zig will inform of this error and gracefully exit. In such cases, you can either add more code to your binary (yeah, no kidding), or fall back to the system linker. Once LLD adds -headerpad <size> option, even if they don't add code signing, we should be able to always generate a valid code signature.

Not sure if this is news or not, just wanted to share since I'm motivated and happy to see Zig working on macos/aarch64.

As an added bonus, did you know that cross-compiling Zig and C to Apple Silicon should also generate valid (i.e., code signed and runnable) binaries? Something like this, on any platform of your choosing, should generate a valid Mach-O for Apple Silicon:

> zig build -Dtarget=aarch64-macos-gnu
> zig build-exe something.zig -target aarch64-macos-gnu
> zig cc hello.c -target aarch64-macos-gnu

The changes required to get this working landed in upstream yesterday in this PR #7318. This again involves a postmortem analysis and patching of the LLD generated binary, and works for Zig and C. For C++ on the other hand, there are other outstanding problems which it looks like will need to be fixed by LLD itself to get this working.

@kubkon kubkon changed the title macOS: lld lacks code signing facilities making Zig unusable on Big Sur, arm64 macOS: lld lacks code signing for macOS/arm64 Dec 20, 2020
@kubkon
Copy link
Member Author

kubkon commented Dec 20, 2020

I should probably update the issue to reflect what's mentioned above. Thanks for pointing this out @kivikakk!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
arch-aarch64 64-bit ARM os-macos upstream An issue with a third party project that Zig uses.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants