authorgravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-02-04 20:44:45+01:00
committergravatar for alex@alexrp.comAlex Rønne Petersen <alex@alexrp.com> 2026-02-04 20:44:45+01:00
logc3edf0ba641fcaf9ccc93e27ca3bf140d4b8e84c
tree43a268a863cc6c1076fe8e99becec9efb292ed94
parentfce7878a9149caa80433e6d650e0bd7f60d345fd
parent012be3efd779764de8cb910f3d07a25c13861c48
signaturebadge-check Signed by SSH key SHA256:7B/LJ7bpR1eX8aCXSr4mtd5M45VMPKcx9zY8e95b5QM

Merge branch 'netbsd-ci'


3 files changed, 90 insertions(+), 52 deletions(-)

.forgejo/workflows/ci.yaml+20-20
...@@ -197,26 +197,26 @@ jobs:...@@ -197,26 +197,26 @@ jobs:
197 run: sh ci/x86_64-linux-release.sh197 run: sh ci/x86_64-linux-release.sh
198 timeout-minutes: 360198 timeout-minutes: 360
199199
200 #x86_64-netbsd-debug:200 x86_64-netbsd-debug:
201 # runs-on: [self-hosted, x86_64-netbsd]201 runs-on: [self-hosted, x86_64-netbsd]
202 # steps:202 steps:
203 # - name: Checkout203 - name: Checkout
204 # uses: https://codeberg.org/ziglang/checkout@19af6bac491e2534a4687a50ee84fa7f13258d28204 uses: https://codeberg.org/ziglang/checkout@19af6bac491e2534a4687a50ee84fa7f13258d28
205 # with:205 with:
206 # fetch-depth: 0206 fetch-depth: 0
207 # - name: Build and Test207 - name: Build and Test
208 # run: sh ci/x86_64-netbsd-debug.sh208 run: sh ci/x86_64-netbsd-debug.sh
209 # timeout-minutes: 120209 timeout-minutes: 120
210 #x86_64-netbsd-release:210 x86_64-netbsd-release:
211 # runs-on: [self-hosted, x86_64-netbsd]211 runs-on: [self-hosted, x86_64-netbsd]
212 # steps:212 steps:
213 # - name: Checkout213 - name: Checkout
214 # uses: https://codeberg.org/ziglang/checkout@19af6bac491e2534a4687a50ee84fa7f13258d28214 uses: https://codeberg.org/ziglang/checkout@19af6bac491e2534a4687a50ee84fa7f13258d28
215 # with:215 with:
216 # fetch-depth: 0216 fetch-depth: 0
217 # - name: Build and Test217 - name: Build and Test
218 # run: sh ci/x86_64-netbsd-release.sh218 run: sh ci/x86_64-netbsd-release.sh
219 # timeout-minutes: 120219 timeout-minutes: 120
220220
221 x86_64-openbsd-debug:221 x86_64-openbsd-debug:
222 runs-on: [self-hosted, x86_64-openbsd]222 runs-on: [self-hosted, x86_64-openbsd]
lib/std/Io/Threaded.zig+54-32
...@@ -628,6 +628,7 @@ const Thread = struct {...@@ -628,6 +628,7 @@ const Thread = struct {
628 cancel_protection: Io.CancelProtection,628 cancel_protection: Io.CancelProtection,
629 /// Always released when `Status.cancelation` is set to `.parked`.629 /// Always released when `Status.cancelation` is set to `.parked`.
630 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,630 futex_waiter: if (use_parking_futex) ?*parking_futex.Waiter else ?noreturn,
631 unpark_flag: UnparkFlag,
631632
632 csprng: Csprng,633 csprng: Csprng,
633634
...@@ -1018,6 +1019,7 @@ const Thread = struct {...@@ -1018,6 +1019,7 @@ const Thread = struct {
1018 if (thread.futex_waiter) |futex_waiter| {1019 if (thread.futex_waiter) |futex_waiter| {
1019 parking_futex.removeCanceledWaiter(futex_waiter);1020 parking_futex.removeCanceledWaiter(futex_waiter);
1020 }1021 }
1022 if (need_unpark_flag) setUnparkFlag(&thread.unpark_flag);
1021 unpark(&.{thread.id}, null);1023 unpark(&.{thread.id}, null);
1022 return false;1024 return false;
1023 },1025 },
...@@ -1559,6 +1561,7 @@ fn worker(t: *Threaded) void {...@@ -1559,6 +1561,7 @@ fn worker(t: *Threaded) void {
1559 }),1561 }),
1560 .cancel_protection = .unblocked,1562 .cancel_protection = .unblocked,
1561 .futex_waiter = undefined,1563 .futex_waiter = undefined,
1564 .unpark_flag = unpark_flag_init,
1562 .csprng = .{},1565 .csprng = .{},
1563 };1566 };
1564 Thread.current = &thread;1567 Thread.current = &thread;
...@@ -17007,6 +17010,7 @@ const parking_futex = struct {...@@ -17007,6 +17010,7 @@ const parking_futex = struct {
17007 /// * Unparking the thread (*after* the above, so that the `Waiter` does not go out of scope17010 /// * Unparking the thread (*after* the above, so that the `Waiter` does not go out of scope
17008 /// while it is still in the `Bucket`).17011 /// while it is still in the `Bucket`).
17009 thread_status: *std.atomic.Value(Thread.Status),17012 thread_status: *std.atomic.Value(Thread.Status),
17013 unpark_flag: if (need_unpark_flag) *UnparkFlag else void,
17010 };17014 };
1701117015
17012 fn bucketForAddress(address: usize) *Bucket {17016 fn bucketForAddress(address: usize) *Bucket {
...@@ -17045,9 +17049,11 @@ const parking_futex = struct {...@@ -17045,9 +17049,11 @@ const parking_futex = struct {
17045 .address = @intFromPtr(ptr),17049 .address = @intFromPtr(ptr),
17046 .tid = self_tid,17050 .tid = self_tid,
17047 .thread_status = undefined, // populated in critical section17051 .thread_status = undefined, // populated in critical section
17052 .unpark_flag = undefined, // populated in critical section
17048 };17053 };
1704917054
17050 var status_buf: std.atomic.Value(Thread.Status) = undefined;17055 var status_buf: std.atomic.Value(Thread.Status) = undefined;
17056 var unpark_flag_buf: UnparkFlag = unpark_flag_init;
1705117057
17052 {17058 {
17053 bucket.mutex.lock();17059 bucket.mutex.lock();
...@@ -17062,7 +17068,7 @@ const parking_futex = struct {...@@ -17062,7 +17068,7 @@ const parking_futex = struct {
1706217068
17063 // This is in the critical section to avoid marking the thread as parked until we're17069 // This is in the critical section to avoid marking the thread as parked until we're
17064 // certain that we're actually going to park.17070 // certain that we're actually going to park.
17065 waiter.thread_status = status: {17071 waiter.thread_status, waiter.unpark_flag = status: {
17066 cancelable: {17072 cancelable: {
17067 if (uncancelable) break :cancelable;17073 if (uncancelable) break :cancelable;
17068 const thread = opt_thread orelse break :cancelable;17074 const thread = opt_thread orelse break :cancelable;
...@@ -17090,19 +17096,19 @@ const parking_futex = struct {...@@ -17090,19 +17096,19 @@ const parking_futex = struct {
17090 .blocked_canceling => unreachable,17096 .blocked_canceling => unreachable,
17091 }17097 }
17092 // We could now be unparked for a cancelation at any time!17098 // We could now be unparked for a cancelation at any time!
17093 break :status &thread.status;17099 break :status .{ &thread.status, if (need_unpark_flag) &thread.unpark_flag };
17094 }17100 }
17095 // This is an uncancelable wait, so just use `status_buf`. Note that the value of17101 // This is an uncancelable wait, so just use `status_buf`. Note that the value of
17096 // `status_buf.awaitable` is irrelevant because this is only visible to futex code,17102 // `status_buf.awaitable` is irrelevant because this is only visible to futex code,
17097 // while only cancelation cares about `awaitable`.17103 // while only cancelation cares about `awaitable`.
17098 status_buf.raw = .{ .cancelation = .parked, .awaitable = .null };17104 status_buf.raw = .{ .cancelation = .parked, .awaitable = .null };
17099 break :status &status_buf;17105 break :status .{ &status_buf, if (need_unpark_flag) &unpark_flag_buf };
17100 };17106 };
1710117107
17102 bucket.waiters.append(&waiter.node);17108 bucket.waiters.append(&waiter.node);
17103 }17109 }
1710417110
17105 if (park(timeout, ptr, waiter.thread_status)) {17111 if (park(timeout, ptr, waiter.unpark_flag)) {
17106 // We were unparked by either `wake` or cancelation, so our current status is either17112 // We were unparked by either `wake` or cancelation, so our current status is either
17107 // `.none` or `.canceling`. In either case, they've already removed `waiter` from17113 // `.none` or `.canceling`. In either case, they've already removed `waiter` from
17108 // `bucket`, so we have nothing more to do!17114 // `bucket`, so we have nothing more to do!
...@@ -17127,7 +17133,7 @@ const parking_futex = struct {...@@ -17127,7 +17133,7 @@ const parking_futex = struct {
17127 // to unpark us. Whoever did that will remove us from `bucket`. Wait for17133 // to unpark us. Whoever did that will remove us from `bucket`. Wait for
17128 // that (and drop the unpark request in doing so).17134 // that (and drop the unpark request in doing so).
17129 // New status is `.none` or `.canceling` respectively.17135 // New status is `.none` or `.canceling` respectively.
17130 park(.none, ptr, waiter.thread_status) catch |e| switch (e) {17136 park(.none, ptr, waiter.unpark_flag) catch |e| switch (e) {
17131 error.Timeout => unreachable,17137 error.Timeout => unreachable,
17132 };17138 };
17133 },17139 },
...@@ -17201,6 +17207,7 @@ const parking_futex = struct {...@@ -17201,6 +17207,7 @@ const parking_futex = struct {
17201 waking_head = node.next;17207 waking_head = node.next;
17202 const waiter: *Waiter = @fieldParentPtr("node", node);17208 const waiter: *Waiter = @fieldParentPtr("node", node);
17203 unpark_buf[unpark_len] = waiter.tid;17209 unpark_buf[unpark_len] = waiter.tid;
17210 if (need_unpark_flag) setUnparkFlag(waiter.unpark_flag);
17204 unpark_len += 1;17211 unpark_len += 1;
17205 if (unpark_len == unpark_buf.len) {17212 if (unpark_len == unpark_buf.len) {
17206 unpark(&unpark_buf, ptr);17213 unpark(&unpark_buf, ptr);
...@@ -17249,7 +17256,7 @@ const parking_sleep = struct {...@@ -17249,7 +17256,7 @@ const parking_sleep = struct {
17249 .blocked_canceling => unreachable,17256 .blocked_canceling => unreachable,
17250 }17257 }
17251 }17258 }
17252 if (park(timeout, null, &thread.status)) {17259 if (park(timeout, null, if (need_unpark_flag) &thread.unpark_flag)) {
17253 // The only reason this could possibly happen is cancelation.17260 // The only reason this could possibly happen is cancelation.
17254 const old_status = thread.status.load(.monotonic);17261 const old_status = thread.status.load(.monotonic);
17255 assert(old_status.cancelation == .canceling);17262 assert(old_status.cancelation == .canceling);
...@@ -17272,7 +17279,7 @@ const parking_sleep = struct {...@@ -17272,7 +17279,7 @@ const parking_sleep = struct {
17272 // us for a cancelation. Whoever did that will have called `unpark`, so17279 // us for a cancelation. Whoever did that will have called `unpark`, so
17273 // drop that unpark request by waiting for it.17280 // drop that unpark request by waiting for it.
17274 // Status is still `.canceling`.17281 // Status is still `.canceling`.
17275 park(.none, null, &thread.status) catch |e| switch (e) {17282 park(.none, null, if (need_unpark_flag) &thread.unpark_flag) catch |e| switch (e) {
17276 error.Timeout => unreachable,17283 error.Timeout => unreachable,
17277 };17284 };
17278 return;17285 return;
...@@ -17288,8 +17295,8 @@ const parking_sleep = struct {...@@ -17288,8 +17295,8 @@ const parking_sleep = struct {
17288 }17295 }
17289 }17296 }
17290 // Uncancelable sleep; we expect not to be manually unparked.17297 // Uncancelable sleep; we expect not to be manually unparked.
17291 var dummy_status: std.atomic.Value(Thread.Status) = .init(.{ .cancelation = .parked, .awaitable = .null });17298 var dummy_flag: UnparkFlag = unpark_flag_init;
17292 if (park(timeout, null, &dummy_status)) {17299 if (park(timeout, null, if (need_unpark_flag) &dummy_flag)) {
17293 unreachable; // unexpected unpark17300 unreachable; // unexpected unpark
17294 } else |err| switch (err) {17301 } else |err| switch (err) {
17295 error.Timeout => return,17302 error.Timeout => return,
...@@ -17322,7 +17329,7 @@ const ParkingMutex = struct {...@@ -17322,7 +17329,7 @@ const ParkingMutex = struct {
17322 }17329 }
17323 };17330 };
17324 const Waiter = struct {17331 const Waiter = struct {
17325 status: std.atomic.Value(Thread.Status),17332 unpark_flag: UnparkFlag,
17326 /// Never modified once the `Waiter` is in the linked list.17333 /// Never modified once the `Waiter` is in the linked list.
17327 next: ?*Waiter,17334 next: ?*Waiter,
17328 /// Never modified once the `Waiter` is in the linked list.17335 /// Never modified once the `Waiter` is in the linked list.
...@@ -17345,7 +17352,7 @@ const ParkingMutex = struct {...@@ -17345,7 +17352,7 @@ const ParkingMutex = struct {
17345 const self_tid = if (Thread.current) |t| t.id else std.Thread.getCurrentId();17352 const self_tid = if (Thread.current) |t| t.id else std.Thread.getCurrentId();
17346 var waiter: Waiter = .{17353 var waiter: Waiter = .{
17347 .next = old_waiter,17354 .next = old_waiter,
17348 .status = .init(.{ .cancelation = .parked, .awaitable = .null }),17355 .unpark_flag = unpark_flag_init,
17349 .tid = self_tid,17356 .tid = self_tid,
17350 };17357 };
17351 if (m.state.cmpxchgWeak(17358 if (m.state.cmpxchgWeak(
...@@ -17357,11 +17364,9 @@ const ParkingMutex = struct {...@@ -17357,11 +17364,9 @@ const ParkingMutex = struct {
17357 continue :state new_state;17364 continue :state new_state;
17358 }17365 }
17359 // We're now in the list of waiters---park until we're given the lock.17366 // We're now in the list of waiters---park until we're given the lock.
17360 park(.none, m, &waiter.status) catch |err| switch (err) {17367 park(.none, m, if (need_unpark_flag) &waiter.unpark_flag) catch |err| switch (err) {
17361 error.Timeout => unreachable,17368 error.Timeout => unreachable,
17362 };17369 };
17363 // We now hold the lock.
17364 assert(waiter.status.load(.monotonic).cancelation == .none);
17365 return;17370 return;
17366 },17371 },
17367 }17372 }
...@@ -17383,7 +17388,7 @@ const ParkingMutex = struct {...@@ -17383,7 +17388,7 @@ const ParkingMutex = struct {
17383 _ => |last_state| {17388 _ => |last_state| {
17384 // The logic here does not have ABA problems, and does some accesses non-atomically,17389 // The logic here does not have ABA problems, and does some accesses non-atomically,
17385 // because `Waiter.next` is owned by the lock holder (that's us!) once the waiter is17390 // because `Waiter.next` is owned by the lock holder (that's us!) once the waiter is
17386 // in the linked list, up until we set `Waiter.status` to `.none`.17391 // in the linked list, up until we unpark the waiter.
1738717392
17388 // Run through the waiter list to the end to ensure fairness. This is obviously not17393 // Run through the waiter list to the end to ensure fairness. This is obviously not
17389 // ideal, but it shouldn't be a big deal in practice provided the critical section17394 // ideal, but it shouldn't be a big deal in practice provided the critical section
...@@ -17412,8 +17417,8 @@ const ParkingMutex = struct {...@@ -17412,8 +17417,8 @@ const ParkingMutex = struct {
17412 }17417 }
17413 }17418 }
17414 // Now we're ready to actually hand the lock over to them.17419 // Now we're ready to actually hand the lock over to them.
17415 const tid = waiter.tid; // load this before the store below potentially invalidates `waiter`17420 const tid = waiter.tid; // load before the unpark below potentially invalidates `waiter`
17416 waiter.status.store(.{ .cancelation = .none, .awaitable = .null }, .release); // release lock17421 if (need_unpark_flag) setUnparkFlag(&waiter.unpark_flag);
17417 unpark(&.{tid}, m);17422 unpark(&.{tid}, m);
17418 return;17423 return;
17419 },17424 },
...@@ -17451,15 +17456,34 @@ fn timeoutToWindowsInterval(timeout: Io.Timeout) ?windows.LARGE_INTEGER {...@@ -17451,15 +17456,34 @@ fn timeoutToWindowsInterval(timeout: Io.Timeout) ?windows.LARGE_INTEGER {
17451 }17456 }
17452}17457}
1745317458
17459/// The API on NetBSD and Illumos sucks and can unpark spuriously (well, it *can't*, but signals
17460/// cause an indistinguishable unblock, and libpthread really likes to leave unparks pending).
17461/// As such, on these targets only, we need to pass around a flag to track whether a thread is
17462/// "actually" being unparked.
17463const need_unpark_flag = switch (native_os) {
17464 .netbsd, .illumos => true,
17465 else => false,
17466};
17467const UnparkFlag = if (need_unpark_flag) std.atomic.Value(bool) else void;
17468const unpark_flag_init: UnparkFlag = if (need_unpark_flag) .init(false);
17469/// Must be called before `unpark`. After this function is called, the thread may be unparked at any
17470/// time, so the caller must not reference values on its stack.
17471fn setUnparkFlag(f: *UnparkFlag) void {
17472 f.store(true, .release);
17473}
17474
17475/// The type passed into `unpark` for the thread ID. You'd think this was just a `std.Thread.Id`,
17476/// but it seems that someone at Microsoft forgot how big their TIDs are supposed to be.
17477const UnparkTid = switch (native_os) {
17478 .windows => usize,
17479 else => std.Thread.Id,
17480};
17481
17454fn park(17482fn park(
17455 timeout: Io.Timeout,17483 timeout: Io.Timeout,
17456 /// This value has no semantic effect, but may allow the OS to optimize the operation.17484 /// This value has no semantic effect, but may allow the OS to optimize the operation.
17457 addr_hint: ?*const anyopaque,17485 addr_hint: ?*const anyopaque,
17458 /// The API on NetBSD and Illumos sucks and can unpark spuriously (well, it *can't*, but signals17486 unpark_flag: if (need_unpark_flag) *UnparkFlag else void,
17459 /// cause an indistinguishable unblock, and libpthread really likes to leave unparks pending).
17460 /// As such, on these targets only, this `status` is checked to determine if an unpark is real.
17461 /// no way to differentiate
17462 status: *std.atomic.Value(Thread.Status),
17463) error{Timeout}!void {17487) error{Timeout}!void {
17464 comptime assert(use_parking_futex or use_parking_sleep);17488 comptime assert(use_parking_futex or use_parking_sleep);
17465 switch (native_os) {17489 switch (native_os) {
...@@ -17502,7 +17526,7 @@ fn park(...@@ -17502,7 +17526,7 @@ fn park(
17502 };17526 };
17503 // It's okay to pass the same timeout in a loop. If it's a duration, the OS actually17527 // It's okay to pass the same timeout in a loop. If it's a duration, the OS actually
17504 // writes the remaining time into the buffer when the syscall returns.17528 // writes the remaining time into the buffer when the syscall returns.
17505 while (status.load(.monotonic).cancelation == .parked) {17529 while (!unpark_flag.swap(false, .acquire)) {
17506 switch (posix.errno(std.c._lwp_park(17530 switch (posix.errno(std.c._lwp_park(
17507 if (clock_real) .REALTIME else .MONOTONIC,17531 if (clock_real) .REALTIME else .MONOTONIC,
17508 .{ .ABSTIME = abstime },17532 .{ .ABSTIME = abstime },
...@@ -17523,12 +17547,6 @@ fn park(...@@ -17523,12 +17547,6 @@ fn park(
17523 else => comptime unreachable,17547 else => comptime unreachable,
17524 }17548 }
17525}17549}
17526
17527const UnparkTid = switch (native_os) {
17528 // `NtAlertMultipleThreadByThreadId` is weird and wants 64-bit thread IDs?
17529 .windows => usize,
17530 else => std.Thread.Id,
17531};
17532/// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation.17550/// `addr_hint` has no semantic effect, but may allow the OS to optimize this operation.
17533fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {17551fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
17534 comptime assert(use_parking_futex or use_parking_sleep);17552 comptime assert(use_parking_futex or use_parking_sleep);
...@@ -17548,8 +17566,8 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {...@@ -17548,8 +17566,8 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
17548 switch (posix.errno(std.c._lwp_unpark_all(@ptrCast(tids.ptr), tids.len, addr_hint))) {17566 switch (posix.errno(std.c._lwp_unpark_all(@ptrCast(tids.ptr), tids.len, addr_hint))) {
17549 .SUCCESS => return,17567 .SUCCESS => return,
17550 // For errors, fall through to a loop over `tids`, though this is only expected to17568 // For errors, fall through to a loop over `tids`, though this is only expected to
17551 // be possible for ENOMEM (and even that is questionable).17569 // be possible for ENOMEM (even that is questionable) and ESRCH (see comment below).
17552 .SRCH => recoverableOsBugDetected(),17570 .SRCH => {},
17553 .FAULT => recoverableOsBugDetected(),17571 .FAULT => recoverableOsBugDetected(),
17554 .INVAL => recoverableOsBugDetected(),17572 .INVAL => recoverableOsBugDetected(),
17555 .NOMEM => {},17573 .NOMEM => {},
...@@ -17558,7 +17576,11 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {...@@ -17558,7 +17576,11 @@ fn unpark(tids: []const UnparkTid, addr_hint: ?*const anyopaque) void {
17558 for (tids) |tid| {17576 for (tids) |tid| {
17559 switch (posix.errno(std.c._lwp_unpark(@bitCast(tid), addr_hint))) {17577 switch (posix.errno(std.c._lwp_unpark(@bitCast(tid), addr_hint))) {
17560 .SUCCESS => {},17578 .SUCCESS => {},
17561 .SRCH => recoverableOsBugDetected(),17579 .SRCH => {
17580 // This can happen in a rare race: the thread might have been spuriously
17581 // unparked, so already observed the changing status, and from there have
17582 // exited. That's okay, because the thread has woken up like we wanted.
17583 },
17562 else => recoverableOsBugDetected(),17584 else => recoverableOsBugDetected(),
17563 }17585 }
17564 }17586 }
src/link/Lld.zig+16
...@@ -356,6 +356,16 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {...@@ -356,6 +356,16 @@ fn linkAsArchive(lld: *Lld, arena: Allocator) !void {
356 if (bad) return error.UnableToWriteArchive;356 if (bad) return error.UnableToWriteArchive;
357}357}
358358
359fn addCommonArgs(argv: *std.array_list.Managed([]const u8), coff: bool) !void {
360 if (builtin.os.tag == .netbsd) {
361 // NetBSD 10.1's `malloc` appears to have some nasty bugs that occur
362 // when doing parallel linking in LLD, manifesting as input and/or
363 // output section memory randomly being unmapped. So just don't do
364 // parallel linking for now.
365 try argv.append(if (coff) "-threads:1" else "--threads=1");
366 }
367}
368
359fn coffLink(lld: *Lld, arena: Allocator) !void {369fn coffLink(lld: *Lld, arena: Allocator) !void {
360 const comp = lld.base.comp;370 const comp = lld.base.comp;
361 const gpa = comp.gpa;371 const gpa = comp.gpa;
...@@ -418,6 +428,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {...@@ -418,6 +428,7 @@ fn coffLink(lld: *Lld, arena: Allocator) !void {
418 // it calls exit() and does not reset all global data between invocations.428 // it calls exit() and does not reset all global data between invocations.
419 const linker_command = "lld-link";429 const linker_command = "lld-link";
420 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });430 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
431 try addCommonArgs(&argv, true);
421432
422 if (target.isMinGW()) {433 if (target.isMinGW()) {
423 try argv.append("-lldmingw");434 try argv.append("-lldmingw");
...@@ -836,6 +847,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {...@@ -836,6 +847,8 @@ fn elfLink(lld: *Lld, arena: Allocator) !void {
836 // it calls exit() and does not reset all global data between invocations.847 // it calls exit() and does not reset all global data between invocations.
837 const linker_command = "ld.lld";848 const linker_command = "ld.lld";
838 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });849 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
850 try addCommonArgs(&argv, false);
851
839 if (is_obj) {852 if (is_obj) {
840 try argv.append("-r");853 try argv.append("-r");
841 }854 }
...@@ -1401,6 +1414,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {...@@ -1401,6 +1414,8 @@ fn wasmLink(lld: *Lld, arena: Allocator) !void {
1401 // it calls exit() and does not reset all global data between invocations.1414 // it calls exit() and does not reset all global data between invocations.
1402 const linker_command = "wasm-ld";1415 const linker_command = "wasm-ld";
1403 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });1416 try argv.appendSlice(&[_][]const u8{ comp.self_exe_path.?, linker_command });
1417 try addCommonArgs(&argv, false);
1418
1404 try argv.append("--error-limit=0");1419 try argv.append("--error-limit=0");
14051420
1406 if (comp.config.lto != .none) {1421 if (comp.config.lto != .none) {
...@@ -1724,6 +1739,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi...@@ -1724,6 +1739,7 @@ fn spawnLld(comp: *Compilation, arena: Allocator, argv: []const []const u8) !voi
1724 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});1739 if (stderr.len > 0) log.warn("unexpected LLD stderr:\n{s}", .{stderr});
1725}1740}
17261741
1742const builtin = @import("builtin");
1727const std = @import("std");1743const std = @import("std");
1728const Io = std.Io;1744const Io = std.Io;
1729const Allocator = std.mem.Allocator;1745const Allocator = std.mem.Allocator;