From b1eba5a996ed862aefae49a1a7d1480a788095ff Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 10 Mar 2024 23:45:29 +0100 Subject: [PATCH 1/5] elf+aarch64: actually write out thunks, and add a proper link test --- src/link/Elf.zig | 10 ++++++++ src/link/Elf/thunks.zig | 2 +- test/link/elf.zig | 55 +++++++++++++++++++++++++++++++++-------- 3 files changed, 56 insertions(+), 11 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index d26f49ca09..ed6ffbcd08 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4565,6 +4565,16 @@ fn writeAtoms(self: *Elf) !void { try self.base.file.?.pwriteAll(buffer, sh_offset); } + for (self.thunks.items) |th| { + const shdr = self.shdrs.items[th.output_section_index]; + const offset = th.value + shdr.sh_offset; + const buffer = try gpa.alloc(u8, th.size(self)); + defer gpa.free(buffer); + var stream = std.io.fixedBufferStream(buffer); + try th.write(self, stream.writer()); + try self.base.file.?.pwriteAll(buffer, offset); + } + try self.reportUndefinedSymbols(&undefs); if (has_reloc_errors) return error.FlushFailure; diff --git a/src/link/Elf/thunks.zig b/src/link/Elf/thunks.zig index 398a2acd93..586cbed236 100644 --- a/src/link/Elf/thunks.zig +++ b/src/link/Elf/thunks.zig @@ -103,7 +103,7 @@ pub const Thunk = struct { } pub fn write(thunk: Thunk, elf_file: *Elf, writer: anytype) !void { - switch (elf_file.options.cpu_arch.?) { + switch (elf_file.getTarget().cpu.arch) { .aarch64 => try aarch64.write(thunk, elf_file, writer), .x86_64, .riscv64 => unreachable, else => @panic("unhandled arch"), diff --git a/test/link/elf.zig b/test/link/elf.zig index d872796cd4..1920feb30c 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -20,16 +20,11 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { .os_tag = .linux, .abi = .gnu, }); - // const aarch64_musl = b.resolveTargetQuery(.{ - // .cpu_arch = .aarch64, - // .os_tag = .linux, - // .abi = .musl, - // }); - // const aarch64_gnu = b.resolveTargetQuery(.{ - // .cpu_arch = .aarch64, - // .os_tag = .linux, - // .abi = .gnu, - // }); + const aarch64_musl = b.resolveTargetQuery(.{ + .cpu_arch = .aarch64, + .os_tag = .linux, + .abi = .musl, + }); const riscv64_musl = b.resolveTargetQuery(.{ .cpu_arch = .riscv64, .os_tag = .linux, @@ -153,6 +148,9 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { elf_step.dependOn(testMismatchedCpuArchitectureError(b, .{ .target = x86_64_musl })); elf_step.dependOn(testZText(b, .{ .target = x86_64_gnu })); + // aarch64 specific tests + elf_step.dependOn(testThunks(b, .{ .target = aarch64_musl })); + // x86_64 self-hosted backend elf_step.dependOn(testEmitRelocatable(b, .{ .use_llvm = false, .target = x86_64_musl })); elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = x86_64_musl })); @@ -2670,6 +2668,43 @@ fn testStrip(b: *Build, opts: Options) *Step { return test_step; } +fn testThunks(b: *Build, opts: Options) *Step { + const test_step = addTestStep(b, "thunks", opts); + + const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = + \\#include + \\__attribute__((aligned(0x8000000))) int bar() { + \\ return 42; + \\} + \\int foobar(); + \\int foo() { + \\ return bar() - foobar(); + \\} + \\__attribute__((aligned(0x8000000))) int foobar() { + \\ return 42; + \\} + \\int main() { + \\ printf("bar=%d, foo=%d, foobar=%d", bar(), foo(), foobar()); + \\ return foo(); + \\} + }); + exe.link_function_sections = true; + exe.linkLibC(); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("bar=42, foo=0, foobar=42"); + run.expectExitCode(0); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.max_bytes = std.math.maxInt(u32); + check.checkInSymtab(); + check.checkContains("_libc_start_main$thunk"); + test_step.dependOn(&check.step); + + return test_step; +} + fn testTlsDfStaticTls(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "tls-df-static-tls", opts); From b1bd3825f84edc7a8f1195b0c842fbbf8346c1cb Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Sun, 10 Mar 2024 23:47:16 +0100 Subject: [PATCH 2/5] test/link/macho: remove redundant 'macho-' prefix from case names --- test/link/macho.zig | 94 ++++++++++++++++++++++----------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/test/link/macho.zig b/test/link/macho.zig index f2067dd70d..24ae2d79a8 100644 --- a/test/link/macho.zig +++ b/test/link/macho.zig @@ -91,7 +91,7 @@ pub fn testAll(b: *Build, build_opts: BuildOptions) *Step { } fn testDeadStrip(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-dead-strip", opts); + const test_step = addTestStep(b, "dead-strip", opts); const obj = addObject(b, opts, .{ .name = "a", .cpp_source_bytes = \\#include @@ -172,7 +172,7 @@ fn testDeadStrip(b: *Build, opts: Options) *Step { } fn testDeadStripDylibs(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-dead-strip-dylibs", opts); + const test_step = addTestStep(b, "dead-strip-dylibs", opts); const main_o = addObject(b, opts, .{ .name = "main", .c_source_bytes = \\#include @@ -221,7 +221,7 @@ fn testDeadStripDylibs(b: *Build, opts: Options) *Step { } fn testDylib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-dylib", opts); + const test_step = addTestStep(b, "dylib", opts); const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = \\#include @@ -258,7 +258,7 @@ fn testDylib(b: *Build, opts: Options) *Step { } fn testDylibVersionTbd(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-dylib-version-tbd", opts); + const test_step = addTestStep(b, "dylib-version-tbd", opts); const tbd = tbd: { const wf = WriteFile.create(b); @@ -294,7 +294,7 @@ fn testDylibVersionTbd(b: *Build, opts: Options) *Step { } fn testEmptyObject(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-empty-object", opts); + const test_step = addTestStep(b, "empty-object", opts); const empty = addObject(b, opts, .{ .name = "empty", .c_source_bytes = "" }); @@ -314,7 +314,7 @@ fn testEmptyObject(b: *Build, opts: Options) *Step { } fn testEmptyZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-empty-zig", opts); + const test_step = addTestStep(b, "empty-zig", opts); const exe = addExecutable(b, opts, .{ .name = "empty", .zig_source_bytes = "pub fn main() void {}" }); @@ -326,7 +326,7 @@ fn testEmptyZig(b: *Build, opts: Options) *Step { } fn testEntryPoint(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-entry-point", opts); + const test_step = addTestStep(b, "entry-point", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = \\#include @@ -357,7 +357,7 @@ fn testEntryPoint(b: *Build, opts: Options) *Step { } fn testEntryPointArchive(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-entry-point-archive", opts); + const test_step = addTestStep(b, "entry-point-archive", opts); const lib = addStaticLibrary(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); @@ -386,7 +386,7 @@ fn testEntryPointArchive(b: *Build, opts: Options) *Step { } fn testEntryPointDylib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-entry-point-dylib", opts); + const test_step = addTestStep(b, "entry-point-dylib", opts); const dylib = addSharedLibrary(b, opts, .{ .name = "a" }); addCSourceBytes(dylib, @@ -440,7 +440,7 @@ fn testEntryPointDylib(b: *Build, opts: Options) *Step { } fn testHeaderpad(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-headerpad", opts); + const test_step = addTestStep(b, "headerpad", opts); const addExe = struct { fn addExe(bb: *Build, o: Options, name: []const u8) *Compile { @@ -548,7 +548,7 @@ fn testHeaderpad(b: *Build, opts: Options) *Step { // Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-header-flags.s fn testHeaderWeakFlags(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-header-weak-flags", opts); + const test_step = addTestStep(b, "header-weak-flags", opts); const obj1 = addObject(b, opts, .{ .name = "a", .asm_source_bytes = \\.globl _x @@ -635,7 +635,7 @@ fn testHeaderWeakFlags(b: *Build, opts: Options) *Step { } fn testHelloC(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-hello-c", opts); + const test_step = addTestStep(b, "hello-c", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = \\#include @@ -659,7 +659,7 @@ fn testHelloC(b: *Build, opts: Options) *Step { } fn testHelloZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-hello-zig", opts); + const test_step = addTestStep(b, "hello-zig", opts); const exe = addExecutable(b, opts, .{ .name = "main", .zig_source_bytes = \\const std = @import("std"); @@ -676,7 +676,7 @@ fn testHelloZig(b: *Build, opts: Options) *Step { } fn testLargeBss(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-large-bss", opts); + const test_step = addTestStep(b, "large-bss", opts); // TODO this test used use a 4GB zerofill section but this actually fails and causes every // linker I tried misbehave in different ways. This only happened on arm64. I thought that @@ -697,7 +697,7 @@ fn testLargeBss(b: *Build, opts: Options) *Step { } fn testLayout(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-layout", opts); + const test_step = addTestStep(b, "layout", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = \\#include @@ -816,7 +816,7 @@ fn testLayout(b: *Build, opts: Options) *Step { } fn testLinkDirectlyCppTbd(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-link-directly-cpp-tbd", opts); + const test_step = addTestStep(b, "link-directly-cpp-tbd", opts); const sdk = std.zig.system.darwin.getSdk(b.allocator, opts.target.result) orelse @panic("macOS SDK is required to run the test"); @@ -887,7 +887,7 @@ fn testLinkingStaticLib(b: *Build, opts: Options) *Step { } fn testLinksection(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-linksection", opts); + const test_step = addTestStep(b, "linksection", opts); const obj = addObject(b, opts, .{ .name = "main", .zig_source_bytes = \\export var test_global: u32 linksection("__DATA,__TestGlobal") = undefined; @@ -914,7 +914,7 @@ fn testLinksection(b: *Build, opts: Options) *Step { } fn testMhExecuteHeader(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-mh-execute-header", opts); + const test_step = addTestStep(b, "mh-execute-header", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); @@ -927,7 +927,7 @@ fn testMhExecuteHeader(b: *Build, opts: Options) *Step { } fn testNoDeadStrip(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-no-dead-strip", opts); + const test_step = addTestStep(b, "no-dead-strip", opts); const exe = addExecutable(b, opts, .{ .name = "name", .c_source_bytes = \\__attribute__((used)) int bogus1 = 0; @@ -954,7 +954,7 @@ fn testNoDeadStrip(b: *Build, opts: Options) *Step { } fn testNoExportsDylib(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-no-exports-dylib", opts); + const test_step = addTestStep(b, "no-exports-dylib", opts); const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "static void abc() {}" }); @@ -967,7 +967,7 @@ fn testNoExportsDylib(b: *Build, opts: Options) *Step { } fn testNeededFramework(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-needed-framework", opts); + const test_step = addTestStep(b, "needed-framework", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); exe.root_module.linkFramework("Cocoa", .{ .needed = true }); @@ -987,7 +987,7 @@ fn testNeededFramework(b: *Build, opts: Options) *Step { } fn testNeededLibrary(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-needed-library", opts); + const test_step = addTestStep(b, "needed-library", opts); const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "int a = 42;" }); @@ -1011,7 +1011,7 @@ fn testNeededLibrary(b: *Build, opts: Options) *Step { } fn testObjc(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-objc", opts); + const test_step = addTestStep(b, "objc", opts); const lib = addStaticLibrary(b, opts, .{ .name = "a", .objc_source_bytes = \\#import @@ -1058,7 +1058,7 @@ fn testObjc(b: *Build, opts: Options) *Step { } fn testObjcpp(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-objcpp", opts); + const test_step = addTestStep(b, "objcpp", opts); const foo_h = foo_h: { const wf = WriteFile.create(b); @@ -1111,7 +1111,7 @@ fn testObjcpp(b: *Build, opts: Options) *Step { } fn testPagezeroSize(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-pagezero-size", opts); + const test_step = addTestStep(b, "pagezero-size", opts); { const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main () { return 0; }" }); @@ -1145,7 +1145,7 @@ fn testPagezeroSize(b: *Build, opts: Options) *Step { } fn testReexportsZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-reexports-zig", opts); + const test_step = addTestStep(b, "reexports-zig", opts); const lib = addStaticLibrary(b, opts, .{ .name = "a", .zig_source_bytes = \\const x: i32 = 42; @@ -1174,7 +1174,7 @@ fn testReexportsZig(b: *Build, opts: Options) *Step { } fn testRelocatable(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-relocatable", opts); + const test_step = addTestStep(b, "relocatable", opts); const a_o = addObject(b, opts, .{ .name = "a", .cpp_source_bytes = \\#include @@ -1242,7 +1242,7 @@ fn testRelocatable(b: *Build, opts: Options) *Step { } fn testRelocatableZig(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-relocatable-zig", opts); + const test_step = addTestStep(b, "relocatable-zig", opts); const a_o = addObject(b, opts, .{ .name = "a", .zig_source_bytes = \\const std = @import("std"); @@ -1293,7 +1293,7 @@ fn testRelocatableZig(b: *Build, opts: Options) *Step { } fn testSearchStrategy(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-search-strategy", opts); + const test_step = addTestStep(b, "search-strategy", opts); const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = \\#include @@ -1361,7 +1361,7 @@ fn testSearchStrategy(b: *Build, opts: Options) *Step { } fn testSectionBoundarySymbols(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-section-boundary-symbols", opts); + const test_step = addTestStep(b, "section-boundary-symbols", opts); const obj1 = addObject(b, opts, .{ .name = "obj1", @@ -1441,7 +1441,7 @@ fn testSectionBoundarySymbols(b: *Build, opts: Options) *Step { } fn testSegmentBoundarySymbols(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-segment-boundary-symbols", opts); + const test_step = addTestStep(b, "segment-boundary-symbols", opts); const obj1 = addObject(b, opts, .{ .name = "a", .cpp_source_bytes = \\constexpr const char* MESSAGE __attribute__((used, section("__DATA_CONST_1,__message_ptr"))) = "codebase"; @@ -1509,7 +1509,7 @@ fn testSegmentBoundarySymbols(b: *Build, opts: Options) *Step { } fn testStackSize(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-stack-size", opts); + const test_step = addTestStep(b, "stack-size", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); exe.stack_size = 0x100000000; @@ -1528,7 +1528,7 @@ fn testStackSize(b: *Build, opts: Options) *Step { } fn testTbdv3(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-tbdv3", opts); + const test_step = addTestStep(b, "tbdv3", opts); const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = "int getFoo() { return 42; }" }); @@ -1566,7 +1566,7 @@ fn testTbdv3(b: *Build, opts: Options) *Step { } fn testTentative(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-tentative", opts); + const test_step = addTestStep(b, "tentative", opts); const exe = addExecutable(b, opts, .{ .name = "main" }); addCSourceBytes(exe, @@ -1592,7 +1592,7 @@ fn testTentative(b: *Build, opts: Options) *Step { } fn testThunks(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-thunks", opts); + const test_step = addTestStep(b, "thunks", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = \\#include @@ -1621,7 +1621,7 @@ fn testThunks(b: *Build, opts: Options) *Step { } fn testTls(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-tls", opts); + const test_step = addTestStep(b, "tls", opts); const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = \\_Thread_local int a; @@ -1655,7 +1655,7 @@ fn testTls(b: *Build, opts: Options) *Step { } fn testTlsLargeTbss(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-tls-large-tbss", opts); + const test_step = addTestStep(b, "tls-large-tbss", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = \\#include @@ -1676,7 +1676,7 @@ fn testTlsLargeTbss(b: *Build, opts: Options) *Step { } fn testTwoLevelNamespace(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-two-level-namespace", opts); + const test_step = addTestStep(b, "two-level-namespace", opts); const liba = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = \\#include @@ -1796,7 +1796,7 @@ fn testTwoLevelNamespace(b: *Build, opts: Options) *Step { } fn testUndefinedFlag(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-undefined-flag", opts); + const test_step = addTestStep(b, "undefined-flag", opts); const obj = addObject(b, opts, .{ .name = "a", .c_source_bytes = "int foo = 42;" }); @@ -1873,7 +1873,7 @@ fn testUndefinedFlag(b: *Build, opts: Options) *Step { } fn testUnwindInfo(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-unwind-info", opts); + const test_step = addTestStep(b, "unwind-info", opts); const all_h = all_h: { const wf = WriteFile.create(b); @@ -2031,7 +2031,7 @@ fn testUnwindInfo(b: *Build, opts: Options) *Step { } fn testUnwindInfoNoSubsectionsArm64(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-unwind-info-no-subsections-arm64", opts); + const test_step = addTestStep(b, "unwind-info-no-subsections-arm64", opts); const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = \\.globl _foo @@ -2091,7 +2091,7 @@ fn testUnwindInfoNoSubsectionsArm64(b: *Build, opts: Options) *Step { } fn testUnwindInfoNoSubsectionsX64(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-unwind-info-no-subsections-x64", opts); + const test_step = addTestStep(b, "unwind-info-no-subsections-x64", opts); const a_o = addObject(b, opts, .{ .name = "a", .asm_source_bytes = \\.globl _foo @@ -2144,7 +2144,7 @@ fn testUnwindInfoNoSubsectionsX64(b: *Build, opts: Options) *Step { // Adapted from https://github.com/llvm/llvm-project/blob/main/lld/test/MachO/weak-binding.s fn testWeakBind(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-weak-bind", opts); + const test_step = addTestStep(b, "weak-bind", opts); const lib = addSharedLibrary(b, opts, .{ .name = "foo", .asm_source_bytes = \\.globl _weak_dysym @@ -2275,7 +2275,7 @@ fn testWeakBind(b: *Build, opts: Options) *Step { } fn testWeakFramework(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-weak-framework", opts); + const test_step = addTestStep(b, "weak-framework", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = "int main() { return 0; }" }); exe.root_module.linkFramework("Cocoa", .{ .weak = true }); @@ -2294,7 +2294,7 @@ fn testWeakFramework(b: *Build, opts: Options) *Step { } fn testWeakLibrary(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-weak-library", opts); + const test_step = addTestStep(b, "weak-library", opts); const dylib = addSharedLibrary(b, opts, .{ .name = "a", .c_source_bytes = \\#include @@ -2337,7 +2337,7 @@ fn testWeakLibrary(b: *Build, opts: Options) *Step { } fn testWeakRef(b: *Build, opts: Options) *Step { - const test_step = addTestStep(b, "macho-weak-ref", opts); + const test_step = addTestStep(b, "weak-ref", opts); const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = \\#include @@ -2356,7 +2356,7 @@ fn testWeakRef(b: *Build, opts: Options) *Step { } fn addTestStep(b: *Build, comptime prefix: []const u8, opts: Options) *Step { - return link.addTestStep(b, "macho-" ++ prefix, opts); + return link.addTestStep(b, "" ++ prefix, opts); } const builtin = @import("builtin"); From faa4bdb0175ee142834be320326063ae99dac1e4 Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Mar 2024 00:07:07 +0100 Subject: [PATCH 3/5] elf+aarch64: fix off-by-one in converging on groups interleaved with thunks --- src/link/Elf/thunks.zig | 6 ++-- test/link/elf.zig | 76 ++++++++++++++++++++++++++--------------- 2 files changed, 51 insertions(+), 31 deletions(-) diff --git a/src/link/Elf/thunks.zig b/src/link/Elf/thunks.zig index 586cbed236..119529b512 100644 --- a/src/link/Elf/thunks.zig +++ b/src/link/Elf/thunks.zig @@ -1,6 +1,7 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void { const gpa = elf_file.base.comp.gpa; const cpu_arch = elf_file.getTarget().cpu.arch; + const max_distance = maxAllowedDistance(cpu_arch); const shdr = &elf_file.shdrs.items[shndx]; const atoms = elf_file.output_sections.get(shndx).?.items; assert(atoms.len > 0); @@ -17,12 +18,11 @@ pub fn createThunks(shndx: u32, elf_file: *Elf) !void { start_atom.value = try advance(shdr, start_atom.size, start_atom.alignment); i += 1; - while (i < atoms.len and - shdr.sh_size - start_atom.value < maxAllowedDistance(cpu_arch)) : (i += 1) - { + while (i < atoms.len) : (i += 1) { const atom_index = atoms[i]; const atom = elf_file.atom(atom_index).?; assert(atom.flags.alive); + if (atom.alignment.forward(shdr.sh_size) - start_atom.value >= max_distance) break; atom.value = try advance(shdr, atom.size, atom.alignment); } diff --git a/test/link/elf.zig b/test/link/elf.zig index 1920feb30c..fd6af3160f 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -2671,36 +2671,56 @@ fn testStrip(b: *Build, opts: Options) *Step { fn testThunks(b: *Build, opts: Options) *Step { const test_step = addTestStep(b, "thunks", opts); - const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = - \\#include - \\__attribute__((aligned(0x8000000))) int bar() { - \\ return 42; - \\} - \\int foobar(); - \\int foo() { - \\ return bar() - foobar(); - \\} - \\__attribute__((aligned(0x8000000))) int foobar() { - \\ return 42; - \\} - \\int main() { - \\ printf("bar=%d, foo=%d, foobar=%d", bar(), foo(), foobar()); - \\ return foo(); - \\} - }); - exe.link_function_sections = true; - exe.linkLibC(); + const src = + \\#include + \\__attribute__((aligned(0x8000000))) int bar() { + \\ return 42; + \\} + \\int foobar(); + \\int foo() { + \\ return bar() - foobar(); + \\} + \\__attribute__((aligned(0x8000000))) int foobar() { + \\ return 42; + \\} + \\int main() { + \\ printf("bar=%d, foo=%d, foobar=%d", bar(), foo(), foobar()); + \\ return foo(); + \\} + ; - const run = addRunArtifact(exe); - run.expectStdOutEqual("bar=42, foo=0, foobar=42"); - run.expectExitCode(0); - test_step.dependOn(&run.step); + { + const exe = addExecutable(b, opts, .{ .name = "main", .c_source_bytes = src }); + exe.link_function_sections = true; + exe.linkLibC(); - const check = exe.checkObject(); - check.max_bytes = std.math.maxInt(u32); - check.checkInSymtab(); - check.checkContains("_libc_start_main$thunk"); - test_step.dependOn(&check.step); + const run = addRunArtifact(exe); + run.expectStdOutEqual("bar=42, foo=0, foobar=42"); + run.expectExitCode(0); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.max_bytes = std.math.maxInt(u32); + check.checkInSymtab(); + check.checkContains("__libc_start_main$thunk"); + test_step.dependOn(&check.step); + } + + { + const exe = addExecutable(b, opts, .{ .name = "main2", .c_source_bytes = src }); + exe.linkLibC(); + + const run = addRunArtifact(exe); + run.expectStdOutEqual("bar=42, foo=0, foobar=42"); + run.expectExitCode(0); + test_step.dependOn(&run.step); + + const check = exe.checkObject(); + check.max_bytes = std.math.maxInt(u32); + check.checkInSymtab(); + check.checkContains("__libc_start_main$thunk"); + test_step.dependOn(&check.step); + } return test_step; } From 55c085b893892f89e7d9930f3ec6c22537ebb54f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Mar 2024 13:25:29 +0100 Subject: [PATCH 4/5] elf: re-use output buffer for emitting thunks --- src/link/Elf.zig | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index ed6ffbcd08..e6182b4333 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -4565,14 +4565,20 @@ fn writeAtoms(self: *Elf) !void { try self.base.file.?.pwriteAll(buffer, sh_offset); } - for (self.thunks.items) |th| { - const shdr = self.shdrs.items[th.output_section_index]; - const offset = th.value + shdr.sh_offset; - const buffer = try gpa.alloc(u8, th.size(self)); - defer gpa.free(buffer); - var stream = std.io.fixedBufferStream(buffer); - try th.write(self, stream.writer()); - try self.base.file.?.pwriteAll(buffer, offset); + if (self.requiresThunks()) { + var buffer = std.ArrayList(u8).init(gpa); + defer buffer.deinit(); + + for (self.thunks.items) |th| { + const thunk_size = th.size(self); + try buffer.ensureUnusedCapacity(thunk_size); + const shdr = self.shdrs.items[th.output_section_index]; + const offset = th.value + shdr.sh_offset; + try th.write(self, buffer.writer()); + assert(buffer.items.len == thunk_size); + try self.base.file.?.pwriteAll(buffer.items, offset); + buffer.clearRetainingCapacity(); + } } try self.reportUndefinedSymbols(&undefs); @@ -4603,12 +4609,12 @@ pub fn updateSymtabSize(self: *Elf) !void { nlocals += 1; } - for (self.thunks.items) |*th| { + if (self.requiresThunks()) for (self.thunks.items) |*th| { th.output_symtab_ctx.ilocal = nlocals + 1; th.calcSymtabSize(self); nlocals += th.output_symtab_ctx.nlocals; strsize += th.output_symtab_ctx.strsize; - } + }; for (files.items) |index| { const file_ptr = self.file(index).?; @@ -4840,9 +4846,9 @@ pub fn writeSymtab(self: *Elf) !void { self.writeSectionSymbols(); - for (self.thunks.items) |th| { + if (self.requiresThunks()) for (self.thunks.items) |th| { th.writeSymtab(self); - } + }; if (self.zigObjectPtr()) |zig_object| { zig_object.asFile().writeSymtab(self); @@ -6007,10 +6013,14 @@ fn fmtDumpState( try writer.print("linker_defined({d}) : (linker defined)\n", .{index}); try writer.print("{}\n", .{linker_defined.fmtSymtab(self)}); } - try writer.writeAll("thunks\n"); - for (self.thunks.items, 0..) |th, index| { - try writer.print("thunk({d}) : {}\n", .{ index, th.fmt(self) }); + + if (self.requiresThunks()) { + try writer.writeAll("thunks\n"); + for (self.thunks.items, 0..) |th, index| { + try writer.print("thunk({d}) : {}\n", .{ index, th.fmt(self) }); + } } + try writer.print("{}\n", .{self.zig_got.fmt(self)}); try writer.print("{}\n", .{self.got.fmt(self)}); try writer.print("{}\n", .{self.plt.fmt(self)}); From 7ba2453b8ec56ccacfccc466183e1aaafbd5210e Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 12 Mar 2024 20:21:48 +0100 Subject: [PATCH 5/5] test/link/elf: do not check for $thunk in thunks tests for now Andrew and I have discovered that on Linux max peak rss value is taken to be `max(build_runner, test_suite)` and since the thunks test emit a huge binary, we will easily exceed the declared maximum for any of the test suites. This can be worked around for now by not checking for $thunk symbols in this test since it doesn't really yield any additional information; however ideally we would implement per-thread local temp arena that can be freed. --- test/link/elf.zig | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/link/elf.zig b/test/link/elf.zig index fd6af3160f..d018d042e3 100644 --- a/test/link/elf.zig +++ b/test/link/elf.zig @@ -2698,12 +2698,6 @@ fn testThunks(b: *Build, opts: Options) *Step { run.expectStdOutEqual("bar=42, foo=0, foobar=42"); run.expectExitCode(0); test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.max_bytes = std.math.maxInt(u32); - check.checkInSymtab(); - check.checkContains("__libc_start_main$thunk"); - test_step.dependOn(&check.step); } { @@ -2714,12 +2708,6 @@ fn testThunks(b: *Build, opts: Options) *Step { run.expectStdOutEqual("bar=42, foo=0, foobar=42"); run.expectExitCode(0); test_step.dependOn(&run.step); - - const check = exe.checkObject(); - check.max_bytes = std.math.maxInt(u32); - check.checkInSymtab(); - check.checkContains("__libc_start_main$thunk"); - test_step.dependOn(&check.step); } return test_step;