| author | |
| committer | |
| log | 291c08f7b0ea4e333c37a0ac378176891f255fa0 |
| tree | ee9571bf196c1fce5ec9d298dffc103c66b4d3ab |
| parent | 87d8cb19e4eed905b93d39554ea9a2a1012f6668 |
| parent | 03ddb42b8bb96815c1bb4b857ffdfb94191ab861 |
| signature |
82 files changed, 1142 insertions(+), 465 deletions(-)
build.zig+1| ... | @@ -489,6 +489,7 @@ pub fn build(b: *Builder) !void { | ... | @@ -489,6 +489,7 @@ pub fn build(b: *Builder) !void { |
| 489 | 489 | ||
| 490 | toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); | 490 | toolchain_step.dependOn(tests.addCompareOutputTests(b, test_filter, modes)); |
| 491 | toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes, skip_non_native, enable_macos_sdk, target)); | 491 | toolchain_step.dependOn(tests.addStandaloneTests(b, test_filter, modes, skip_non_native, enable_macos_sdk, target)); |
| 492 | toolchain_step.dependOn(tests.addLinkTests(b, test_filter, modes, enable_macos_sdk)); | ||
| 492 | toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); | 493 | toolchain_step.dependOn(tests.addStackTraceTests(b, test_filter, modes)); |
| 493 | toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes)); | 494 | toolchain_step.dependOn(tests.addCliTests(b, test_filter, modes)); |
| 494 | toolchain_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); | 495 | toolchain_step.dependOn(tests.addAssembleAndLinkTests(b, test_filter, modes)); |
ci/azure/macos_script+1| ... | @@ -76,6 +76,7 @@ release/bin/zig build test-run-translated-c -Denable-macos-sdk | ... | @@ -76,6 +76,7 @@ release/bin/zig build test-run-translated-c -Denable-macos-sdk |
| 76 | release/bin/zig build docs -Denable-macos-sdk | 76 | release/bin/zig build docs -Denable-macos-sdk |
| 77 | release/bin/zig build test-fmt -Denable-macos-sdk | 77 | release/bin/zig build test-fmt -Denable-macos-sdk |
| 78 | release/bin/zig build test-cases -Denable-macos-sdk -Dsingle-threaded | 78 | release/bin/zig build test-cases -Denable-macos-sdk -Dsingle-threaded |
| 79 | release/bin/zig build test-link -Denable-macos-sdk | ||
| 79 | 80 | ||
| 80 | if [ "${BUILD_REASON}" != "PullRequest" ]; then | 81 | if [ "${BUILD_REASON}" != "PullRequest" ]; then |
| 81 | mv ../LICENSE release/ | 82 | mv ../LICENSE release/ |
lib/std/build.zig+13| ... | @@ -24,6 +24,7 @@ pub const TranslateCStep = @import("build/TranslateCStep.zig"); | ... | @@ -24,6 +24,7 @@ pub const TranslateCStep = @import("build/TranslateCStep.zig"); |
| 24 | pub const WriteFileStep = @import("build/WriteFileStep.zig"); | 24 | pub const WriteFileStep = @import("build/WriteFileStep.zig"); |
| 25 | pub const RunStep = @import("build/RunStep.zig"); | 25 | pub const RunStep = @import("build/RunStep.zig"); |
| 26 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); | 26 | pub const CheckFileStep = @import("build/CheckFileStep.zig"); |
| 27 | pub const CheckObjectStep = @import("build/CheckObjectStep.zig"); | ||
| 27 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); | 28 | pub const InstallRawStep = @import("build/InstallRawStep.zig"); |
| 28 | pub const OptionsStep = @import("build/OptionsStep.zig"); | 29 | pub const OptionsStep = @import("build/OptionsStep.zig"); |
| 29 | 30 | ||
| ... | @@ -1582,6 +1583,9 @@ pub const LibExeObjStep = struct { | ... | @@ -1582,6 +1583,9 @@ pub const LibExeObjStep = struct { |
| 1582 | /// (Darwin) Path to entitlements file | 1583 | /// (Darwin) Path to entitlements file |
| 1583 | entitlements: ?[]const u8 = null, | 1584 | entitlements: ?[]const u8 = null, |
| 1584 | 1585 | ||
| 1586 | /// (Darwin) Size of the pagezero segment. | ||
| 1587 | pagezero_size: ?u64 = null, | ||
| 1588 | |||
| 1585 | /// Position Independent Code | 1589 | /// Position Independent Code |
| 1586 | force_pic: ?bool = null, | 1590 | force_pic: ?bool = null, |
| 1587 | 1591 | ||
| ... | @@ -1861,6 +1865,10 @@ pub const LibExeObjStep = struct { | ... | @@ -1861,6 +1865,10 @@ pub const LibExeObjStep = struct { |
| 1861 | return run_step; | 1865 | return run_step; |
| 1862 | } | 1866 | } |
| 1863 | 1867 | ||
| 1868 | pub fn checkObject(self: *LibExeObjStep, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | ||
| 1869 | return CheckObjectStep.create(self.builder, self.getOutputSource(), obj_format); | ||
| 1870 | } | ||
| 1871 | |||
| 1864 | pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { | 1872 | pub fn setLinkerScriptPath(self: *LibExeObjStep, source: FileSource) void { |
| 1865 | self.linker_script = source.dupe(self.builder); | 1873 | self.linker_script = source.dupe(self.builder); |
| 1866 | source.addStepDependencies(&self.step); | 1874 | source.addStepDependencies(&self.step); |
| ... | @@ -2638,6 +2646,10 @@ pub const LibExeObjStep = struct { | ... | @@ -2638,6 +2646,10 @@ pub const LibExeObjStep = struct { |
| 2638 | if (self.entitlements) |entitlements| { | 2646 | if (self.entitlements) |entitlements| { |
| 2639 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); | 2647 | try zig_args.appendSlice(&[_][]const u8{ "--entitlements", entitlements }); |
| 2640 | } | 2648 | } |
| 2649 | if (self.pagezero_size) |pagezero_size| { | ||
| 2650 | const size = try std.fmt.allocPrint(builder.allocator, "{x}", .{pagezero_size}); | ||
| 2651 | try zig_args.appendSlice(&[_][]const u8{ "-pagezero_size", size }); | ||
| 2652 | } | ||
| 2641 | 2653 | ||
| 2642 | if (self.bundle_compiler_rt) |x| { | 2654 | if (self.bundle_compiler_rt) |x| { |
| 2643 | if (x) { | 2655 | if (x) { |
| ... | @@ -3443,6 +3455,7 @@ pub const Step = struct { | ... | @@ -3443,6 +3455,7 @@ pub const Step = struct { |
| 3443 | write_file, | 3455 | write_file, |
| 3444 | run, | 3456 | run, |
| 3445 | check_file, | 3457 | check_file, |
| 3458 | check_object, | ||
| 3446 | install_raw, | 3459 | install_raw, |
| 3447 | options, | 3460 | options, |
| 3448 | custom, | 3461 | custom, |
lib/std/build/CheckObjectStep.zig created+392| ... | @@ -0,0 +1,392 @@ | ||
| 1 | const std = @import("../std.zig"); | ||
| 2 | const assert = std.debug.assert; | ||
| 3 | const build = std.build; | ||
| 4 | const fs = std.fs; | ||
| 5 | const macho = std.macho; | ||
| 6 | const mem = std.mem; | ||
| 7 | const testing = std.testing; | ||
| 8 | |||
| 9 | const CheckObjectStep = @This(); | ||
| 10 | |||
| 11 | const Allocator = mem.Allocator; | ||
| 12 | const Builder = build.Builder; | ||
| 13 | const Step = build.Step; | ||
| 14 | |||
| 15 | pub const base_id = .check_obj; | ||
| 16 | |||
| 17 | step: Step, | ||
| 18 | builder: *Builder, | ||
| 19 | source: build.FileSource, | ||
| 20 | max_bytes: usize = 20 * 1024 * 1024, | ||
| 21 | checks: std.ArrayList(Check), | ||
| 22 | dump_symtab: bool = false, | ||
| 23 | obj_format: std.Target.ObjectFormat, | ||
| 24 | |||
| 25 | pub fn create(builder: *Builder, source: build.FileSource, obj_format: std.Target.ObjectFormat) *CheckObjectStep { | ||
| 26 | const gpa = builder.allocator; | ||
| 27 | const self = gpa.create(CheckObjectStep) catch unreachable; | ||
| 28 | self.* = .{ | ||
| 29 | .builder = builder, | ||
| 30 | .step = Step.init(.check_file, "CheckObject", gpa, make), | ||
| 31 | .source = source.dupe(builder), | ||
| 32 | .checks = std.ArrayList(Check).init(gpa), | ||
| 33 | .obj_format = obj_format, | ||
| 34 | }; | ||
| 35 | self.source.addStepDependencies(&self.step); | ||
| 36 | return self; | ||
| 37 | } | ||
| 38 | |||
| 39 | const Action = union(enum) { | ||
| 40 | match: MatchAction, | ||
| 41 | compute_eq: ComputeEqAction, | ||
| 42 | }; | ||
| 43 | |||
| 44 | /// MatchAction is the main building block of standard matchers with optional eat-all token `{*}` | ||
| 45 | /// and extractors by name such as `{n_value}`. Please note this action is very simplistic in nature | ||
| 46 | /// i.e., it won't really handle edge cases/nontrivial examples. But given that we do want to use | ||
| 47 | /// it mainly to test the output of our object format parser-dumpers when testing the linkers, etc. | ||
| 48 | /// it should be plenty useful in its current form. | ||
| 49 | const MatchAction = struct { | ||
| 50 | needle: []const u8, | ||
| 51 | |||
| 52 | /// Will return true if the `needle` was found in the `haystack`. | ||
| 53 | /// Some examples include: | ||
| 54 | /// | ||
| 55 | /// LC 0 => will match in its entirety | ||
| 56 | /// vmaddr {vmaddr} => will match `vmaddr` and then extract the following value as u64 | ||
| 57 | /// and save under `vmaddr` global name (see `global_vars` param) | ||
| 58 | /// name {*}libobjc{*}.dylib => will match `name` followed by a token which contains `libobjc` and `.dylib` | ||
| 59 | /// in that order with other letters in between | ||
| 60 | fn match(act: MatchAction, haystack: []const u8, global_vars: anytype) !bool { | ||
| 61 | var hay_it = mem.tokenize(u8, mem.trim(u8, haystack, " "), " "); | ||
| 62 | var needle_it = mem.tokenize(u8, mem.trim(u8, act.needle, " "), " "); | ||
| 63 | |||
| 64 | while (needle_it.next()) |needle_tok| { | ||
| 65 | const hay_tok = hay_it.next() orelse return false; | ||
| 66 | |||
| 67 | if (mem.indexOf(u8, needle_tok, "{*}")) |index| { | ||
| 68 | // We have fuzzy matchers within the search pattern, so we match substrings. | ||
| 69 | var start = index; | ||
| 70 | var n_tok = needle_tok; | ||
| 71 | var h_tok = hay_tok; | ||
| 72 | while (true) { | ||
| 73 | n_tok = n_tok[start + 3 ..]; | ||
| 74 | const inner = if (mem.indexOf(u8, n_tok, "{*}")) |sub_end| | ||
| 75 | n_tok[0..sub_end] | ||
| 76 | else | ||
| 77 | n_tok; | ||
| 78 | if (mem.indexOf(u8, h_tok, inner) == null) return false; | ||
| 79 | start = mem.indexOf(u8, n_tok, "{*}") orelse break; | ||
| 80 | } | ||
| 81 | } else if (mem.startsWith(u8, needle_tok, "{")) { | ||
| 82 | const closing_brace = mem.indexOf(u8, needle_tok, "}") orelse return error.MissingClosingBrace; | ||
| 83 | if (closing_brace != needle_tok.len - 1) return error.ClosingBraceNotLast; | ||
| 84 | |||
| 85 | const name = needle_tok[1..closing_brace]; | ||
| 86 | if (name.len == 0) return error.MissingBraceValue; | ||
| 87 | const value = try std.fmt.parseInt(u64, hay_tok, 16); | ||
| 88 | try global_vars.putNoClobber(name, value); | ||
| 89 | } else { | ||
| 90 | if (!mem.eql(u8, hay_tok, needle_tok)) return false; | ||
| 91 | } | ||
| 92 | } | ||
| 93 | |||
| 94 | return true; | ||
| 95 | } | ||
| 96 | }; | ||
| 97 | |||
| 98 | /// ComputeEqAction can be used to perform an operation on the extracted global variables | ||
| 99 | /// using the MatchAction. It currently only supports an addition. The operation is required | ||
| 100 | /// to be specified in Reverse Polish Notation to ease in operator-precedence parsing (well, | ||
| 101 | /// to avoid any parsing really). | ||
| 102 | /// For example, if the two extracted values were saved as `vmaddr` and `entryoff` respectively | ||
| 103 | /// they could then be added with this simple program `vmaddr entryoff +`. | ||
| 104 | const ComputeEqAction = struct { | ||
| 105 | expected: []const u8, | ||
| 106 | var_stack: std.ArrayList([]const u8), | ||
| 107 | op_stack: std.ArrayList(Op), | ||
| 108 | |||
| 109 | const Op = enum { | ||
| 110 | add, | ||
| 111 | }; | ||
| 112 | }; | ||
| 113 | |||
| 114 | const Check = struct { | ||
| 115 | builder: *Builder, | ||
| 116 | actions: std.ArrayList(Action), | ||
| 117 | |||
| 118 | fn create(b: *Builder) Check { | ||
| 119 | return .{ | ||
| 120 | .builder = b, | ||
| 121 | .actions = std.ArrayList(Action).init(b.allocator), | ||
| 122 | }; | ||
| 123 | } | ||
| 124 | |||
| 125 | fn match(self: *Check, needle: []const u8) void { | ||
| 126 | self.actions.append(.{ | ||
| 127 | .match = .{ .needle = self.builder.dupe(needle) }, | ||
| 128 | }) catch unreachable; | ||
| 129 | } | ||
| 130 | |||
| 131 | fn computeEq(self: *Check, act: ComputeEqAction) void { | ||
| 132 | self.actions.append(.{ | ||
| 133 | .compute_eq = act, | ||
| 134 | }) catch unreachable; | ||
| 135 | } | ||
| 136 | }; | ||
| 137 | |||
| 138 | /// Creates a new sequence of actions with `phrase` as the first anchor searched phrase. | ||
| 139 | pub fn checkStart(self: *CheckObjectStep, phrase: []const u8) void { | ||
| 140 | var new_check = Check.create(self.builder); | ||
| 141 | new_check.match(phrase); | ||
| 142 | self.checks.append(new_check) catch unreachable; | ||
| 143 | } | ||
| 144 | |||
| 145 | /// Adds another searched phrase to the latest created Check with `CheckObjectStep.checkStart(...)`. | ||
| 146 | /// Asserts at least one check already exists. | ||
| 147 | pub fn checkNext(self: *CheckObjectStep, phrase: []const u8) void { | ||
| 148 | assert(self.checks.items.len > 0); | ||
| 149 | const last = &self.checks.items[self.checks.items.len - 1]; | ||
| 150 | last.match(phrase); | ||
| 151 | } | ||
| 152 | |||
| 153 | /// Creates a new check checking specifically symbol table parsed and dumped from the object | ||
| 154 | /// file. | ||
| 155 | /// Issuing this check will force parsing and dumping of the symbol table. | ||
| 156 | pub fn checkInSymtab(self: *CheckObjectStep) void { | ||
| 157 | self.dump_symtab = true; | ||
| 158 | const symtab_label = switch (self.obj_format) { | ||
| 159 | .macho => MachODumper.symtab_label, | ||
| 160 | else => @panic("TODO other parsers"), | ||
| 161 | }; | ||
| 162 | self.checkStart(symtab_label); | ||
| 163 | } | ||
| 164 | |||
| 165 | /// Creates a new standalone, singular check which allows running simple binary operations | ||
| 166 | /// on the extracted variables. It will then compare the reduced program with the value of | ||
| 167 | /// the expected variable. | ||
| 168 | pub fn checkComputeEq(self: *CheckObjectStep, program: []const u8, expected: []const u8) void { | ||
| 169 | const gpa = self.builder.allocator; | ||
| 170 | var ca = ComputeEqAction{ | ||
| 171 | .expected = expected, | ||
| 172 | .var_stack = std.ArrayList([]const u8).init(gpa), | ||
| 173 | .op_stack = std.ArrayList(ComputeEqAction.Op).init(gpa), | ||
| 174 | }; | ||
| 175 | |||
| 176 | var it = mem.tokenize(u8, program, " "); | ||
| 177 | while (it.next()) |next| { | ||
| 178 | if (mem.eql(u8, next, "+")) { | ||
| 179 | ca.op_stack.append(.add) catch unreachable; | ||
| 180 | } else { | ||
| 181 | ca.var_stack.append(self.builder.dupe(next)) catch unreachable; | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | var new_check = Check.create(self.builder); | ||
| 186 | new_check.computeEq(ca); | ||
| 187 | self.checks.append(new_check) catch unreachable; | ||
| 188 | } | ||
| 189 | |||
| 190 | fn make(step: *Step) !void { | ||
| 191 | const self = @fieldParentPtr(CheckObjectStep, "step", step); | ||
| 192 | |||
| 193 | const gpa = self.builder.allocator; | ||
| 194 | const src_path = self.source.getPath(self.builder); | ||
| 195 | const contents = try fs.cwd().readFileAlloc(gpa, src_path, self.max_bytes); | ||
| 196 | |||
| 197 | const output = switch (self.obj_format) { | ||
| 198 | .macho => try MachODumper.parseAndDump(contents, .{ | ||
| 199 | .gpa = gpa, | ||
| 200 | .dump_symtab = self.dump_symtab, | ||
| 201 | }), | ||
| 202 | .elf => @panic("TODO elf parser"), | ||
| 203 | .coff => @panic("TODO coff parser"), | ||
| 204 | .wasm => @panic("TODO wasm parser"), | ||
| 205 | else => unreachable, | ||
| 206 | }; | ||
| 207 | |||
| 208 | var vars = std.StringHashMap(u64).init(gpa); | ||
| 209 | |||
| 210 | for (self.checks.items) |chk| { | ||
| 211 | var it = mem.tokenize(u8, output, "\r\n"); | ||
| 212 | for (chk.actions.items) |act| { | ||
| 213 | switch (act) { | ||
| 214 | .match => |match_act| { | ||
| 215 | while (it.next()) |line| { | ||
| 216 | if (try match_act.match(line, &vars)) break; | ||
| 217 | } else { | ||
| 218 | std.debug.print( | ||
| 219 | \\ | ||
| 220 | \\========= Expected to find: ========================== | ||
| 221 | \\{s} | ||
| 222 | \\========= But parsed file does not contain it: ======= | ||
| 223 | \\{s} | ||
| 224 | \\ | ||
| 225 | , .{ match_act.needle, output }); | ||
| 226 | return error.TestFailed; | ||
| 227 | } | ||
| 228 | }, | ||
| 229 | .compute_eq => |c_eq| { | ||
| 230 | var values = std.ArrayList(u64).init(gpa); | ||
| 231 | try values.ensureTotalCapacity(c_eq.var_stack.items.len); | ||
| 232 | for (c_eq.var_stack.items) |vv| { | ||
| 233 | const val = vars.get(vv) orelse { | ||
| 234 | std.debug.print( | ||
| 235 | \\ | ||
| 236 | \\========= Variable was not extracted: =========== | ||
| 237 | \\{s} | ||
| 238 | \\========= From parsed file: ===================== | ||
| 239 | \\{s} | ||
| 240 | \\ | ||
| 241 | , .{ vv, output }); | ||
| 242 | return error.TestFailed; | ||
| 243 | }; | ||
| 244 | values.appendAssumeCapacity(val); | ||
| 245 | } | ||
| 246 | |||
| 247 | var op_i: usize = 1; | ||
| 248 | var reduced: u64 = values.items[0]; | ||
| 249 | for (c_eq.op_stack.items) |op| { | ||
| 250 | const other = values.items[op_i]; | ||
| 251 | switch (op) { | ||
| 252 | .add => { | ||
| 253 | reduced += other; | ||
| 254 | }, | ||
| 255 | } | ||
| 256 | } | ||
| 257 | |||
| 258 | const expected = vars.get(c_eq.expected) orelse { | ||
| 259 | std.debug.print( | ||
| 260 | \\ | ||
| 261 | \\========= Variable was not extracted: =========== | ||
| 262 | \\{s} | ||
| 263 | \\========= From parsed file: ===================== | ||
| 264 | \\{s} | ||
| 265 | \\ | ||
| 266 | , .{ c_eq.expected, output }); | ||
| 267 | return error.TestFailed; | ||
| 268 | }; | ||
| 269 | try testing.expectEqual(reduced, expected); | ||
| 270 | }, | ||
| 271 | } | ||
| 272 | } | ||
| 273 | } | ||
| 274 | } | ||
| 275 | |||
| 276 | const Opts = struct { | ||
| 277 | gpa: ?Allocator = null, | ||
| 278 | dump_symtab: bool = false, | ||
| 279 | }; | ||
| 280 | |||
| 281 | const MachODumper = struct { | ||
| 282 | const symtab_label = "symtab"; | ||
| 283 | |||
| 284 | fn parseAndDump(bytes: []const u8, opts: Opts) ![]const u8 { | ||
| 285 | const gpa = opts.gpa orelse unreachable; // MachO dumper requires an allocator | ||
| 286 | var stream = std.io.fixedBufferStream(bytes); | ||
| 287 | const reader = stream.reader(); | ||
| 288 | |||
| 289 | const hdr = try reader.readStruct(macho.mach_header_64); | ||
| 290 | if (hdr.magic != macho.MH_MAGIC_64) { | ||
| 291 | return error.InvalidMagicNumber; | ||
| 292 | } | ||
| 293 | |||
| 294 | var output = std.ArrayList(u8).init(gpa); | ||
| 295 | const writer = output.writer(); | ||
| 296 | |||
| 297 | var symtab_cmd: ?macho.symtab_command = null; | ||
| 298 | var i: u16 = 0; | ||
| 299 | while (i < hdr.ncmds) : (i += 1) { | ||
| 300 | var cmd = try macho.LoadCommand.read(gpa, reader); | ||
| 301 | |||
| 302 | if (opts.dump_symtab and cmd.cmd() == .SYMTAB) { | ||
| 303 | symtab_cmd = cmd.symtab; | ||
| 304 | } | ||
| 305 | |||
| 306 | try dumpLoadCommand(cmd, i, writer); | ||
| 307 | try writer.writeByte('\n'); | ||
| 308 | } | ||
| 309 | |||
| 310 | if (symtab_cmd) |cmd| { | ||
| 311 | try writer.writeAll(symtab_label ++ "\n"); | ||
| 312 | const strtab = bytes[cmd.stroff..][0..cmd.strsize]; | ||
| 313 | const raw_symtab = bytes[cmd.symoff..][0 .. cmd.nsyms * @sizeOf(macho.nlist_64)]; | ||
| 314 | const symtab = mem.bytesAsSlice(macho.nlist_64, raw_symtab); | ||
| 315 | |||
| 316 | for (symtab) |sym| { | ||
| 317 | if (sym.stab()) continue; | ||
| 318 | const sym_name = mem.sliceTo(@ptrCast([*:0]const u8, strtab.ptr + sym.n_strx), 0); | ||
| 319 | try writer.print("{s} {x}\n", .{ sym_name, sym.n_value }); | ||
| 320 | } | ||
| 321 | } | ||
| 322 | |||
| 323 | return output.toOwnedSlice(); | ||
| 324 | } | ||
| 325 | |||
| 326 | fn dumpLoadCommand(lc: macho.LoadCommand, index: u16, writer: anytype) !void { | ||
| 327 | // print header first | ||
| 328 | try writer.print( | ||
| 329 | \\LC {d} | ||
| 330 | \\cmd {s} | ||
| 331 | \\cmdsize {d} | ||
| 332 | , .{ index, @tagName(lc.cmd()), lc.cmdsize() }); | ||
| 333 | |||
| 334 | switch (lc.cmd()) { | ||
| 335 | .SEGMENT_64 => { | ||
| 336 | // TODO dump section headers | ||
| 337 | const seg = lc.segment.inner; | ||
| 338 | try writer.writeByte('\n'); | ||
| 339 | try writer.print( | ||
| 340 | \\segname {s} | ||
| 341 | \\vmaddr {x} | ||
| 342 | \\vmsize {x} | ||
| 343 | \\fileoff {x} | ||
| 344 | \\filesz {x} | ||
| 345 | , .{ | ||
| 346 | seg.segName(), | ||
| 347 | seg.vmaddr, | ||
| 348 | seg.vmsize, | ||
| 349 | seg.fileoff, | ||
| 350 | seg.filesize, | ||
| 351 | }); | ||
| 352 | }, | ||
| 353 | |||
| 354 | .ID_DYLIB, | ||
| 355 | .LOAD_DYLIB, | ||
| 356 | => { | ||
| 357 | const dylib = lc.dylib.inner.dylib; | ||
| 358 | try writer.writeByte('\n'); | ||
| 359 | try writer.print( | ||
| 360 | \\name {s} | ||
| 361 | \\timestamp {d} | ||
| 362 | \\current version {x} | ||
| 363 | \\compatibility version {x} | ||
| 364 | , .{ | ||
| 365 | mem.sliceTo(lc.dylib.data, 0), | ||
| 366 | dylib.timestamp, | ||
| 367 | dylib.current_version, | ||
| 368 | dylib.compatibility_version, | ||
| 369 | }); | ||
| 370 | }, | ||
| 371 | |||
| 372 | .MAIN => { | ||
| 373 | try writer.writeByte('\n'); | ||
| 374 | try writer.print( | ||
| 375 | \\entryoff {x} | ||
| 376 | \\stacksize {x} | ||
| 377 | , .{ lc.main.entryoff, lc.main.stacksize }); | ||
| 378 | }, | ||
| 379 | |||
| 380 | .RPATH => { | ||
| 381 | try writer.writeByte('\n'); | ||
| 382 | try writer.print( | ||
| 383 | \\path {s} | ||
| 384 | , .{ | ||
| 385 | mem.sliceTo(lc.rpath.data, 0), | ||
| 386 | }); | ||
| 387 | }, | ||
| 388 | |||
| 389 | else => {}, | ||
| 390 | } | ||
| 391 | } | ||
| 392 | }; | ||
lib/std/build/RunStep.zig+1| ... | @@ -149,6 +149,7 @@ fn make(step: *Step) !void { | ... | @@ -149,6 +149,7 @@ fn make(step: *Step) !void { |
| 149 | const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; | 149 | const cwd = if (self.cwd) |cwd| self.builder.pathFromRoot(cwd) else self.builder.build_root; |
| 150 | 150 | ||
| 151 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); | 151 | var argv_list = ArrayList([]const u8).init(self.builder.allocator); |
| 152 | |||
| 152 | for (self.argv.items) |arg| { | 153 | for (self.argv.items) |arg| { |
| 153 | switch (arg) { | 154 | switch (arg) { |
| 154 | .bytes => |bytes| try argv_list.append(bytes), | 155 | .bytes => |bytes| try argv_list.append(bytes), |
src/link/MachO.zig+92-72| ... | @@ -934,6 +934,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No | ... | @@ -934,6 +934,11 @@ pub fn flushModule(self: *MachO, comp: *Compilation, prog_node: *std.Progress.No |
| 934 | try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size})); | 934 | try argv.append(try std.fmt.allocPrint(arena, "0x{x}", .{pagezero_size})); |
| 935 | } | 935 | } |
| 936 | 936 | ||
| 937 | if (self.base.options.entry) |entry| { | ||
| 938 | try argv.append("-e"); | ||
| 939 | try argv.append(entry); | ||
| 940 | } | ||
| 941 | |||
| 937 | try argv.appendSlice(positionals.items); | 942 | try argv.appendSlice(positionals.items); |
| 938 | 943 | ||
| 939 | try argv.append("-o"); | 944 | try argv.append("-o"); |
| ... | @@ -3371,13 +3376,12 @@ fn addCodeSignatureLC(self: *MachO) !void { | ... | @@ -3371,13 +3376,12 @@ fn addCodeSignatureLC(self: *MachO) !void { |
| 3371 | fn setEntryPoint(self: *MachO) !void { | 3376 | fn setEntryPoint(self: *MachO) !void { |
| 3372 | if (self.base.options.output_mode != .Exe) return; | 3377 | if (self.base.options.output_mode != .Exe) return; |
| 3373 | 3378 | ||
| 3374 | // TODO we should respect the -entry flag passed in by the user to set a custom | ||
| 3375 | // entrypoint. For now, assume default of `_main`. | ||
| 3376 | const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; | 3379 | const seg = self.load_commands.items[self.text_segment_cmd_index.?].segment; |
| 3377 | const n_strx = self.strtab_dir.getKeyAdapted(@as([]const u8, "_main"), StringIndexAdapter{ | 3380 | const entry_name = self.base.options.entry orelse "_main"; |
| 3381 | const n_strx = self.strtab_dir.getKeyAdapted(entry_name, StringIndexAdapter{ | ||
| 3378 | .bytes = &self.strtab, | 3382 | .bytes = &self.strtab, |
| 3379 | }) orelse { | 3383 | }) orelse { |
| 3380 | log.err("'_main' export not found", .{}); | 3384 | log.err("entrypoint '{s}' not found", .{entry_name}); |
| 3381 | return error.MissingMainEntrypoint; | 3385 | return error.MissingMainEntrypoint; |
| 3382 | }; | 3386 | }; |
| 3383 | const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; | 3387 | const resolv = self.symbol_resolver.get(n_strx) orelse unreachable; |
| ... | @@ -5711,28 +5715,46 @@ fn writeDyldInfoData(self: *MachO) !void { | ... | @@ -5711,28 +5715,46 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 5711 | 5715 | ||
| 5712 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; | 5716 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 5713 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only; | 5717 | const dyld_info = &self.load_commands.items[self.dyld_info_cmd_index.?].dyld_info_only; |
| 5718 | |||
| 5719 | const rebase_off = mem.alignForwardGeneric(u64, seg.inner.fileoff, @alignOf(u64)); | ||
| 5714 | const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items); | 5720 | const rebase_size = try bind.rebaseInfoSize(rebase_pointers.items); |
| 5715 | const bind_size = try bind.bindInfoSize(bind_pointers.items); | 5721 | dyld_info.rebase_off = @intCast(u32, rebase_off); |
| 5716 | const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items); | 5722 | dyld_info.rebase_size = @intCast(u32, rebase_size); |
| 5717 | const export_size = trie.size; | 5723 | log.debug("writing rebase info from 0x{x} to 0x{x}", .{ |
| 5724 | dyld_info.rebase_off, | ||
| 5725 | dyld_info.rebase_off + dyld_info.rebase_size, | ||
| 5726 | }); | ||
| 5718 | 5727 | ||
| 5719 | dyld_info.rebase_off = @intCast(u32, seg.inner.fileoff); | 5728 | const bind_off = mem.alignForwardGeneric(u64, dyld_info.rebase_off + dyld_info.rebase_size, @alignOf(u64)); |
| 5720 | dyld_info.rebase_size = @intCast(u32, mem.alignForwardGeneric(u64, rebase_size, @alignOf(u64))); | 5729 | const bind_size = try bind.bindInfoSize(bind_pointers.items); |
| 5721 | seg.inner.filesize += dyld_info.rebase_size; | 5730 | dyld_info.bind_off = @intCast(u32, bind_off); |
| 5731 | dyld_info.bind_size = @intCast(u32, bind_size); | ||
| 5732 | log.debug("writing bind info from 0x{x} to 0x{x}", .{ | ||
| 5733 | dyld_info.bind_off, | ||
| 5734 | dyld_info.bind_off + dyld_info.bind_size, | ||
| 5735 | }); | ||
| 5722 | 5736 | ||
| 5723 | dyld_info.bind_off = dyld_info.rebase_off + dyld_info.rebase_size; | 5737 | const lazy_bind_off = mem.alignForwardGeneric(u64, dyld_info.bind_off + dyld_info.bind_size, @alignOf(u64)); |
| 5724 | dyld_info.bind_size = @intCast(u32, mem.alignForwardGeneric(u64, bind_size, @alignOf(u64))); | 5738 | const lazy_bind_size = try bind.lazyBindInfoSize(lazy_bind_pointers.items); |
| 5725 | seg.inner.filesize += dyld_info.bind_size; | 5739 | dyld_info.lazy_bind_off = @intCast(u32, lazy_bind_off); |
| 5740 | dyld_info.lazy_bind_size = @intCast(u32, lazy_bind_size); | ||
| 5741 | log.debug("writing lazy bind info from 0x{x} to 0x{x}", .{ | ||
| 5742 | dyld_info.lazy_bind_off, | ||
| 5743 | dyld_info.lazy_bind_off + dyld_info.lazy_bind_size, | ||
| 5744 | }); | ||
| 5726 | 5745 | ||
| 5727 | dyld_info.lazy_bind_off = dyld_info.bind_off + dyld_info.bind_size; | 5746 | const export_off = mem.alignForwardGeneric(u64, dyld_info.lazy_bind_off + dyld_info.lazy_bind_size, @alignOf(u64)); |
| 5728 | dyld_info.lazy_bind_size = @intCast(u32, mem.alignForwardGeneric(u64, lazy_bind_size, @alignOf(u64))); | 5747 | const export_size = trie.size; |
| 5729 | seg.inner.filesize += dyld_info.lazy_bind_size; | 5748 | dyld_info.export_off = @intCast(u32, export_off); |
| 5749 | dyld_info.export_size = @intCast(u32, export_size); | ||
| 5750 | log.debug("writing export trie from 0x{x} to 0x{x}", .{ | ||
| 5751 | dyld_info.export_off, | ||
| 5752 | dyld_info.export_off + dyld_info.export_size, | ||
| 5753 | }); | ||
| 5730 | 5754 | ||
| 5731 | dyld_info.export_off = dyld_info.lazy_bind_off + dyld_info.lazy_bind_size; | 5755 | seg.inner.filesize = dyld_info.export_off + dyld_info.export_size - seg.inner.fileoff; |
| 5732 | dyld_info.export_size = @intCast(u32, mem.alignForwardGeneric(u64, export_size, @alignOf(u64))); | ||
| 5733 | seg.inner.filesize += dyld_info.export_size; | ||
| 5734 | 5756 | ||
| 5735 | const needed_size = dyld_info.rebase_size + dyld_info.bind_size + dyld_info.lazy_bind_size + dyld_info.export_size; | 5757 | const needed_size = dyld_info.export_off + dyld_info.export_size - dyld_info.rebase_off; |
| 5736 | var buffer = try self.base.allocator.alloc(u8, needed_size); | 5758 | var buffer = try self.base.allocator.alloc(u8, needed_size); |
| 5737 | defer self.base.allocator.free(buffer); | 5759 | defer self.base.allocator.free(buffer); |
| 5738 | mem.set(u8, buffer, 0); | 5760 | mem.set(u8, buffer, 0); |
| ... | @@ -5740,14 +5762,15 @@ fn writeDyldInfoData(self: *MachO) !void { | ... | @@ -5740,14 +5762,15 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 5740 | var stream = std.io.fixedBufferStream(buffer); | 5762 | var stream = std.io.fixedBufferStream(buffer); |
| 5741 | const writer = stream.writer(); | 5763 | const writer = stream.writer(); |
| 5742 | 5764 | ||
| 5765 | const base_off = dyld_info.rebase_off; | ||
| 5743 | try bind.writeRebaseInfo(rebase_pointers.items, writer); | 5766 | try bind.writeRebaseInfo(rebase_pointers.items, writer); |
| 5744 | try stream.seekBy(@intCast(i64, dyld_info.rebase_size) - @intCast(i64, rebase_size)); | 5767 | try stream.seekTo(dyld_info.bind_off - base_off); |
| 5745 | 5768 | ||
| 5746 | try bind.writeBindInfo(bind_pointers.items, writer); | 5769 | try bind.writeBindInfo(bind_pointers.items, writer); |
| 5747 | try stream.seekBy(@intCast(i64, dyld_info.bind_size) - @intCast(i64, bind_size)); | 5770 | try stream.seekTo(dyld_info.lazy_bind_off - base_off); |
| 5748 | 5771 | ||
| 5749 | try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer); | 5772 | try bind.writeLazyBindInfo(lazy_bind_pointers.items, writer); |
| 5750 | try stream.seekBy(@intCast(i64, dyld_info.lazy_bind_size) - @intCast(i64, lazy_bind_size)); | 5773 | try stream.seekTo(dyld_info.export_off - base_off); |
| 5751 | 5774 | ||
| 5752 | _ = try trie.write(writer); | 5775 | _ = try trie.write(writer); |
| 5753 | 5776 | ||
| ... | @@ -5758,7 +5781,7 @@ fn writeDyldInfoData(self: *MachO) !void { | ... | @@ -5758,7 +5781,7 @@ fn writeDyldInfoData(self: *MachO) !void { |
| 5758 | 5781 | ||
| 5759 | try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); | 5782 | try self.base.file.?.pwriteAll(buffer, dyld_info.rebase_off); |
| 5760 | try self.populateLazyBindOffsetsInStubHelper( | 5783 | try self.populateLazyBindOffsetsInStubHelper( |
| 5761 | buffer[dyld_info.rebase_size + dyld_info.bind_size ..][0..dyld_info.lazy_bind_size], | 5784 | buffer[dyld_info.lazy_bind_off - base_off ..][0..dyld_info.lazy_bind_size], |
| 5762 | ); | 5785 | ); |
| 5763 | self.load_commands_dirty = true; | 5786 | self.load_commands_dirty = true; |
| 5764 | } | 5787 | } |
| ... | @@ -5928,32 +5951,31 @@ fn writeFunctionStarts(self: *MachO) !void { | ... | @@ -5928,32 +5951,31 @@ fn writeFunctionStarts(self: *MachO) !void { |
| 5928 | } else break; | 5951 | } else break; |
| 5929 | } | 5952 | } |
| 5930 | 5953 | ||
| 5931 | const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64)); | 5954 | var buffer = std.ArrayList(u8).init(self.base.allocator); |
| 5932 | var buffer = try self.base.allocator.alloc(u8, max_size); | 5955 | defer buffer.deinit(); |
| 5933 | defer self.base.allocator.free(buffer); | ||
| 5934 | mem.set(u8, buffer, 0); | ||
| 5935 | 5956 | ||
| 5936 | var stream = std.io.fixedBufferStream(buffer); | 5957 | const max_size = @intCast(usize, offsets.items.len * @sizeOf(u64)); |
| 5937 | const writer = stream.writer(); | 5958 | try buffer.ensureTotalCapacity(max_size); |
| 5938 | 5959 | ||
| 5939 | for (offsets.items) |offset| { | 5960 | for (offsets.items) |offset| { |
| 5940 | try std.leb.writeULEB128(writer, offset); | 5961 | try std.leb.writeULEB128(buffer.writer(), offset); |
| 5941 | } | 5962 | } |
| 5942 | 5963 | ||
| 5943 | const needed_size = @intCast(u32, mem.alignForwardGeneric(u64, stream.pos, @sizeOf(u64))); | ||
| 5944 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; | 5964 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 5945 | const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data; | 5965 | const fn_cmd = &self.load_commands.items[self.function_starts_cmd_index.?].linkedit_data; |
| 5946 | 5966 | ||
| 5947 | fn_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | 5967 | const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); |
| 5948 | fn_cmd.datasize = needed_size; | 5968 | const datasize = buffer.items.len; |
| 5949 | seg.inner.filesize += needed_size; | 5969 | fn_cmd.dataoff = @intCast(u32, dataoff); |
| 5970 | fn_cmd.datasize = @intCast(u32, datasize); | ||
| 5971 | seg.inner.filesize = fn_cmd.dataoff + fn_cmd.datasize - seg.inner.fileoff; | ||
| 5950 | 5972 | ||
| 5951 | log.debug("writing function starts info from 0x{x} to 0x{x}", .{ | 5973 | log.debug("writing function starts info from 0x{x} to 0x{x}", .{ |
| 5952 | fn_cmd.dataoff, | 5974 | fn_cmd.dataoff, |
| 5953 | fn_cmd.dataoff + fn_cmd.datasize, | 5975 | fn_cmd.dataoff + fn_cmd.datasize, |
| 5954 | }); | 5976 | }); |
| 5955 | 5977 | ||
| 5956 | try self.base.file.?.pwriteAll(buffer[0..needed_size], fn_cmd.dataoff); | 5978 | try self.base.file.?.pwriteAll(buffer.items, fn_cmd.dataoff); |
| 5957 | self.load_commands_dirty = true; | 5979 | self.load_commands_dirty = true; |
| 5958 | } | 5980 | } |
| 5959 | 5981 | ||
| ... | @@ -6001,11 +6023,12 @@ fn writeDices(self: *MachO) !void { | ... | @@ -6001,11 +6023,12 @@ fn writeDices(self: *MachO) !void { |
| 6001 | 6023 | ||
| 6002 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; | 6024 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 6003 | const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data; | 6025 | const dice_cmd = &self.load_commands.items[self.data_in_code_cmd_index.?].linkedit_data; |
| 6004 | const needed_size = @intCast(u32, buf.items.len); | ||
| 6005 | 6026 | ||
| 6006 | dice_cmd.dataoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | 6027 | const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); |
| 6007 | dice_cmd.datasize = needed_size; | 6028 | const datasize = buf.items.len; |
| 6008 | seg.inner.filesize += needed_size; | 6029 | dice_cmd.dataoff = @intCast(u32, dataoff); |
| 6030 | dice_cmd.datasize = @intCast(u32, datasize); | ||
| 6031 | seg.inner.filesize = dice_cmd.dataoff + dice_cmd.datasize - seg.inner.fileoff; | ||
| 6009 | 6032 | ||
| 6010 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ | 6033 | log.debug("writing data-in-code from 0x{x} to 0x{x}", .{ |
| 6011 | dice_cmd.dataoff, | 6034 | dice_cmd.dataoff, |
| ... | @@ -6022,7 +6045,8 @@ fn writeSymbolTable(self: *MachO) !void { | ... | @@ -6022,7 +6045,8 @@ fn writeSymbolTable(self: *MachO) !void { |
| 6022 | 6045 | ||
| 6023 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; | 6046 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 6024 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; | 6047 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; |
| 6025 | symtab.symoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | 6048 | const symoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(macho.nlist_64)); |
| 6049 | symtab.symoff = @intCast(u32, symoff); | ||
| 6026 | 6050 | ||
| 6027 | var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator); | 6051 | var locals = std.ArrayList(macho.nlist_64).init(self.base.allocator); |
| 6028 | defer locals.deinit(); | 6052 | defer locals.deinit(); |
| ... | @@ -6122,7 +6146,7 @@ fn writeSymbolTable(self: *MachO) !void { | ... | @@ -6122,7 +6146,7 @@ fn writeSymbolTable(self: *MachO) !void { |
| 6122 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); | 6146 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(undefs.items), undefs_off); |
| 6123 | 6147 | ||
| 6124 | symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs); | 6148 | symtab.nsyms = @intCast(u32, nlocals + nexports + nundefs); |
| 6125 | seg.inner.filesize += locals_size + exports_size + undefs_size; | 6149 | seg.inner.filesize = symtab.symoff + symtab.nsyms * @sizeOf(macho.nlist_64) - seg.inner.fileoff; |
| 6126 | 6150 | ||
| 6127 | // Update dynamic symbol table. | 6151 | // Update dynamic symbol table. |
| 6128 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab; | 6152 | const dysymtab = &self.load_commands.items[self.dysymtab_cmd_index.?].dysymtab; |
| ... | @@ -6142,22 +6166,21 @@ fn writeSymbolTable(self: *MachO) !void { | ... | @@ -6142,22 +6166,21 @@ fn writeSymbolTable(self: *MachO) !void { |
| 6142 | const nstubs = @intCast(u32, self.stubs_table.keys().len); | 6166 | const nstubs = @intCast(u32, self.stubs_table.keys().len); |
| 6143 | const ngot_entries = @intCast(u32, self.got_entries_table.keys().len); | 6167 | const ngot_entries = @intCast(u32, self.got_entries_table.keys().len); |
| 6144 | 6168 | ||
| 6145 | dysymtab.indirectsymoff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | 6169 | const indirectsymoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); |
| 6170 | dysymtab.indirectsymoff = @intCast(u32, indirectsymoff); | ||
| 6146 | dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; | 6171 | dysymtab.nindirectsyms = nstubs * 2 + ngot_entries; |
| 6147 | 6172 | ||
| 6148 | const needed_size = dysymtab.nindirectsyms * @sizeOf(u32); | 6173 | seg.inner.filesize = dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32) - seg.inner.fileoff; |
| 6149 | seg.inner.filesize += needed_size; | ||
| 6150 | 6174 | ||
| 6151 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ | 6175 | log.debug("writing indirect symbol table from 0x{x} to 0x{x}", .{ |
| 6152 | dysymtab.indirectsymoff, | 6176 | dysymtab.indirectsymoff, |
| 6153 | dysymtab.indirectsymoff + needed_size, | 6177 | dysymtab.indirectsymoff + dysymtab.nindirectsyms * @sizeOf(u32), |
| 6154 | }); | 6178 | }); |
| 6155 | 6179 | ||
| 6156 | var buf = try self.base.allocator.alloc(u8, needed_size); | 6180 | var buf = std.ArrayList(u8).init(self.base.allocator); |
| 6157 | defer self.base.allocator.free(buf); | 6181 | defer buf.deinit(); |
| 6158 | 6182 | try buf.ensureTotalCapacity(dysymtab.nindirectsyms * @sizeOf(u32)); | |
| 6159 | var stream = std.io.fixedBufferStream(buf); | 6183 | const writer = buf.writer(); |
| 6160 | var writer = stream.writer(); | ||
| 6161 | 6184 | ||
| 6162 | stubs.reserved1 = 0; | 6185 | stubs.reserved1 = 0; |
| 6163 | for (self.stubs_table.keys()) |key| { | 6186 | for (self.stubs_table.keys()) |key| { |
| ... | @@ -6191,7 +6214,9 @@ fn writeSymbolTable(self: *MachO) !void { | ... | @@ -6191,7 +6214,9 @@ fn writeSymbolTable(self: *MachO) !void { |
| 6191 | } | 6214 | } |
| 6192 | } | 6215 | } |
| 6193 | 6216 | ||
| 6194 | try self.base.file.?.pwriteAll(buf, dysymtab.indirectsymoff); | 6217 | assert(buf.items.len == dysymtab.nindirectsyms * @sizeOf(u32)); |
| 6218 | |||
| 6219 | try self.base.file.?.pwriteAll(buf.items, dysymtab.indirectsymoff); | ||
| 6195 | self.load_commands_dirty = true; | 6220 | self.load_commands_dirty = true; |
| 6196 | } | 6221 | } |
| 6197 | 6222 | ||
| ... | @@ -6201,18 +6226,16 @@ fn writeStringTable(self: *MachO) !void { | ... | @@ -6201,18 +6226,16 @@ fn writeStringTable(self: *MachO) !void { |
| 6201 | 6226 | ||
| 6202 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; | 6227 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 6203 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; | 6228 | const symtab = &self.load_commands.items[self.symtab_cmd_index.?].symtab; |
| 6204 | symtab.stroff = @intCast(u32, seg.inner.fileoff + seg.inner.filesize); | 6229 | const stroff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, @alignOf(u64)); |
| 6205 | symtab.strsize = @intCast(u32, mem.alignForwardGeneric(u64, self.strtab.items.len, @alignOf(u64))); | 6230 | const strsize = self.strtab.items.len; |
| 6206 | seg.inner.filesize += symtab.strsize; | 6231 | symtab.stroff = @intCast(u32, stroff); |
| 6232 | symtab.strsize = @intCast(u32, strsize); | ||
| 6233 | seg.inner.filesize = symtab.stroff + symtab.strsize - seg.inner.fileoff; | ||
| 6207 | 6234 | ||
| 6208 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); | 6235 | log.debug("writing string table from 0x{x} to 0x{x}", .{ symtab.stroff, symtab.stroff + symtab.strsize }); |
| 6209 | 6236 | ||
| 6210 | try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); | 6237 | try self.base.file.?.pwriteAll(self.strtab.items, symtab.stroff); |
| 6211 | 6238 | ||
| 6212 | if (symtab.strsize > self.strtab.items.len) { | ||
| 6213 | // This is potentially the last section, so we need to pad it out. | ||
| 6214 | try self.base.file.?.pwriteAll(&[_]u8{0}, seg.inner.fileoff + seg.inner.filesize - 1); | ||
| 6215 | } | ||
| 6216 | self.load_commands_dirty = true; | 6239 | self.load_commands_dirty = true; |
| 6217 | } | 6240 | } |
| 6218 | 6241 | ||
| ... | @@ -6236,25 +6259,22 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { | ... | @@ -6236,25 +6259,22 @@ fn writeCodeSignaturePadding(self: *MachO, code_sig: *CodeSignature) !void { |
| 6236 | const tracy = trace(@src()); | 6259 | const tracy = trace(@src()); |
| 6237 | defer tracy.end(); | 6260 | defer tracy.end(); |
| 6238 | 6261 | ||
| 6239 | const linkedit_segment = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; | 6262 | const seg = &self.load_commands.items[self.linkedit_segment_cmd_index.?].segment; |
| 6240 | const code_sig_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; | 6263 | const cs_cmd = &self.load_commands.items[self.code_signature_cmd_index.?].linkedit_data; |
| 6241 | // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file | 6264 | // Code signature data has to be 16-bytes aligned for Apple tools to recognize the file |
| 6242 | // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 | 6265 | // https://github.com/opensource-apple/cctools/blob/fdb4825f303fd5c0751be524babd32958181b3ed/libstuff/checkout.c#L271 |
| 6243 | const fileoff = mem.alignForwardGeneric(u64, linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize, 16); | 6266 | const dataoff = mem.alignForwardGeneric(u64, seg.inner.fileoff + seg.inner.filesize, 16); |
| 6244 | const padding = fileoff - (linkedit_segment.inner.fileoff + linkedit_segment.inner.filesize); | 6267 | const datasize = code_sig.estimateSize(dataoff); |
| 6245 | const needed_size = code_sig.estimateSize(fileoff); | 6268 | cs_cmd.dataoff = @intCast(u32, dataoff); |
| 6246 | code_sig_cmd.dataoff = @intCast(u32, fileoff); | 6269 | cs_cmd.datasize = @intCast(u32, code_sig.estimateSize(dataoff)); |
| 6247 | code_sig_cmd.datasize = needed_size; | ||
| 6248 | 6270 | ||
| 6249 | // Advance size of __LINKEDIT segment | 6271 | // Advance size of __LINKEDIT segment |
| 6250 | linkedit_segment.inner.filesize += needed_size + padding; | 6272 | seg.inner.filesize = cs_cmd.dataoff + cs_cmd.datasize - seg.inner.fileoff; |
| 6251 | if (linkedit_segment.inner.vmsize < linkedit_segment.inner.filesize) { | 6273 | seg.inner.vmsize = mem.alignForwardGeneric(u64, seg.inner.filesize, self.page_size); |
| 6252 | linkedit_segment.inner.vmsize = mem.alignForwardGeneric(u64, linkedit_segment.inner.filesize, self.page_size); | 6274 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ dataoff, dataoff + datasize }); |
| 6253 | } | ||
| 6254 | log.debug("writing code signature padding from 0x{x} to 0x{x}", .{ fileoff, fileoff + needed_size }); | ||
| 6255 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file | 6275 | // Pad out the space. We need to do this to calculate valid hashes for everything in the file |
| 6256 | // except for code signature data. | 6276 | // except for code signature data. |
| 6257 | try self.base.file.?.pwriteAll(&[_]u8{0}, fileoff + needed_size - 1); | 6277 | try self.base.file.?.pwriteAll(&[_]u8{0}, dataoff + datasize - 1); |
| 6258 | self.load_commands_dirty = true; | 6278 | self.load_commands_dirty = true; |
| 6259 | } | 6279 | } |
| 6260 | 6280 |
test/link.zig created+64| ... | @@ -0,0 +1,64 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const builtin = @import("builtin"); | ||
| 3 | const tests = @import("tests.zig"); | ||
| 4 | |||
| 5 | pub fn addCases(cases: *tests.StandaloneContext) void { | ||
| 6 | cases.addBuildFile("test/link/bss/build.zig", .{ | ||
| 7 | .build_modes = false, // we only guarantee zerofill for undefined in Debug | ||
| 8 | }); | ||
| 9 | |||
| 10 | cases.addBuildFile("test/link/common_symbols/build.zig", .{ | ||
| 11 | .build_modes = true, | ||
| 12 | }); | ||
| 13 | |||
| 14 | cases.addBuildFile("test/link/common_symbols_alignment/build.zig", .{ | ||
| 15 | .build_modes = true, | ||
| 16 | }); | ||
| 17 | |||
| 18 | cases.addBuildFile("test/link/interdependent_static_c_libs/build.zig", .{ | ||
| 19 | .build_modes = true, | ||
| 20 | }); | ||
| 21 | |||
| 22 | cases.addBuildFile("test/link/static_lib_as_system_lib/build.zig", .{ | ||
| 23 | .build_modes = true, | ||
| 24 | }); | ||
| 25 | |||
| 26 | cases.addBuildFile("test/link/tls/build.zig", .{ | ||
| 27 | .build_modes = true, | ||
| 28 | }); | ||
| 29 | |||
| 30 | if (builtin.os.tag == .macos) { | ||
| 31 | cases.addBuildFile("test/link/macho/entry/build.zig", .{ | ||
| 32 | .build_modes = true, | ||
| 33 | }); | ||
| 34 | |||
| 35 | cases.addBuildFile("test/link/macho/pagezero/build.zig", .{ | ||
| 36 | .build_modes = false, | ||
| 37 | }); | ||
| 38 | |||
| 39 | cases.addBuildFile("test/link/macho/dylib/build.zig", .{ | ||
| 40 | .build_modes = true, | ||
| 41 | }); | ||
| 42 | |||
| 43 | cases.addBuildFile("test/link/macho/frameworks/build.zig", .{ | ||
| 44 | .build_modes = true, | ||
| 45 | .requires_macos_sdk = true, | ||
| 46 | }); | ||
| 47 | |||
| 48 | // Try to build and run an Objective-C executable. | ||
| 49 | cases.addBuildFile("test/link/macho/objc/build.zig", .{ | ||
| 50 | .build_modes = true, | ||
| 51 | .requires_macos_sdk = true, | ||
| 52 | }); | ||
| 53 | |||
| 54 | // Try to build and run an Objective-C++ executable. | ||
| 55 | cases.addBuildFile("test/link/macho/objcpp/build.zig", .{ | ||
| 56 | .build_modes = true, | ||
| 57 | .requires_macos_sdk = true, | ||
| 58 | }); | ||
| 59 | |||
| 60 | cases.addBuildFile("test/link/macho/stack_size/build.zig", .{ | ||
| 61 | .build_modes = true, | ||
| 62 | }); | ||
| 63 | } | ||
| 64 | } | ||
test/link/bss/build.zig created+14| ... | @@ -0,0 +1,14 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const mode = b.standardReleaseOptions(); | ||
| 5 | const test_step = b.step("test", "Test"); | ||
| 6 | |||
| 7 | const exe = b.addExecutable("bss", "main.zig"); | ||
| 8 | b.default_step.dependOn(&exe.step); | ||
| 9 | exe.setBuildMode(mode); | ||
| 10 | |||
| 11 | const run = exe.run(); | ||
| 12 | run.expectStdOutEqual("0, 1, 0\n"); | ||
| 13 | test_step.dependOn(&run.step); | ||
| 14 | } | ||
test/link/bss/main.zig created+13| ... | @@ -0,0 +1,13 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | // Stress test zerofill layout | ||
| 4 | var buffer: [0x1000000]u64 = undefined; | ||
| 5 | |||
| 6 | pub fn main() anyerror!void { | ||
| 7 | buffer[0x10] = 1; | ||
| 8 | try std.io.getStdOut().writer().print("{d}, {d}, {d}\n", .{ | ||
| 9 | buffer[0], | ||
| 10 | buffer[0x10], | ||
| 11 | buffer[0x1000000 - 1], | ||
| 12 | }); | ||
| 13 | } | ||
test/link/common_symbols/a.c created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | int i; | ||
| 2 | int j; | ||
| 3 | |||
| 4 | int add_to_i_and_j(int x) { | ||
| 5 | return x + i + j; | ||
| 6 | } | ||
test/link/common_symbols/b.c created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | long i; | ||
| 2 | int j = 2; | ||
| 3 | int k; | ||
| 4 | |||
| 5 | void incr_i() { | ||
| 6 | i++; | ||
| 7 | } | ||
test/link/common_symbols/build.zig created+16| ... | @@ -0,0 +1,16 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const mode = b.standardReleaseOptions(); | ||
| 5 | |||
| 6 | const lib_a = b.addStaticLibrary("a", null); | ||
| 7 | lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"}); | ||
| 8 | lib_a.setBuildMode(mode); | ||
| 9 | |||
| 10 | const test_exe = b.addTest("main.zig"); | ||
| 11 | test_exe.setBuildMode(mode); | ||
| 12 | test_exe.linkLibrary(lib_a); | ||
| 13 | |||
| 14 | const test_step = b.step("test", "Test it"); | ||
| 15 | test_step.dependOn(&test_exe.step); | ||
| 16 | } | ||
test/link/common_symbols/c.c created+5| ... | @@ -0,0 +1,5 @@ | ||
| 1 | extern int k; | ||
| 2 | |||
| 3 | int common_defined_externally() { | ||
| 4 | return k; | ||
| 5 | } | ||
test/link/common_symbols/main.zig created+16| ... | @@ -0,0 +1,16 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | |||
| 4 | extern fn common_defined_externally() c_int; | ||
| 5 | extern fn incr_i() void; | ||
| 6 | extern fn add_to_i_and_j(x: c_int) c_int; | ||
| 7 | |||
| 8 | test "undef shadows common symbol: issue #9937" { | ||
| 9 | try expect(common_defined_externally() == 0); | ||
| 10 | } | ||
| 11 | |||
| 12 | test "import C common symbols" { | ||
| 13 | incr_i(); | ||
| 14 | const res = add_to_i_and_j(2); | ||
| 15 | try expect(res == 5); | ||
| 16 | } | ||
test/link/common_symbols_alignment/a.c created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | int foo; | ||
| 2 | __attribute__((aligned(4096))) int bar; | ||
test/link/common_symbols_alignment/build.zig created+16| ... | @@ -0,0 +1,16 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const mode = b.standardReleaseOptions(); | ||
| 5 | |||
| 6 | const lib_a = b.addStaticLibrary("a", null); | ||
| 7 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); | ||
| 8 | lib_a.setBuildMode(mode); | ||
| 9 | |||
| 10 | const test_exe = b.addTest("main.zig"); | ||
| 11 | test_exe.setBuildMode(mode); | ||
| 12 | test_exe.linkLibrary(lib_a); | ||
| 13 | |||
| 14 | const test_step = b.step("test", "Test it"); | ||
| 15 | test_step.dependOn(&test_exe.step); | ||
| 16 | } | ||
test/link/common_symbols_alignment/main.zig created+9| ... | @@ -0,0 +1,9 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | extern var foo: i32; | ||
| 4 | extern var bar: i32; | ||
| 5 | |||
| 6 | test { | ||
| 7 | try std.testing.expect(@ptrToInt(&foo) % 4 == 0); | ||
| 8 | try std.testing.expect(@ptrToInt(&bar) % 4096 == 0); | ||
| 9 | } | ||
test/link/interdependent_static_c_libs/a.c created+4| ... | @@ -0,0 +1,4 @@ | ||
| 1 | #include "a.h" | ||
| 2 | int32_t add(int32_t a, int32_t b) { | ||
| 3 | return a + b; | ||
| 4 | } | ||
test/link/interdependent_static_c_libs/a.h created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | #include <stdint.h> | ||
| 2 | int32_t add(int32_t a, int32_t b); | ||
test/link/interdependent_static_c_libs/b.c created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | #include "a.h" | ||
| 2 | #include "b.h" | ||
| 3 | |||
| 4 | int32_t sub(int32_t a, int32_t b) { | ||
| 5 | return add(a, -1 * b); | ||
| 6 | } | ||
test/link/interdependent_static_c_libs/b.h created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | #include <stdint.h> | ||
| 2 | int32_t sub(int32_t a, int32_t b); | ||
test/link/interdependent_static_c_libs/build.zig created+24| ... | @@ -0,0 +1,24 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const mode = b.standardReleaseOptions(); | ||
| 5 | |||
| 6 | const lib_a = b.addStaticLibrary("a", null); | ||
| 7 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | ||
| 8 | lib_a.setBuildMode(mode); | ||
| 9 | lib_a.addIncludePath("."); | ||
| 10 | |||
| 11 | const lib_b = b.addStaticLibrary("b", null); | ||
| 12 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); | ||
| 13 | lib_b.setBuildMode(mode); | ||
| 14 | lib_b.addIncludePath("."); | ||
| 15 | |||
| 16 | const test_exe = b.addTest("main.zig"); | ||
| 17 | test_exe.setBuildMode(mode); | ||
| 18 | test_exe.linkLibrary(lib_a); | ||
| 19 | test_exe.linkLibrary(lib_b); | ||
| 20 | test_exe.addIncludePath("."); | ||
| 21 | |||
| 22 | const test_step = b.step("test", "Test it"); | ||
| 23 | test_step.dependOn(&test_exe.step); | ||
| 24 | } | ||
test/link/interdependent_static_c_libs/main.zig created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const c = @cImport(@cInclude("b.h")); | ||
| 4 | |||
| 5 | test "import C sub" { | ||
| 6 | const result = c.sub(2, 1); | ||
| 7 | try expect(result == 1); | ||
| 8 | } | ||
test/link/macho/dylib/a.c created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | #include <stdio.h> | ||
| 2 | |||
| 3 | char world[] = "world"; | ||
| 4 | |||
| 5 | char* hello() { | ||
| 6 | return "Hello"; | ||
| 7 | } | ||
test/link/macho/dylib/build.zig created+49| ... | @@ -0,0 +1,49 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub fn build(b: *Builder) void { | ||
| 5 | const mode = b.standardReleaseOptions(); | ||
| 6 | |||
| 7 | const test_step = b.step("test", "Test"); | ||
| 8 | test_step.dependOn(b.getInstallStep()); | ||
| 9 | |||
| 10 | const dylib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | ||
| 11 | dylib.setBuildMode(mode); | ||
| 12 | dylib.addCSourceFile("a.c", &.{}); | ||
| 13 | dylib.linkLibC(); | ||
| 14 | dylib.install(); | ||
| 15 | |||
| 16 | const check_dylib = dylib.checkObject(.macho); | ||
| 17 | check_dylib.checkStart("cmd ID_DYLIB"); | ||
| 18 | check_dylib.checkNext("name @rpath/liba.dylib"); | ||
| 19 | check_dylib.checkNext("timestamp 2"); | ||
| 20 | check_dylib.checkNext("current version 10000"); | ||
| 21 | check_dylib.checkNext("compatibility version 10000"); | ||
| 22 | |||
| 23 | test_step.dependOn(&check_dylib.step); | ||
| 24 | |||
| 25 | const exe = b.addExecutable("main", null); | ||
| 26 | exe.setBuildMode(mode); | ||
| 27 | exe.addCSourceFile("main.c", &.{}); | ||
| 28 | exe.linkSystemLibrary("a"); | ||
| 29 | exe.linkLibC(); | ||
| 30 | exe.addLibraryPath(b.pathFromRoot("zig-out/lib/")); | ||
| 31 | exe.addRPath(b.pathFromRoot("zig-out/lib")); | ||
| 32 | |||
| 33 | const check_exe = exe.checkObject(.macho); | ||
| 34 | check_exe.checkStart("cmd LOAD_DYLIB"); | ||
| 35 | check_exe.checkNext("name @rpath/liba.dylib"); | ||
| 36 | check_exe.checkNext("timestamp 2"); | ||
| 37 | check_exe.checkNext("current version 10000"); | ||
| 38 | check_exe.checkNext("compatibility version 10000"); | ||
| 39 | |||
| 40 | check_exe.checkStart("cmd RPATH"); | ||
| 41 | check_exe.checkNext(std.fmt.allocPrint(b.allocator, "path {s}", .{b.pathFromRoot("zig-out/lib")}) catch unreachable); | ||
| 42 | |||
| 43 | test_step.dependOn(&check_exe.step); | ||
| 44 | |||
| 45 | const run = exe.run(); | ||
| 46 | run.cwd = b.pathFromRoot("."); | ||
| 47 | run.expectStdOutEqual("Hello world"); | ||
| 48 | test_step.dependOn(&run.step); | ||
| 49 | } | ||
test/link/macho/dylib/main.c created+9| ... | @@ -0,0 +1,9 @@ | ||
| 1 | #include <stdio.h> | ||
| 2 | |||
| 3 | char* hello(); | ||
| 4 | extern char world[]; | ||
| 5 | |||
| 6 | int main() { | ||
| 7 | printf("%s %s", hello(), world); | ||
| 8 | return 0; | ||
| 9 | } | ||
test/link/macho/entry/build.zig created+34| ... | @@ -0,0 +1,34 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub fn build(b: *Builder) void { | ||
| 5 | const mode = b.standardReleaseOptions(); | ||
| 6 | |||
| 7 | const test_step = b.step("test", "Test"); | ||
| 8 | test_step.dependOn(b.getInstallStep()); | ||
| 9 | |||
| 10 | const exe = b.addExecutable("main", null); | ||
| 11 | exe.setBuildMode(mode); | ||
| 12 | exe.addCSourceFile("main.c", &.{}); | ||
| 13 | exe.linkLibC(); | ||
| 14 | exe.entry_symbol_name = "_non_main"; | ||
| 15 | |||
| 16 | const check_exe = exe.checkObject(.macho); | ||
| 17 | |||
| 18 | check_exe.checkStart("segname __TEXT"); | ||
| 19 | check_exe.checkNext("vmaddr {vmaddr}"); | ||
| 20 | |||
| 21 | check_exe.checkStart("cmd MAIN"); | ||
| 22 | check_exe.checkNext("entryoff {entryoff}"); | ||
| 23 | |||
| 24 | check_exe.checkInSymtab(); | ||
| 25 | check_exe.checkNext("_non_main {n_value}"); | ||
| 26 | |||
| 27 | check_exe.checkComputeEq("vmaddr entryoff +", "n_value"); | ||
| 28 | |||
| 29 | test_step.dependOn(&check_exe.step); | ||
| 30 | |||
| 31 | const run = exe.run(); | ||
| 32 | run.expectStdOutEqual("42"); | ||
| 33 | test_step.dependOn(&run.step); | ||
| 34 | } | ||
test/link/macho/entry/main.c created+6| ... | @@ -0,0 +1,6 @@ | ||
| 1 | #include <stdio.h> | ||
| 2 | |||
| 3 | int non_main() { | ||
| 4 | printf("%d", 42); | ||
| 5 | return 0; | ||
| 6 | } | ||
test/link/macho/frameworks/build.zig created+32| ... | @@ -0,0 +1,32 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub fn build(b: *Builder) void { | ||
| 5 | const mode = b.standardReleaseOptions(); | ||
| 6 | |||
| 7 | const test_step = b.step("test", "Test the program"); | ||
| 8 | |||
| 9 | const exe = b.addExecutable("test", null); | ||
| 10 | b.default_step.dependOn(&exe.step); | ||
| 11 | exe.addCSourceFile("main.c", &[0][]const u8{}); | ||
| 12 | exe.setBuildMode(mode); | ||
| 13 | exe.linkLibC(); | ||
| 14 | exe.linkFramework("Cocoa"); | ||
| 15 | |||
| 16 | const check = exe.checkObject(.macho); | ||
| 17 | check.checkStart("cmd LOAD_DYLIB"); | ||
| 18 | check.checkNext("name {*}Cocoa"); | ||
| 19 | |||
| 20 | switch (mode) { | ||
| 21 | .Debug, .ReleaseSafe => { | ||
| 22 | check.checkStart("cmd LOAD_DYLIB"); | ||
| 23 | check.checkNext("name {*}libobjc{*}.dylib"); | ||
| 24 | }, | ||
| 25 | else => {}, | ||
| 26 | } | ||
| 27 | |||
| 28 | test_step.dependOn(&check.step); | ||
| 29 | |||
| 30 | const run_cmd = exe.run(); | ||
| 31 | test_step.dependOn(&run_cmd.step); | ||
| 32 | } | ||
test/link/macho/frameworks/main.c created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | #include <assert.h> | ||
| 2 | #include <objc/runtime.h> | ||
| 3 | |||
| 4 | int main() { | ||
| 5 | assert(objc_getClass("NSObject") > 0); | ||
| 6 | assert(objc_getClass("NSApplication") > 0); | ||
| 7 | } | ||
test/link/macho/objc/Foo.h created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | #import <Foundation/Foundation.h> | ||
| 2 | |||
| 3 | @interface Foo : NSObject | ||
| 4 | |||
| 5 | - (NSString *)name; | ||
| 6 | |||
| 7 | @end | ||
test/link/macho/objc/Foo.m created+11| ... | @@ -0,0 +1,11 @@ | ||
| 1 | #import "Foo.h" | ||
| 2 | |||
| 3 | @implementation Foo | ||
| 4 | |||
| 5 | - (NSString *)name | ||
| 6 | { | ||
| 7 | NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; | ||
| 8 | return str; | ||
| 9 | } | ||
| 10 | |||
| 11 | @end | ||
test/link/macho/objc/build.zig created+22| ... | @@ -0,0 +1,22 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub fn build(b: *Builder) void { | ||
| 5 | const mode = b.standardReleaseOptions(); | ||
| 6 | |||
| 7 | const test_step = b.step("test", "Test the program"); | ||
| 8 | |||
| 9 | const exe = b.addExecutable("test", null); | ||
| 10 | b.default_step.dependOn(&exe.step); | ||
| 11 | exe.addIncludePath("."); | ||
| 12 | exe.addCSourceFile("Foo.m", &[0][]const u8{}); | ||
| 13 | exe.addCSourceFile("test.m", &[0][]const u8{}); | ||
| 14 | exe.setBuildMode(mode); | ||
| 15 | exe.linkLibC(); | ||
| 16 | // TODO when we figure out how to ship framework stubs for cross-compilation, | ||
| 17 | // populate paths to the sysroot here. | ||
| 18 | exe.linkFramework("Foundation"); | ||
| 19 | |||
| 20 | const run_cmd = exe.run(); | ||
| 21 | test_step.dependOn(&run_cmd.step); | ||
| 22 | } | ||
test/link/macho/objc/test.m created+12| ... | @@ -0,0 +1,12 @@ | ||
| 1 | #import "Foo.h" | ||
| 2 | #import <assert.h> | ||
| 3 | |||
| 4 | int main(int argc, char *argv[]) | ||
| 5 | { | ||
| 6 | @autoreleasepool { | ||
| 7 | Foo *foo = [[Foo alloc] init]; | ||
| 8 | NSString *result = [foo name]; | ||
| 9 | assert([result isEqualToString:@"Zig"]); | ||
| 10 | return 0; | ||
| 11 | } | ||
| 12 | } | ||
test/link/macho/objcpp/Foo.h created+7| ... | @@ -0,0 +1,7 @@ | ||
| 1 | #import <Foundation/Foundation.h> | ||
| 2 | |||
| 3 | @interface Foo : NSObject | ||
| 4 | |||
| 5 | - (NSString *)name; | ||
| 6 | |||
| 7 | @end | ||
test/link/macho/objcpp/Foo.mm created+11| ... | @@ -0,0 +1,11 @@ | ||
| 1 | #import "Foo.h" | ||
| 2 | |||
| 3 | @implementation Foo | ||
| 4 | |||
| 5 | - (NSString *)name | ||
| 6 | { | ||
| 7 | NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; | ||
| 8 | return str; | ||
| 9 | } | ||
| 10 | |||
| 11 | @end | ||
test/link/macho/objcpp/build.zig created+24| ... | @@ -0,0 +1,24 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub fn build(b: *Builder) void { | ||
| 5 | const mode = b.standardReleaseOptions(); | ||
| 6 | |||
| 7 | const test_step = b.step("test", "Test the program"); | ||
| 8 | |||
| 9 | const exe = b.addExecutable("test", null); | ||
| 10 | b.default_step.dependOn(&exe.step); | ||
| 11 | exe.addIncludePath("."); | ||
| 12 | exe.addCSourceFile("Foo.mm", &[0][]const u8{}); | ||
| 13 | exe.addCSourceFile("test.mm", &[0][]const u8{}); | ||
| 14 | exe.setBuildMode(mode); | ||
| 15 | exe.linkLibCpp(); | ||
| 16 | // TODO when we figure out how to ship framework stubs for cross-compilation, | ||
| 17 | // populate paths to the sysroot here. | ||
| 18 | exe.linkFramework("Foundation"); | ||
| 19 | |||
| 20 | const run_cmd = exe.run(); | ||
| 21 | run_cmd.expectStdOutEqual("Hello from C++ and Zig"); | ||
| 22 | |||
| 23 | test_step.dependOn(&run_cmd.step); | ||
| 24 | } | ||
test/link/macho/objcpp/test.mm created+14| ... | @@ -0,0 +1,14 @@ | ||
| 1 | #import "Foo.h" | ||
| 2 | #import <assert.h> | ||
| 3 | #include <iostream> | ||
| 4 | |||
| 5 | int main(int argc, char *argv[]) | ||
| 6 | { | ||
| 7 | @autoreleasepool { | ||
| 8 | Foo *foo = [[Foo alloc] init]; | ||
| 9 | NSString *result = [foo name]; | ||
| 10 | std::cout << "Hello from C++ and " << [result UTF8String]; | ||
| 11 | assert([result isEqualToString:@"Zig"]); | ||
| 12 | return 0; | ||
| 13 | } | ||
| 14 | } | ||
test/link/macho/pagezero/build.zig created+43| ... | @@ -0,0 +1,43 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub fn build(b: *Builder) void { | ||
| 5 | const mode = b.standardReleaseOptions(); | ||
| 6 | |||
| 7 | const test_step = b.step("test", "Test"); | ||
| 8 | test_step.dependOn(b.getInstallStep()); | ||
| 9 | |||
| 10 | { | ||
| 11 | const exe = b.addExecutable("pagezero", null); | ||
| 12 | exe.setBuildMode(mode); | ||
| 13 | exe.addCSourceFile("main.c", &.{}); | ||
| 14 | exe.linkLibC(); | ||
| 15 | exe.pagezero_size = 0x4000; | ||
| 16 | |||
| 17 | const check = exe.checkObject(.macho); | ||
| 18 | check.checkStart("LC 0"); | ||
| 19 | check.checkNext("segname __PAGEZERO"); | ||
| 20 | check.checkNext("vmaddr 0"); | ||
| 21 | check.checkNext("vmsize 4000"); | ||
| 22 | |||
| 23 | check.checkStart("segname __TEXT"); | ||
| 24 | check.checkNext("vmaddr 4000"); | ||
| 25 | |||
| 26 | test_step.dependOn(&check.step); | ||
| 27 | } | ||
| 28 | |||
| 29 | { | ||
| 30 | const exe = b.addExecutable("no_pagezero", null); | ||
| 31 | exe.setBuildMode(mode); | ||
| 32 | exe.addCSourceFile("main.c", &.{}); | ||
| 33 | exe.linkLibC(); | ||
| 34 | exe.pagezero_size = 0; | ||
| 35 | |||
| 36 | const check = exe.checkObject(.macho); | ||
| 37 | check.checkStart("LC 0"); | ||
| 38 | check.checkNext("segname __TEXT"); | ||
| 39 | check.checkNext("vmaddr 0"); | ||
| 40 | |||
| 41 | test_step.dependOn(&check.step); | ||
| 42 | } | ||
| 43 | } | ||
test/link/macho/pagezero/main.c created+3| ... | @@ -0,0 +1,3 @@ | ||
| 1 | int main(int argc, char* argv[]) { | ||
| 2 | return 0; | ||
| 3 | } | ||
test/link/macho/stack_size/build.zig created+24| ... | @@ -0,0 +1,24 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub fn build(b: *Builder) void { | ||
| 5 | const mode = b.standardReleaseOptions(); | ||
| 6 | |||
| 7 | const test_step = b.step("test", "Test"); | ||
| 8 | test_step.dependOn(b.getInstallStep()); | ||
| 9 | |||
| 10 | const exe = b.addExecutable("main", null); | ||
| 11 | exe.setBuildMode(mode); | ||
| 12 | exe.addCSourceFile("main.c", &.{}); | ||
| 13 | exe.linkLibC(); | ||
| 14 | exe.stack_size = 0x100000000; | ||
| 15 | |||
| 16 | const check_exe = exe.checkObject(.macho); | ||
| 17 | check_exe.checkStart("cmd MAIN"); | ||
| 18 | check_exe.checkNext("stacksize 100000000"); | ||
| 19 | |||
| 20 | test_step.dependOn(&check_exe.step); | ||
| 21 | |||
| 22 | const run = exe.run(); | ||
| 23 | test_step.dependOn(&run.step); | ||
| 24 | } | ||
test/link/macho/stack_size/main.c created+3| ... | @@ -0,0 +1,3 @@ | ||
| 1 | int main(int argc, char* argv[]) { | ||
| 2 | return 0; | ||
| 3 | } | ||
test/link/static_lib_as_system_lib/a.c created+4| ... | @@ -0,0 +1,4 @@ | ||
| 1 | #include "a.h" | ||
| 2 | int32_t add(int32_t a, int32_t b) { | ||
| 3 | return a + b; | ||
| 4 | } | ||
test/link/static_lib_as_system_lib/a.h created+2| ... | @@ -0,0 +1,2 @@ | ||
| 1 | #include <stdint.h> | ||
| 2 | int32_t add(int32_t a, int32_t b); | ||
test/link/static_lib_as_system_lib/build.zig created+23| ... | @@ -0,0 +1,23 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub fn build(b: *Builder) void { | ||
| 5 | const mode = b.standardReleaseOptions(); | ||
| 6 | |||
| 7 | const lib_a = b.addStaticLibrary("a", null); | ||
| 8 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | ||
| 9 | lib_a.setBuildMode(mode); | ||
| 10 | lib_a.addIncludePath("."); | ||
| 11 | lib_a.install(); | ||
| 12 | |||
| 13 | const test_exe = b.addTest("main.zig"); | ||
| 14 | test_exe.setBuildMode(mode); | ||
| 15 | test_exe.linkSystemLibrary("a"); // force linking liba.a as -la | ||
| 16 | test_exe.addSystemIncludePath("."); | ||
| 17 | const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable; | ||
| 18 | test_exe.addLibraryPath(search_path); | ||
| 19 | |||
| 20 | const test_step = b.step("test", "Test it"); | ||
| 21 | test_step.dependOn(b.getInstallStep()); | ||
| 22 | test_step.dependOn(&test_exe.step); | ||
| 23 | } | ||
test/link/static_lib_as_system_lib/main.zig created+8| ... | @@ -0,0 +1,8 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const c = @cImport(@cInclude("a.h")); | ||
| 4 | |||
| 5 | test "import C add" { | ||
| 6 | const result = c.add(2, 1); | ||
| 7 | try expect(result == 3); | ||
| 8 | } | ||
test/link/tls/a.c created+5| ... | @@ -0,0 +1,5 @@ | ||
| 1 | _Thread_local int a; | ||
| 2 | |||
| 3 | int getA() { | ||
| 4 | return a; | ||
| 5 | } | ||
test/link/tls/build.zig created+18| ... | @@ -0,0 +1,18 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const mode = b.standardReleaseOptions(); | ||
| 5 | |||
| 6 | const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | ||
| 7 | lib.setBuildMode(mode); | ||
| 8 | lib.addCSourceFile("a.c", &.{}); | ||
| 9 | lib.linkLibC(); | ||
| 10 | |||
| 11 | const test_exe = b.addTest("main.zig"); | ||
| 12 | test_exe.setBuildMode(mode); | ||
| 13 | test_exe.linkLibrary(lib); | ||
| 14 | test_exe.linkLibC(); | ||
| 15 | |||
| 16 | const test_step = b.step("test", "Test it"); | ||
| 17 | test_step.dependOn(&test_exe.step); | ||
| 18 | } | ||
test/link/tls/main.zig created+15| ... | @@ -0,0 +1,15 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | extern threadlocal var a: i32; | ||
| 4 | extern fn getA() i32; | ||
| 5 | |||
| 6 | fn getA2() i32 { | ||
| 7 | return a; | ||
| 8 | } | ||
| 9 | |||
| 10 | test { | ||
| 11 | a = 2; | ||
| 12 | try std.testing.expect(getA() == 2); | ||
| 13 | try std.testing.expect(2 == getA2()); | ||
| 14 | try std.testing.expect(getA() == getA2()); | ||
| 15 | } | ||
test/standalone.zig+4-33| ... | @@ -13,31 +13,12 @@ pub fn addCases(cases: *tests.StandaloneContext) void { | ... | @@ -13,31 +13,12 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 13 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); | 13 | cases.addBuildFile("test/standalone/main_pkg_path/build.zig", .{}); |
| 14 | cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); | 14 | cases.addBuildFile("test/standalone/shared_library/build.zig", .{}); |
| 15 | cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); | 15 | cases.addBuildFile("test/standalone/mix_o_files/build.zig", .{}); |
| 16 | if (builtin.os.tag == .macos) { | 16 | cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ |
| 17 | // Zig's macOS linker does not yet support LTO for LLVM IR files: | 17 | .build_modes = true, |
| 18 | // https://github.com/ziglang/zig/issues/8680 | 18 | .cross_targets = true, |
| 19 | cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ | 19 | }); |
| 20 | .build_modes = false, | ||
| 21 | .cross_targets = true, | ||
| 22 | }); | ||
| 23 | } else { | ||
| 24 | cases.addBuildFile("test/standalone/mix_c_files/build.zig", .{ | ||
| 25 | .build_modes = true, | ||
| 26 | .cross_targets = true, | ||
| 27 | }); | ||
| 28 | } | ||
| 29 | cases.addBuildFile("test/standalone/global_linkage/build.zig", .{}); | 20 | cases.addBuildFile("test/standalone/global_linkage/build.zig", .{}); |
| 30 | cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); | 21 | cases.addBuildFile("test/standalone/static_c_lib/build.zig", .{}); |
| 31 | cases.addBuildFile("test/standalone/link_interdependent_static_c_libs/build.zig", .{}); | ||
| 32 | cases.addBuildFile("test/standalone/link_static_lib_as_system_lib/build.zig", .{}); | ||
| 33 | cases.addBuildFile("test/standalone/link_common_symbols/build.zig", .{}); | ||
| 34 | cases.addBuildFile("test/standalone/link_frameworks/build.zig", .{ | ||
| 35 | .requires_macos_sdk = true, | ||
| 36 | }); | ||
| 37 | cases.addBuildFile("test/standalone/link_common_symbols_alignment/build.zig", .{}); | ||
| 38 | if (builtin.os.tag == .macos) { | ||
| 39 | cases.addBuildFile("test/standalone/link_import_tls_dylib/build.zig", .{}); | ||
| 40 | } | ||
| 41 | cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); | 22 | cases.addBuildFile("test/standalone/issue_339/build.zig", .{}); |
| 42 | cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); | 23 | cases.addBuildFile("test/standalone/issue_8550/build.zig", .{}); |
| 43 | cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); | 24 | cases.addBuildFile("test/standalone/issue_794/build.zig", .{}); |
| ... | @@ -69,16 +50,6 @@ pub fn addCases(cases: *tests.StandaloneContext) void { | ... | @@ -69,16 +50,6 @@ pub fn addCases(cases: *tests.StandaloneContext) void { |
| 69 | if (builtin.os.tag == .linux) { | 50 | if (builtin.os.tag == .linux) { |
| 70 | cases.addBuildFile("test/standalone/pie/build.zig", .{}); | 51 | cases.addBuildFile("test/standalone/pie/build.zig", .{}); |
| 71 | } | 52 | } |
| 72 | // Try to build and run an Objective-C executable. | ||
| 73 | cases.addBuildFile("test/standalone/objc/build.zig", .{ | ||
| 74 | .build_modes = true, | ||
| 75 | .requires_macos_sdk = true, | ||
| 76 | }); | ||
| 77 | // Try to build and run an Objective-C++ executable. | ||
| 78 | cases.addBuildFile("test/standalone/objcpp/build.zig", .{ | ||
| 79 | .build_modes = true, | ||
| 80 | .requires_macos_sdk = true, | ||
| 81 | }); | ||
| 82 | 53 | ||
| 83 | // Ensure the development tools are buildable. | 54 | // Ensure the development tools are buildable. |
| 84 | cases.add("tools/gen_spirv_spec.zig"); | 55 | cases.add("tools/gen_spirv_spec.zig"); |
test/standalone/link_common_symbols/a.c deleted-6| ... | @@ -1,6 +0,0 @@ | ||
| 1 | int i; | ||
| 2 | int j; | ||
| 3 | |||
| 4 | int add_to_i_and_j(int x) { | ||
| 5 | return x + i + j; | ||
| 6 | } | ||
test/standalone/link_common_symbols/b.c deleted-7| ... | @@ -1,7 +0,0 @@ | ||
| 1 | long i; | ||
| 2 | int j = 2; | ||
| 3 | int k; | ||
| 4 | |||
| 5 | void incr_i() { | ||
| 6 | i++; | ||
| 7 | } | ||
test/standalone/link_common_symbols/build.zig deleted-16| ... | @@ -1,16 +0,0 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const mode = b.standardReleaseOptions(); | ||
| 5 | |||
| 6 | const lib_a = b.addStaticLibrary("a", null); | ||
| 7 | lib_a.addCSourceFiles(&.{ "c.c", "a.c", "b.c" }, &.{"-fcommon"}); | ||
| 8 | lib_a.setBuildMode(mode); | ||
| 9 | |||
| 10 | const test_exe = b.addTest("main.zig"); | ||
| 11 | test_exe.setBuildMode(mode); | ||
| 12 | test_exe.linkLibrary(lib_a); | ||
| 13 | |||
| 14 | const test_step = b.step("test", "Test it"); | ||
| 15 | test_step.dependOn(&test_exe.step); | ||
| 16 | } | ||
test/standalone/link_common_symbols/c.c deleted-5| ... | @@ -1,5 +0,0 @@ | ||
| 1 | extern int k; | ||
| 2 | |||
| 3 | int common_defined_externally() { | ||
| 4 | return k; | ||
| 5 | } | ||
test/standalone/link_common_symbols/main.zig deleted-16| ... | @@ -1,16 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | |||
| 4 | extern fn common_defined_externally() c_int; | ||
| 5 | extern fn incr_i() void; | ||
| 6 | extern fn add_to_i_and_j(x: c_int) c_int; | ||
| 7 | |||
| 8 | test "undef shadows common symbol: issue #9937" { | ||
| 9 | try expect(common_defined_externally() == 0); | ||
| 10 | } | ||
| 11 | |||
| 12 | test "import C common symbols" { | ||
| 13 | incr_i(); | ||
| 14 | const res = add_to_i_and_j(2); | ||
| 15 | try expect(res == 5); | ||
| 16 | } | ||
test/standalone/link_common_symbols_alignment/a.c deleted-2| ... | @@ -1,2 +0,0 @@ | ||
| 1 | int foo; | ||
| 2 | __attribute__((aligned(4096))) int bar; | ||
test/standalone/link_common_symbols_alignment/build.zig deleted-16| ... | @@ -1,16 +0,0 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const mode = b.standardReleaseOptions(); | ||
| 5 | |||
| 6 | const lib_a = b.addStaticLibrary("a", null); | ||
| 7 | lib_a.addCSourceFiles(&.{"a.c"}, &.{"-fcommon"}); | ||
| 8 | lib_a.setBuildMode(mode); | ||
| 9 | |||
| 10 | const test_exe = b.addTest("main.zig"); | ||
| 11 | test_exe.setBuildMode(mode); | ||
| 12 | test_exe.linkLibrary(lib_a); | ||
| 13 | |||
| 14 | const test_step = b.step("test", "Test it"); | ||
| 15 | test_step.dependOn(&test_exe.step); | ||
| 16 | } | ||
test/standalone/link_common_symbols_alignment/main.zig deleted-9| ... | @@ -1,9 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | extern var foo: i32; | ||
| 4 | extern var bar: i32; | ||
| 5 | |||
| 6 | test { | ||
| 7 | try std.testing.expect(@ptrToInt(&foo) % 4 == 0); | ||
| 8 | try std.testing.expect(@ptrToInt(&bar) % 4096 == 0); | ||
| 9 | } | ||
test/standalone/link_frameworks/build.zig deleted-34| ... | @@ -1,34 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const CrossTarget = std.zig.CrossTarget; | ||
| 4 | |||
| 5 | fn isRunnableTarget(t: CrossTarget) bool { | ||
| 6 | // TODO I think we might be able to run this on Linux via Darling. | ||
| 7 | // Add a check for that here, and return true if Darling is available. | ||
| 8 | if (t.isNative() and t.getOsTag() == .macos) | ||
| 9 | return true | ||
| 10 | else | ||
| 11 | return false; | ||
| 12 | } | ||
| 13 | |||
| 14 | pub fn build(b: *Builder) void { | ||
| 15 | const mode = b.standardReleaseOptions(); | ||
| 16 | const target = b.standardTargetOptions(.{}); | ||
| 17 | |||
| 18 | const test_step = b.step("test", "Test the program"); | ||
| 19 | |||
| 20 | const exe = b.addExecutable("test", null); | ||
| 21 | b.default_step.dependOn(&exe.step); | ||
| 22 | exe.addCSourceFile("main.c", &[0][]const u8{}); | ||
| 23 | exe.setBuildMode(mode); | ||
| 24 | exe.setTarget(target); | ||
| 25 | exe.linkLibC(); | ||
| 26 | // TODO when we figure out how to ship framework stubs for cross-compilation, | ||
| 27 | // populate paths to the sysroot here. | ||
| 28 | exe.linkFramework("Cocoa"); | ||
| 29 | |||
| 30 | if (isRunnableTarget(target)) { | ||
| 31 | const run_cmd = exe.run(); | ||
| 32 | test_step.dependOn(&run_cmd.step); | ||
| 33 | } | ||
| 34 | } | ||
test/standalone/link_frameworks/main.c deleted-7| ... | @@ -1,7 +0,0 @@ | ||
| 1 | #include <assert.h> | ||
| 2 | #include <objc/runtime.h> | ||
| 3 | |||
| 4 | int main() { | ||
| 5 | assert(objc_getClass("NSObject") > 0); | ||
| 6 | assert(objc_getClass("NSApplication") > 0); | ||
| 7 | } | ||
test/standalone/link_import_tls_dylib/a.c deleted-1| ... | @@ -1 +0,0 @@ | ||
| 1 | _Thread_local int a; | ||
test/standalone/link_import_tls_dylib/build.zig deleted-16| ... | @@ -1,16 +0,0 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const mode = b.standardReleaseOptions(); | ||
| 5 | |||
| 6 | const lib = b.addSharedLibrary("a", null, b.version(1, 0, 0)); | ||
| 7 | lib.setBuildMode(mode); | ||
| 8 | lib.addCSourceFile("a.c", &.{}); | ||
| 9 | |||
| 10 | const test_exe = b.addTest("main.zig"); | ||
| 11 | test_exe.setBuildMode(mode); | ||
| 12 | test_exe.linkLibrary(lib); | ||
| 13 | |||
| 14 | const test_step = b.step("test", "Test it"); | ||
| 15 | test_step.dependOn(&test_exe.step); | ||
| 16 | } | ||
test/standalone/link_import_tls_dylib/main.zig deleted-7| ... | @@ -1,7 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | |||
| 3 | extern threadlocal var a: i32; | ||
| 4 | |||
| 5 | test { | ||
| 6 | try std.testing.expect(a == 0); | ||
| 7 | } | ||
test/standalone/link_interdependent_static_c_libs/a.c deleted-4| ... | @@ -1,4 +0,0 @@ | ||
| 1 | #include "a.h" | ||
| 2 | int32_t add(int32_t a, int32_t b) { | ||
| 3 | return a + b; | ||
| 4 | } | ||
test/standalone/link_interdependent_static_c_libs/a.h deleted-2| ... | @@ -1,2 +0,0 @@ | ||
| 1 | #include <stdint.h> | ||
| 2 | int32_t add(int32_t a, int32_t b); | ||
test/standalone/link_interdependent_static_c_libs/b.c deleted-6| ... | @@ -1,6 +0,0 @@ | ||
| 1 | #include "a.h" | ||
| 2 | #include "b.h" | ||
| 3 | |||
| 4 | int32_t sub(int32_t a, int32_t b) { | ||
| 5 | return add(a, -1 * b); | ||
| 6 | } | ||
test/standalone/link_interdependent_static_c_libs/b.h deleted-2| ... | @@ -1,2 +0,0 @@ | ||
| 1 | #include <stdint.h> | ||
| 2 | int32_t sub(int32_t a, int32_t b); | ||
test/standalone/link_interdependent_static_c_libs/build.zig deleted-24| ... | @@ -1,24 +0,0 @@ | ||
| 1 | const Builder = @import("std").build.Builder; | ||
| 2 | |||
| 3 | pub fn build(b: *Builder) void { | ||
| 4 | const mode = b.standardReleaseOptions(); | ||
| 5 | |||
| 6 | const lib_a = b.addStaticLibrary("a", null); | ||
| 7 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | ||
| 8 | lib_a.setBuildMode(mode); | ||
| 9 | lib_a.addIncludePath("."); | ||
| 10 | |||
| 11 | const lib_b = b.addStaticLibrary("b", null); | ||
| 12 | lib_b.addCSourceFile("b.c", &[_][]const u8{}); | ||
| 13 | lib_b.setBuildMode(mode); | ||
| 14 | lib_b.addIncludePath("."); | ||
| 15 | |||
| 16 | const test_exe = b.addTest("main.zig"); | ||
| 17 | test_exe.setBuildMode(mode); | ||
| 18 | test_exe.linkLibrary(lib_a); | ||
| 19 | test_exe.linkLibrary(lib_b); | ||
| 20 | test_exe.addIncludePath("."); | ||
| 21 | |||
| 22 | const test_step = b.step("test", "Test it"); | ||
| 23 | test_step.dependOn(&test_exe.step); | ||
| 24 | } | ||
test/standalone/link_interdependent_static_c_libs/main.zig deleted-8| ... | @@ -1,8 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const c = @cImport(@cInclude("b.h")); | ||
| 4 | |||
| 5 | test "import C sub" { | ||
| 6 | const result = c.sub(2, 1); | ||
| 7 | try expect(result == 1); | ||
| 8 | } | ||
test/standalone/link_static_lib_as_system_lib/a.c deleted-4| ... | @@ -1,4 +0,0 @@ | ||
| 1 | #include "a.h" | ||
| 2 | int32_t add(int32_t a, int32_t b) { | ||
| 3 | return a + b; | ||
| 4 | } | ||
test/standalone/link_static_lib_as_system_lib/a.h deleted-2| ... | @@ -1,2 +0,0 @@ | ||
| 1 | #include <stdint.h> | ||
| 2 | int32_t add(int32_t a, int32_t b); | ||
test/standalone/link_static_lib_as_system_lib/build.zig deleted-23| ... | @@ -1,23 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | |||
| 4 | pub fn build(b: *Builder) void { | ||
| 5 | const mode = b.standardReleaseOptions(); | ||
| 6 | |||
| 7 | const lib_a = b.addStaticLibrary("a", null); | ||
| 8 | lib_a.addCSourceFile("a.c", &[_][]const u8{}); | ||
| 9 | lib_a.setBuildMode(mode); | ||
| 10 | lib_a.addIncludePath("."); | ||
| 11 | lib_a.install(); | ||
| 12 | |||
| 13 | const test_exe = b.addTest("main.zig"); | ||
| 14 | test_exe.setBuildMode(mode); | ||
| 15 | test_exe.linkSystemLibrary("a"); // force linking liba.a as -la | ||
| 16 | test_exe.addSystemIncludePath("."); | ||
| 17 | const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable; | ||
| 18 | test_exe.addLibraryPath(search_path); | ||
| 19 | |||
| 20 | const test_step = b.step("test", "Test it"); | ||
| 21 | test_step.dependOn(b.getInstallStep()); | ||
| 22 | test_step.dependOn(&test_exe.step); | ||
| 23 | } | ||
test/standalone/link_static_lib_as_system_lib/main.zig deleted-8| ... | @@ -1,8 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const expect = std.testing.expect; | ||
| 3 | const c = @cImport(@cInclude("a.h")); | ||
| 4 | |||
| 5 | test "import C add" { | ||
| 6 | const result = c.add(2, 1); | ||
| 7 | try expect(result == 3); | ||
| 8 | } | ||
test/standalone/objc/Foo.h deleted-7| ... | @@ -1,7 +0,0 @@ | ||
| 1 | #import <Foundation/Foundation.h> | ||
| 2 | |||
| 3 | @interface Foo : NSObject | ||
| 4 | |||
| 5 | - (NSString *)name; | ||
| 6 | |||
| 7 | @end | ||
test/standalone/objc/Foo.m deleted-11| ... | @@ -1,11 +0,0 @@ | ||
| 1 | #import "Foo.h" | ||
| 2 | |||
| 3 | @implementation Foo | ||
| 4 | |||
| 5 | - (NSString *)name | ||
| 6 | { | ||
| 7 | NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; | ||
| 8 | return str; | ||
| 9 | } | ||
| 10 | |||
| 11 | @end | ||
test/standalone/objc/build.zig deleted-36| ... | @@ -1,36 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const CrossTarget = std.zig.CrossTarget; | ||
| 4 | |||
| 5 | fn isRunnableTarget(t: CrossTarget) bool { | ||
| 6 | // TODO I think we might be able to run this on Linux via Darling. | ||
| 7 | // Add a check for that here, and return true if Darling is available. | ||
| 8 | if (t.isNative() and t.getOsTag() == .macos) | ||
| 9 | return true | ||
| 10 | else | ||
| 11 | return false; | ||
| 12 | } | ||
| 13 | |||
| 14 | pub fn build(b: *Builder) void { | ||
| 15 | const mode = b.standardReleaseOptions(); | ||
| 16 | const target = b.standardTargetOptions(.{}); | ||
| 17 | |||
| 18 | const test_step = b.step("test", "Test the program"); | ||
| 19 | |||
| 20 | const exe = b.addExecutable("test", null); | ||
| 21 | b.default_step.dependOn(&exe.step); | ||
| 22 | exe.addIncludePath("."); | ||
| 23 | exe.addCSourceFile("Foo.m", &[0][]const u8{}); | ||
| 24 | exe.addCSourceFile("test.m", &[0][]const u8{}); | ||
| 25 | exe.setBuildMode(mode); | ||
| 26 | exe.setTarget(target); | ||
| 27 | exe.linkLibC(); | ||
| 28 | // TODO when we figure out how to ship framework stubs for cross-compilation, | ||
| 29 | // populate paths to the sysroot here. | ||
| 30 | exe.linkFramework("Foundation"); | ||
| 31 | |||
| 32 | if (isRunnableTarget(target)) { | ||
| 33 | const run_cmd = exe.run(); | ||
| 34 | test_step.dependOn(&run_cmd.step); | ||
| 35 | } | ||
| 36 | } | ||
test/standalone/objc/test.m deleted-12| ... | @@ -1,12 +0,0 @@ | ||
| 1 | #import "Foo.h" | ||
| 2 | #import <assert.h> | ||
| 3 | |||
| 4 | int main(int argc, char *argv[]) | ||
| 5 | { | ||
| 6 | @autoreleasepool { | ||
| 7 | Foo *foo = [[Foo alloc] init]; | ||
| 8 | NSString *result = [foo name]; | ||
| 9 | assert([result isEqualToString:@"Zig"]); | ||
| 10 | return 0; | ||
| 11 | } | ||
| 12 | } | ||
test/standalone/objcpp/Foo.h deleted-7| ... | @@ -1,7 +0,0 @@ | ||
| 1 | #import <Foundation/Foundation.h> | ||
| 2 | |||
| 3 | @interface Foo : NSObject | ||
| 4 | |||
| 5 | - (NSString *)name; | ||
| 6 | |||
| 7 | @end | ||
test/standalone/objcpp/Foo.mm deleted-11| ... | @@ -1,11 +0,0 @@ | ||
| 1 | #import "Foo.h" | ||
| 2 | |||
| 3 | @implementation Foo | ||
| 4 | |||
| 5 | - (NSString *)name | ||
| 6 | { | ||
| 7 | NSString *str = [[NSString alloc] initWithFormat:@"Zig"]; | ||
| 8 | return str; | ||
| 9 | } | ||
| 10 | |||
| 11 | @end | ||
test/standalone/objcpp/build.zig deleted-36| ... | @@ -1,36 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const Builder = std.build.Builder; | ||
| 3 | const CrossTarget = std.zig.CrossTarget; | ||
| 4 | |||
| 5 | fn isRunnableTarget(t: CrossTarget) bool { | ||
| 6 | // TODO I think we might be able to run this on Linux via Darling. | ||
| 7 | // Add a check for that here, and return true if Darling is available. | ||
| 8 | if (t.isNative() and t.getOsTag() == .macos) | ||
| 9 | return true | ||
| 10 | else | ||
| 11 | return false; | ||
| 12 | } | ||
| 13 | |||
| 14 | pub fn build(b: *Builder) void { | ||
| 15 | const mode = b.standardReleaseOptions(); | ||
| 16 | const target = b.standardTargetOptions(.{}); | ||
| 17 | |||
| 18 | const test_step = b.step("test", "Test the program"); | ||
| 19 | |||
| 20 | const exe = b.addExecutable("test", null); | ||
| 21 | b.default_step.dependOn(&exe.step); | ||
| 22 | exe.addIncludePath("."); | ||
| 23 | exe.addCSourceFile("Foo.mm", &[0][]const u8{}); | ||
| 24 | exe.addCSourceFile("test.mm", &[0][]const u8{}); | ||
| 25 | exe.setBuildMode(mode); | ||
| 26 | exe.setTarget(target); | ||
| 27 | exe.linkLibCpp(); | ||
| 28 | // TODO when we figure out how to ship framework stubs for cross-compilation, | ||
| 29 | // populate paths to the sysroot here. | ||
| 30 | exe.linkFramework("Foundation"); | ||
| 31 | |||
| 32 | if (isRunnableTarget(target)) { | ||
| 33 | const run_cmd = exe.run(); | ||
| 34 | test_step.dependOn(&run_cmd.step); | ||
| 35 | } | ||
| 36 | } | ||
test/standalone/objcpp/test.mm deleted-14| ... | @@ -1,14 +0,0 @@ | ||
| 1 | #import "Foo.h" | ||
| 2 | #import <assert.h> | ||
| 3 | #include <iostream> | ||
| 4 | |||
| 5 | int main(int argc, char *argv[]) | ||
| 6 | { | ||
| 7 | @autoreleasepool { | ||
| 8 | Foo *foo = [[Foo alloc] init]; | ||
| 9 | NSString *result = [foo name]; | ||
| 10 | std::cout << "Hello from C++ and " << [result UTF8String]; | ||
| 11 | assert([result isEqualToString:@"Zig"]); | ||
| 12 | return 0; | ||
| 13 | } | ||
| 14 | } | ||
test/tests.zig+24-1| ... | @@ -21,6 +21,7 @@ const assemble_and_link = @import("assemble_and_link.zig"); | ... | @@ -21,6 +21,7 @@ const assemble_and_link = @import("assemble_and_link.zig"); |
| 21 | const translate_c = @import("translate_c.zig"); | 21 | const translate_c = @import("translate_c.zig"); |
| 22 | const run_translated_c = @import("run_translated_c.zig"); | 22 | const run_translated_c = @import("run_translated_c.zig"); |
| 23 | const gen_h = @import("gen_h.zig"); | 23 | const gen_h = @import("gen_h.zig"); |
| 24 | const link = @import("link.zig"); | ||
| 24 | 25 | ||
| 25 | // Implementations | 26 | // Implementations |
| 26 | pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; | 27 | pub const TranslateCContext = @import("src/translate_c.zig").TranslateCContext; |
| ... | @@ -479,6 +480,27 @@ pub fn addStandaloneTests( | ... | @@ -479,6 +480,27 @@ pub fn addStandaloneTests( |
| 479 | return cases.step; | 480 | return cases.step; |
| 480 | } | 481 | } |
| 481 | 482 | ||
| 483 | pub fn addLinkTests( | ||
| 484 | b: *build.Builder, | ||
| 485 | test_filter: ?[]const u8, | ||
| 486 | modes: []const Mode, | ||
| 487 | enable_macos_sdk: bool, | ||
| 488 | ) *build.Step { | ||
| 489 | const cases = b.allocator.create(StandaloneContext) catch unreachable; | ||
| 490 | cases.* = StandaloneContext{ | ||
| 491 | .b = b, | ||
| 492 | .step = b.step("test-link", "Run the linker tests"), | ||
| 493 | .test_index = 0, | ||
| 494 | .test_filter = test_filter, | ||
| 495 | .modes = modes, | ||
| 496 | .skip_non_native = true, | ||
| 497 | .enable_macos_sdk = enable_macos_sdk, | ||
| 498 | .target = .{}, | ||
| 499 | }; | ||
| 500 | link.addCases(cases); | ||
| 501 | return cases.step; | ||
| 502 | } | ||
| 503 | |||
| 482 | pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { | 504 | pub fn addCliTests(b: *build.Builder, test_filter: ?[]const u8, modes: []const Mode) *build.Step { |
| 483 | _ = test_filter; | 505 | _ = test_filter; |
| 484 | _ = modes; | 506 | _ = modes; |
| ... | @@ -973,7 +995,8 @@ pub const StandaloneContext = struct { | ... | @@ -973,7 +995,8 @@ pub const StandaloneContext = struct { |
| 973 | } | 995 | } |
| 974 | 996 | ||
| 975 | if (features.cross_targets and !self.target.isNative()) { | 997 | if (features.cross_targets and !self.target.isNative()) { |
| 976 | const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{self.target.zigTriple(b.allocator) catch unreachable}) catch unreachable; | 998 | const target_triple = self.target.zigTriple(b.allocator) catch unreachable; |
| 999 | const target_arg = fmt.allocPrint(b.allocator, "-Dtarget={s}", .{target_triple}) catch unreachable; | ||
| 977 | zig_args.append(target_arg) catch unreachable; | 1000 | zig_args.append(target_arg) catch unreachable; |
| 978 | } | 1001 | } |
| 979 | 1002 |