From 7ee458b93b5ebbc3d6343a799cf6a3a7823f483a Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Wed, 19 Feb 2025 12:33:21 -0600 Subject: [PATCH] Linker pipeline --- build.zig | 23 ++++++++++--- src/LLVM.zig | 70 +++++++++++++++++++++++++++++++++++++- src/llvm.cpp | 87 ++++++++++++++++++++++++++++++++++++++++++++---- src/llvm_api.zig | 5 +++ 4 files changed, 173 insertions(+), 12 deletions(-) diff --git a/build.zig b/build.zig index a05bd3b..50a0d2b 100644 --- a/build.zig +++ b/build.zig @@ -102,12 +102,22 @@ const LLVM = struct { llvm.addIncludePath(.{ .cwd_relative = "/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/14.2.1/../../../../include/c++/14.2.1" }); llvm.addIncludePath(.{ .cwd_relative = "/usr/bin/../lib64/gcc/x86_64-pc-linux-gnu/14.2.1/../../../../include/c++/14.2.1/x86_64-pc-linux-gnu" }); - llvm.addObjectFile(.{ .cwd_relative = "/usr/lib/libstdc++.so.6" }); - llvm.linkSystemLibrary("unwind", .{}); - llvm.linkSystemLibrary("z", .{}); + const needed_libraries: []const []const u8 = &.{ "unwind", "z" }; - for (llvm_libs.items) |llvm_lib| { - llvm.linkSystemLibrary(llvm_lib, .{}); + llvm.addObjectFile(.{ .cwd_relative = "/usr/lib/libstdc++.so.6" }); + + const lld_libs: []const []const u8 = &.{ "lldCommon", "lldCOFF", "lldELF", "lldMachO", "lldMinGW", "lldWasm" }; + + for (needed_libraries) |lib| { + llvm.linkSystemLibrary(lib, .{}); + } + + for (llvm_libs.items) |lib| { + llvm.linkSystemLibrary(lib, .{}); + } + + for (lld_libs) |lib| { + llvm.linkSystemLibrary(lib, .{}); } return LLVM{ @@ -118,6 +128,9 @@ const LLVM = struct { fn link(llvm: LLVM, target: *std.Build.Step.Compile) void { if (target.root_module != llvm.module) { target.root_module.addImport("llvm", llvm.module); + } else { + // TODO: should we allow this case? + unreachable; } } }; diff --git a/src/LLVM.zig b/src/LLVM.zig index 3ea9f6d..a894a30 100644 --- a/src/LLVM.zig +++ b/src/LLVM.zig @@ -653,6 +653,14 @@ pub const DwarfEmissionKind = enum(c_int) { line_tables_only, }; +pub const lld = struct { + pub const Result = extern struct { + stdout: String, + stderr: String, + success: bool, + }; +}; + pub const Thread = struct { context: *Context, i1: Integer, @@ -734,6 +742,21 @@ pub fn initialize_all() void { }; } +const LldArgvBuilder = struct { + buffer: [1024]?[*:0]const u8 = undefined, + count: usize = 0, + + pub fn add(builder: *LldArgvBuilder, arg: [*:0]const u8) void { + builder.buffer[builder.count] = arg; + builder.count += 1; + } + + pub fn flush(builder: *LldArgvBuilder) [:null]const ?[*:0]const u8 { + builder.buffer[builder.count] = null; + return builder.buffer[0..builder.count :null]; + } +}; + pub fn experiment() void { const thread = &global.threads[0]; thread.initialize(); @@ -780,8 +803,9 @@ pub fn experiment() void { module.set_target(target_machine); module.run_optimization_pipeline(target_machine, OptimizationPipelineOptions.default(.{ .optimization_level = .O3, .debug_info = 1 })); + const object_path = ".zig-cache/foo.o"; const result = module.run_code_generation_pipeline(target_machine, CodeGenerationPipelineOptions{ - .output_file_path = String.from_slice(".zig-cache/foo.o"), + .output_file_path = String.from_slice(object_path), .output_dwarf_file_path = .{}, .flags = .{ .code_generation_file_type = .object_file, @@ -792,4 +816,48 @@ pub fn experiment() void { if (result != .success) { unreachable; } + + var arg_builder = LldArgvBuilder{}; + arg_builder.add("ld.lld"); + arg_builder.add("--error-limit=0"); + arg_builder.add("-o"); + arg_builder.add(".zig-cache/foo"); + const objects: []const [*:0]const u8 = &.{object_path}; + for (objects) |object| { + arg_builder.add(object); + } + + arg_builder.add("-L/usr/lib"); + + const link_libcpp = false; + if (link_libcpp) { + arg_builder.add("-lstdc++"); + } + + const link_libc = true; + + const dynamic_linker = true; + if (dynamic_linker) { + arg_builder.add("-dynamic-linker"); + arg_builder.add("/usr/lib/ld-linux-x86-64.so.2"); + } + + if (link_libc) { + arg_builder.add("/usr/lib/Scrt1.o"); + arg_builder.add("-lc"); + } + + const lld_args = arg_builder.flush(); + const lld_result = api.lld_elf_link(lld_args.ptr, lld_args.len, true, false); + const success = lld_result.success and lld_result.stderr.length == 0; + if (!success) { + if (lld_result.stdout.length != 0) { + lib.print_string(lld_result.stdout.to_slice() orelse unreachable); + } + + if (lld_result.stderr.length != 0) { + lib.print_string(lld_result.stderr.to_slice() orelse unreachable); + } + lib.libc.exit(1); + } } diff --git a/src/llvm.cpp b/src/llvm.cpp index abf16ba..db8b8a6 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -1,5 +1,13 @@ #include +typedef uint8_t u8; +typedef uint16_t u16; +typedef uint32_t u32; +typedef uint64_t u64; + +#define EXPORT extern "C" +#define fn static + #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/Verifier.h" @@ -21,13 +29,8 @@ #include "llvm/Support/FileSystem.h" -#define EXPORT extern "C" -#define fn static +#include "lld/Common/Driver.h" -typedef uint8_t u8; -typedef uint16_t u16; -typedef uint32_t u32; -typedef uint64_t u64; using namespace llvm; @@ -905,3 +908,75 @@ EXPORT BBLLVMCodeGenerationPipelineResult llvm_module_run_code_generation_pipeli return BBLLVMCodeGenerationPipelineResult::success; } + +struct LLDResult +{ + BBLLVMString stdout_string; + BBLLVMString stderr_string; + bool success; +}; + +#define lld_api_args() const char** argument_pointer, u64 argument_count, bool exit_early, bool disable_output +#define lld_api_function_decl(link_name) LLDResult lld_ ## link_name ## _link(lld_api_args()) +#define lld_api_function_signature(name) bool name(llvm::ArrayRef args, llvm::raw_ostream &stdoutOS, llvm::raw_ostream &stderrOS, bool exitEarly, bool disableOutput) + +#define lld_link_decl(link_name) \ +namespace link_name \ +{\ + lld_api_function_signature(link);\ +} + +typedef lld_api_function_signature(LinkerFunction); + +namespace lld +{ + lld_link_decl(coff); + lld_link_decl(elf); + lld_link_decl(mingw); + lld_link_decl(macho); + lld_link_decl(wasm); +} + +fn LLDResult lld_api_generic(lld_api_args(), LinkerFunction linker_function) +{ + LLDResult result = {}; + auto arguments = llvm::ArrayRef(argument_pointer, argument_count); + + std::string stdout_string; + llvm::raw_string_ostream stdout_stream(stdout_string); + + std::string stderr_string; + llvm::raw_string_ostream stderr_stream(stderr_string); + + result.success = linker_function(arguments, stdout_stream, stderr_stream, exit_early, disable_output); + + auto stdout_length = stdout_string.length(); + if (stdout_length) + { + auto* stdout_pointer = new char[stdout_length]; + memcpy(stdout_pointer, stdout_string.data(), stdout_length); + result.stdout_string = { stdout_pointer, stdout_length }; + } + + auto stderr_length = stderr_string.length(); + if (stderr_length) + { + auto* stderr_pointer = new char[stderr_length]; + memcpy(stderr_pointer, stderr_string.data(), stderr_length); + result.stderr_string = { stderr_pointer, stderr_length }; + } + + return result; +} + +#define lld_api_function_impl(link_name) \ +EXPORT lld_api_function_decl(link_name)\ +{\ + return lld_api_generic(argument_pointer, argument_count, exit_early, disable_output, lld::link_name::link);\ +} + +lld_api_function_impl(coff) +lld_api_function_impl(elf) +lld_api_function_impl(mingw) +lld_api_function_impl(macho) +lld_api_function_impl(wasm) diff --git a/src/llvm_api.zig b/src/llvm_api.zig index 6ceb27e..403ae44 100644 --- a/src/llvm_api.zig +++ b/src/llvm_api.zig @@ -1,4 +1,5 @@ const llvm = @import("LLVM.zig"); +const lld = llvm.lld; const Bool = c_int; @@ -118,3 +119,7 @@ pub fn get_initializer(comptime llvm_arch: llvm.Architecture) type { } }; } + +// LLD + +pub extern fn lld_elf_link(argument_pointer: [*:null]const ?[*:0]const u8, argument_length: u64, exit_early: bool, disable_output: bool) lld.Result;