authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-20 15:27:17+02:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2026-10-20 15:27:17+02:00
log67f773beaeb299024676d21d7f15342a100690d5
treec9d9a40dc149eb09683d89c97e62bacc756816dc
parent4bf2fe2ed8cac4305eb86c9da107803c32c535d1
parent6a5ce4aa99dc5c3ca86187845de24bd521644056

Merge pull request 'Elf2: refactors, fixes, and performance enhancements' (#37326) from mlugg/elf2-thingies into master

Reviewed-on: https://codeberg.org/ziglang/zig/pulls/37326

13 files changed, 1177 insertions(+), 923 deletions(-)

build.zig+13-8
......@@ -352,8 +352,9 @@ pub fn build(b: *std.Build) !void {
352352 const cmake_cfg = if (static_llvm) null else blk: {
353353 const io = b.graph.io;
354354 const cwd: Io.Dir = .cwd();
355 if (findConfigH(b, config_h_path_option)) |config_h_path| {
356 const file_contents = cwd.readFileAlloc(io, config_h_path, arena, .limited(max_config_h_bytes)) catch unreachable;
355 if (try findConfigH(b, config_h_path_option)) |config_h_path| {
356 b.dependOnFileContents(b.graph.cwdRelativePath(config_h_path));
357 const file_contents = try cwd.readFileAlloc(io, config_h_path, arena, .limited(max_config_h_bytes));
357358 break :blk parseConfigH(b, file_contents);
358359 } else {
359360 std.log.warn("config.h could not be located automatically", .{});
......@@ -1187,7 +1188,7 @@ const CMakeConfig = struct {
11871188
11881189const max_config_h_bytes = 1 * 1024 * 1024;
11891190
1190fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
1191fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) std.mem.Allocator.Error!?[]const u8 {
11911192 const arena = b.graph.arena;
11921193 const io = b.graph.io;
11931194 const cwd: Io.Dir = .cwd();
......@@ -1205,22 +1206,26 @@ fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
12051206
12061207 var check_dir = fs.path.dirname(b.graph.zig_exe).?;
12071208 while (true) {
1208 var dir = cwd.openDir(io, check_dir, .{}) catch unreachable;
1209 var dir = cwd.openDir(io, check_dir, .{}) catch |err| {
1210 std.debug.panic("failed to open {q}: {t}", .{ check_dir, err });
1211 };
12091212 defer dir.close(io);
12101213
12111214 // Check if config.h is present in dir
12121215 var config_h_or_err = dir.openFile(io, "config.h", .{});
12131216 if (config_h_or_err) |*file| {
12141217 file.close(io);
1215 return fs.path.join(
1218 return try fs.path.join(
12161219 arena,
12171220 &[_][]const u8{ check_dir, "config.h" },
1218 ) catch unreachable;
1219 } else |e| switch (e) {
1221 );
1222 } else |err| switch (err) {
12201223 error.FileNotFound => {},
1221 else => unreachable,
1224 else => |e| std.debug.panic("failed to check for config.h in {q}: {t}", .{ check_dir, e }),
12221225 }
12231226
1227 b.dependOnFileMetadata(b.graph.cwdRelativePath(check_dir).path(b, "config.h"));
1228
12241229 // Check if we reached the source root by looking for .git, and bail if so
12251230 var git_dir_or_err = dir.openDir(io, ".git", .{});
12261231 if (git_dir_or_err) |*git_dir| {
lib/compiler/Maker.zig+59-11
......@@ -1101,6 +1101,9 @@ pub fn main(init: process.Init.Minimal) !void {
11011101 if (help_menu) printUsageWithoutConfiguration(&graph, "build.zig compilation failure");
11021102 break :c true;
11031103 },
1104 error.StdoutWriteFailed => {
1105 fatal("failed writing to stdout: {t}", .{stdout_writer_allocation.err.?});
1106 },
11041107 else => |e| w: {
11051108 log.err("configuration failed: {t}", .{e});
11061109 break :w false;
......@@ -1163,7 +1166,14 @@ const ConfigureOptions = struct {
11631166 error_bundle: ?*ErrorBundle = null,
11641167};
11651168
1166fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1169const ConfigureError = Allocator.Error || Io.Cancelable || error{
1170 Unexpected,
1171 AlreadyReported,
1172 FailedButCacheIntact,
1173 StdoutWriteFailed,
1174};
1175
1176fn configure(graph: *Graph, options: ConfigureOptions) ConfigureError!ScannedConfig {
11671177 const configure_argv = options.configure_argv;
11681178 const gpa = graph.cache.gpa;
11691179 const io = graph.io;
......@@ -1328,7 +1338,19 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
13281338 defer job_queue.deinit();
13291339
13301340 if (options.system_pkg_dir_path == null) {
1331 try http_client.initDefaultProxies(arena, &graph.environ_map);
1341 http_client.initDefaultProxies(arena, &graph.environ_map) catch |err| switch (err) {
1342 error.NameTooLong,
1343 error.InvalidHostName,
1344 error.InvalidPort,
1345 error.InvalidFormat,
1346 error.UnexpectedCharacter,
1347 error.UriMissingHost,
1348 => |e| {
1349 log.err("failed to initialize HTTP client: {t}", .{e});
1350 return error.AlreadyReported;
1351 },
1352 else => |e| return e,
1353 };
13321354 }
13331355
13341356 try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);
......@@ -1410,10 +1432,10 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
14101432 .writer = initStdoutWriter(io),
14111433 .mode = try .detect(io, .stdout(), NO_COLOR, CLICOLOR_FORCE),
14121434 }, arena, graph.global_cache_root, &job_queue, &fetch, &fork_set) catch |err| switch (err) {
1413 error.WriteFailed => return stdout_writer_allocation.err.?,
1435 error.WriteFailed => return error.StdoutWriteFailed,
14141436 else => |e| return e,
14151437 };
1416 try stdout_writer_allocation.flush();
1438 stdout_writer_allocation.flush() catch return error.StdoutWriteFailed;
14171439 process.exit(0);
14181440 }
14191441
......@@ -1444,11 +1466,17 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
14441466 .root_dir = graph.local_cache_root,
14451467 .sub_path = try arena.print("o/{s}/dependencies.zig", .{&hex_digest}),
14461468 };
1447 var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic(
1469 var atomic_file = dependencies_zig_path.root_dir.handle.createFileAtomic(
14481470 io,
14491471 dependencies_zig_path.sub_path,
14501472 .{ .make_path = true, .replace = true },
1451 );
1473 ) catch |err| switch (err) {
1474 error.Canceled => |e| return e,
1475 else => |e| {
1476 log.err("failed to write {qf}: {t}", .{ dependencies_zig_path, e });
1477 return error.AlreadyReported;
1478 },
1479 };
14521480 defer atomic_file.deinit(io);
14531481 atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err|
14541482 fatal("writing dependencies.zig contents: {t}", .{err});
......@@ -1596,11 +1624,17 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
15961624 .root_dir = graph.local_cache_root,
15971625 .sub_path = try arena.print("tmp" ++ Dir.path.sep_str ++ "{x}", .{randInt(io, u64)}),
15981626 };
1599 const config_tmp_file: Io.File = try config_tmp_path.root_dir.handle.createFile(
1627 const config_tmp_file: Io.File = config_tmp_path.root_dir.handle.createFile(
16001628 io,
16011629 config_tmp_path.sub_path,
16021630 .{ .read = true, .exclusive = true },
1603 );
1631 ) catch |err| switch (err) {
1632 error.Canceled => |e| return e,
1633 else => |e| {
1634 log.err("failed to create {qf}: {t}", .{ config_tmp_path, e });
1635 return error.AlreadyReported;
1636 },
1637 };
16041638 defer config_tmp_file.close(io);
16051639
16061640 const term = term: {
......@@ -1663,11 +1697,19 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
16631697
16641698 if (config_man) |man| for (configuration.path_deps) |path_dep| {
16651699 const path = try confPathDepToCachePath(arena, graph, &configuration, path_dep);
1666 try man.addDiscoveredPath(.{
1700 var diag: Cache.Manifest.AddDiscoveredPathDiagnostic = undefined;
1701 man.addDiscoveredPath(.{
16671702 .discovered_path = .{ .unresolved = path },
16681703 .handle = if (path_dep.flags.is_directory) .{ .dir = null } else .{ .file = null },
16691704 .metadata_only = path_dep.flags.metadata_only,
1670 });
1705 .diagnostic = &diag,
1706 }) catch |err| switch (err) {
1707 error.FileSystemFailure => {
1708 log.err("failed to register dependency on {qf}: {f}", .{ path, diag });
1709 return error.AlreadyReported;
1710 },
1711 else => |e| return e,
1712 };
16711713 };
16721714
16731715 // If it is poisoned, there is no point in moving it to cached
......@@ -2294,6 +2336,7 @@ fn cacheCatOne(input_hex_digest: []const u8, contents: []const u8, writer: *Io.W
22942336 try file_obj.field("digest", @as([]const u8, &hex_digest), .{});
22952337 if (file.flags.is_directory) try file_obj.field("directory", true, .{});
22962338 if (file.flags.metadata_only) try file_obj.field("metadata", true, .{});
2339 if (!file.flags.exists) try file_obj.field("exists", false, .{});
22972340 try file_obj.field("prefix", file.flags.prefix, .{});
22982341 try file_obj.field("path", path, .{});
22992342 try file_obj.end();
......@@ -3328,7 +3371,12 @@ fn printStepStatus(maker: *Maker, step_index: Configuration.Step.Index, stderr:
33283371 try writer.writeAll(" invalid_manifest");
33293372 try stderr.setColor(.green);
33303373 },
3331 .path_deleted, .directory_status_changed, .metadata_changed, .contents_changed => |path, tag| {
3374 .path_created,
3375 .path_deleted,
3376 .directory_status_changed,
3377 .metadata_changed,
3378 .contents_changed,
3379 => |path, tag| {
33323380 try stderr.setColor(.blue);
33333381 try writer.print(" {t}: {s}", .{ tag, path });
33343382 try stderr.setColor(.green);
lib/compiler/Maker/Step.zig+15-2
......@@ -84,6 +84,7 @@ const ResultCacheStatus = union(enum) {
8484 incomplete_manifest,
8585 invalid_manifest,
8686 path_deleted: [*:0]const u8,
87 path_created: [*:0]const u8,
8788 directory_status_changed: [*:0]const u8,
8889 metadata_changed: [*:0]const u8,
8990 contents_changed: [*:0]const u8,
......@@ -815,7 +816,13 @@ fn setResultCacheStatus(s: *Step, man: *const Cache.Manifest, status: Cache.Mani
815816 .hit => .hit,
816817 .incomplete_manifest => .incomplete_manifest,
817818 .invalid_manifest => .invalid_manifest,
818 inline .path_deleted, .directory_status_changed, .metadata_changed, .contents_changed => |off, tag| @unionInit(
819
820 inline .path_created,
821 .path_deleted,
822 .directory_status_changed,
823 .metadata_changed,
824 .contents_changed,
825 => |off, tag| @unionInit(
819826 ResultCacheStatus,
820827 @tagName(tag),
821828 try gpa.dupeSentinel(u8, off.path(man.contents.items), 0),
......@@ -827,7 +834,13 @@ fn setResultCacheStatus(s: *Step, man: *const Cache.Manifest, status: Cache.Mani
827834fn clearResultCacheStatus(s: *Step, gpa: Allocator) void {
828835 switch (s.result_cache_status) {
829836 .hit, .incomplete_manifest, .invalid_manifest => {},
830 .path_deleted, .directory_status_changed, .metadata_changed, .contents_changed => |path| {
837
838 .path_created,
839 .path_deleted,
840 .directory_status_changed,
841 .metadata_changed,
842 .contents_changed,
843 => |path| {
831844 gpa.free(std.mem.sliceTo(path, 0));
832845 },
833846 }
lib/std/Build.zig+7-3
......@@ -2643,9 +2643,11 @@ pub fn systemIntegrationOption(
26432643
26442644/// Indicates that the build.zig logic depends on a particular file's contents.
26452645///
2646/// If the file is created, deleted, or has its contents changed, the configure
2647/// phase will be repeated. If the inode or mtime change, but the file contents
2648/// remain the same, it will not cause the configure logic to be repeated.
2646/// The file must exist when this function is called.
2647///
2648/// If the file is deleted, or has its contents changed, the configure phase
2649/// will be repeated. If the inode or mtime change, but the file contents remain
2650/// the same, it will not cause the configure logic to be repeated.
26492651///
26502652/// This is an alternative to `Graph.poisonCache` that avoids making every invocation
26512653/// of `zig build` into a cache miss.
......@@ -2695,6 +2697,8 @@ pub fn dependOnFileMetadata(b: *Build, lazy_path: LazyPath) void {
26952697
26962698/// Indicates that the build.zig logic depends on a particular directory's entries.
26972699///
2700/// The directory must exist when this function is called.
2701///
26982702/// This is an alternative to `Graph.poisonCache` that avoids making every invocation
26992703/// of `zig build` into a cache miss.
27002704///
lib/std/Build/Cache.zig+98-62
......@@ -56,7 +56,7 @@ pub fn prefixes(cache: *const Cache) []const Directory {
5656 return cache.prefixes_buffer[0..cache.prefixes_len];
5757}
5858
59const PrefixIndex = u6;
59const PrefixIndex = u3;
6060
6161const PrefixedPath = struct {
6262 prefix: PrefixIndex,
......@@ -453,7 +453,9 @@ pub const Manifest = struct {
453453 pub const Flags = packed struct(u8) {
454454 is_directory: bool,
455455 metadata_only: bool,
456 exists: bool,
456457 prefix: PrefixIndex,
458 _: u2 = 0,
457459 };
458460
459461 /// Prefixes path names in encoded directory contents. Starts numbering
......@@ -576,6 +578,7 @@ pub const Manifest = struct {
576578
577579 /// Returns true if the stat was changed. Updates the `file` with the new stat value.
578580 fn setStatChanged(file: *File, m: *Manifest, s: Stat) Io.Cancelable!bool {
581 assert(file.flags.exists);
579582 if (s.size == file.size and
580583 s.mtime.nanoseconds == file.mtime and
581584 s.inode == file.inode)
......@@ -693,12 +696,12 @@ pub const Manifest = struct {
693696 };
694697
695698 /// Add a file or directory path as a dependency of process being cached.
696 /// When `hit` is called, the contents will be checked to ensure
699 /// When `check` is called, the contents will be checked to ensure
697700 /// that it matches the contents from previous times.
698701 ///
699702 /// The contents of the input file may be requested and subsequently
700703 /// obtained via methods of the returned `InputPath.Index` after calling
701 /// `hit`.
704 /// `check`.
702705 ///
703706 /// Contents of a directory are considered to be the sorted list of file
704707 /// names of direct entries, separated by null byte. Each file name is
......@@ -726,6 +729,7 @@ pub const Manifest = struct {
726729 .prefix = new_prefix,
727730 .is_directory = is_directory,
728731 .metadata_only = options.metadata_only,
732 .exists = true, // modified later if necessary
729733 };
730734
731735 const gop = m.files.getOrPutAssumeCapacityContext(new_file_offset, .{ .contents = m.contents.items });
......@@ -841,6 +845,7 @@ pub const Manifest = struct {
841845 hit,
842846 incomplete_manifest,
843847 invalid_manifest,
848 path_created: File.Offset,
844849 path_deleted: File.Offset,
845850 directory_status_changed: File.Offset,
846851 metadata_changed: File.Offset,
......@@ -853,7 +858,7 @@ pub const Manifest = struct {
853858 pub fn format(this: @This(), w: *Io.Writer) Io.Writer.Error!void {
854859 switch (this.check_result) {
855860 .hit, .incomplete_manifest, .invalid_manifest => return w.writeAll(@tagName(this.check_result)),
856 .path_deleted, .directory_status_changed, .metadata_changed, .contents_changed => |off| {
861 .path_created, .path_deleted, .directory_status_changed, .metadata_changed, .contents_changed => |off| {
857862 const path = off.path(this.manifest.contents.items);
858863 return w.print("{t} {s}", .{ this.check_result, path });
859864 },
......@@ -1192,38 +1197,36 @@ pub const Manifest = struct {
11921197 };
11931198 if (!input_path.have_stat and stat_path_ok) {
11941199 // Since this is an input file, FileNotFound counts as a failure, not a cache miss.
1195 const actual_stat = switch (input_path.getHandle(input_file)) {
1196 .none => parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) {
1197 error.Canceled => |e| return e,
1198 else => |e| return fail(diag, .{ .file_stat = .{
1199 .file_offset = file_off,
1200 .err = e,
1201 } }),
1202 },
1203 .file => |opened_file| opened_file.stat(io) catch |err| switch (err) {
1204 error.Canceled => |e| return e,
1205 else => |e| return fail(diag, .{ .file_stat = .{
1206 .file_offset = file_off,
1207 .err = e,
1208 } }),
1209 },
1210 .dir => |opened_dir| opened_dir.stat(io) catch |err| switch (err) {
1211 error.Canceled => |e| return e,
1212 else => |e| return fail(diag, .{ .file_stat = .{
1213 .file_offset = file_off,
1214 .err = e,
1215 } }),
1216 },
1200 const stat_result = switch (input_path.getHandle(input_file)) {
1201 .none => parent_dir.statFile(io, file_path, .{}),
1202 .file => |opened_file| opened_file.stat(io),
1203 .dir => |opened_dir| opened_dir.stat(io),
12171204 };
1218 const actual_is_directory = actual_stat.kind == .directory;
1219 if (actual_is_directory != input_file.flags.is_directory) {
1220 // Since this is an input file, this is a failure, not a cache miss.
1221 return fail(diag, .{ .file_stat = .{
1205 if (stat_result) |actual_stat| {
1206 const actual_is_directory = actual_stat.kind == .directory;
1207 if (actual_is_directory != input_file.flags.is_directory) {
1208 // Since this is an input file, this is a failure, not a cache miss.
1209 return fail(diag, .{ .file_stat = .{
1210 .file_offset = file_off,
1211 .err = if (actual_is_directory) error.IsDir else error.NotDir,
1212 } });
1213 }
1214 try input_file.setStat(m, .init(actual_stat));
1215 } else |err| switch (err) {
1216 error.Canceled => |e| return e,
1217
1218 error.FileNotFound => |e| if (input_file.flags.metadata_only) {
1219 input_file.flags.exists = false;
1220 } else return fail(diag, .{ .file_stat = .{
12221221 .file_offset = file_off,
1223 .err = if (actual_is_directory) error.IsDir else error.NotDir,
1224 } });
1222 .err = e,
1223 } }),
1224
1225 else => |e| return fail(diag, .{ .file_stat = .{
1226 .file_offset = file_off,
1227 .err = e,
1228 } }),
12251229 }
1226 try input_file.setStat(m, .init(actual_stat));
12271230 }
12281231
12291232 if (input_file.flags.metadata_only) {
......@@ -1369,6 +1372,9 @@ pub const Manifest = struct {
13691372 // space to compute the digest in case stat changed, in order to compare with `disk_file.digest`.
13701373
13711374 if (input_path.have_stat) {
1375 if (!disk_file.flags.exists) {
1376 return .{ .path_created = file_off };
1377 }
13721378 const changed = try disk_file.setStatChanged(m, input_file.stat());
13731379 if (!changed) return .hit;
13741380 }
......@@ -1376,29 +1382,28 @@ pub const Manifest = struct {
13761382 if (disk_file.flags.metadata_only) {
13771383 if (!input_path.have_stat) {
13781384 // Since this is an input file, FileNotFound counts as a failure, not a cache miss.
1379 const actual_stat = switch (input_path.getHandle(input_file)) {
1380 .none => parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) {
1381 error.Canceled => |e| return e,
1382 else => |e| return fail(diag, .{ .file_stat = .{
1383 .file_offset = file_off,
1384 .err = e,
1385 } }),
1386 },
1387 .file => |opened_file| opened_file.stat(io) catch |err| switch (err) {
1388 error.Canceled => |e| return e,
1389 else => |e| return fail(diag, .{ .file_stat = .{
1390 .file_offset = file_off,
1391 .err = e,
1392 } }),
1393 },
1394 .dir => |opened_dir| opened_dir.stat(io) catch |err| switch (err) {
1395 error.Canceled => |e| return e,
1396 else => |e| return fail(diag, .{ .file_stat = .{
1385 const stat_result = switch (input_path.getHandle(input_file)) {
1386 .none => parent_dir.statFile(io, file_path, .{}),
1387 .file => |opened_file| opened_file.stat(io),
1388 .dir => |opened_dir| opened_dir.stat(io),
1389 };
1390 const actual_stat = stat_result catch |err| switch (err) {
1391 error.Canceled => |e| return e,
1392 error.FileNotFound => |e| switch (disk_file.flags.exists) {
1393 false => return .hit,
1394 true => return fail(diag, .{ .file_stat = .{
13971395 .file_offset = file_off,
13981396 .err = e,
13991397 } }),
14001398 },
1399 else => |e| return fail(diag, .{ .file_stat = .{
1400 .file_offset = file_off,
1401 .err = e,
1402 } }),
14011403 };
1404 if (!disk_file.flags.exists) {
1405 return .{ .path_created = file_off };
1406 }
14021407 // In the other case (have_stat=true), this check is redundant since we already asserted the flags are
14031408 // identical, which contains the prefix.
14041409 const actual_is_directory = actual_stat.kind == .directory;
......@@ -1559,7 +1564,10 @@ pub const Manifest = struct {
15591564
15601565 if (file.flags.metadata_only) {
15611566 const actual_stat = parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) {
1562 error.FileNotFound => return .{ .path_deleted = file_off },
1567 error.FileNotFound => switch (file.flags.exists) {
1568 false => return .hit,
1569 true => return .{ .path_deleted = file_off },
1570 },
15631571 error.Canceled => |e| return e,
15641572 else => |e| return fail(diag, .{ .file_stat = .{
15651573 .file_offset = file_off,
......@@ -1567,6 +1575,10 @@ pub const Manifest = struct {
15671575 } }),
15681576 };
15691577
1578 if (!file.flags.exists) {
1579 return .{ .path_created = file_off };
1580 }
1581
15701582 const actual_is_directory = actual_stat.kind == .directory;
15711583 if (actual_is_directory != file.flags.is_directory) return .{ .directory_status_changed = file_off };
15721584
......@@ -1576,6 +1588,8 @@ pub const Manifest = struct {
15761588 return .hit;
15771589 }
15781590
1591 assert(file.flags.exists); // file not existing can only be tracked with metadata_only
1592
15791593 if (file.flags.is_directory) {
15801594 const opened_dir = parent_dir.openDir(io, file_path, .{
15811595 .iterate = true,
......@@ -1727,7 +1741,14 @@ pub const Manifest = struct {
17271741 pub fn format(this: @This(), w: *Io.Writer) Io.Writer.Error!void {
17281742 switch (this) {
17291743 .none => return w.writeAll("none"),
1730 else => |err, tag| return w.print("{t}: {t}", .{ tag, err }),
1744
1745 .open_file,
1746 .open_dir,
1747 .stat_file,
1748 .stat_dir,
1749 .read_file,
1750 .read_dir,
1751 => |err, tag| return w.print("{t}: {t}", .{ tag, err }),
17311752 }
17321753 }
17331754 };
......@@ -1767,6 +1788,7 @@ pub const Manifest = struct {
17671788 .prefix = new_prefix,
17681789 .is_directory = is_directory,
17691790 .metadata_only = options.metadata_only,
1791 .exists = true, // modified later if necessary
17701792 };
17711793
17721794 const gop = m.files.getOrPutAssumeCapacityContext(new_file_offset, .{ .contents = m.contents.items });
......@@ -1810,18 +1832,25 @@ pub const Manifest = struct {
18101832 } else {
18111833 const dir = cache.prefixes()[prefix].handle;
18121834 const sub_path = file_offset.path(m.contents.items);
1813 const handle = dir.openDir(io, sub_path, .{
1835 if (dir.openDir(io, sub_path, .{
18141836 .access_sub_paths = false,
18151837 .iterate = true,
1816 }) catch |err| switch (err) {
1838 })) |handle| {
1839 defer handle.close(io);
1840 try populateDirectory(m, header, need_stat, handle, options.contents, metadata_only, options.diagnostic);
1841 } else |err| switch (err) {
18171842 error.Canceled => |e| return e,
1843 error.FileNotFound => |e| if (options.metadata_only) {
1844 header.flags.exists = false;
1845 } else {
1846 if (options.diagnostic) |d| d.* = .{ .open_dir = e };
1847 return error.FileSystemFailure;
1848 },
18181849 else => |e| {
18191850 if (options.diagnostic) |d| d.* = .{ .open_dir = e };
18201851 return error.FileSystemFailure;
18211852 },
1822 };
1823 defer handle.close(io);
1824 try populateDirectory(m, header, need_stat, handle, options.contents, metadata_only, options.diagnostic);
1853 }
18251854 },
18261855
18271856 .file => |opt_handle| if (opt_handle) |handle| {
......@@ -1829,15 +1858,22 @@ pub const Manifest = struct {
18291858 } else {
18301859 const dir = cache.prefixes()[prefix].handle;
18311860 const sub_path = file_offset.path(m.contents.items);
1832 const handle = dir.openFile(io, sub_path, .{ .mode = .read_only }) catch |err| switch (err) {
1861 if (dir.openFile(io, sub_path, .{ .mode = .read_only })) |handle| {
1862 defer handle.close(io);
1863 try populateFile(m, header, need_stat, handle, options.contents, metadata_only, options.diagnostic);
1864 } else |err| switch (err) {
18331865 error.Canceled => |e| return e,
1866 error.FileNotFound => |e| if (options.metadata_only) {
1867 header.flags.exists = false;
1868 } else {
1869 if (options.diagnostic) |d| d.* = .{ .open_file = e };
1870 return error.FileSystemFailure;
1871 },
18341872 else => |e| {
18351873 if (options.diagnostic) |d| d.* = .{ .open_file = e };
18361874 return error.FileSystemFailure;
18371875 },
1838 };
1839 defer handle.close(io);
1840 try populateFile(m, header, need_stat, handle, options.contents, metadata_only, options.diagnostic);
1876 }
18411877 },
18421878 }
18431879 }
src/link/Coff.zig+1-1
......@@ -6100,7 +6100,7 @@ pub fn idle(coff: *Coff) !bool {
61006100 break :task;
61016101 }
61026102 while (coff.mf.updates.pop()) |ni| : (coff.mf.update_prog_node.completeOne()) {
6103 if (ni.pendingDelete(&coff.mf)) continue;
6103 ni.cleanUpdated(&coff.mf);
61046104 const clean_moved = ni.cleanMoved(&coff.mf);
61056105 const clean_resized = ni.cleanResized(&coff.mf);
61066106 const clean_next_moved = ni.cleanNextMoved(&coff.mf);
src/link/Dwarf.zig+37-16
......@@ -2662,7 +2662,6 @@ fn initWipNavInner(
26622662 try wip_nav.strp(@"extern".name.toSlice(ip));
26632663 try wip_nav.refType(.fromInterned(if (maybe_func_type) |func_type| func_type.return_type else @"extern".ty));
26642664 if (maybe_func_type) |func_type| {
2665 try wip_nav.infoAddrSym(sym_index, 0);
26662665 try diw.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));
26672666 if (func_type.param_types.len > 0 or func_type.is_var_args) {
26682667 for (func_type.param_types.get(ip)) |param_type| {
......@@ -2672,7 +2671,7 @@ fn initWipNavInner(
26722671 if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args);
26732672 try diw.writeUleb128(@backingInt(AbbrevCode.null));
26742673 }
2675 } else try wip_nav.infoExprLoc(.{ .addr_reloc = sym_index });
2674 }
26762675 },
26772676 .syntax => switch (ip.isFunctionType(@"extern".ty)) {
26782677 false => continue :nav_val .{ .undef = @"extern".ty },
......@@ -2690,7 +2689,6 @@ fn initWipNavInner(
26902689 }, &nav, inst_info.file, &decl);
26912690 try wip_nav.strp(@"extern".name.toSlice(ip));
26922691 try wip_nav.refType(.fromInterned(func_type.return_type));
2693 try wip_nav.infoAddrSym(sym_index, 0);
26942692 try diw.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));
26952693 if (func_type.param_types.len > 0 or func_type.is_var_args) {
26962694 for (func_type.param_types.get(ip)) |param_type| {
......@@ -2817,13 +2815,19 @@ fn initWipNavInner(
28172815 else => {
28182816 const diw = &wip_nav.debug_info.writer;
28192817 try wip_nav.declCommon(.{
2820 .decl = .decl_var,
2818 .decl = switch (decl.linkage) {
2819 .@"extern" => .decl_extern_var,
2820 else => .decl_var,
2821 },
28212822 .decl_specification = switch (decl.kind) {
28222823 .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable,
28232824 .@"const" => .decl_specification_const,
28242825 .@"var" => .decl_specification_var,
28252826 },
2826 .decl_instance = .decl_instance_var,
2827 .decl_instance = switch (decl.linkage) {
2828 .@"extern" => .decl_instance_extern_var,
2829 else => .decl_instance_var,
2830 },
28272831 }, &nav, inst_info.file, &decl);
28282832 try wip_nav.strp(switch (decl.linkage) {
28292833 .normal => nav.fqn,
......@@ -2842,20 +2846,24 @@ fn initWipNavInner(
28422846 .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable,
28432847 .@"const" => {
28442848 const const_ty_reloc_index = try wip_nav.refForward();
2845 try wip_nav.infoExprLoc(loc);
2849 if (decl.linkage != .@"extern") try wip_nav.infoExprLoc(loc);
28462850 try diw.writeUleb128(nav.resolved.?.@"align".toByteUnits() orelse
28472851 ty.abiAlignment(zcu).toByteUnits().?);
2848 try diw.writeByte(@intFromBool(decl.linkage != .normal));
2852 if (decl.linkage != .@"extern") {
2853 try diw.writeByte(@intFromBool(decl.linkage != .normal));
2854 }
28492855 wip_nav.finishForward(const_ty_reloc_index);
28502856 try wip_nav.abbrevCode(.is_const);
28512857 try wip_nav.refType(ty);
28522858 },
28532859 .@"var" => {
28542860 try wip_nav.refType(ty);
2855 try wip_nav.infoExprLoc(loc);
2861 if (decl.linkage != .@"extern") try wip_nav.infoExprLoc(loc);
28562862 try diw.writeUleb128(nav.resolved.?.@"align".toByteUnits() orelse
28572863 ty.abiAlignment(zcu).toByteUnits().?);
2858 try diw.writeByte(@intFromBool(decl.linkage != .normal));
2864 if (decl.linkage != .@"extern") {
2865 try diw.writeByte(@intFromBool(decl.linkage != .normal));
2866 }
28592867 },
28602868 }
28612869 },
......@@ -5138,6 +5146,7 @@ const AbbrevCode = enum {
51385146 decl_union,
51395147 decl_packed_union,
51405148 decl_var,
5149 decl_extern_var,
51415150 decl_const,
51425151 decl_const_runtime_bits,
51435152 decl_const_comptime_state,
......@@ -5160,6 +5169,7 @@ const AbbrevCode = enum {
51605169 decl_instance_union,
51615170 decl_instance_packed_union,
51625171 decl_instance_var,
5172 decl_instance_extern_var,
51635173 decl_instance_const,
51645174 decl_instance_const_runtime_bits,
51655175 decl_instance_const_comptime_state,
......@@ -5360,6 +5370,15 @@ const AbbrevCode = enum {
53605370 .{ .external, .flag },
53615371 },
53625372 },
5373 .decl_extern_var = .{
5374 .tag = .variable,
5375 .attrs = decl_abbrev_common_attrs ++ .{
5376 .{ .linkage_name, .strp },
5377 .{ .type, .ref_addr },
5378 .{ .alignment, .udata },
5379 .{ .external, .flag_present },
5380 },
5381 },
53635382 .decl_const = .{
53645383 .tag = .constant,
53655384 .attrs = decl_abbrev_common_attrs ++ .{
......@@ -5443,7 +5462,6 @@ const AbbrevCode = enum {
54435462 .attrs = decl_abbrev_common_attrs ++ .{
54445463 .{ .linkage_name, .strp },
54455464 .{ .type, .ref_addr },
5446 .{ .low_pc, .addr },
54475465 .{ .external, .flag_present },
54485466 .{ .noreturn, .flag },
54495467 },
......@@ -5454,7 +5472,6 @@ const AbbrevCode = enum {
54545472 .attrs = decl_abbrev_common_attrs ++ .{
54555473 .{ .linkage_name, .strp },
54565474 .{ .type, .ref_addr },
5457 .{ .low_pc, .addr },
54585475 .{ .external, .flag_present },
54595476 .{ .noreturn, .flag },
54605477 },
......@@ -5536,6 +5553,15 @@ const AbbrevCode = enum {
55365553 .{ .external, .flag },
55375554 },
55385555 },
5556 .decl_instance_extern_var = .{
5557 .tag = .variable,
5558 .attrs = decl_instance_abbrev_common_attrs ++ .{
5559 .{ .linkage_name, .strp },
5560 .{ .location, .exprloc },
5561 .{ .alignment, .udata },
5562 .{ .external, .flag_present },
5563 },
5564 },
55395565 .decl_instance_const = .{
55405566 .tag = .constant,
55415567 .attrs = decl_instance_abbrev_common_attrs ++ .{
......@@ -5619,7 +5645,6 @@ const AbbrevCode = enum {
56195645 .attrs = decl_instance_abbrev_common_attrs ++ .{
56205646 .{ .linkage_name, .strp },
56215647 .{ .type, .ref_addr },
5622 .{ .low_pc, .addr },
56235648 .{ .external, .flag_present },
56245649 .{ .noreturn, .flag },
56255650 },
......@@ -5630,7 +5655,6 @@ const AbbrevCode = enum {
56305655 .attrs = decl_instance_abbrev_common_attrs ++ .{
56315656 .{ .linkage_name, .strp },
56325657 .{ .type, .ref_addr },
5633 .{ .low_pc, .addr },
56345658 .{ .external, .flag_present },
56355659 .{ .noreturn, .flag },
56365660 },
......@@ -6048,7 +6072,6 @@ const AbbrevCode = enum {
60486072 .{ .ZIG_parent, .ref_addr },
60496073 .{ .linkage_name, .strp },
60506074 .{ .type, .ref_addr },
6051 .{ .low_pc, .addr },
60526075 .{ .external, .flag_present },
60536076 .{ .noreturn, .flag },
60546077 },
......@@ -6060,7 +6083,6 @@ const AbbrevCode = enum {
60606083 .{ .ZIG_parent, .ref_addr },
60616084 .{ .linkage_name, .strp },
60626085 .{ .type, .ref_addr },
6063 .{ .low_pc, .addr },
60646086 .{ .external, .flag_present },
60656087 .{ .noreturn, .flag },
60666088 },
......@@ -6071,7 +6093,6 @@ const AbbrevCode = enum {
60716093 .{ .ZIG_parent, .ref_addr },
60726094 .{ .linkage_name, .strp },
60736095 .{ .type, .ref_addr },
6074 .{ .location, .exprloc },
60756096 .{ .external, .flag_present },
60766097 },
60776098 },
src/link/Dwarf2.zig+13-24
......@@ -2101,11 +2101,9 @@ pub fn updateExtern(
21012101 dwarf: *Dwarf,
21022102 pt: Zcu.PerThread,
21032103 di_nw: *link.MappedFile.Node.Writer,
2104 arch: std.Target.Cpu.Arch,
2105 si: link.File.SymbolId,
21062104 global: InternPool.Nav.Index,
21072105) link.Error!void {
2108 dwarf.updateExternInner(pt, di_nw, arch, si, global) catch |err| switch (err) {
2106 dwarf.updateExternInner(pt, di_nw, global) catch |err| switch (err) {
21092107 else => |e| return e,
21102108 error.WriteFailed => return dwarf.reportWriteError(di_nw),
21112109 };
......@@ -2114,20 +2112,17 @@ fn updateExternInner(
21142112 dwarf: *Dwarf,
21152113 pt: Zcu.PerThread,
21162114 di_nw: *link.MappedFile.Node.Writer,
2117 arch: std.Target.Cpu.Arch,
2118 si: link.File.SymbolId,
21192115 global: InternPool.Nav.Index,
21202116) link.EmitError!void {
21212117 const zcu = pt.zcu;
21222118 const ip = &zcu.intern_pool;
21232119 const nav = ip.getNav(global);
21242120 log.debug("updateExtern({f})", .{nav.fqn.fmt(ip)});
2125 const nav_align, const nav_const, const nav_threadlocal, const nav_extern = nav_resolved: {
2121 const nav_align, const nav_const, const nav_extern = nav_resolved: {
21262122 const nav_resolved = nav.resolved.?;
21272123 break :nav_resolved .{
21282124 nav_resolved.@"align",
21292125 nav_resolved.@"const",
2130 nav_resolved.@"threadlocal",
21312126 ip.indexToKey(nav_resolved.value).@"extern",
21322127 };
21332128 };
......@@ -2162,7 +2157,7 @@ fn updateExternInner(
21622157 try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private);
21632158 try dwarf.strx(di_nw, nav.name.toSlice(ip));
21642159 } else {
2165 try dwarf.abbrevCode(di_nw, .decl_var);
2160 try dwarf.abbrevCode(di_nw, .decl_extern_var);
21662161 try dwarf.refType(pt, di_nw, .fromInterned(zcu.fileRootType(inst_info.file)));
21672162 try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian);
21682163 try di_w.writeUleb128(decl.src_column + 1);
......@@ -2173,7 +2168,6 @@ fn updateExternInner(
21732168 try dwarf.strx(di_nw, nav_extern.name.toSlice(ip));
21742169 if (maybe_func_type) |func_type| {
21752170 try dwarf.refType(pt, di_nw, .fromInterned(func_type.return_type));
2176 try dwarf.addrSym(di_nw, si, 0);
21772171 try di_w.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));
21782172 for (func_type.param_types.get(ip)) |param_type| {
21792173 try dwarf.abbrevCode(di_nw, .extern_param);
......@@ -2182,23 +2176,13 @@ fn updateExternInner(
21822176 if (func_type.is_var_args) try dwarf.abbrevCode(di_nw, .is_var_args);
21832177 if (!is_empty) try di_w.writeUleb128(@backingInt(AbbrevCode.null));
21842178 } else {
2185 const addr: Loc = .{ .addr_sym = .{ .si = si } };
2186 const loc: Loc = if (nav_threadlocal) switch (arch) {
2187 .x86_64 => .{ .form_tls_address = &addr },
2188 else => unreachable,
2189 } else addr;
21902179 const type_offset = di_w.end;
21912180 if (nav_const)
21922181 try dwarf.secOffsetPlaceholder(di_w)
21932182 else
21942183 try dwarf.refType(pt, di_nw, .fromInterned(nav_extern.ty));
2195 try dwarf.exprLoc(di_nw, loc);
21962184 try di_w.writeUleb128(nav_align.toByteUnits() orelse
21972185 Type.fromInterned(nav_extern.ty).abiAlignment(zcu).toByteUnits().?);
2198 switch (nav_extern.source) {
2199 .builtin => {},
2200 .syntax => try di_w.writeByte(@intFromBool(decl.linkage != .normal)),
2201 }
22022186 if (nav_const) {
22032187 try dwarf.secOffsetFinish(di_nw, type_offset, di_nw.ni, di_w.end);
22042188 try dwarf.abbrevCode(di_nw, .is_const);
......@@ -4800,6 +4784,7 @@ pub const AbbrevCode = enum {
48004784 decl_func_generic,
48014785 decl_extern_empty_func,
48024786 decl_extern_func,
4787 decl_extern_var,
48034788 decl_specification_empty_struct,
48044789 decl_specification_struct,
48054790 type_decl_specification_empty_struct,
......@@ -5285,7 +5270,6 @@ pub const AbbrevCode = enum {
52855270 .attrs = decl_attrs ++ .{
52865271 .{ .linkage_name, .strx },
52875272 .{ .type, .ref_addr },
5288 .{ .low_pc, .addr },
52895273 .{ .external, .flag_present },
52905274 .{ .noreturn, .flag },
52915275 },
......@@ -5296,11 +5280,19 @@ pub const AbbrevCode = enum {
52965280 .attrs = decl_attrs ++ .{
52975281 .{ .linkage_name, .strx },
52985282 .{ .type, .ref_addr },
5299 .{ .low_pc, .addr },
53005283 .{ .external, .flag_present },
53015284 .{ .noreturn, .flag },
53025285 },
53035286 },
5287 .decl_extern_var = .{
5288 .tag = .variable,
5289 .attrs = decl_attrs ++ .{
5290 .{ .linkage_name, .strx },
5291 .{ .type, .ref_addr },
5292 .{ .alignment, .udata },
5293 .{ .external, .flag_present },
5294 },
5295 },
53045296 .decl_specification_empty_struct = .{
53055297 .tag = .structure_type,
53065298 .attrs = decl_specification_attrs,
......@@ -5995,7 +5987,6 @@ pub const AbbrevCode = enum {
59955987 .{ .ZIG_parent, .ref_addr },
59965988 .{ .linkage_name, .strx },
59975989 .{ .type, .ref_addr },
5998 .{ .low_pc, .addr },
59995990 .{ .external, .flag_present },
60005991 .{ .noreturn, .flag },
60015992 },
......@@ -6007,7 +5998,6 @@ pub const AbbrevCode = enum {
60075998 .{ .ZIG_parent, .ref_addr },
60085999 .{ .linkage_name, .strx },
60096000 .{ .type, .ref_addr },
6010 .{ .low_pc, .addr },
60116001 .{ .external, .flag_present },
60126002 .{ .noreturn, .flag },
60136003 },
......@@ -6018,7 +6008,6 @@ pub const AbbrevCode = enum {
60186008 .{ .ZIG_parent, .ref_addr },
60196009 .{ .linkage_name, .strx },
60206010 .{ .type, .ref_addr },
6021 .{ .location, .exprloc },
60226011 .{ .alignment, .udata },
60236012 .{ .external, .flag_present },
60246013 },
src/link/Elf2.zig+879-770
......@@ -244,8 +244,9 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
244244changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void),
245245/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
246246/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`
247/// section in `flush` only when it is actually necessary. See also `nodeWantsDsoRelocation`.
247/// section in `flush` only when it is actually necessary. See also `nodeAllowsDynamicReloc`.
248248textrel_count: u32,
249entry_gsi: Symbol.Global.Index.Optional,
249250
250251dwarf: Dwarf,
251252dwarf_shared: std.enums.EnumArray(Dwarf.SharedSection, dwarf_relocs.Shared),
......@@ -259,6 +260,8 @@ dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl),
259260
260261overflowed_reloc_count: u32,
261262misaligned_reloc_count: u32,
263illegal_dynamic_type_reloc_count: u32,
264illegal_dynamic_section_reloc_count: u32,
262265
263266const_prog_node: std.Progress.Node,
264267input_prog_node: std.Progress.Node,
......@@ -308,9 +311,6 @@ const Node = union(enum) {
308311 /// emitted a copy relocation.
309312 ///
310313 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.
311 ///
312 /// TODO: currently, the `elf.copied_globals` entry may not be there---this case exists because
313 /// `MappedFile` does not (yet?) support deleting nodes. See logic in `updateGlobalDynamic`.
314314 copied_global: Symbol.Global.Index,
315315 /// May contain relocations.
316316 nav: NavMapIndex,
......@@ -464,6 +464,135 @@ const Node = union(enum) {
464464 fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index {
465465 return @fromBackingInt(@backingInt(atom));
466466 }
467
468 fn fmtDebug(ni: MappedFile.Node.Index, elf: *Elf) FormatDebug {
469 return .{ .ni = ni, .elf = elf };
470 }
471 const FormatDebug = struct {
472 ni: MappedFile.Node.Index,
473 elf: *Elf,
474 pub fn format(ctx: FormatDebug, w: *Io.Writer) Io.Writer.Error!void {
475 const elf = ctx.elf;
476 const node = elf.getNode(ctx.ni);
477 try w.writeAll(@tagName(node));
478 switch (node) {
479 // nodes with no payload
480 .deleted,
481 .archive,
482 .archive_header,
483 .elf,
484 .ehdr,
485 .shdr,
486 .debug_addr,
487 .eh_frame_footer,
488 .debug_str_offsets,
489 .unit_padding,
490 .archive_elf_member_header,
491 => |tag| comptime assert(@TypeOf(tag) == void),
492
493 .archive_input_member => |ii| try w.print("({f}{f})", .{
494 ii.path(elf).fmtEscapeString(),
495 fmtMemberString(ii.member(elf)),
496 }),
497 .segment => |phndx| switch (elf.phdrSlice()) {
498 inline else => |phdr| {
499 const ph = &phdr[phndx];
500 try w.writeByte('(');
501 const pt = elf.targetLoad(&ph.type);
502 if (std.enums.tagName(std.elf.PT, pt)) |pt_name|
503 try w.writeAll(pt_name)
504 else inline for (@typeInfo(std.elf.PT).@"enum".decl_names) |decl_name| {
505 const decl_val = @field(std.elf.PT, decl_name);
506 if (@TypeOf(decl_val) != std.elf.PT) continue;
507 if (pt == @field(std.elf.PT, decl_name)) break try w.writeAll(decl_name);
508 } else try w.print("0x{x}", .{pt});
509 try w.writeAll(", ");
510 const pf = elf.targetLoad(&ph.flags);
511 if (pf.R) try w.writeByte('R');
512 if (pf.W) try w.writeByte('W');
513 if (pf.X) try w.writeByte('X');
514 try w.writeByte(')');
515 },
516 },
517 .section, .section_manual_size => |shndx| try w.print("({q})", .{
518 shndx.name(elf).slice(elf),
519 }),
520 .input_section => |isi| {
521 const ii = isi.input(elf);
522 try w.print("({f}{f}, {q})", .{
523 ii.path(elf).fmtEscapeString(),
524 fmtMemberString(ii.member(elf)),
525 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
526 });
527 },
528 .copied_global => |gsi| try w.print("({q})", .{
529 gsi.rawName(elf).slice(elf),
530 }),
531 .nav => |nmi| {
532 const ip = &elf.base.comp.zcu.?.intern_pool;
533 try w.print("({qf})", .{
534 ip.getNav(nmi.nav(elf)).fqn.fmt(ip),
535 });
536 },
537 .uav => |umi| {
538 const zcu = elf.base.comp.zcu.?;
539 const val: Value = .fromInterned(umi.uavValue(elf));
540 try w.print("({f}, {f})", .{
541 val.typeOf(zcu).fmt(zcu),
542 val.fmtValue(zcu),
543 });
544 },
545 inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{
546 Type.fromInterned(lmi.lazySymbol(elf).ty).fmt(elf.base.comp.zcu.?),
547 }),
548
549 .debug_shared => |ss| try w.print("({t})", .{ss}),
550
551 .unit_frame,
552 .unit_frame_cie,
553 .unit_debug_info,
554 .unit_debug_info_header,
555 .unit_debug_info_footer,
556 .unit_debug_line,
557 .unit_debug_line_header,
558 .unit_debug_rnglists,
559 => |ui| try w.print("({q})", .{
560 ui.mod(&elf.dwarf).fully_qualified_name,
561 }),
562
563 .const_debug_info => |cpi| switch (cpi.val(&elf.dwarf.const_pool)) {
564 .generic_poison_type => try w.writeAll("(anytype)"),
565 else => |val| try w.print("({f})", .{
566 Value.fromInterned(val).fmtValue(elf.base.comp.zcu.?),
567 }),
568 },
569 .global_debug_info => |gi| {
570 const ip = &elf.base.comp.zcu.?.intern_pool;
571 try w.print("({qf})", .{
572 ip.getNav(gi.nav(&elf.dwarf)).fqn.fmt(ip),
573 });
574 },
575 .func_frame_fde, .func_debug_info, .func_debug_line => |fi| {
576 const ip = &elf.base.comp.zcu.?.intern_pool;
577 try w.print("({qf})", .{
578 ip.getNav(fi.nav(&elf.dwarf)).fqn.fmt(ip),
579 });
580 },
581 .decl_debug_info => |di| {
582 const comp = elf.base.comp;
583 const zcu = comp.zcu.?;
584 const ip = &zcu.intern_pool;
585 const src_inst = di.srcInst(&elf.dwarf);
586 const path = zcu.fileByIndex(src_inst.resolveFile(ip)).path;
587 if (src_inst.resolve(ip)) |inst| {
588 try w.print("({f}, %{d})", .{ path.fmt(comp), inst });
589 } else {
590 try w.print("({f}, lost)", .{path.fmt(comp)});
591 }
592 },
593 }
594 }
595 };
467596};
468597
469598const InputSection = struct {
......@@ -947,10 +1076,14 @@ const dwarf_relocs = struct {
9471076 const Addr = struct {
9481077 first_target_reloc: NodeReloc.Index,
9491078 symbol_relocs: std.ArrayList(SymbolReloc.Index),
1079 /// Index into `symbol_relocs`.
1080 pending_reloc_index: usize,
9501081 };
9511082 const StrOffsets = struct {
9521083 first_target_reloc: NodeReloc.Index,
9531084 node_relocs: std.ArrayList(NodeReloc.Index),
1085 /// Index into `node_relocs`.
1086 pending_reloc_index: usize,
9541087 };
9551088 const Unit = struct {
9561089 frame_cie_first_target_reloc: NodeReloc.Index,
......@@ -1279,7 +1412,13 @@ const SymbolReloc = struct {
12791412 /// relocation entry. The entry will be removed if we discover a definition which allows us to
12801413 /// statically resolve the relocation.
12811414 rela_index: Section.RelaIndex.Optional,
1282 result: enum(u8) { ok, overflowed, misaligned },
1415 result: enum(u8) {
1416 ok,
1417 overflowed,
1418 misaligned,
1419 illegal_dynamic_type,
1420 illegal_dynamic_section,
1421 },
12831422
12841423 /// Determines the section in which this relocation will be placed if it is outstanding.
12851424 ///
......@@ -1813,32 +1952,28 @@ const SymbolReloc = struct {
18131952 }
18141953 };
18151954
1816 fn flushMovedNode(reloc: *SymbolReloc, elf: *Elf, node_vaddr: u64) void {
1817 if (reloc.rela_index.unwrap()) |rela_index| {
1818 // The node has moved, so the offset of the relocation within the section might have
1819 // changed, so update the `offset` field of the `ElfN.Rela` entry.
1820 reloc.relaSection(elf).relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
1821 }
1822 // This is not just the inverse of the above condition, because if `reloc` is relative
1823 // to the base of this DSO, then `rela_index` is an `R_*_RELATIVE` relocation, but we
1824 // still need to call `SymbolReloc.apply` to update that relocation's addend.
1825 if (elf.ehdrType() != .REL) {
1826 reloc.apply(elf);
1827 }
1828 }
1829
18301955 fn apply(reloc: *SymbolReloc, elf: *Elf) void {
1831 assert(elf.ehdrType() != .REL);
18321956 const node = reloc.node.unwrap() orelse return; // deleted
18331957 if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
18341958 // There's no point applying the relocation now, because it will be re-applied by
18351959 // `flushMoved` at some point anyway.
18361960 return;
18371961 }
1962 if (elf.ehdrType() == .REL) {
1963 // All we need to do is update the output relocation.
1964 const rela_index = reloc.rela_index.unwrap().?;
1965 const reloc_addr = elf.getNodeVAddr(node) + reloc.offset;
1966 reloc.relaSection(elf).relaSetOffset(elf, rela_index, reloc_addr);
1967 return;
1968 }
18381969 switch (reloc.result) {
18391970 .ok => {},
18401971 .overflowed => elf.overflowed_reloc_count -= 1,
18411972 .misaligned => elf.misaligned_reloc_count -= 1,
1973 .illegal_dynamic_type, .illegal_dynamic_section => {
1974 // This status won't change until a call to `updateDynamicReloc`.
1975 return;
1976 },
18421977 }
18431978 if (reloc.applyInner(elf)) {
18441979 @branchHint(.likely);
......@@ -1900,7 +2035,11 @@ const SymbolReloc = struct {
19002035 // is required, meaning we can handle it now and return early.
19012036 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
19022037 .static => unreachable,
1903 .dynamic => return, // the relocation happens at runtime
2038 .dynamic => {
2039 // The relocation happens at runtime; we just need to update its offset.
2040 elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr);
2041 return;
2042 },
19042043 .static_relative => {
19052044 // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address
19062045 // relocation. The value computed above is valid, but instead of writing it to the
......@@ -1912,6 +2051,7 @@ const SymbolReloc = struct {
19122051 }
19132052 assert(reloc.type.action.simple.cast == .unsigned);
19142053 assert(reloc.type.action.simple.shift == .@"0");
2054 elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr);
19152055 elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val);
19162056 return;
19172057 },
......@@ -1944,6 +2084,8 @@ const SymbolReloc = struct {
19442084 .ok => {},
19452085 .overflowed => elf.overflowed_reloc_count -= 1,
19462086 .misaligned => elf.misaligned_reloc_count -= 1,
2087 .illegal_dynamic_type => elf.illegal_dynamic_type_reloc_count -= 1,
2088 .illegal_dynamic_section => elf.illegal_dynamic_section_reloc_count -= 1,
19472089 }
19482090
19492091 reloc.* = undefined;
......@@ -1957,14 +2099,211 @@ const SymbolReloc = struct {
19572099 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
19582100 switch (elf.ehdrType()) {
19592101 .REL => {},
1960 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node.unwrap().?)) {
1961 .no => unreachable, // there *was* a dynamic relocation!
1962 .yes => {},
1963 .yes_textrel => elf.textrel_count -= 1,
2102 .EXEC, .DYN => switch (elf.nodeAllowsDynamicReloc(reloc.node.unwrap().?)) {
2103 .not_alloc => unreachable, // there *was* a dynamic relocation!
2104 .ok => {},
2105 .ok_textrel => elf.textrel_count -= 1,
19642106 },
19652107 }
19662108 reloc.rela_index = .none;
19672109 }
2110
2111 fn updateDynamicReloc(reloc: *SymbolReloc, elf: *Elf) Error!void {
2112 assert(elf.ehdrType() != .REL);
2113 const reloc_node = reloc.node.unwrap().?; // deleted
2114
2115 // Delete any current output relocation or error.
2116 switch (reloc.result) {
2117 .ok, .overflowed, .misaligned => {
2118 reloc.deleteOutputRel(elf);
2119 },
2120 .illegal_dynamic_type => {
2121 elf.illegal_dynamic_type_reloc_count -= 1;
2122 reloc.result = .ok;
2123 },
2124 .illegal_dynamic_section => {
2125 elf.illegal_dynamic_section_reloc_count -= 1;
2126 reloc.result = .ok;
2127 },
2128 }
2129
2130 assert(reloc.rela_index == .none);
2131
2132 const symbol_class = elf.classifySymbolValue(reloc.target);
2133
2134 switch (reloc.target.unwrap()) {
2135 .local => {},
2136 .global => |gsi| switch (symbol_class) {
2137 .static => gsi.ptr(elf).status.any_static_target_relocs = true,
2138 .static_relative => gsi.ptr(elf).status.any_static_relative_target_relocs = true,
2139 .dynamic => gsi.ptr(elf).status.any_dynamic_target_relocs = true,
2140 },
2141 }
2142
2143 const want_dynamic_reloc: enum {
2144 /// `reloc` cannot be statically resolved, but is not an allowed dynamic relocation.
2145 illegal,
2146 relative,
2147 abs_addr,
2148 dtp_off,
2149 tp_off,
2150 size32,
2151 size64,
2152 } = switch (symbol_class) {
2153 .static => return,
2154
2155 .static_relative => switch (reloc.type.target) {
2156 // We need a runtime relocation if the relocation depends on our load address, which
2157 // is the case iff the relocation resolves to the absolute value of a symbol. Relocs
2158 // which are PC-relative, or which resolve to something other than a symbol address
2159 // (e.g. a symbol's size), are fine.
2160
2161 .pltrel,
2162 .rel,
2163 .size,
2164 .dtpoff,
2165 .tpoff,
2166 => return,
2167
2168 .abs, .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2169 true => .relative,
2170 false => .illegal,
2171 },
2172
2173 .special => switch (reloc.type.action.special) {
2174 .larch_pcala_hi20,
2175 .larch_pcala64_lo20,
2176 .larch_pcala64_hi12,
2177 .larch_b21,
2178 .larch_b26,
2179 .larch_call36,
2180 .sparc_wdisp10,
2181 .sparc_wdisp16,
2182 .sparc_le_hix22,
2183 => return,
2184
2185 .sparc_olo10,
2186 .sparc_hix22,
2187 => .illegal,
2188 },
2189 },
2190
2191 .dynamic => switch (reloc.type.target) {
2192 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
2193 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
2194 .pltabs => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2195 true => .relative,
2196 false => .illegal,
2197 },
2198 // ...although PC-relative PLT relocations don't even need that that!
2199 .pltrel => return,
2200 // PC-relative relocations are not valid dynamic relocations.
2201 .rel => .illegal,
2202 // On the few targets supporting size relocations, they are valid at runtime.
2203 .size => switch (reloc.type.action.simple.dest) {
2204 .@"32" => .size32,
2205 .@"64" => .size64,
2206 else => .illegal,
2207 },
2208 // Absolute addresses and TLS offsets can be lowered at runtime provided they
2209 // are address-sized.
2210 .dtpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2211 true => .dtp_off,
2212 false => .illegal,
2213 },
2214 .tpoff => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2215 true => .tp_off,
2216 false => .illegal,
2217 },
2218 .abs => switch (reloc.type.action.simple.dest.isAddr(elf)) {
2219 true => .abs_addr,
2220 false => .illegal,
2221 },
2222
2223 .special => switch (reloc.type.action.special) {
2224 .larch_b21,
2225 .larch_b26,
2226 .larch_call36,
2227 .sparc_wdisp10,
2228 .sparc_wdisp16,
2229 => return,
2230
2231 .larch_pcala_hi20,
2232 .larch_pcala64_lo20,
2233 .larch_pcala64_hi12,
2234 .sparc_olo10,
2235 .sparc_hix22,
2236 .sparc_le_hix22,
2237 => .illegal,
2238 },
2239 },
2240 };
2241
2242 const is_textrel: bool = switch (elf.nodeAllowsDynamicReloc(reloc_node)) {
2243 .not_alloc => {
2244 // The section containing the relocation is not loaded into memory. These sections
2245 // have slightly different rules. In other ELF linkers, they basically just let any
2246 // relocation through at any time---they will bind to a local symbol definition if
2247 // it exists, or just to 0 otherwise. I think that's a little scary though, so until
2248 // I come across a compelling reason for us to mimic that behaviour, let's be more
2249 // conservative and only allow what seems necessary:
2250 const allow: bool = switch (want_dynamic_reloc) {
2251 // Relocations which target the value/[d]tpoff of a symbol with a local
2252 // definition, and which write their result to a full address-sized word, have
2253 // clear use cases (e.g. DWARF uses these), so let them through.
2254 .relative,
2255 .abs_addr,
2256 .dtp_off,
2257 .tp_off,
2258 => switch (reloc.target.unwrap()) {
2259 .local => true,
2260 .global => |gsi| gsi.resolveAlias(elf).defined(elf),
2261 },
2262
2263 // Everything else I'm going to disallow for now. Remember that we're already in
2264 // the dynamic case here, so this isn't disallowing (e.g.) all size relocations
2265 // in these sections, just size relocations *targeting dynamic symbols*!
2266 .size32, .size64, .illegal => false,
2267 };
2268 if (!allow) {
2269 // ...but in this case we actually do need to trigger an error.
2270 reloc.result = .illegal_dynamic_section;
2271 elf.illegal_dynamic_section_reloc_count += 1;
2272 }
2273 return;
2274 },
2275 .ok => false,
2276 .ok_textrel => true,
2277 };
2278
2279 const rel_type: MachineRelocType = rel: switch (want_dynamic_reloc) {
2280 .illegal => {
2281 reloc.result = .illegal_dynamic_type;
2282 elf.illegal_dynamic_type_reloc_count += 1;
2283 return;
2284 },
2285 .relative => .relative(elf),
2286 .abs_addr => .absAddr(elf),
2287 .dtp_off => .dtpOff(elf),
2288 .tp_off => .tpOff(elf),
2289 .size32 => MachineRelocType.size32(elf) orelse continue :rel .illegal,
2290 .size64 => MachineRelocType.size64(elf) orelse continue :rel .illegal,
2291 };
2292 const target_dynsym: u32 = switch (want_dynamic_reloc) {
2293 .illegal => unreachable, // handled above
2294 .relative => 0,
2295 else => reloc.target.unwrap().global.dynsymIndex(elf).?,
2296 };
2297
2298 try elf.shndx.rela_dyn.relaEnsureAdditionalCapacity(elf, 1);
2299 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
2300 .type = rel_type,
2301 .offset = elf.getNodeVAddr(reloc_node) + reloc.offset,
2302 .raw_sym_index = target_dynsym,
2303 .addend = reloc.addend.normal,
2304 }).toOptional();
2305 if (is_textrel) elf.textrel_count += 1;
2306 }
19682307};
19692308
19702309/// A relocation targeting an arbitrary node (within a section) with a fixed addend.
......@@ -1992,34 +2331,14 @@ const NodeReloc = struct {
19922331 }
19932332 };
19942333
1995 fn flushMovedNode(reloc: *NodeReloc, elf: *Elf, node_vaddr: u64) void {
1996 if (reloc.rela_index.unwrap()) |rela_index| {
1997 assert(elf.ehdrType() == .REL);
1998 // The node has moved, so the offset of the relocation within the section might have
1999 // changed, so update the `offset` field of the `ElfN.Rela` entry.
2000 elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaSetOffset(elf, rela_index, node_vaddr + reloc.offset);
2001 } else {
2002 assert(elf.ehdrType() != .REL);
2003 reloc.apply(elf);
2004 }
2005 }
2006
2007 fn flushMovedTarget(reloc: *NodeReloc, elf: *Elf, target_section_offset: u64) void {
2008 if (reloc.rela_index.unwrap()) |rela_index| {
2009 assert(elf.ehdrType() == .REL);
2010 // The target has moved, so the `addend` field of the `ElfN.Rela` entry needs to be updated.
2011 elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx.relaSetAddend(elf, rela_index, target_section_offset +% @as(u64, @bitCast(reloc.addend)));
2012 } else {
2013 assert(elf.ehdrType() != .REL);
2014 reloc.apply(elf);
2015 }
2016 }
2017
20182334 fn apply(reloc: *NodeReloc, elf: *Elf) void {
20192335 const node = reloc.node.unwrap() orelse return; // deleted
20202336 if (reloc.rela_index.unwrap()) |rela_index| {
20212337 assert(elf.ehdrType() == .REL);
2022 _ = rela_index;
2338 // Ensure the output relocation is up-to-date.
2339 const rela_shndx = elf.getNodeShndx(reloc.node.unwrap().?).get(elf).rela.shndx;
2340 rela_shndx.relaSetOffset(elf, rela_index, elf.getNodeVAddr(node) + reloc.offset);
2341 rela_shndx.relaSetAddend(elf, rela_index, elf.getNodeVAddr(node) +% @as(u64, @bitCast(reloc.addend)));
20232342 } else {
20242343 assert(elf.ehdrType() != .REL);
20252344 if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(&elf.mf)) {
......@@ -2755,6 +3074,8 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
27553074} || Error)!Symbol.Global.Index {
27563075 _ = opts.lib_name; // TODO
27573076
3077 log.debug("addGlobalSymbol({q})", .{opts.name});
3078
27583079 const gpa = elf.base.comp.gpa;
27593080
27603081 try elf.symtab.ensureUnusedCapacity(gpa, 1);
......@@ -2802,9 +3123,7 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
28023123 }
28033124 }
28043125
2805 if (elf.mergeGlobalSymbolVisibility(gsi, opts.visibility)) {
2806 force_apply_relocs = true;
2807 }
3126 elf.mergeGlobalSymbolVisibility(gsi, opts.visibility);
28083127
28093128 // There's already a symbol with this name, we just need to potentially set its value, and
28103129 // to update its bind and visibility.
......@@ -2988,10 +3307,15 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
29883307/// some operation which requires relocations to be re-applied, it can pass `force_apply_relocs` as
29893308/// `true` to guarantee this function to do so.
29903309fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_relocs: bool) Error!void {
3310 const trace = tracy.trace(@src());
3311 defer trace.end();
3312
29913313 const gpa = elf.base.comp.gpa;
29923314
29933315 const gsi = orig_gsi.resolveAlias(elf);
29943316
3317 trace.addText(gsi.rawName(elf).slice(elf));
3318
29953319 const maybe_alias_gsi = gsi.findAliaser(elf);
29963320
29973321 var apply_relocs: bool = force_apply_relocs;
......@@ -3055,14 +3379,23 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
30553379 },
30563380 };
30573381
3058 const omit_dynsym: bool = omit: {
3059 if (elf.shndx.dynsym == .UNDEF) break :omit true;
3060 break :omit switch (visibility) {
3061 .HIDDEN, .INTERNAL => true,
3062 .DEFAULT, .PROTECTED => false,
3063 };
3382 const have_dynsym: bool = switch (elf.shndx.dynsym) {
3383 .UNDEF => false,
3384 else => switch (visibility) {
3385 .HIDDEN, .INTERNAL => false,
3386 // An undefined protected dynamic symbol would be pointless, because protected symbols
3387 // don't bind to external definitions; so only add to dynsym if we have a definition.
3388 .PROTECTED => gsi.defined(elf),
3389 // Executables conventionally do not put symbols in their dynamic symbol table unless a
3390 // definition is seen, while dynamic libraries just chuck 'em all in.
3391 .DEFAULT => switch (elf.base.comp.config.output_mode) {
3392 .Exe => gsi.defined(elf) or maybe_dso_global != null,
3393 .Lib => true,
3394 .Obj => unreachable, // we have a dynamic symbol table
3395 },
3396 },
30643397 };
3065 if (omit_dynsym) {
3398 if (!have_dynsym) {
30663399 if (try elf.deleteOwnedDynsym(gsi)) {
30673400 apply_relocs = true;
30683401 }
......@@ -3089,9 +3422,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
30893422 if (elf.ehdrType() != .REL) {
30903423 assert(elf.classifySymbolValue(.global(gsi)) != .dynamic);
30913424 }
3092 gsi.deleteDynamicTargetRelocs(elf);
3425
3426 if (try gsi.updateDynamicTargetRelocs(elf)) {
3427 apply_relocs = true;
3428 }
30933429 if (maybe_alias_gsi) |alias_gsi| {
3094 alias_gsi.deleteDynamicTargetRelocs(elf);
3430 if (try alias_gsi.updateDynamicTargetRelocs(elf)) {
3431 apply_relocs = true;
3432 }
30953433 }
30963434
30973435 if (apply_relocs) {
......@@ -3335,10 +3673,8 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
33353673 } else {
33363674 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
33373675 // We previously made a copy relocation, but now have deemed it unnecessary.
3676 try elf.deleteNode(copied_global_kv.value.node);
33383677 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
3339 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the
3340 // space), we should delete `copied_global_kv.value.node`, which is an
3341 // "orphaned" `copied_global` node.
33423678 apply_relocs = true;
33433679 }
33443680 }
......@@ -3352,10 +3688,12 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
33523688 _ = elf.unknown_globals.swapRemove(gsi);
33533689 }
33543690
3355 if (elf.classifySymbolValue(.global(gsi)) != .dynamic) {
3356 gsi.deleteDynamicTargetRelocs(elf);
3357 if (maybe_alias_gsi) |alias_gsi| {
3358 alias_gsi.deleteDynamicTargetRelocs(elf);
3691 if (try gsi.updateDynamicTargetRelocs(elf)) {
3692 apply_relocs = true;
3693 }
3694 if (maybe_alias_gsi) |alias_gsi| {
3695 if (try alias_gsi.updateDynamicTargetRelocs(elf)) {
3696 apply_relocs = true;
33593697 }
33603698 }
33613699
......@@ -3478,15 +3816,12 @@ fn setGlobalSymbolBind(
34783816/// undefined---their visibility values are combined to determine the resulting visibility.
34793817///
34803818/// Every call to this function *must* be followed by a call to `updateGlobalDynamic` since changes
3481/// to symbol visibility can affect the dynamic symbol table entry. If this function returns `true`,
3482/// then that `updateGlobalDynamic` call must have the `force_apply_relocs` argument set to `true`,
3483/// because this symbol's classification has changed (it is no longer a dynamic symbol) so
3484/// relocations targeting the symbol must be re-applied.
3819/// to symbol visibility can affect the dynamic symbol table entry.
34853820fn mergeGlobalSymbolVisibility(
34863821 elf: *Elf,
34873822 gsi: Symbol.Global.Index,
34883823 other_visibility: std.elf.STV,
3489) bool {
3824) void {
34903825 const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
34913826 inline else => |sym| elf.targetLoad(&sym.other).visibility,
34923827 };
......@@ -3518,13 +3853,13 @@ fn mergeGlobalSymbolVisibility(
35183853 ),
35193854 }
35203855
3521 if (elf.ehdrType() == .REL) {
3522 return false;
3523 }
3856 if (elf.ehdrType() == .REL) return;
35243857
35253858 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or
35263859 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to
35273860 // a relocatable), then the symbol should be "demoted" to binding `STB_LOCAL` in the output.
3861 // TODO: after the latest batch of refactors, I'm unsure if this logic should be here anymore. I
3862 // think it should probably live in `updateGlobalDynamic` instead.
35283863 const old_demoted: bool = switch (old_visibility) {
35293864 .INTERNAL, .HIDDEN => true,
35303865 .DEFAULT, .PROTECTED => false,
......@@ -3542,16 +3877,6 @@ fn mergeGlobalSymbolVisibility(
35423877 }
35433878 elf.moveDemotedGlobal(gsi);
35443879 }
3545
3546 const old_protected: bool = switch (old_visibility) {
3547 .PROTECTED, .INTERNAL, .HIDDEN => true,
3548 .DEFAULT => false,
3549 };
3550 const new_protected: bool = switch (new_visibility) {
3551 .PROTECTED, .INTERNAL, .HIDDEN => true,
3552 .DEFAULT => false,
3553 };
3554 return old_protected != new_protected;
35553880}
35563881/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
35573882/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF
......@@ -3613,10 +3938,8 @@ fn deleteOwnedDynsym(elf: *Elf, gsi: Symbol.Global.Index) std.mem.Allocator.Erro
36133938
36143939 var apply_relocs: bool = false;
36153940 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
3941 try elf.deleteNode(copied_global_kv.value.node);
36163942 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);
3617 // TODO: once `MappedFile` has a way to delete a node (so it can re-use the
3618 // space), we should delete `copied_global_kv.value.node`, which is an
3619 // "orphaned" `copied_global` node.
36203943 apply_relocs = true;
36213944 }
36223945 if (elf.plt.getIndex(gsi)) |plt_index| {
......@@ -3930,85 +4253,52 @@ const Symbol = struct {
39304253 return null;
39314254 }
39324255
3933 /// Scans through all relocations targeting `gsi`, deletes their dynamic relocation
3934 /// entries, and adds `R_*_RELATIVE` relocation entries as needed.
4256 /// Updates the dynamic relocations associated with relocations targeting `gsi`, adding
4257 /// or removing them as needed based on the current classification of `gsi`.
39354258 ///
3936 /// Asserts we are creating a DSO.
3937 fn deleteDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) void {
3938 if (elf.shndx.dynamic == .UNDEF) return;
4259 /// Returns `true` if any relocations were updated, in which case relocations targeting
4260 /// `gsi` must be re-applied by the caller.
4261 fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool {
4262 if (elf.shndx.dynamic == .UNDEF) return false;
39394263 const sym_id: Symbol.Id = .global(gsi);
4264
39404265 switch (elf.classifySymbolValue(sym_id)) {
39414266 .static => {
39424267 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
39434268 !gsi.ptr(elf).status.any_static_relative_target_relocs)
39444269 {
3945 return;
4270 return false;
39464271 }
3947 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
3948 gsi.ptr(elf).status.any_static_relative_target_relocs = false;
3949 gsi.ptr(elf).status.any_static_target_relocs = true;
39504272 },
39514273 .static_relative => {
39524274 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
39534275 !gsi.ptr(elf).status.any_static_target_relocs)
39544276 {
3955 return;
4277 return false;
4278 }
4279 },
4280 .dynamic => {
4281 if (!gsi.ptr(elf).status.any_static_target_relocs and
4282 !gsi.ptr(elf).status.any_static_relative_target_relocs)
4283 {
4284 return false;
39564285 }
3957 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
3958 gsi.ptr(elf).status.any_static_target_relocs = false;
3959 gsi.ptr(elf).status.any_static_relative_target_relocs = true;
39604286 },
3961 // TODO: this function needs to support a symbol becoming dynamic which wasn't
3962 // previously dynamic (and when it does, the function should be renamed to
3963 // `updateTargetOutputRelocs`). But for now, this isn't supported.
3964 .dynamic => unreachable,
39654287 }
4288
4289 gsi.ptr(elf).status.any_static_target_relocs = false;
4290 gsi.ptr(elf).status.any_static_relative_target_relocs = false;
4291 gsi.ptr(elf).status.any_dynamic_target_relocs = false;
4292
39664293 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
39674294 while (ri != .none) {
39684295 const reloc = ri.get(elf);
39694296 assert(reloc.target == sym_id);
3970 reloc.deleteOutputRel(elf);
4297 try reloc.updateDynamicReloc(elf);
39714298 ri = reloc.next;
39724299 }
3973 switch (elf.classifySymbolValue(sym_id)) {
3974 .static => return,
3975 .static_relative => {},
3976 .dynamic => unreachable,
3977 }
3978 // We removed the symbol relocations, now add R_*_RELATIVE relocations where needed.
3979 ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3980 while (ri != .none) {
3981 const reloc = ri.get(elf);
3982 ri = reloc.next;
3983 assert(reloc.target == sym_id);
3984 switch (reloc.type.target) {
3985 // Only relocations which resolve to absolute addresses require runtime
3986 // `R_*_RELATIVE` relocations.
3987 .special,
3988 .pltrel,
3989 .rel,
3990 .dtpoff,
3991 .tpoff,
3992 .size,
3993 => continue,
3994
3995 .abs, .pltabs => {},
3996 }
3997 if (!reloc.type.action.simple.dest.isAddr(elf)) continue;
3998 const node = reloc.node.unwrap().?;
3999 switch (elf.nodeWantsDsoRelocation(node)) {
4000 .no => continue,
4001 .yes_textrel => elf.textrel_count += 1,
4002 .yes => {},
4003 }
4004 // There is capacity for a relocation because we just deleted one earlier.
4005 reloc.rela_index = elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
4006 .type = .relative(elf),
4007 .offset = elf.getNodeVAddr(node) + reloc.offset,
4008 .raw_sym_index = 0,
4009 .addend = 0,
4010 }).toOptional();
4011 }
4300
4301 return true;
40124302 }
40134303
40144304 const Optional = enum(u32) {
......@@ -4139,7 +4429,20 @@ const Symbol = struct {
41394429 }
41404430
41414431 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
4432 const trace = tracy.trace(@src());
4433 defer trace.end();
4434
41424435 const sym_index = sym_id.index(elf);
4436
4437 trace.addText(name: {
4438 // There isn't a general `name` method on `Symbol.Id` because it would be incorrect
4439 // to ever actually care about the name of a local symbol.
4440 const name: String(.strtab) = switch (elf.symPtr(sym_index)) {
4441 inline else => |sym| @fromBackingInt(elf.targetLoad(&sym.name)),
4442 };
4443 break :name name.slice(elf);
4444 });
4445
41434446 switch (elf.symPtr(sym_index)) {
41444447 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
41454448 }
......@@ -4164,14 +4467,12 @@ const Symbol = struct {
41644467 }
41654468
41664469 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
4167 if (elf.ehdrType() != .REL) {
4168 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
4169 while (ri != .none) {
4170 const reloc = ri.get(elf);
4171 assert(reloc.target == sym_id);
4172 reloc.apply(elf);
4173 ri = reloc.next;
4174 }
4470 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
4471 while (ri != .none) {
4472 const reloc = ri.get(elf);
4473 assert(reloc.target == sym_id);
4474 reloc.apply(elf);
4475 ri = reloc.next;
41754476 }
41764477
41774478 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
......@@ -4251,61 +4552,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
42514552 .EXEC => false,
42524553 };
42534554
4254 if (elf.shndx.dynamic == .UNDEF) {
4255 // This is a static non-PIE executable---every symbol has a statically known value.
4256 return .static;
4257 }
4258
42594555 const resolved_sym = sym.resolveAlias(elf);
42604556
4261 const shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(resolved_sym.index(elf))) {
4557 const symtab_shndx: Section.Index, const visibility: std.elf.STV = switch (elf.symPtr(resolved_sym.index(elf))) {
42624558 inline else => |sym_ptr| .{
42634559 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
42644560 elf.targetLoad(&sym_ptr.other).visibility,
42654561 },
42664562 };
42674563
4268 switch (resolved_sym.unwrap()) {
4269 .local => {
4270 assert(shndx != .UNDEF);
4564 const shndx: Section.Index = switch (resolved_sym.unwrap()) {
4565 .local => shndx: {
4566 assert(symtab_shndx != .UNDEF);
42714567 assert(visibility == .DEFAULT);
4568 break :shndx symtab_shndx;
42724569 },
4273 .global => |gsi| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {
4274 // An unprotected symbol in a DSO which is not an executable is subject to runtime
4275 // preemption, so a dynamic relocation is required for it even if we have a definition.
4276 return .dynamic;
4277 } else if (elf.copied_globals.contains(gsi)) {
4278 // This becomes a locally-defined symbol in `.data`.
4279 return if (runtime_load_addr) .static_relative else .static;
4280 },
4281 }
4570 .global => |gsi| shndx: {
4571 const shndx: Section.Index = switch (symtab_shndx) {
4572 .UNDEF => if (elf.copied_globals.contains(gsi)) .data else .UNDEF,
4573 else => |shndx| shndx,
4574 };
42824575
4283 return switch (shndx) {
4284 .UNDEF => switch (visibility) {
4285 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {
4286 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section
4287 // This is a static PIE---we'll see a definition at some point, so there'd be no
4288 // point in adding an invalid dynamic relocation (the only valid dynamic relation
4289 // type in a static PIE is `R_*_RELATIVE`).
4290 return .static;
4291 } else .dynamic, // external symbol
4576 // Check for dynamic symbols before falling into the static path.
4577 if (gsi.dynsymIndex(elf) != null and visibility == .DEFAULT) {
4578 if (shndx == .UNDEF) {
4579 // There is no local definition of this symbol.
4580 return .dynamic;
4581 } else if (comp.config.output_mode != .Exe) {
4582 // There is a local definition, but it is preemptible at runtime.
4583 return .dynamic;
4584 }
4585 }
42924586
4293 // If the symbol *cannot* be external, then there's no point making a dynamic relocation
4294 // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`.
4295 .INTERNAL, .HIDDEN, .PROTECTED => .static,
4587 break :shndx shndx;
42964588 },
4589 };
42974590
4298 .ABS => .static,
4591 if (runtime_load_addr and
4592 shndx != .UNDEF and
4593 shndx != .ABS and
4594 shndx.flags(elf).ALLOC and
4595 !shndx.flags(elf).TLS)
4596 {
4597 // This symbol's value is a virtual address relative to a runtime-known load address.
4598 return .static_relative;
4599 }
42994600
4300 else => if (runtime_load_addr and
4301 shndx.flags(elf).ALLOC and
4302 !shndx.flags(elf).TLS)
4303 {
4304 return .static_relative;
4305 } else {
4306 return .static;
4307 },
4308 };
4601 return .static;
43094602}
43104603
43114604pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
......@@ -4848,6 +5141,7 @@ fn create(
48485141 .section_by_name = .empty,
48495142 .changed_symtab_index = .empty,
48505143 .textrel_count = 0,
5144 .entry_gsi = undefined,
48515145
48525146 .dwarf = .init(&elf.base, switch (comp.config.debug_format) {
48535147 .strip => .@"32", // for .eh_frame
......@@ -4860,10 +5154,12 @@ fn create(
48605154 .dwarf_addr = .{
48615155 .first_target_reloc = .none,
48625156 .symbol_relocs = .empty,
5157 .pending_reloc_index = 0,
48635158 },
48645159 .dwarf_str_offsets = .{
48655160 .first_target_reloc = .none,
48665161 .node_relocs = .empty,
5162 .pending_reloc_index = 0,
48675163 },
48685164 .dwarf_units = &.{},
48695165 .dwarf_consts = .empty,
......@@ -4873,6 +5169,8 @@ fn create(
48735169
48745170 .overflowed_reloc_count = 0,
48755171 .misaligned_reloc_count = 0,
5172 .illegal_dynamic_type_reloc_count = 0,
5173 .illegal_dynamic_section_reloc_count = 0,
48765174
48775175 .const_prog_node = .none,
48785176 .input_prog_node = .none,
......@@ -5078,9 +5376,10 @@ fn initHeaders(
50785376 defer phnum += 1;
50795377 break :phndx phnum;
50805378 } else undefined,
5081 // `data` must be assigned after all other loadable segments so that it has the greatest
5082 // phndx of any loadable segment. This is so that `targetSegmentLoadAddressRestrictions`
5083 // can be obeyed (specifically, the `.data_last` restriction, needed on SPARC).
5379 // `data` must be assigned after all other loadable segments so that it has the last
5380 // phndx of the loadable segments. This causes `updateSegmentLoadAddresses` to put
5381 // that segment's virtual address after the others, which is a de-facto requirement
5382 // on SPARC. More details are in a comment in `updateSegmentLoadAddresses`.
50845383 .data = phndx: {
50855384 defer phnum += 1;
50865385 break :phndx phnum;
......@@ -5214,7 +5513,7 @@ fn initHeaders(
52145513 // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node
52155514 .alignment = node_block_align.max(addr_align),
52165515 .moved = true,
5217 .bubbles_moved = false,
5516 .bubbles_moved = false, // see loadable segment handling in `flushMoved`
52185517 }), .{ .segment = phndx.rodata });
52195518 elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata);
52205519
......@@ -5223,14 +5522,13 @@ fn initHeaders(
52235522 .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above
52245523 .moved = true,
52255524 .resized = true,
5226 .bubbles_moved = false,
52275525 }), .{ .segment = phndx.phdr });
52285526 elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr);
52295527
52305528 elf.ni.text = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{
52315529 .alignment = node_block_align,
52325530 .moved = true,
5233 .bubbles_moved = false,
5531 .bubbles_moved = false, // see loadable segment handling in `flushMoved`
52345532 }), .{ .segment = phndx.text });
52355533 elf.phdrs.items[phndx.text] = .wrap(elf.ni.text);
52365534
......@@ -5238,7 +5536,7 @@ fn initHeaders(
52385536 // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node
52395537 .alignment = node_block_align.max(addr_align),
52405538 .moved = true,
5241 .bubbles_moved = false,
5539 .bubbles_moved = false, // see loadable segment handling in `flushMoved`
52425540 }), .{ .segment = phndx.data });
52435541 elf.phdrs.items[phndx.data] = .wrap(elf.ni.data);
52445542
......@@ -5246,7 +5544,7 @@ fn initHeaders(
52465544 try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{
52475545 .alignment = node_block_align,
52485546 .moved = true,
5249 .bubbles_moved = false,
5547 .bubbles_moved = false, // see loadable segment handling in `flushMoved`
52505548 }),
52515549 .{ .segment = phndx.plt },
52525550 ));
......@@ -5256,7 +5554,6 @@ fn initHeaders(
52565554 // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above.
52575555 .alignment = node_block_align.max(addr_align),
52585556 .moved = true,
5259 .bubbles_moved = false,
52605557 }), .{ .segment = phndx.relro });
52615558 elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro);
52625559
......@@ -5265,7 +5562,7 @@ fn initHeaders(
52655562 try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{
52665563 .alignment = node_block_align,
52675564 .moved = true,
5268 .bubbles_moved = false,
5565 .bubbles_moved = false, // see TLS segment handling in `flushMoved`
52695566 }),
52705567 .{ .segment = phndx.tls },
52715568 ));
......@@ -5358,25 +5655,10 @@ fn initHeaders(
53585655 const ElfN = ct_class.ElfN();
53595656 const target_endian = elf.targetEndian();
53605657
5361 populate_phdrs: {
5362 // Initially we will give every `PT_LOAD` segment this address. When we re-allocate
5363 // segments in the virtual address space in `flushMoved` and `flushResized`, we will
5364 // move some segments to higher addresses to prevent overlap. This address therefore
5365 // becomes the image's "base address"; i.e. the first `PT_LOAD` segment will start
5366 // at this address. The base address could eventually end up higher than this due to
5367 // how we re-allocate the address space, but never lower.
5368 const base_vaddr: u64 = switch (@"type") {
5369 .REL => break :populate_phdrs,
5370 .DYN => 0,
5371 .EXEC => switch (machine) {
5372 .AARCH64 => 0x200000,
5373 .LOONGARCH => 0x10000,
5374 .PPC64 => 0x10000000,
5375 .RISCV => 0x10000,
5376 .SPARCV9 => 0x100000,
5377 .X86_64 => 0x200000,
5378 },
5379 };
5658 if (@"type" != .REL) {
5659 // Initially we will give every `PT_LOAD` segment the base address. They will be
5660 // moved to non-overlapping addresses by `updateSegmentLoadAddresses`.
5661 const base_vaddr: u64 = elf.baseLoadableSegmentVAddr();
53805662
53815663 // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the
53825664 // binary, their *nodes* are not aligned to this boundary---ELF only requires that
......@@ -5683,7 +5965,6 @@ fn initHeaders(
56835965 .size = interp.len + 1,
56845966 .moved = true,
56855967 .resized = true,
5686 .bubbles_moved = false,
56875968 }),
56885969 .{ .segment = phndx.interp },
56895970 );
......@@ -5705,7 +5986,6 @@ fn initHeaders(
57055986 try elf.ni.data_rel_ro.addFloatingChild(gpa, &elf.mf, .{
57065987 .alignment = addr_align,
57075988 .moved = true,
5708 .bubbles_moved = false,
57095989 }),
57105990 .{ .segment = phndx.dynamic },
57115991 );
......@@ -5959,7 +6239,6 @@ fn initHeaders(
59596239 .size = @sizeOf(Dwarf.EhFrameHdr),
59606240 .alignment = .@"4",
59616241 .moved = true,
5962 .bubbles_moved = false,
59636242 }),
59646243 .{ .segment = phndx.gnu_eh_frame },
59656244 );
......@@ -6272,6 +6551,31 @@ fn initHeaders(
62726551 .code_view => unreachable,
62736552 }
62746553
6554 // If we have an entry point symbol, create the undefined reference now.
6555 elf.entry_gsi = entry: switch (elf.options.entry) {
6556 .default => switch (comp.config.output_mode) {
6557 .Exe => continue :entry .enabled,
6558 .Lib, .Obj => continue :entry .disabled,
6559 },
6560 .enabled => continue :entry .{ .named = "_start" },
6561 .disabled => .none,
6562 .named => |sym_name| .wrap(elf.addGlobalSymbol(.{
6563 .node = .none,
6564 .name = sym_name,
6565 .value = 0,
6566 .size = 0,
6567 .type = .NOTYPE,
6568 .bind = .strong,
6569 .visibility = .DEFAULT,
6570 .shndx = .UNDEF,
6571 }) catch |err| switch (err) {
6572 error.MultipleDefinitions => unreachable, // shndx is undef
6573 error.MultipleDefaultVersions => unreachable, // not a versioned symbol
6574 error.UndefinedDefaultVersion => unreachable, // not a versioned symbol
6575 else => |e| return e,
6576 }),
6577 };
6578
62756579 assert(elf.nodes.len == expected_nodes_len);
62766580 assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF
62776581
......@@ -6303,21 +6607,7 @@ fn initHeaders(
63036607 },
63046608 };
63056609
6306 if (@"type" != .REL) switch (elf.targetSegmentLoadAddressRestrictions()) {
6307 .none => {},
6308 .data_last => switch (elf.phdrSlice()) {
6309 inline else => |phdr| {
6310 // Ensure that the segment after `.data` (if any) is not a loadable segment.
6311 const next_phndx = phndx.data + 1;
6312 if (next_phndx < phdr.len) {
6313 switch (elf.targetLoad(&phdr[next_phndx].type)) {
6314 .NULL, .LOAD => unreachable, // data segment should be the last loadable segment
6315 else => {},
6316 }
6317 }
6318 },
6319 },
6320 };
6610 try elf.updateSegmentLoadAddresses();
63216611}
63226612
63236613pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
......@@ -6482,43 +6772,6 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
64826772 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
64836773 return parent_vaddr + offset;
64846774}
6485fn computeNodeSectionOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 {
6486 const parent_ni = ni.parent(&elf.mf).unwrap().?;
6487 const parent_section_offset = parent_section_offset: switch (elf.getNode(parent_ni)) {
6488 .deleted,
6489 .archive,
6490 .archive_header,
6491 .archive_input_member,
6492 .archive_elf_member_header,
6493 .elf,
6494 .ehdr,
6495 .shdr,
6496 .segment,
6497 => unreachable,
6498 .section, .section_manual_size => 0,
6499 .input_section, .copied_global => unreachable,
6500 .nav, .uav, .lazy_code, .lazy_const_data => unreachable,
6501 .debug_shared, .debug_addr, .eh_frame_footer, .debug_str_offsets, .unit_padding => unreachable,
6502 .unit_frame, .unit_debug_info, .unit_debug_line => {
6503 const parent_section_offset, _ = parent_ni.location(&elf.mf).resolve(&elf.mf);
6504 break :parent_section_offset parent_section_offset;
6505 },
6506 .unit_frame_cie,
6507 .unit_debug_info_header,
6508 .unit_debug_info_footer,
6509 .unit_debug_line_header,
6510 .unit_debug_rnglists,
6511 .const_debug_info,
6512 .global_debug_info,
6513 .func_frame_fde,
6514 .func_debug_info,
6515 .func_debug_line,
6516 .decl_debug_info,
6517 => unreachable,
6518 };
6519 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
6520 return parent_section_offset + offset;
6521}
65226775fn computeNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 {
65236776 return ni.fileLocation(&elf.mf, false).offset - elf.ni.elf.fileLocation(&elf.mf, false).offset;
65246777}
......@@ -6660,7 +6913,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
66606913fn flushMovedNodeRelocs(
66616914 elf: *Elf,
66626915 node: MappedFile.Node.Index,
6663 node_vaddr: u64,
66646916 opts: struct {
66656917 first_symbol_reloc: SymbolReloc.Index = .none,
66666918 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,
......@@ -6676,7 +6928,7 @@ fn flushMovedNodeRelocs(
66766928 if (reloc.node == opts.skip_symbol_relocs) continue;
66776929 break;
66786930 }
6679 reloc.flushMovedNode(elf, node_vaddr);
6931 reloc.apply(elf);
66806932 }
66816933 }
66826934
......@@ -6687,7 +6939,7 @@ fn flushMovedNodeRelocs(
66876939 if (reloc.node == opts.skip_node_relocs) continue;
66886940 break;
66896941 }
6690 reloc.flushMovedNode(elf, node_vaddr);
6942 reloc.apply(elf);
66916943 }
66926944 }
66936945
......@@ -6755,6 +7007,21 @@ fn ehdrType(elf: *const Elf) EhdrType {
67557007 }
67567008}
67577009
7010fn baseLoadableSegmentVAddr(elf: *const Elf) u64 {
7011 return switch (elf.ehdrType()) {
7012 .REL => unreachable,
7013 .DYN => 0,
7014 .EXEC => switch (elf.ehdrMachine()) {
7015 .AARCH64 => 0x200000,
7016 .LOONGARCH => 0x10000,
7017 .PPC64 => 0x10000000,
7018 .RISCV => 0x10000,
7019 .SPARCV9 => 0x100000,
7020 .X86_64 => 0x200000,
7021 },
7022 };
7023}
7024
67587025fn targetPtrSize(elf: *const Elf) u8 {
67597026 return elf.identClass().size();
67607027}
......@@ -6873,30 +7140,6 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo {
68737140 // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`.
68747141 };
68757142}
6876/// Specifies any restrictions the current target has regarding how segments are ordered in the
6877/// virtual address space. Most targets do not have any such restrictions.
6878fn targetSegmentLoadAddressRestrictions(elf: *const Elf) enum {
6879 none,
6880 /// The "mutable data" segment must be the last loadable segment in the virtual address space.
6881 data_last,
6882} {
6883 return switch (elf.ehdrMachine()) {
6884 .AARCH64,
6885 .PPC64,
6886 .RISCV,
6887 .X86_64,
6888 .LOONGARCH,
6889 => .none,
6890
6891 // SPARC uses `R_SPARC_PC{10,22}` relocations to construct pointers to the GOT, but these
6892 // relocations write an *unsigned* PC-relative offset. This cannot even be worked around by
6893 // using a larger code model, because the crt `_start` assembly always uses these specific
6894 // relocations. Therefore, to avoid relocation errors, all code must appear before the GOT
6895 // in the virtual address space. The easiest way for us to do that is to ensure that the
6896 // "mutable data" segment, containing the GOT, is the last segment in the address space.
6897 .SPARCV9 => .data_last,
6898 };
6899}
69007143fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
69017144 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
69027145 const Child = pointer_ty.child;
......@@ -7326,6 +7569,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!v
73267569 .{elf.mf.io_err.?},
73277570 ),
73287571 };
7572 try elf.updateSegmentLoadAddresses();
73297573}
73307574fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void {
73317575 const comp = elf.base.comp;
......@@ -8605,6 +8849,7 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
86058849 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
86068850 else => |e| return e,
86078851 };
8852 try elf.updateSegmentLoadAddresses();
86088853}
86098854fn prelinkInner(elf: *Elf) Error!void {
86108855 const comp = elf.base.comp;
......@@ -8659,24 +8904,27 @@ fn prelinkInner(elf: *Elf) Error!void {
86598904 if (comp.zcu) |_| self_hosted_codegen: {
86608905 if (comp.config.use_llvm) break :self_hosted_codegen;
86618906
8662 // We're using self-hosted codegen---add an input representing the Zig "object".
8663 try elf.inputs.ensureUnusedCapacity(gpa, 1);
8664 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
8665 defer gpa.free(zcu_name);
8666 const zcu_file_symbol = try elf.addLocalSymbol(.{
8667 .node = .none,
8668 .name = zcu_name,
8669 .value = 0,
8670 .size = 0,
8671 .type = .FILE,
8672 .shndx = .ABS,
8673 });
8674 elf.inputs.addOneAssumeCapacity().* = .{
8675 .path = elf.base.emit,
8676 .member = null,
8677 .extra = .{ .file_symbol = zcu_file_symbol },
8678 };
8679 elf.input_pending_index += 1;
8907 if (elf.archive == null) {
8908 // Self-hosted codegen is logically emitting a relocatable which is then immediately
8909 // being linked into the output file, so add that fictional Zig object file input.
8910 try elf.inputs.ensureUnusedCapacity(gpa, 1);
8911 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
8912 defer gpa.free(zcu_name);
8913 const zcu_file_symbol = try elf.addLocalSymbol(.{
8914 .node = .none,
8915 .name = zcu_name,
8916 .value = 0,
8917 .size = 0,
8918 .type = .FILE,
8919 .shndx = .ABS,
8920 });
8921 elf.inputs.addOneAssumeCapacity().* = .{
8922 .path = elf.base.emit,
8923 .member = null,
8924 .extra = .{ .file_symbol = zcu_file_symbol },
8925 };
8926 elf.input_pending_index += 1;
8927 }
86808928
86818929 switch (elf.shndx.debug_addr) {
86828930 .UNDEF => {},
......@@ -8758,6 +9006,7 @@ pub fn zcuFilesReady(elf: *Elf, zcu: *Zcu) link.Error!void {
87589006 .{elf.mf.io_err.?},
87599007 ),
87609008 };
9009 try elf.updateSegmentLoadAddresses();
87619010}
87629011fn zcuFilesReadyInner(elf: *Elf, zcu: *Zcu) Error!void {
87639012 const gpa = zcu.gpa;
......@@ -9126,7 +9375,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
91269375 .size = opts.node_align.forward(opts.size),
91279376 .alignment = opts.addralign.max(opts.node_align),
91289377 .resized = opts.size > 0,
9129 .bubbles_moved = opts.flags.ALLOC,
9378 .bubbles_moved = opts.flags.ALLOC, // see handling in `flushMoved`
91309379 }),
91319380 if (opts.manual_size) .{ .section_manual_size = shndx } else .{ .section = shndx },
91329381 )),
......@@ -9265,6 +9514,7 @@ fn addRelocAssumeCapacity(
92659514 .rela_index = rela_index.toOptional(),
92669515 .result = .ok,
92679516 });
9517 ri.get(elf).apply(elf);
92689518 },
92699519 .DYN, .EXEC => switch (elf.ehdrMachine()) {
92709520 .AARCH64 => switch (@"type".AARCH64) {
......@@ -9622,18 +9872,21 @@ fn addSymbolRelocAssumeCapacityInner(
96229872) Error!void {
96239873 assert(elf.ehdrType() != .REL);
96249874
9625 const rela_index: Section.RelaIndex.Optional = r: {
9626 if (elf.shndx.dynamic == .UNDEF) break :r .none;
9627
9628 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to
9629 // determine the vaddr of `node`.
9630 const node_vaddr = elf.getNodeVAddr(node);
9631
9632 // If this is `true`, we will try to create a copy relocation for the target symbol if it is
9633 // not locally defined. If the relocation value is always computed from the target symbol's
9634 // value (even for an external target symbol), and if the target symbol might be of type
9635 // STT_OBJECT, this should probably be `true`.
9636 const try_copy_reloc: bool = switch (@"type".target) {
9875 request_static_target: {
9876 if (elf.shndx.dynamic == .UNDEF) break :request_static_target;
9877 const gsi: Symbol.Global.Index = switch (target.unwrap()) {
9878 .local => break :request_static_target,
9879 .global => |gsi| gsi,
9880 };
9881 // The target symbol may be dynamic, in which case its value cannot be resolved statically.
9882 // However, there are strategies (such as copy relocations) the linker can employ which can
9883 // effectively convert dynamic symbols into static symbols. If statically resolving this
9884 // relocation would require knowing the symbol value, we can set a flag so that those
9885 // strategies are used when possible to make this relocation statically resolvable.
9886 //
9887 // (This `switch` should return `false` for TLS relocations, because we can't do anything
9888 // about TLS symbols being dynamic.)
9889 const want_static_value: bool = switch (@"type".target) {
96379890 .rel, .abs => true,
96389891
96399892 .pltrel,
......@@ -9662,92 +9915,13 @@ fn addSymbolRelocAssumeCapacityInner(
96629915 => false,
96639916 },
96649917 };
9665 if (try_copy_reloc) switch (target.unwrap()) {
9666 .local => {},
9667 .global => |gsi| if (!gsi.ptr(elf).status.want_static_value) {
9668 gsi.ptr(elf).status.want_static_value = true;
9669 try elf.updateGlobalDynamic(gsi, false);
9670 },
9671 };
9672
9673 classify: switch (elf.classifySymbolValue(target)) {
9674 .static => {
9675 switch (target.unwrap()) {
9676 .local => {},
9677 .global => |gsi| gsi.ptr(elf).status.any_static_target_relocs = true,
9678 }
9679 break :r .none;
9680 },
9681 .static_relative => {
9682 switch (target.unwrap()) {
9683 .local => {},
9684 .global => |gsi| gsi.ptr(elf).status.any_static_relative_target_relocs = true,
9685 }
9686 switch (@"type".target) {
9687 // Only relocations which resolve to absolute addresses require runtime
9688 // `R_*_RELATIVE` relocations.
9689 .special,
9690 .pltrel,
9691 .rel,
9692 .dtpoff,
9693 .tpoff,
9694 .size,
9695 => break :r .none,
9918 if (!want_static_value) break :request_static_target;
96969919
9697 .abs, .pltabs => {},
9698 }
9699 if (!@"type".action.simple.dest.isAddr(elf)) break :r .none;
9700 switch (elf.nodeWantsDsoRelocation(node)) {
9701 .no => break :r .none,
9702 .yes => {},
9703 .yes_textrel => elf.textrel_count += 1,
9704 }
9705 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
9706 .type = .relative(elf),
9707 .offset = node_vaddr + offset,
9708 .raw_sym_index = 0,
9709 .addend = 0,
9710 }).toOptional();
9711 },
9712 .dynamic => {
9713 target.unwrap().global.ptr(elf).status.any_dynamic_target_relocs = true;
9714 const dynamic_reloc_type: MachineRelocType = switch (@"type".target) {
9715 // PLT relocations targeting dynamic symbols actually target that symbol's PLT
9716 // entry, so we should emit an `R_*_RELATIVE` relocation instead.
9717 .pltabs => continue :classify .static_relative,
9718 // ...although PC-relative PLT relocations don't even need that!
9719 .pltrel => break :r .none,
9720 // Weird sizes or computations are not supported as runtime relocations.
9721 .special => break :r .none,
9722 // Relative addresses are not supported as runtime relocations.
9723 .rel => break :r .none,
9724
9725 // On the few targets supporting size relocations, they are valid at runtime.
9726 .size => switch (@"type".action.simple.dest) {
9727 .@"32" => MachineRelocType.size32(elf) orelse break :r .none,
9728 .@"64" => MachineRelocType.size64(elf) orelse break :r .none,
9729 else => break :r .none,
9730 },
9731 // Absolute addresses and TLS offsets can be lowered at runtime provided they
9732 // are address-sized.
9733 .dtpoff => if (@"type".action.simple.dest.isAddr(elf)) .dtpOff(elf) else break :r .none,
9734 .tpoff => if (@"type".action.simple.dest.isAddr(elf)) .tpOff(elf) else break :r .none,
9735 .abs => if (@"type".action.simple.dest.isAddr(elf)) .absAddr(elf) else break :r .none,
9736 };
9737 switch (elf.nodeWantsDsoRelocation(node)) {
9738 .no => break :r .none,
9739 .yes => {},
9740 .yes_textrel => elf.textrel_count += 1,
9741 }
9742 break :r elf.shndx.rela_dyn.relaAddOneAssumeCapacity(elf, .{
9743 .type = dynamic_reloc_type,
9744 .offset = node_vaddr + offset,
9745 .raw_sym_index = target.unwrap().global.dynsymIndex(elf).?,
9746 .addend = addend.normal,
9747 }).toOptional();
9748 },
9920 if (!gsi.ptr(elf).status.want_static_value) {
9921 gsi.ptr(elf).status.want_static_value = true;
9922 try elf.updateGlobalDynamic(gsi, false);
97499923 }
9750 };
9924 }
97519925
97529926 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
97539927 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;
......@@ -9762,12 +9936,13 @@ fn addSymbolRelocAssumeCapacityInner(
97629936 .type = @"type",
97639937 .next = next,
97649938 .prev = .none,
9765 .rela_index = rela_index,
9939 .rela_index = .none,
97669940 .result = .ok,
97679941 });
97689942 if (@"type".dependsOnTlsSize(elf)) {
97699943 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
97709944 }
9945 try ri.get(elf).updateDynamicReloc(elf);
97719946
97729947 // Actually apply the new relocation!
97739948 ri.get(elf).apply(elf);
......@@ -10098,18 +10273,19 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
1009810273 };
1009910274}
1010010275
10101/// If `node` cannot contain runtime relocations, returns `.no`.
10276/// If `node` cannot contain dynamic relocations because it is not loaded into memory at runtime,
10277/// returns `.not_alloc`.
1010210278///
10103/// If `node` can contain runtime relocations, returns `.yes_textrel` if such a relocation requires
10104/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.
10105fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {
10279/// Otherwise, returns `.ok_textrel` if placing a dynamic relocation in this node requires the
10280/// presence of a `DT_TEXTREL` dynamic entry, or `.ok` otherwise.
10281fn nodeAllowsDynamicReloc(elf: *Elf, node: MappedFile.Node.Index) enum { ok, ok_textrel, not_alloc } {
1010610282 const shndx = elf.getNodeShndx(node);
1010710283 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
1010810284 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
1010910285 };
10110 if (!shf.ALLOC) return .no;
10111 if (!shf.WRITE) return .yes_textrel;
10112 return .yes;
10286 if (!shf.ALLOC) return .not_alloc;
10287 if (!shf.WRITE) return .ok_textrel;
10288 return .ok;
1011310289}
1011410290
1011510291pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {
......@@ -10120,6 +10296,7 @@ pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
1012010296 .{elf.mf.io_err.?},
1012110297 ),
1012210298 };
10299 try elf.updateSegmentLoadAddresses();
1012310300}
1012410301fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {
1012510302 const comp = elf.base.comp;
......@@ -10130,15 +10307,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
1013010307 const nav = ip.getNav(nav_index);
1013110308 const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?;
1013210309 switch (ip.indexToKey(nav.resolved.?.value)) {
10133 .@"extern" => |@"extern"| {
10310 .@"extern" => {
1013410311 if (mod.strip or elf.ehdrMachine() != .X86_64) return;
10135 const si = try elf.externSymbol(.{
10136 .name = @"extern".name.toSlice(ip),
10137 .lib_name = @"extern".lib_name.toSlice(ip),
10138 .type = elf.navType(nav.resolved.?),
10139 .linkage = @"extern".linkage,
10140 .visibility = @"extern".visibility,
10141 });
1014210312 const dwarf_gi = try elf.dwarf.getGlobal(nav_index);
1014310313 const dwarf_global = dwarf_gi.get(&elf.dwarf);
1014410314 const debug_info_ni = dwarf_global.debug_info_ni.unwrap().?;
......@@ -10147,10 +10317,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
1014710317 debug_info_ni.writer(gpa, &elf.mf, &di_nw);
1014810318 defer di_nw.deinit();
1014910319 elf.resetNodeRelocs(debug_info_ni);
10150 try elf.dwarf.updateExtern(pt, &di_nw, switch (elf.ehdrMachine()) {
10151 else => unreachable,
10152 .X86_64 => .x86_64,
10153 }, si, nav_index);
10320 try elf.dwarf.updateExtern(pt, &di_nw, nav_index);
1015410321 },
1015510322 else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {
1015610323 const nmi = try elf.navMapIndex(zcu, nav_index);
......@@ -10219,6 +10386,7 @@ pub fn updateContainerType(
1021910386 .{elf.mf.io_err.?},
1022010387 ),
1022110388 };
10389 try elf.updateSegmentLoadAddresses();
1022210390}
1022310391fn updateContainerTypeInner(
1022410392 elf: *Elf,
......@@ -10360,6 +10528,7 @@ pub fn updateFunc(
1036010528 .{elf.mf.io_err.?},
1036110529 ),
1036210530 };
10531 try elf.updateSegmentLoadAddresses();
1036310532}
1036410533fn updateFuncInner(
1036510534 elf: *Elf,
......@@ -10607,6 +10776,7 @@ pub fn lostTracking(
1060710776 elf.mf.io_err.?,
1060810777 }),
1060910778 };
10779 try elf.updateSegmentLoadAddresses();
1061010780}
1061110781
1061210782pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
......@@ -10616,6 +10786,7 @@ pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
1061610786 });
1061710787 if (elf.dwarf.const_pool.getIfExists(.anyerror_type)) |cpi|
1061810788 try elf.updateConstInner(pt, cpi, .anyerror_type, .complete);
10789 try elf.updateSegmentLoadAddresses();
1061910790}
1062010791
1062110792pub fn flush(
......@@ -10663,6 +10834,8 @@ fn flushInner(
1066310834
1066410835 try elf.prepareDynamic();
1066510836
10837 try elf.updateSegmentLoadAddresses();
10838
1066610839 while (try elf.idle()) {}
1066710840
1066810841 assert(elf.input_pending_index == elf.inputs.items.len);
......@@ -10678,10 +10851,28 @@ fn flushInner(
1067810851 defer elf.mf.nodes_lock.unlock();
1067910852
1068010853 if (elf.overflowed_reloc_count > 0) {
10681 diags.addError("failed to apply {d} relocations: overflow", .{elf.overflowed_reloc_count});
10854 diags.addError(
10855 "failed to apply {d} relocations: overflow",
10856 .{elf.overflowed_reloc_count},
10857 );
1068210858 }
1068310859 if (elf.misaligned_reloc_count > 0) {
10684 diags.addError("failed to apply {d} relocations: misaligned value", .{elf.misaligned_reloc_count});
10860 diags.addError(
10861 "failed to apply {d} relocations: misaligned value",
10862 .{elf.misaligned_reloc_count},
10863 );
10864 }
10865 if (elf.illegal_dynamic_type_reloc_count > 0) {
10866 diags.addError(
10867 "refusing to emit {d} dynamic relocations with illegal types",
10868 .{elf.illegal_dynamic_type_reloc_count},
10869 );
10870 }
10871 if (elf.illegal_dynamic_section_reloc_count > 0) {
10872 diags.addError(
10873 "refusing to emit {d} dynamic relocations in non-allocated sections",
10874 .{elf.illegal_dynamic_section_reloc_count},
10875 );
1068510876 }
1068610877
1068710878 if (elf.archive) |*archive| {
......@@ -10698,20 +10889,22 @@ fn flushInner(
1069810889 elf.flushDynamic();
1069910890
1070010891 const entry_addr: u64 = entry: {
10701 const sym_name: []const u8 = name: switch (elf.options.entry) {
10702 .default => switch (comp.config.output_mode) {
10703 .Exe => continue :name .enabled,
10704 .Lib, .Obj => continue :name .disabled,
10705 },
10706 .disabled => break :entry 0,
10707 .enabled => "_start",
10708 .named => |named| named,
10709 };
10710 const gsi = elf.globalByName(.{
10711 .name = sym_name,
10712 .version = null,
10713 }) orelse break :entry 0;
10714 break :entry Symbol.Id.global(gsi).value(elf);
10892 const entry_gsi = elf.entry_gsi.unwrap() orelse break :entry 0;
10893
10894 // Even though we already reported undefined globals earlier, we also need to check here
10895 // whether the entry point is defined. There are two reasons for this:
10896 //
10897 // * If we are emitting something other than an executable, we skipped the undefined globals
10898 // check, but there could still be an entry point (e.g. due to `-fentry=foo` on the CLI).
10899 //
10900 // * The entry point might be defined in an input DSO, which would satisfy the undefined
10901 // symbol reference but is insufficient here: the entry point needs a local definition.
10902
10903 if (!entry_gsi.resolveAlias(elf).defined(elf)) {
10904 return diags.fail("undefined entry point symbol '{s}'", .{entry_gsi.name(elf)});
10905 }
10906
10907 break :entry Symbol.Id.global(entry_gsi).value(elf);
1071510908 };
1071610909 switch (elf.ehdrPtr()) {
1071710910 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
......@@ -10719,9 +10912,12 @@ fn flushInner(
1071910912
1072010913 try elf.mf.flush();
1072110914
10722 if (elf.options.enable_link_snapshots)
10723 elf.dumpStderr() catch |err|
10724 return diags.fail("dumping link snapshot failed: {t}", .{err});
10915 if (elf.options.enable_link_snapshots) {
10916 elf.dumpStderr() catch |err| switch (err) {
10917 error.Canceled => |e| return e,
10918 else => |e| return diags.fail("dumping link snapshot failed: {t}", .{e}),
10919 };
10920 }
1072510921}
1072610922
1072710923pub fn idle(elf: *Elf) link.Error!bool {
......@@ -10729,6 +10925,9 @@ pub fn idle(elf: *Elf) link.Error!bool {
1072910925 elf.mf.nodes_lock.lock();
1073010926 defer elf.mf.nodes_lock.unlock();
1073110927
10928 const trace = tracy.trace(@src());
10929 defer trace.end();
10930
1073210931 const comp = elf.base.comp;
1073310932 const diags = &comp.link_diags;
1073410933
......@@ -10857,7 +11056,7 @@ pub fn idle(elf: *Elf) link.Error!bool {
1085711056 break :task;
1085811057 }
1085911058 while (elf.mf.updates.pop()) |ni| : (elf.mf.update_prog_node.completeOne()) {
10860 if (ni.pendingDelete(&elf.mf)) continue;
11059 ni.cleanUpdated(&elf.mf);
1086111060 const clean_moved = ni.cleanMoved(&elf.mf);
1086211061 const clean_resized = ni.cleanResized(&elf.mf);
1086311062 const clean_next_moved = ni.cleanNextMoved(&elf.mf);
......@@ -10872,6 +11071,9 @@ pub fn idle(elf: *Elf) link.Error!bool {
1087211071 if (elf.bss_moved) {
1087311072 elf.bss_moved = false;
1087411073
11074 const idle_prog_node = elf.mf.update_prog_node.start(".bss", 0);
11075 defer idle_prog_node.end();
11076
1087511077 const data_phndx = elf.getNode(elf.ni.data).segment;
1087611078 const data_offset: u64, const data_vaddr: u64, const data_filesz: u64 = switch (elf.phdrSlice()) {
1087711079 inline else => |phdr| .{
......@@ -10902,6 +11104,30 @@ pub fn idle(elf: *Elf) link.Error!bool {
1090211104
1090311105 break :task;
1090411106 }
11107 if (elf.dwarf_addr.pending_reloc_index < elf.dwarf_addr.symbol_relocs.items.len) {
11108 const idle_prog_node = elf.mf.update_prog_node.start(@tagName(Node.debug_addr), 0);
11109 defer idle_prog_node.end();
11110 const debug_addr_ni = elf.dwarf.debug_addr.ni.unwrap().?;
11111 for (elf.dwarf_addr.symbol_relocs.items[elf.dwarf_addr.pending_reloc_index..]) |symbol_ri| {
11112 const symbol_reloc = symbol_ri.get(elf);
11113 assert(symbol_reloc.node.unwrap().? == debug_addr_ni);
11114 symbol_reloc.apply(elf);
11115 }
11116 elf.dwarf_addr.pending_reloc_index = elf.dwarf_addr.symbol_relocs.items.len;
11117 break :task;
11118 }
11119 if (elf.dwarf_str_offsets.pending_reloc_index < elf.dwarf_str_offsets.node_relocs.items.len) {
11120 const idle_prog_node = elf.mf.update_prog_node.start(@tagName(Node.debug_str_offsets), 0);
11121 defer idle_prog_node.end();
11122 const debug_str_offsets_ni = elf.dwarf.debug_str_offsets.ni.unwrap().?;
11123 for (elf.dwarf_str_offsets.node_relocs.items[elf.dwarf_str_offsets.pending_reloc_index..]) |node_ri| {
11124 const node_reloc = node_ri.get(elf);
11125 assert(node_reloc.node.unwrap().? == debug_str_offsets_ni);
11126 node_reloc.apply(elf);
11127 }
11128 elf.dwarf_str_offsets.pending_reloc_index = elf.dwarf_str_offsets.node_relocs.items.len;
11129 break :task;
11130 }
1090511131 }
1090611132 if (elf.inputs.items.len - elf.input_pending_index > 0) return true;
1090711133 if (elf.input_sections.items.len - elf.input_section_pending_index > 0) return true;
......@@ -10909,6 +11135,8 @@ pub fn idle(elf: *Elf) link.Error!bool {
1090911135 if (elf.changed_symtab_index.count() > 0) return true;
1091011136 if (elf.mf.updates.items.len > 0) return true;
1091111137 if (elf.bss_moved) return true;
11138 if (elf.dwarf_addr.pending_reloc_index < elf.dwarf_addr.symbol_relocs.items.len) return true;
11139 if (elf.dwarf_str_offsets.pending_reloc_index < elf.dwarf_str_offsets.node_relocs.items.len) return true;
1091211140 return false;
1091311141}
1091411142
......@@ -11039,7 +11267,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
1103911267fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {
1104011268 while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) {
1104111269 const debug_addr_ni = elf.dwarf.debug_addr.ni.unwrap().?;
11042 try debug_addr_ni.moved(gpa, &elf.mf);
1104311270 var da_nw: link.MappedFile.Node.Writer = undefined;
1104411271 debug_addr_ni.writer(gpa, &elf.mf, &da_nw);
1104511272 defer da_nw.deinit();
......@@ -11052,7 +11279,6 @@ fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {
1105211279 }
1105311280 while (elf.dwarf.debug_str_offsets.map.count() - elf.dwarf.debug_str_offsets.pending_index > 0) {
1105411281 const debug_str_offsets_ni = elf.dwarf.debug_str_offsets.ni.unwrap().?;
11055 try debug_str_offsets_ni.moved(gpa, &elf.mf);
1105611282 var dso_nw: link.MappedFile.Node.Writer = undefined;
1105711283 debug_str_offsets_ni.writer(gpa, &elf.mf, &dso_nw);
1105811284 defer dso_nw.deinit();
......@@ -11297,6 +11523,7 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void {
1129711523fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
1129811524 const trace = tracy.trace(@src());
1129911525 defer trace.end();
11526 trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)});
1130011527
1130111528 switch (elf.getNode(ni)) {
1130211529 .deleted => unreachable,
......@@ -11315,7 +11542,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1131511542 else => unreachable,
1131611543
1131711544 .NULL, .LOAD => {
11318 try elf.allocateSegmentLoadAddress(phndx);
11545 // Our address changes are handled by `updateSegmentLoadAddresses`, so
11546 // there's nothing we need to do here. Don't bubble moved, because our
11547 // child nodes only care if our virtual address changes, which is
11548 // handled by that other function.
11549 return;
1131911550 },
1132011551
1132111552 .DYNAMIC,
......@@ -11343,6 +11574,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1134311574 elf,
1134411575 tbss_addr,
1134511576 );
11577
11578 // Do not bubble moved---our child nodes don't care about our virtual
11579 // address changing, because symbols in the TLS segment have values
11580 // defined relative to the base of the segment. (This is also why we
11581 // don't update tbss symbols here.)
11582 return;
1134611583 },
1134711584 }
1134811585 },
......@@ -11382,7 +11619,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1138211619 reloc.apply(elf);
1138311620 }
1138411621 } else if (shndx == elf.shndx.plt) {
11385 elf.flushMovedNodeRelocs(ni, addr, .{
11622 elf.flushMovedNodeRelocs(ni, .{
1138611623 .first_symbol_reloc = elf.plt_first_symbol_reloc,
1138711624 });
1138811625 elf.flushMovedPltSection(.plt, old_addr, addr);
......@@ -11391,10 +11628,16 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1139111628 } else if (shndx == elf.shndx.plt_sec) {
1139211629 elf.flushMovedPltSection(.plt_sec, old_addr, addr);
1139311630 } else if (shndx == elf.shndx.eh_frame_hdr) {
11394 elf.flushMovedNodeRelocs(ni, addr, .{
11631 elf.flushMovedNodeRelocs(ni, .{
1139511632 .first_symbol_reloc = elf.eh_frame_hdr_first_symbol_reloc,
1139611633 });
1139711634 }
11635
11636 if (!flags.ALLOC) {
11637 // Do not bubble moved---our virtual address hasn't changed (because it's always 0
11638 // for non-ALLOC sections), so there's nothing for our children to worry about.
11639 return;
11640 }
1139811641 },
1139911642 .input_section => |isi| {
1140011643 const old_section_addr = isi.ptr(elf).vaddr;
......@@ -11438,17 +11681,13 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1143811681 }
1143911682 }
1144011683
11441 elf.flushMovedNodeRelocs(ni, new_section_addr, .{
11684 elf.flushMovedNodeRelocs(ni, .{
1144211685 .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc,
1144311686 .first_got_reloc = isi.ptrConst(elf).first_got_reloc,
1144411687 });
1144511688 },
1144611689 .copied_global => |global_name| {
11447 const copied_global = elf.copied_globals.getPtr(global_name) orelse {
11448 // TODO: this node is orphaned, which is possible because `MappedFile` does not yet
11449 // support deleting nodes. See logic in `updateGlobalDynamic`.
11450 return;
11451 };
11690 const copied_global = elf.copied_globals.getPtr(global_name).?;
1145211691 assert(copied_global.node == ni);
1145311692
1145411693 const new_addr = elf.computeNodeVAddr(ni);
......@@ -11465,7 +11704,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1146511704 Symbol.Id.global(gsi).flushMoved(elf, new_addr);
1146611705 }
1146711706 }
11468 elf.flushMovedNodeRelocs(ni, new_addr, .{
11707 elf.flushMovedNodeRelocs(ni, .{
1146911708 .first_symbol_reloc = mi.firstSymbolReloc(elf),
1147011709 .skip_symbol_relocs = switch (tag) {
1147111710 else => comptime unreachable,
......@@ -11479,12 +11718,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1147911718 });
1148011719 },
1148111720 .debug_shared => |ss| {
11482 const target_section_offset = elf.computeNodeSectionOffset(ni);
1148311721 var target_ri = elf.dwarf_shared.getPtr(ss).first_target_reloc;
1148411722 while (target_ri != .none) {
1148511723 const target_reloc = target_ri.get(elf);
1148611724 assert(target_reloc.target == ni);
11487 target_reloc.flushMovedTarget(elf, target_section_offset);
11725 target_reloc.apply(elf);
1148811726 target_ri = target_reloc.next;
1148911727 }
1149011728 },
......@@ -11495,119 +11733,110 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1149511733 .unit_debug_line,
1149611734 => {},
1149711735 .debug_addr => {
11498 const target_section_offset = elf.computeNodeSectionOffset(ni);
1149911736 var target_ri = elf.dwarf_addr.first_target_reloc;
1150011737 while (target_ri != .none) {
1150111738 const target_reloc = target_ri.get(elf);
1150211739 assert(target_reloc.target == ni);
11503 target_reloc.flushMovedTarget(elf, target_section_offset);
11740 target_reloc.apply(elf);
1150411741 target_ri = target_reloc.next;
1150511742 }
11506 const node_vaddr = elf.computeNodeVAddr(ni);
1150711743 for (elf.dwarf_addr.symbol_relocs.items) |symbol_ri| {
1150811744 const symbol_reloc = symbol_ri.get(elf);
1150911745 assert(symbol_reloc.node.unwrap().? == ni);
11510 symbol_reloc.flushMovedNode(elf, node_vaddr);
11746 symbol_reloc.apply(elf);
1151111747 }
11748 elf.dwarf_addr.pending_reloc_index = elf.dwarf_addr.symbol_relocs.items.len;
1151211749 },
1151311750 .debug_str_offsets => {
11514 const target_section_offset = elf.computeNodeSectionOffset(ni);
1151511751 var target_ri = elf.dwarf_str_offsets.first_target_reloc;
1151611752 while (target_ri != .none) {
1151711753 const target_reloc = target_ri.get(elf);
1151811754 assert(target_reloc.target == ni);
11519 target_reloc.flushMovedTarget(elf, target_section_offset);
11755 target_reloc.apply(elf);
1152011756 target_ri = target_reloc.next;
1152111757 }
11522 const node_vaddr = elf.computeNodeVAddr(ni);
1152311758 for (elf.dwarf_str_offsets.node_relocs.items) |node_ri| {
1152411759 const node_reloc = node_ri.get(elf);
1152511760 assert(node_reloc.node.unwrap().? == ni);
11526 node_reloc.flushMovedNode(elf, node_vaddr);
11761 node_reloc.apply(elf);
1152711762 }
11763 elf.dwarf_str_offsets.pending_reloc_index = elf.dwarf_str_offsets.node_relocs.items.len;
1152811764 },
1152911765 .unit_frame_cie => |ui| {
11530 const target_section_offset = elf.computeNodeSectionOffset(ni);
1153111766 var target_ri = elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc;
1153211767 while (target_ri != .none) {
1153311768 const target_reloc = target_ri.get(elf);
1153411769 assert(target_reloc.target == ni);
11535 target_reloc.flushMovedTarget(elf, target_section_offset);
11770 target_reloc.apply(elf);
1153611771 target_ri = target_reloc.next;
1153711772 }
1153811773 },
1153911774 .unit_debug_info_header => |ui| {
1154011775 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];
11541 const target_section_offset = elf.computeNodeSectionOffset(ni);
1154211776 var target_ri = dwarf_unit.debug_info_header_first_target_reloc;
1154311777 while (target_ri != .none) {
1154411778 const target_reloc = target_ri.get(elf);
1154511779 assert(target_reloc.target == ni);
11546 target_reloc.flushMovedTarget(elf, target_section_offset);
11780 target_reloc.apply(elf);
1154711781 target_ri = target_reloc.next;
1154811782 }
11549 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
11783 elf.flushMovedNodeRelocs(ni, .{
1155011784 .first_node_reloc = dwarf_unit.debug_info_header_first_node_reloc,
1155111785 });
1155211786 },
1155311787 .unit_debug_info_footer => {},
1155411788 .unit_debug_line_header => |ui| {
1155511789 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];
11556 const target_section_offset = elf.computeNodeSectionOffset(ni);
1155711790 var target_ri = dwarf_unit.debug_line_header_first_target_reloc;
1155811791 while (target_ri != .none) {
1155911792 const target_reloc = target_ri.get(elf);
1156011793 assert(target_reloc.target == ni);
11561 target_reloc.flushMovedTarget(elf, target_section_offset);
11794 target_reloc.apply(elf);
1156211795 target_ri = target_reloc.next;
1156311796 }
11564 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
11797 elf.flushMovedNodeRelocs(ni, .{
1156511798 .first_node_reloc = dwarf_unit.debug_line_header_first_node_reloc,
1156611799 });
1156711800 },
1156811801 .unit_debug_rnglists => |ui| {
1156911802 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];
11570 const target_section_offset = elf.computeNodeSectionOffset(ni);
1157111803 var target_ri = dwarf_unit.debug_rnglists_first_target_reloc;
1157211804 while (target_ri != .none) {
1157311805 const target_reloc = target_ri.get(elf);
1157411806 assert(target_reloc.target == ni);
11575 target_reloc.flushMovedTarget(elf, target_section_offset);
11807 target_reloc.apply(elf);
1157611808 target_ri = target_reloc.next;
1157711809 }
11578 const node_vaddr = elf.computeNodeVAddr(ni);
1157911810 for (dwarf_unit.debug_rnglists_symbol_relocs.items) |symbol_ri| {
1158011811 const symbol_reloc = symbol_ri.get(elf);
1158111812 assert(symbol_reloc.node.unwrap().? == ni);
11582 symbol_reloc.flushMovedNode(elf, node_vaddr);
11813 symbol_reloc.apply(elf);
1158311814 }
1158411815 },
1158511816 .const_debug_info => |cpi| {
1158611817 const dwarf_const = &elf.dwarf_consts.get(cpi).?;
11587 const target_section_offset = elf.computeNodeSectionOffset(ni);
1158811818 var target_ri = dwarf_const.debug_info_first_target_reloc;
1158911819 while (target_ri != .none) {
1159011820 const target_reloc = target_ri.get(elf);
1159111821 assert(target_reloc.target == ni);
11592 target_reloc.flushMovedTarget(elf, target_section_offset);
11822 target_reloc.apply(elf);
1159311823 target_ri = target_reloc.next;
1159411824 }
11595 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
11825 elf.flushMovedNodeRelocs(ni, .{
1159611826 .first_symbol_reloc = dwarf_const.debug_info_first_symbol_reloc,
1159711827 .first_node_reloc = dwarf_const.debug_info_first_node_reloc,
1159811828 });
1159911829 },
1160011830 .global_debug_info => |gi| {
1160111831 const dwarf_global = &elf.dwarf_globals.items[@backingInt(gi)];
11602 const target_section_offset = elf.computeNodeSectionOffset(ni);
1160311832 var target_ri = dwarf_global.debug_info_first_target_reloc;
1160411833 while (target_ri != .none) {
1160511834 const target_reloc = target_ri.get(elf);
1160611835 assert(target_reloc.target == ni);
11607 target_reloc.flushMovedTarget(elf, target_section_offset);
11836 target_reloc.apply(elf);
1160811837 target_ri = target_reloc.next;
1160911838 }
11610 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
11839 elf.flushMovedNodeRelocs(ni, .{
1161111840 .first_symbol_reloc = dwarf_global.debug_info_first_symbol_reloc,
1161211841 .first_node_reloc = dwarf_global.debug_info_first_node_reloc,
1161311842 });
......@@ -11623,22 +11852,21 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1162311852 elf.dwarf.updateEhFrameFde(ni.slice(&elf.mf), offset);
1162411853 },
1162511854 }
11626 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
11855 elf.flushMovedNodeRelocs(ni, .{
1162711856 .first_symbol_reloc = dwarf_func.frame_fde_first_symbol_reloc,
1162811857 .first_node_reloc = dwarf_func.frame_fde_first_node_reloc,
1162911858 });
1163011859 },
1163111860 .func_debug_info => |fi| {
1163211861 const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)];
11633 const target_section_offset = elf.computeNodeSectionOffset(ni);
1163411862 var target_ri = dwarf_func.debug_info_first_target_reloc;
1163511863 while (target_ri != .none) {
1163611864 const target_reloc = target_ri.get(elf);
1163711865 assert(target_reloc.target == ni);
11638 target_reloc.flushMovedTarget(elf, target_section_offset);
11866 target_reloc.apply(elf);
1163911867 target_ri = target_reloc.next;
1164011868 }
11641 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
11869 elf.flushMovedNodeRelocs(ni, .{
1164211870 .first_symbol_reloc = dwarf_func.debug_info_first_symbol_reloc,
1164311871 .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav|
1164411872 nav.lsi.index().ptr(elf).node
......@@ -11650,7 +11878,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1165011878 },
1165111879 .func_debug_line => |fi| {
1165211880 const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)];
11653 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
11881 elf.flushMovedNodeRelocs(ni, .{
1165411882 .first_symbol_reloc = dwarf_func.debug_line_first_symbol_reloc,
1165511883 .first_node_reloc = dwarf_func.debug_line_first_node_reloc,
1165611884 .skip_node_relocs = fi.get(&elf.dwarf).debug_info_ni,
......@@ -11658,15 +11886,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
1165811886 },
1165911887 .decl_debug_info => |di| {
1166011888 const dwarf_decl = &elf.dwarf_decls.get(di).?;
11661 const target_section_offset = elf.computeNodeSectionOffset(ni);
1166211889 var target_ri = dwarf_decl.debug_info_first_target_reloc;
1166311890 while (target_ri != .none) {
1166411891 const target_reloc = target_ri.get(elf);
1166511892 assert(target_reloc.target == ni);
11666 target_reloc.flushMovedTarget(elf, target_section_offset);
11893 target_reloc.apply(elf);
1166711894 target_ri = target_reloc.next;
1166811895 }
11669 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{
11896 elf.flushMovedNodeRelocs(ni, .{
1167011897 .first_node_reloc = dwarf_decl.debug_info_first_node_reloc,
1167111898 });
1167211899 },
......@@ -11737,166 +11964,156 @@ fn flushMovedNobitsSymbols(
1173711964 }
1173811965}
1173911966
11740/// Updates `vaddr` and `paddr` of the segment header, and marks everything in the segment as moved.
11741fn updateSegmentLoadAddress(
11742 elf: *Elf,
11743 segment_ni: MappedFile.Node.Index,
11744 new_vaddr: u64,
11745) std.mem.Allocator.Error!void {
11746 const phndx = elf.getNode(segment_ni).segment;
11747
11748 switch (elf.phdrSlice()) {
11749 inline else => |phdr| {
11750 assert(new_vaddr != elf.targetLoad(&phdr[phndx].vaddr));
11751 elf.targetStore(&phdr[phndx].vaddr, @intCast(new_vaddr));
11752 elf.targetStore(&phdr[phndx].paddr, @intCast(new_vaddr));
11753 },
11754 }
11755
11756 try segment_ni.childrenMoved(elf.base.comp.gpa, &elf.mf);
11757 if (segment_ni == elf.ni.data) elf.bss_moved = true;
11758}
11759
11760/// Given the index of a `PT_LOAD`/`PT_NULL` segment, assumes that the phdr's `offset` and `filesz`
11761/// have been updated as needed by the caller, and updates the `@"align"`, `vaddr`, `paddr`, and
11762/// `memsz` fields of the segment, in order to place it at a valid virtual address.
11967/// If any loadable segments have invalid virtual addresses, assigns them new valid addresses and
11968/// queues the necessary idle tasks (e.g. re-applying relocations targeting those segments).
1176311969///
11764/// TODO: this function is currently a source of non-determinism in the linker, because handling the
11765/// moving or resizing of a segment could reorder them and thereby affect how we handle *future*
11766/// changes to segments.
11767fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void {
11768 const segment_ni = elf.phdrs.items[orig_phndx].unwrap().?;
11769 assert(elf.getNode(segment_ni).segment == orig_phndx);
11970/// This function must be called at the end of every link task in case tghe task invalidated segment
11971/// addresses (which can happen in response to any node movement/resize, or any section resize). It
11972/// cannot be called from `idle` as this function does not have the required idempotency guarantee
11973/// so calling it non-deterministically it would lead to unreproducible output files.
11974fn updateSegmentLoadAddresses(elf: *Elf) std.mem.Allocator.Error!void {
11975 if (elf.ehdrType() == .REL) return;
11976
1177011977 const page_align = elf.targetPageAlign();
11771 const node_align = segment_ni.alignment(&elf.mf);
11772 const ph_align = page_align.max(node_align);
11773
11774 // If we determine that the segment's virtual address needs to move, then it's a good idea to
11775 // make it less likely that it needs to move *again* in the future, because it is expensive to
11776 // change a segment's load address (a lot of re-flushing is necessary). To do that, we reserve
11777 // more virtual address space than we need (multiplying the actual size by this value). That
11778 // way, there will usually be padding between segments which they can grow into.
11779 //
11780 // TODO: we might want to decrease this multiplier, or even omit it entirely, in cases where
11781 // virtual address space is constrained. For instance, 32-bit targets, or targets where short
11782 // PC-relative relocations between segments are common.
11783 const reserve_size_multiplier = 4;
1178411978
1178511979 switch (elf.phdrSlice()) {
1178611980 inline else => |phdr| {
11787 const offset = elf.targetLoad(&phdr[orig_phndx].offset);
11788 const filesz = elf.targetLoad(&phdr[orig_phndx].filesz);
11789
11790 const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr);
11791 assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr);
11792
11793 const bss_size = if (segment_ni == elf.ni.data)
11794 elf.shndx.bss.size(elf)
11795 else
11796 0;
11797 const memsz = filesz + bss_size;
11798
11799 var vaddr: u64 = orig_vaddr;
11800
11801 elf.targetStore(&phdr[orig_phndx].@"align", @intCast(ph_align.toByteUnits()));
11802 elf.targetStore(&phdr[orig_phndx].memsz, @intCast(memsz));
11803 elf.targetStore(&phdr[orig_phndx].type, if (memsz > 0) .LOAD else .NULL);
11804
11805 // First, we will shift the virtual address as needed in order to maintain the required
11806 // property that vaddr is congruent to offset modulo the phdr alignment.
11807 {
11808 // Compute the candidate address by undoing the current offset and then re-offsetting
11809 vaddr = std.mem.alignBackward(u64, vaddr, ph_align.toByteUnits()) + offset % ph_align.toByteUnits();
11810 // If `node_align` is greater than `page_align`, the address we just set might be in
11811 // the previous segment. The first page we "own" is the one in which the old vaddr
11812 // resides, so check against that.
11813 const first_good_vaddr = std.mem.alignBackward(u64, orig_vaddr, page_align.toByteUnits());
11814 if (vaddr < first_good_vaddr) {
11815 // Yep, we crossed into the previous segment's pages, so correct for that by
11816 // offsetting our address by another `ph_align`.
11817 vaddr += ph_align.toByteUnits();
11818 assert(vaddr >= first_good_vaddr);
11981 // Somewhere in the phdrs is a contiguous sequence of all "loadable" segments (segments
11982 // with a `type` of `PT_NULL` or `PT_LOAD`); find the start of that sequence. In theory
11983 // we could memoize this, but there's no real need since the number of phdrs is tiny.
11984 const load_phdrs_start = for (phdr, 0..) |*ph, phndx| {
11985 switch (elf.targetLoad(&ph.type)) {
11986 .NULL, .LOAD => break phndx,
11987 else => continue,
1181911988 }
11820 }
11821
11822 // If our size has changed, or if the address shift above caused our "end" address to
11823 // cross a page boundary, then we might be overlapping with the next segment's pages. In
11824 // that case, we will jump past that segment and give ourselves a new address after it.
11825 // We'll need to repeat this for every loadable phdr after us, until we're no longer
11826 // overlapping anything.
11827 var phndx = orig_phndx;
11828 for (phdr[orig_phndx + 1 ..], orig_phndx + 1..) |*next_ph, next_phndx| {
11829 switch (elf.targetLoad(&next_ph.type)) {
11989 } else unreachable; // we always create loadable (`PT_NULL`/`PT_LOAD`) segments
11990
11991 // Update these fields for each loadable segment:
11992 //
11993 // * `memsz`
11994 // * `type` (`PT_NULL` vs `PT_LOAD`)
11995 // * `align`
11996 // * `offset` (in theory this should happen in `updateElfOffset`, but this logic needs
11997 // to track that value, so it's easier to just populate it here instead)
11998 //
11999 // ...and check if the current `vaddr` values for the loadable segments are valid (i.e.
12000 // compatible with the segments' file offsets and address ranges do not overlap).
12001 var vaddrs_valid: bool = true;
12002 var first_free_vaddr: u64 = elf.baseLoadableSegmentVAddr();
12003 const load_phdrs_end: usize = for (
12004 phdr[load_phdrs_start..],
12005 elf.phdrs.items[load_phdrs_start..],
12006 load_phdrs_start..,
12007 ) |*ph, segment_oni, phndx| {
12008 switch (elf.targetLoad(&ph.type)) {
1183012009 .NULL, .LOAD => {},
11831 else => {
11832 // All loadable segments have contiguous indices, so this indicates we have
11833 // become the last loadable segment, meaning we definitely don't overlap any
11834 // other loadable segment.
11835 break;
11836 },
12010 else => break phndx, // reached end of loadable segments
1183712011 }
11838
11839 const next_vaddr = elf.targetLoad(&next_ph.vaddr);
11840 // Find the first virtual address which the next phdr "owns" by aligning its vaddr
11841 // backwards to the start of the page.
11842 const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits());
11843
11844 // Check if the segment fits here. We apply `reserve_size_multiplier`, but only if
11845 // the segment is already known to be moving---making it easier to grow in-place is
11846 // the whole point of the multiplier!
11847 {
11848 const target_size = if (vaddr == orig_vaddr) memsz else memsz * reserve_size_multiplier;
11849 if (vaddr + target_size <= next_page_vaddr) {
11850 break; // hooray, we fit here!
12012 const segment_ni = segment_oni.unwrap().?; // all loadable segments have nodes
12013
12014 // We need to know the segment's file offset here anyway, so we may as well just
12015 // populate the phdr `offset` field now instead of in `flushElfOffset`.
12016 const ph_offset = elf.computeNodeElfOffset(segment_ni);
12017 elf.targetStore(&ph.offset, @intCast(ph_offset));
12018
12019 // The alignment of the segment affects which virtual addresses are valid.
12020 const ph_align = segment_ni.alignment(&elf.mf).max(page_align);
12021 elf.targetStore(&ph.@"align", @intCast(ph_align.toByteUnits()));
12022
12023 // The size of the segment in memory is equal to the size of the segment in the file
12024 // plus the size of any `SHT_NOBITS` section(s) at the end of the segment.
12025 const ph_memsz: u64 = memsz: {
12026 _, const file_size: u64 = segment_ni.location(&elf.mf).resolve(&elf.mf);
12027 if (segment_ni == elf.ni.data) {
12028 break :memsz file_size + elf.shndx.bss.size(elf);
12029 } else {
12030 break :memsz file_size;
1185112031 }
12032 };
12033 elf.targetStore(&ph.memsz, @intCast(ph_memsz));
12034 elf.targetStore(&ph.type, if (ph_memsz == 0) .NULL else .LOAD);
12035
12036 // Check if the existing virtual address of the segment is valid.
12037 const ph_vaddr = elf.targetLoad(&ph.vaddr);
12038 assert(elf.targetLoad(&ph.paddr) == ph_vaddr);
12039 if (ph_vaddr < first_free_vaddr or
12040 ph_offset % ph_align.toByteUnits() != ph_vaddr % ph_align.toByteUnits())
12041 {
12042 @branchHint(.unlikely);
12043 vaddrs_valid = false;
1185212044 }
1185312045
11854 const next_ni = elf.phdrs.items[next_phndx].unwrap().?;
12046 // The next segment's pages must not overlap our own.
12047 first_free_vaddr = page_align.forward(ph_vaddr + ph_memsz);
12048 } else phdr.len;
1185512049
11856 // This segment don't fit here, but before deciding how to proceed, we need to
11857 // consider any target-specific restrictions we are subject to.
11858 switch (elf.targetSegmentLoadAddressRestrictions()) {
11859 .none => {},
11860 .data_last => if (next_ni == elf.ni.data) {
11861 // We can't leapfrog over the data segment. Instead, that segment just needs
11862 // to be shifted forwards to make space for us, and we'll then `break` with
11863 // our current vaddr.
11864
11865 if (next_phndx + 1 < phdr.len) switch (elf.targetLoad(&phdr[next_phndx + 1].type)) {
11866 .NULL, .LOAD => unreachable, // data segment should be the last loadable segment
11867 else => {},
11868 };
12050 // There should be no more loadable segments after the contiguous range.
12051 for (phdr[load_phdrs_end..]) |*ph| switch (elf.targetLoad(&ph.type)) {
12052 .NULL, .LOAD => unreachable, // non-contiguous loadable segment
12053 else => {},
12054 };
1186912055
11870 const free_vaddr = vaddr + memsz * reserve_size_multiplier;
12056 if (vaddrs_valid) {
12057 @branchHint(.likely);
12058 return;
12059 }
1187112060
11872 const next_align = page_align.max(next_ni.alignment(&elf.mf));
11873 const next_offset = elf.targetLoad(&next_ph.offset);
11874 const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits();
12061 // It's time to actually allocate virtual addresses for all segments. Our current
12062 // strategy for this preserves the order of segments in memory, but this isn't a hard
12063 // requirement in general.
12064 //
12065 // However, a warning! If this logic *is* ever changed to re-order segments, it may need
12066 // to abide by target-specific restrictions. For instance, on SPARC, because the GOT is
12067 // usually accessed with the unsigned relocations `R_SPARC_PC{10,22}`, it is effectively
12068 // a requirement for all code to have a lower virtual address than the GOT, which means
12069 // that the "text" segment must be ordered before the "data" segment on that target.
1187512070
11876 try elf.updateSegmentLoadAddress(next_ni, next_new_vaddr);
12071 const gpa = elf.base.comp.gpa;
1187712072
11878 break;
11879 },
12073 // If we determine that a segment's virtual address needs to move, then it's a good idea
12074 // to space it out a bit from the surrounding segments, making it less likely that more
12075 // segment moves are necessary in the future (because every segment movement invalidates
12076 // a lot of idle work). We do that by multiplying each segment's size by this multiplier
12077 // and treating that amount of space as "reserved" for the segment to grow into.
12078 //
12079 // TODO: we might want to decrease this multiplier, or even omit it entirely, in cases
12080 // where the virtual address space is constrained; for instance, 32-bit targets, or
12081 // targets where short PC-relative relocations between segments are common.
12082 const reserve_size_multiplier = 4;
12083
12084 first_free_vaddr = elf.baseLoadableSegmentVAddr();
12085 var preferred_vaddr = first_free_vaddr;
12086
12087 for (load_phdrs_start..load_phdrs_end) |phndx| {
12088 const ph_align: Alignment = .fromByteUnits(elf.targetLoad(&phdr[phndx].@"align"));
12089 assert(ph_align.compare(.gte, page_align));
12090 const ph_offset = elf.targetLoad(&phdr[phndx].offset);
12091 const ph_memsz = elf.targetLoad(&phdr[phndx].memsz);
12092
12093 // If the existing address is valid, then don't do anything.
12094 const ph_vaddr: u64 = elf.targetLoad(&phdr[phndx].vaddr);
12095 if (ph_vaddr >= first_free_vaddr and
12096 ph_vaddr % ph_align.toByteUnits() == ph_offset % ph_align.toByteUnits())
12097 {
12098 // The current virtual address is already valid, so just stay put. Perhaps the
12099 // next segment will have to move, but the only alternative was *us* moving!
12100 first_free_vaddr = page_align.forward(ph_vaddr + ph_memsz);
12101 preferred_vaddr = page_align.forward(ph_vaddr + ph_memsz * reserve_size_multiplier);
12102 continue;
1188012103 }
1188112104
11882 // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But
11883 // first we need to adjust `vaddr` to come after it.
11884 const next_size = elf.targetLoad(&next_ph.memsz);
11885 // Instead of putting ourselves right after `next_ph`, we'll go a bit later in the
11886 // address space so that `next_ph` has address space to grow into (like above).
11887 vaddr = ph_align.forward(@intCast(next_vaddr + next_size * 4)) + offset % ph_align.toByteUnits();
12105 // Otherwise, put ourselves at a new address just after the previous segment.
12106 const new_vaddr = ph_align.forward(preferred_vaddr) + ph_offset % ph_align.toByteUnits();
12107 assert(new_vaddr != ph_vaddr); // `ph_vaddr` was not valid
1188812108
11889 // Now just swap the phdrs and update our `phndx`.
11890 std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph);
11891 elf.phdrs.items[phndx] = .wrap(next_ni);
11892 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx };
11893 elf.phdrs.items[next_phndx] = .wrap(segment_ni);
11894 elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) };
11895 phndx = @intCast(next_phndx);
11896 }
12109 elf.targetStore(&phdr[phndx].vaddr, @intCast(new_vaddr));
12110 elf.targetStore(&phdr[phndx].paddr, @intCast(new_vaddr));
12111 const segment_ni = elf.phdrs.items[phndx].unwrap().?;
12112 try segment_ni.childrenMoved(gpa, &elf.mf);
12113 if (segment_ni == elf.ni.data) elf.bss_moved = true;
1189712114
11898 if (vaddr != orig_vaddr) {
11899 try elf.updateSegmentLoadAddress(segment_ni, vaddr);
12115 first_free_vaddr = page_align.forward(new_vaddr + ph_memsz);
12116 preferred_vaddr = page_align.forward(new_vaddr + ph_memsz * reserve_size_multiplier);
1190012117 }
1190112118 },
1190212119 }
......@@ -11905,6 +12122,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
1190512122fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
1190612123 const trace = tracy.trace(@src());
1190712124 defer trace.end();
12125 trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)});
1190812126
1190912127 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
1191012128 switch (elf.getNode(ni)) {
......@@ -11933,7 +12151,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1193312151 else => unreachable,
1193412152 .NULL, .LOAD => {
1193512153 elf.targetStore(&ph.filesz, @intCast(size));
11936 try elf.allocateSegmentLoadAddress(phndx);
1193712154 if (ni == elf.ni.data) elf.bss_moved = true;
1193812155 },
1193912156 .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => {
......@@ -11946,7 +12163,11 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1194612163
1194712164 elf.targetStore(&ph.filesz, @intCast(size));
1194812165
11949 // Our memsz must include the `.tbss` section.
12166 // Our memsz must include the `.tbss` section. After adding that in, we will
12167 // realign to the TLS segment alignment. This is not strictly required, but
12168 // the TLS runtime will pad the module's TLS block to the segment alignment
12169 // anyway, so it's simpler for us to just make the memsz aligned so that our
12170 // memsz matches the size of the module's TLS block.
1195012171 const tls_align: Alignment = .fromByteUnits(elf.targetLoad(&ph.@"align"));
1195112172 elf.targetStore(&ph.memsz, @intCast(
1195212173 tls_align.forward(size + elf.shndx.tbss.size(elf)),
......@@ -12040,6 +12261,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1204012261fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
1204112262 const trace = tracy.trace(@src());
1204212263 defer trace.end();
12264 trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)});
1204312265
1204412266 const node = elf.getNode(ni);
1204512267 switch (node) {
......@@ -12480,7 +12702,6 @@ fn ensurePltEntry(elf: *Elf, gsi: Symbol.Global.Index) Error!bool {
1248012702
1248112703 // relocate the PLT entry to point to the .GOT.PLT entry
1248212704 const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset;
12483 // TODO: handle overflow gracefully
1248412705 const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]);
1248512706 const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]);
1248612707 elf.targetStore(inst0, .{
......@@ -12646,7 +12867,6 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
1264612867 const target_slice = plt_slice[plt_offset..];
1264712868
1264812869 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;
12649 // TODO: handle overflow gracefully
1265012870 const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]);
1265112871 const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]);
1265212872
......@@ -12698,6 +12918,7 @@ pub fn updateExports(
1269812918 };
1269912919 }
1270012920 try elf.genPending(pt);
12921 try elf.updateSegmentLoadAddresses();
1270112922}
1270212923fn updateExportInner(elf: *Elf, export_index: Zcu.Export.Index) Error!void {
1270312924 const zcu = elf.base.comp.zcu.?;
......@@ -12801,111 +13022,8 @@ pub fn printNode(
1280113022 ni: MappedFile.Node.Index,
1280213023 indent: usize,
1280313024) Io.Writer.Error!void {
12804 const node = elf.getNode(ni);
1280513025 try w.splatByteAll(' ', indent);
12806 try w.writeAll(@tagName(node));
12807 switch (node) {
12808 else => {},
12809 .segment => |phndx| switch (elf.phdrSlice()) {
12810 inline else => |phdr| {
12811 const ph = &phdr[phndx];
12812 try w.writeByte('(');
12813 const pt = elf.targetLoad(&ph.type);
12814 if (std.enums.tagName(std.elf.PT, pt)) |pt_name|
12815 try w.writeAll(pt_name)
12816 else inline for (@typeInfo(std.elf.PT).@"enum".decl_names) |decl_name| {
12817 const decl_val = @field(std.elf.PT, decl_name);
12818 if (@TypeOf(decl_val) != std.elf.PT) continue;
12819 if (pt == @field(std.elf.PT, decl_name)) break try w.writeAll(decl_name);
12820 } else try w.print("0x{x}", .{pt});
12821 try w.writeAll(", ");
12822 const pf = elf.targetLoad(&ph.flags);
12823 if (pf.R) try w.writeByte('R');
12824 if (pf.W) try w.writeByte('W');
12825 if (pf.X) try w.writeByte('X');
12826 try w.writeByte(')');
12827 },
12828 },
12829 .section, .section_manual_size => |shndx| try w.print("({s})", .{shndx.name(elf).slice(elf)}),
12830 .input_section => |isi| {
12831 const ii = isi.input(elf);
12832 try w.print("({f}{f}, {s})", .{
12833 ii.path(elf).fmtEscapeString(),
12834 fmtMemberString(ii.member(elf)),
12835 elf.getNodeShndx(isi.node(elf)).name(elf).slice(elf),
12836 });
12837 },
12838 .copied_global => |gsi| try w.print("(copy:{s})", .{gsi.rawName(elf).slice(elf)}),
12839 .nav => |nmi| {
12840 const zcu = elf.base.comp.zcu.?;
12841 const ip = &zcu.intern_pool;
12842 const nav = ip.getNav(nmi.nav(elf));
12843 try w.print("({f}, {f})", .{
12844 Type.fromInterned(nav.resolved.?.type).fmt(zcu),
12845 nav.fqn.fmt(ip),
12846 });
12847 },
12848 .uav => |umi| {
12849 const zcu = elf.base.comp.zcu.?;
12850 const val: Value = .fromInterned(umi.uavValue(elf));
12851 try w.print("({f}, {f})", .{
12852 val.typeOf(zcu).fmt(zcu),
12853 val.fmtValue(zcu),
12854 });
12855 },
12856 inline .lazy_code, .lazy_const_data => |lmi| try w.print("({f})", .{
12857 Type.fromInterned(lmi.lazySymbol(elf).ty).fmt(elf.base.comp.zcu.?),
12858 }),
12859 .debug_shared => |ss| try w.print("({})", .{ss}),
12860 .unit_frame,
12861 .unit_frame_cie,
12862 .unit_debug_info,
12863 .unit_debug_info_header,
12864 .unit_debug_info_footer,
12865 .unit_debug_line,
12866 .unit_debug_line_header,
12867 .unit_debug_rnglists,
12868 => |ui| try w.print("({s})", .{ui.mod(&elf.dwarf).fully_qualified_name}),
12869 .const_debug_info => |cpi| switch (cpi.val(&elf.dwarf.const_pool)) {
12870 .generic_poison_type => try w.writeAll("(anytype)"),
12871 else => |val| try w.print("({f})", .{
12872 Value.fromInterned(val).fmtValue(elf.base.comp.zcu.?),
12873 }),
12874 },
12875 .global_debug_info => |gi| {
12876 const zcu = elf.base.comp.zcu.?;
12877 const ip = &zcu.intern_pool;
12878 const nav = ip.getNav(gi.nav(&elf.dwarf));
12879 try w.writeByte('(');
12880 if (nav.resolved) |resolved| try w.print("{f}, ", .{
12881 Type.fromInterned(resolved.type).fmt(zcu),
12882 });
12883 try w.print("{f}", .{nav.fqn.fmt(ip)});
12884 if (nav.resolved) |resolved| try w.print(", {f}", .{
12885 Value.fromInterned(resolved.value).fmtValue(zcu),
12886 });
12887 try w.writeByte(')');
12888 },
12889 .func_frame_fde, .func_debug_info, .func_debug_line => |fi| {
12890 const zcu = elf.base.comp.zcu.?;
12891 const ip = &zcu.intern_pool;
12892 const nav = ip.getNav(fi.nav(&elf.dwarf));
12893 try w.writeByte('(');
12894 if (nav.resolved) |resolved| try w.print("{f}, ", .{
12895 Type.fromInterned(resolved.type).fmt(zcu),
12896 });
12897 try w.print("{f})", .{nav.fqn.fmt(ip)});
12898 },
12899 .decl_debug_info => |di| {
12900 const comp = elf.base.comp;
12901 const zcu = comp.zcu.?;
12902 const ip = &zcu.intern_pool;
12903 const src_inst = di.srcInst(&elf.dwarf);
12904 try w.print("({f}, ", .{zcu.fileByIndex(src_inst.resolveFile(ip)).path.fmt(comp)});
12905 if (src_inst.resolve(ip)) |inst| try w.print("%{d}", .{inst}) else try w.writeAll("lost");
12906 try w.writeByte(')');
12907 },
12908 }
13026 try w.print("{f}", .{Node.fmtDebug(ni, elf)});
1290913027 {
1291013028 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];
1291113029 const off, const size = mf_node.location().resolve(&elf.mf);
......@@ -12975,15 +13093,8 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error
1297513093 switch (elf.phdrSlice()) {
1297613094 inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) {
1297713095 .NULL, .LOAD => {
12978 // The `@"align"` field is managed by `allocateSegmentLoadAddress`.
12979 //
12980 // It's very likely that the node was moved and/or resized when we realigned it
12981 // just above, but it is possible that it was not moved *but* still has an
12982 // unaligned virtual address. In that case, we need to ensure the segment's
12983 // virtual address range will be recomputed.
12984 if (!min_align.check(@intCast(elf.targetLoad(&phdr[phndx].vaddr)))) {
12985 try seg_ni.moved(gpa, &elf.mf);
12986 }
13096 // The `@"align"` field is managed by `updateSegmentLoadAddresses`, which will
13097 // be called at the end of the current link task.
1298713098 },
1298813099 else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max(
1298913100 elf.targetLoad(&phdr[phndx].@"align"),
......@@ -13008,11 +13119,9 @@ pub fn addNodeAssumeCapacity(elf: *Elf, ni: MappedFile.Node.Index, node: Node) M
1300813119 return ni;
1300913120}
1301013121
13011fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocator.Error!void {
13012 const ni = node.unwrap().?;
13013 try ni.delete(elf.base.comp.gpa, &elf.mf);
13014 elf.nodes.set(@backingInt(ni), .deleted);
13015 node.* = .none;
13122fn deleteNode(elf: *Elf, node: MappedFile.Node.Index) std.mem.Allocator.Error!void {
13123 try node.delete(elf.base.comp.gpa, &elf.mf);
13124 elf.nodes.set(@backingInt(node), .deleted);
1301613125}
1301713126
1301813127/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a
src/link/MachO2.zig+1
......@@ -3342,6 +3342,7 @@ pub fn idle(macho: *MachO) link.Error!bool {
33423342 }
33433343
33443344 while (macho.mf.updates.pop()) |ni| {
3345 ni.cleanUpdated(&macho.mf);
33453346 const clean_moved = ni.cleanMoved(&macho.mf);
33463347 const clean_resized = ni.cleanResized(&macho.mf);
33473348 const clean_next_moved = ni.cleanNextMoved(&macho.mf);
src/link/MappedFile.zig+53-22
......@@ -207,6 +207,7 @@ pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancel
207207 .resized = false,
208208 .next_moved = false,
209209 .has_content = false,
210 .updated = false,
210211 },
211212 .location_payload = switch (root_location) {
212213 .small => |small| .{ .small = small },
......@@ -287,7 +288,14 @@ pub const Node = extern struct {
287288 next_moved: bool,
288289 /// Whether this node might contain initialized bytes.
289290 has_content: bool,
290 unused: u17 = 0,
291
292 /// Whether this node is currently in `updates`. Usually this is just equal to whether any
293 /// of `moved`, `resized`, or `next_moved` is set, but when a node is deleted and later
294 /// reused, it may still be in `updates`, so preserving this flag through the deletion
295 /// prevents us from adding it to `updates` a second time.
296 updated: bool,
297
298 unused: u16 = 0,
291299 };
292300
293301 pub const Location = union(enum(u1)) {
......@@ -494,6 +502,11 @@ pub const Node = extern struct {
494502 }
495503 }
496504
505 pub fn cleanUpdated(ni: Node.Index, mf: *MappedFile) void {
506 assert(ni.get(mf).flags.updated);
507 ni.get(mf).flags.updated = false;
508 }
509
497510 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {
498511 var cur_ni = ni;
499512 while (cur_ni != .root) {
......@@ -518,9 +531,11 @@ pub const Node = extern struct {
518531 if (node.prev.unwrap()) |prev_ni| prev_ni.nextMovedAssumeCapacity(mf);
519532 if (ni.hasMoved(mf)) return;
520533 node.flags.moved = true;
521 if (node.flags.resized or node.flags.next_moved) return;
522 mf.updates.appendAssumeCapacity(ni);
523 mf.update_prog_node.increaseEstimatedTotalItems(1);
534 if (!node.flags.updated) {
535 node.flags.updated = true;
536 mf.updates.appendAssumeCapacity(ni);
537 mf.update_prog_node.increaseEstimatedTotalItems(1);
538 }
524539 }
525540
526541 pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {
......@@ -539,9 +554,11 @@ pub const Node = extern struct {
539554 const node = ni.get(mf);
540555 if (node.flags.resized) return;
541556 node.flags.resized = true;
542 if (node.flags.moved or node.flags.next_moved) return;
543 mf.updates.appendAssumeCapacity(ni);
544 mf.update_prog_node.increaseEstimatedTotalItems(1);
557 if (!node.flags.updated) {
558 node.flags.updated = true;
559 mf.updates.appendAssumeCapacity(ni);
560 mf.update_prog_node.increaseEstimatedTotalItems(1);
561 }
545562 }
546563
547564 pub fn hasNextMoved(ni: Node.Index, mf: *const MappedFile) bool {
......@@ -560,9 +577,11 @@ pub const Node = extern struct {
560577 const node = ni.get(mf);
561578 if (!node.flags.enable_next_moved or node.flags.next_moved) return;
562579 node.flags.next_moved = true;
563 if (node.flags.moved or node.flags.resized) return;
564 mf.updates.appendAssumeCapacity(ni);
565 mf.update_prog_node.increaseEstimatedTotalItems(1);
580 if (!node.flags.updated) {
581 node.flags.updated = true;
582 mf.updates.appendAssumeCapacity(ni);
583 mf.update_prog_node.increaseEstimatedTotalItems(1);
584 }
566585 }
567586
568587 pub fn alignment(ni: Node.Index, mf: *const MappedFile) Alignment {
......@@ -647,19 +666,30 @@ pub const Node = extern struct {
647666 pub fn delete(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {
648667 const node = ni.get(mf);
649668 assert(node.first == .none and node.last == .none); // has children
650 mf.removeNodesFromChildList(gpa, ni, ni);
651 const updated = node.flags.moved or node.flags.resized or node.flags.next_moved;
652 node.* = undefined;
653 node.next = ni.toOptional();
654 if (!updated) assert(ni.pendingDelete(mf));
655 }
656
657 pub fn pendingDelete(ni: Node.Index, mf: *MappedFile) bool {
658 const node = ni.get(mf);
659 if (node.next != ni.toOptional()) return false;
660 node.next = mf.free_ni;
669 try mf.removeNodesFromChildList(gpa, ni, ni);
670 node.* = .{
671 .parent = undefined,
672 .prev = undefined,
673 .next = mf.free_ni,
674 .first = undefined,
675 .last = undefined,
676 .flags = .{
677 .position = .floating,
678 .alignment = .@"1",
679 .bubbles_moved = false,
680 .enable_next_moved = false,
681 .location_tag = .small,
682 .moved = false,
683 .resized = false,
684 .next_moved = false,
685 .has_content = false,
686 // It is important that we preserve this flag! When the node gets reused, this
687 // prevents it being redundantly added to `mf.updates` if it is already there.
688 .updated = node.flags.updated,
689 },
690 .location_payload = undefined,
691 };
661692 mf.free_ni = ni.toOptional();
662 return true;
663693 }
664694
665695 /// Ensures that the size of `ni` is at least `min_size`. Valid for any node.
......@@ -946,6 +976,7 @@ fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {
946976 .resized = false,
947977 .next_moved = false,
948978 .has_content = false,
979 .updated = false,
949980 },
950981 .location_payload = switch (location) {
951982 .small => |small| .{ .small = small },
src/link/Queue.zig+1
......@@ -196,6 +196,7 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) Io.Cancelable!void {
196196 };
197197 }
198198
199 have_idle_tasks = true;
199200 zcu_tasks: while (true) {
200201 var task_buf: [128]ZcuTask = undefined;
201202 const limit: usize = if (have_idle_tasks) 0 else 1;
test/debugger/storage-4
......@@ -67,12 +67,8 @@ Process #... stopped
6767(u64) global_threadlocal1 = 0xb4d643528c042121
6868(u64) global_threadlocal2 = 0x43faea1cf5ad7a22
6969(u64) extern_const = 0x1b0c91ea0470b0b2
70(u64) extern_const = 0x1b0c91ea0470b0b2
71(u64) extern_var = 0x19bc17b3f0b61ebc
7270(u64) extern_var = 0x19bc17b3f0b61ebc
7371(u64) extern_threadlocal1 = 0x3034c4ce967ed64d
74(u64) extern_threadlocal1 = 0x3034c4ce967ed64d
75(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb
7672(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb
7773(lldb) frame variable --show-all-children --show-types --format hex -- param1 param2 param3 param4 param5 param6 param7 param8 local_comptime_val 'local_comptime_ptr.@"0"' local_comptime_ptr[0] local_const local_var
7874(u64) param1 = 0x6a607e08125c7e00