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 {...@@ -352,8 +352,9 @@ pub fn build(b: *std.Build) !void {
352 const cmake_cfg = if (static_llvm) null else blk: {352 const cmake_cfg = if (static_llvm) null else blk: {
353 const io = b.graph.io;353 const io = b.graph.io;
354 const cwd: Io.Dir = .cwd();354 const cwd: Io.Dir = .cwd();
355 if (findConfigH(b, config_h_path_option)) |config_h_path| {355 if (try 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;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));
357 break :blk parseConfigH(b, file_contents);358 break :blk parseConfigH(b, file_contents);
358 } else {359 } else {
359 std.log.warn("config.h could not be located automatically", .{});360 std.log.warn("config.h could not be located automatically", .{});
...@@ -1187,7 +1188,7 @@ const CMakeConfig = struct {...@@ -1187,7 +1188,7 @@ const CMakeConfig = struct {
11871188
1188const max_config_h_bytes = 1 * 1024 * 1024;1189const 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 {
1191 const arena = b.graph.arena;1192 const arena = b.graph.arena;
1192 const io = b.graph.io;1193 const io = b.graph.io;
1193 const cwd: Io.Dir = .cwd();1194 const cwd: Io.Dir = .cwd();
...@@ -1205,22 +1206,26 @@ fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {...@@ -1205,22 +1206,26 @@ fn findConfigH(b: *std.Build, config_h_path_option: ?[]const u8) ?[]const u8 {
12051206
1206 var check_dir = fs.path.dirname(b.graph.zig_exe).?;1207 var check_dir = fs.path.dirname(b.graph.zig_exe).?;
1207 while (true) {1208 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 };
1209 defer dir.close(io);1212 defer dir.close(io);
12101213
1211 // Check if config.h is present in dir1214 // Check if config.h is present in dir
1212 var config_h_or_err = dir.openFile(io, "config.h", .{});1215 var config_h_or_err = dir.openFile(io, "config.h", .{});
1213 if (config_h_or_err) |*file| {1216 if (config_h_or_err) |*file| {
1214 file.close(io);1217 file.close(io);
1215 return fs.path.join(1218 return try fs.path.join(
1216 arena,1219 arena,
1217 &[_][]const u8{ check_dir, "config.h" },1220 &[_][]const u8{ check_dir, "config.h" },
1218 ) catch unreachable;1221 );
1219 } else |e| switch (e) {1222 } else |err| switch (err) {
1220 error.FileNotFound => {},1223 error.FileNotFound => {},
1221 else => unreachable,1224 else => |e| std.debug.panic("failed to check for config.h in {q}: {t}", .{ check_dir, e }),
1222 }1225 }
12231226
1227 b.dependOnFileMetadata(b.graph.cwdRelativePath(check_dir).path(b, "config.h"));
1228
1224 // Check if we reached the source root by looking for .git, and bail if so1229 // Check if we reached the source root by looking for .git, and bail if so
1225 var git_dir_or_err = dir.openDir(io, ".git", .{});1230 var git_dir_or_err = dir.openDir(io, ".git", .{});
1226 if (git_dir_or_err) |*git_dir| {1231 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 {...@@ -1101,6 +1101,9 @@ pub fn main(init: process.Init.Minimal) !void {
1101 if (help_menu) printUsageWithoutConfiguration(&graph, "build.zig compilation failure");1101 if (help_menu) printUsageWithoutConfiguration(&graph, "build.zig compilation failure");
1102 break :c true;1102 break :c true;
1103 },1103 },
1104 error.StdoutWriteFailed => {
1105 fatal("failed writing to stdout: {t}", .{stdout_writer_allocation.err.?});
1106 },
1104 else => |e| w: {1107 else => |e| w: {
1105 log.err("configuration failed: {t}", .{e});1108 log.err("configuration failed: {t}", .{e});
1106 break :w false;1109 break :w false;
...@@ -1163,7 +1166,14 @@ const ConfigureOptions = struct {...@@ -1163,7 +1166,14 @@ const ConfigureOptions = struct {
1163 error_bundle: ?*ErrorBundle = null,1166 error_bundle: ?*ErrorBundle = null,
1164};1167};
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 {
1167 const configure_argv = options.configure_argv;1177 const configure_argv = options.configure_argv;
1168 const gpa = graph.cache.gpa;1178 const gpa = graph.cache.gpa;
1169 const io = graph.io;1179 const io = graph.io;
...@@ -1328,7 +1338,19 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1328,7 +1338,19 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1328 defer job_queue.deinit();1338 defer job_queue.deinit();
13291339
1330 if (options.system_pkg_dir_path == null) {1340 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 };
1332 }1354 }
13331355
1334 try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);1356 try job_queue.all_fetches.ensureUnusedCapacity(gpa, 1);
...@@ -1410,10 +1432,10 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1410,10 +1432,10 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1410 .writer = initStdoutWriter(io),1432 .writer = initStdoutWriter(io),
1411 .mode = try .detect(io, .stdout(), NO_COLOR, CLICOLOR_FORCE),1433 .mode = try .detect(io, .stdout(), NO_COLOR, CLICOLOR_FORCE),
1412 }, arena, graph.global_cache_root, &job_queue, &fetch, &fork_set) catch |err| switch (err) {1434 }, 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,
1414 else => |e| return e,1436 else => |e| return e,
1415 };1437 };
1416 try stdout_writer_allocation.flush();1438 stdout_writer_allocation.flush() catch return error.StdoutWriteFailed;
1417 process.exit(0);1439 process.exit(0);
1418 }1440 }
14191441
...@@ -1444,11 +1466,17 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1444,11 +1466,17 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1444 .root_dir = graph.local_cache_root,1466 .root_dir = graph.local_cache_root,
1445 .sub_path = try arena.print("o/{s}/dependencies.zig", .{&hex_digest}),1467 .sub_path = try arena.print("o/{s}/dependencies.zig", .{&hex_digest}),
1446 };1468 };
1447 var atomic_file = try dependencies_zig_path.root_dir.handle.createFileAtomic(1469 var atomic_file = dependencies_zig_path.root_dir.handle.createFileAtomic(
1448 io,1470 io,
1449 dependencies_zig_path.sub_path,1471 dependencies_zig_path.sub_path,
1450 .{ .make_path = true, .replace = true },1472 .{ .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 };
1452 defer atomic_file.deinit(io);1480 defer atomic_file.deinit(io);
1453 atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err|1481 atomic_file.file.writeStreamingAll(io, dependencies_source.items) catch |err|
1454 fatal("writing dependencies.zig contents: {t}", .{err});1482 fatal("writing dependencies.zig contents: {t}", .{err});
...@@ -1596,11 +1624,17 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1596,11 +1624,17 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
1596 .root_dir = graph.local_cache_root,1624 .root_dir = graph.local_cache_root,
1597 .sub_path = try arena.print("tmp" ++ Dir.path.sep_str ++ "{x}", .{randInt(io, u64)}),1625 .sub_path = try arena.print("tmp" ++ Dir.path.sep_str ++ "{x}", .{randInt(io, u64)}),
1598 };1626 };
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(
1600 io,1628 io,
1601 config_tmp_path.sub_path,1629 config_tmp_path.sub_path,
1602 .{ .read = true, .exclusive = true },1630 .{ .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 };
1604 defer config_tmp_file.close(io);1638 defer config_tmp_file.close(io);
16051639
1606 const term = term: {1640 const term = term: {
...@@ -1663,11 +1697,19 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {...@@ -1663,11 +1697,19 @@ fn configure(graph: *Graph, options: ConfigureOptions) !ScannedConfig {
16631697
1664 if (config_man) |man| for (configuration.path_deps) |path_dep| {1698 if (config_man) |man| for (configuration.path_deps) |path_dep| {
1665 const path = try confPathDepToCachePath(arena, graph, &configuration, path_dep);1699 const path = try confPathDepToCachePath(arena, graph, &configuration, path_dep);
1666 try man.addDiscoveredPath(.{1700 var diag: Cache.Manifest.AddDiscoveredPathDiagnostic = undefined;
1701 man.addDiscoveredPath(.{
1667 .discovered_path = .{ .unresolved = path },1702 .discovered_path = .{ .unresolved = path },
1668 .handle = if (path_dep.flags.is_directory) .{ .dir = null } else .{ .file = null },1703 .handle = if (path_dep.flags.is_directory) .{ .dir = null } else .{ .file = null },
1669 .metadata_only = path_dep.flags.metadata_only,1704 .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 };
1671 };1713 };
16721714
1673 // If it is poisoned, there is no point in moving it to cached1715 // 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...@@ -2294,6 +2336,7 @@ fn cacheCatOne(input_hex_digest: []const u8, contents: []const u8, writer: *Io.W
2294 try file_obj.field("digest", @as([]const u8, &hex_digest), .{});2336 try file_obj.field("digest", @as([]const u8, &hex_digest), .{});
2295 if (file.flags.is_directory) try file_obj.field("directory", true, .{});2337 if (file.flags.is_directory) try file_obj.field("directory", true, .{});
2296 if (file.flags.metadata_only) try file_obj.field("metadata", true, .{});2338 if (file.flags.metadata_only) try file_obj.field("metadata", true, .{});
2339 if (!file.flags.exists) try file_obj.field("exists", false, .{});
2297 try file_obj.field("prefix", file.flags.prefix, .{});2340 try file_obj.field("prefix", file.flags.prefix, .{});
2298 try file_obj.field("path", path, .{});2341 try file_obj.field("path", path, .{});
2299 try file_obj.end();2342 try file_obj.end();
...@@ -3328,7 +3371,12 @@ fn printStepStatus(maker: *Maker, step_index: Configuration.Step.Index, stderr:...@@ -3328,7 +3371,12 @@ fn printStepStatus(maker: *Maker, step_index: Configuration.Step.Index, stderr:
3328 try writer.writeAll(" invalid_manifest");3371 try writer.writeAll(" invalid_manifest");
3329 try stderr.setColor(.green);3372 try stderr.setColor(.green);
3330 },3373 },
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| {
3332 try stderr.setColor(.blue);3380 try stderr.setColor(.blue);
3333 try writer.print(" {t}: {s}", .{ tag, path });3381 try writer.print(" {t}: {s}", .{ tag, path });
3334 try stderr.setColor(.green);3382 try stderr.setColor(.green);
lib/compiler/Maker/Step.zig+15-2
...@@ -84,6 +84,7 @@ const ResultCacheStatus = union(enum) {...@@ -84,6 +84,7 @@ const ResultCacheStatus = union(enum) {
84 incomplete_manifest,84 incomplete_manifest,
85 invalid_manifest,85 invalid_manifest,
86 path_deleted: [*:0]const u8,86 path_deleted: [*:0]const u8,
87 path_created: [*:0]const u8,
87 directory_status_changed: [*:0]const u8,88 directory_status_changed: [*:0]const u8,
88 metadata_changed: [*:0]const u8,89 metadata_changed: [*:0]const u8,
89 contents_changed: [*:0]const u8,90 contents_changed: [*:0]const u8,
...@@ -815,7 +816,13 @@ fn setResultCacheStatus(s: *Step, man: *const Cache.Manifest, status: Cache.Mani...@@ -815,7 +816,13 @@ fn setResultCacheStatus(s: *Step, man: *const Cache.Manifest, status: Cache.Mani
815 .hit => .hit,816 .hit => .hit,
816 .incomplete_manifest => .incomplete_manifest,817 .incomplete_manifest => .incomplete_manifest,
817 .invalid_manifest => .invalid_manifest,818 .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(
819 ResultCacheStatus,826 ResultCacheStatus,
820 @tagName(tag),827 @tagName(tag),
821 try gpa.dupeSentinel(u8, off.path(man.contents.items), 0),828 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...@@ -827,7 +834,13 @@ fn setResultCacheStatus(s: *Step, man: *const Cache.Manifest, status: Cache.Mani
827fn clearResultCacheStatus(s: *Step, gpa: Allocator) void {834fn clearResultCacheStatus(s: *Step, gpa: Allocator) void {
828 switch (s.result_cache_status) {835 switch (s.result_cache_status) {
829 .hit, .incomplete_manifest, .invalid_manifest => {},836 .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| {
831 gpa.free(std.mem.sliceTo(path, 0));844 gpa.free(std.mem.sliceTo(path, 0));
832 },845 },
833 }846 }
lib/std/Build.zig+7-3
...@@ -2643,9 +2643,11 @@ pub fn systemIntegrationOption(...@@ -2643,9 +2643,11 @@ pub fn systemIntegrationOption(
26432643
2644/// Indicates that the build.zig logic depends on a particular file's contents.2644/// Indicates that the build.zig logic depends on a particular file's contents.
2645///2645///
2646/// If the file is created, deleted, or has its contents changed, the configure2646/// The file must exist when this function is called.
2647/// phase will be repeated. If the inode or mtime change, but the file contents2647///
2648/// remain the same, it will not cause the configure logic to be repeated.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.
2649///2651///
2650/// This is an alternative to `Graph.poisonCache` that avoids making every invocation2652/// This is an alternative to `Graph.poisonCache` that avoids making every invocation
2651/// of `zig build` into a cache miss.2653/// of `zig build` into a cache miss.
...@@ -2695,6 +2697,8 @@ pub fn dependOnFileMetadata(b: *Build, lazy_path: LazyPath) void {...@@ -2695,6 +2697,8 @@ pub fn dependOnFileMetadata(b: *Build, lazy_path: LazyPath) void {
26952697
2696/// Indicates that the build.zig logic depends on a particular directory's entries.2698/// Indicates that the build.zig logic depends on a particular directory's entries.
2697///2699///
2700/// The directory must exist when this function is called.
2701///
2698/// This is an alternative to `Graph.poisonCache` that avoids making every invocation2702/// This is an alternative to `Graph.poisonCache` that avoids making every invocation
2699/// of `zig build` into a cache miss.2703/// of `zig build` into a cache miss.
2700///2704///
lib/std/Build/Cache.zig+98-62
...@@ -56,7 +56,7 @@ pub fn prefixes(cache: *const Cache) []const Directory {...@@ -56,7 +56,7 @@ pub fn prefixes(cache: *const Cache) []const Directory {
56 return cache.prefixes_buffer[0..cache.prefixes_len];56 return cache.prefixes_buffer[0..cache.prefixes_len];
57}57}
5858
59const PrefixIndex = u6;59const PrefixIndex = u3;
6060
61const PrefixedPath = struct {61const PrefixedPath = struct {
62 prefix: PrefixIndex,62 prefix: PrefixIndex,
...@@ -453,7 +453,9 @@ pub const Manifest = struct {...@@ -453,7 +453,9 @@ pub const Manifest = struct {
453 pub const Flags = packed struct(u8) {453 pub const Flags = packed struct(u8) {
454 is_directory: bool,454 is_directory: bool,
455 metadata_only: bool,455 metadata_only: bool,
456 exists: bool,
456 prefix: PrefixIndex,457 prefix: PrefixIndex,
458 _: u2 = 0,
457 };459 };
458460
459 /// Prefixes path names in encoded directory contents. Starts numbering461 /// Prefixes path names in encoded directory contents. Starts numbering
...@@ -576,6 +578,7 @@ pub const Manifest = struct {...@@ -576,6 +578,7 @@ pub const Manifest = struct {
576578
577 /// Returns true if the stat was changed. Updates the `file` with the new stat value.579 /// Returns true if the stat was changed. Updates the `file` with the new stat value.
578 fn setStatChanged(file: *File, m: *Manifest, s: Stat) Io.Cancelable!bool {580 fn setStatChanged(file: *File, m: *Manifest, s: Stat) Io.Cancelable!bool {
581 assert(file.flags.exists);
579 if (s.size == file.size and582 if (s.size == file.size and
580 s.mtime.nanoseconds == file.mtime and583 s.mtime.nanoseconds == file.mtime and
581 s.inode == file.inode)584 s.inode == file.inode)
...@@ -693,12 +696,12 @@ pub const Manifest = struct {...@@ -693,12 +696,12 @@ pub const Manifest = struct {
693 };696 };
694697
695 /// Add a file or directory path as a dependency of process being cached.698 /// Add a file or directory path as a dependency of process being cached.
696 /// When `hit` is called, the contents will be checked to ensure699 /// When `check` is called, the contents will be checked to ensure
697 /// that it matches the contents from previous times.700 /// that it matches the contents from previous times.
698 ///701 ///
699 /// The contents of the input file may be requested and subsequently702 /// The contents of the input file may be requested and subsequently
700 /// obtained via methods of the returned `InputPath.Index` after calling703 /// obtained via methods of the returned `InputPath.Index` after calling
701 /// `hit`.704 /// `check`.
702 ///705 ///
703 /// Contents of a directory are considered to be the sorted list of file706 /// Contents of a directory are considered to be the sorted list of file
704 /// names of direct entries, separated by null byte. Each file name is707 /// names of direct entries, separated by null byte. Each file name is
...@@ -726,6 +729,7 @@ pub const Manifest = struct {...@@ -726,6 +729,7 @@ pub const Manifest = struct {
726 .prefix = new_prefix,729 .prefix = new_prefix,
727 .is_directory = is_directory,730 .is_directory = is_directory,
728 .metadata_only = options.metadata_only,731 .metadata_only = options.metadata_only,
732 .exists = true, // modified later if necessary
729 };733 };
730734
731 const gop = m.files.getOrPutAssumeCapacityContext(new_file_offset, .{ .contents = m.contents.items });735 const gop = m.files.getOrPutAssumeCapacityContext(new_file_offset, .{ .contents = m.contents.items });
...@@ -841,6 +845,7 @@ pub const Manifest = struct {...@@ -841,6 +845,7 @@ pub const Manifest = struct {
841 hit,845 hit,
842 incomplete_manifest,846 incomplete_manifest,
843 invalid_manifest,847 invalid_manifest,
848 path_created: File.Offset,
844 path_deleted: File.Offset,849 path_deleted: File.Offset,
845 directory_status_changed: File.Offset,850 directory_status_changed: File.Offset,
846 metadata_changed: File.Offset,851 metadata_changed: File.Offset,
...@@ -853,7 +858,7 @@ pub const Manifest = struct {...@@ -853,7 +858,7 @@ pub const Manifest = struct {
853 pub fn format(this: @This(), w: *Io.Writer) Io.Writer.Error!void {858 pub fn format(this: @This(), w: *Io.Writer) Io.Writer.Error!void {
854 switch (this.check_result) {859 switch (this.check_result) {
855 .hit, .incomplete_manifest, .invalid_manifest => return w.writeAll(@tagName(this.check_result)),860 .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| {
857 const path = off.path(this.manifest.contents.items);862 const path = off.path(this.manifest.contents.items);
858 return w.print("{t} {s}", .{ this.check_result, path });863 return w.print("{t} {s}", .{ this.check_result, path });
859 },864 },
...@@ -1192,38 +1197,36 @@ pub const Manifest = struct {...@@ -1192,38 +1197,36 @@ pub const Manifest = struct {
1192 };1197 };
1193 if (!input_path.have_stat and stat_path_ok) {1198 if (!input_path.have_stat and stat_path_ok) {
1194 // Since this is an input file, FileNotFound counts as a failure, not a cache miss.1199 // 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)) {1200 const stat_result = switch (input_path.getHandle(input_file)) {
1196 .none => parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) {1201 .none => parent_dir.statFile(io, file_path, .{}),
1197 error.Canceled => |e| return e,1202 .file => |opened_file| opened_file.stat(io),
1198 else => |e| return fail(diag, .{ .file_stat = .{1203 .dir => |opened_dir| opened_dir.stat(io),
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 },
1217 };1204 };
1218 const actual_is_directory = actual_stat.kind == .directory;1205 if (stat_result) |actual_stat| {
1219 if (actual_is_directory != input_file.flags.is_directory) {1206 const actual_is_directory = actual_stat.kind == .directory;
1220 // Since this is an input file, this is a failure, not a cache miss.1207 if (actual_is_directory != input_file.flags.is_directory) {
1221 return fail(diag, .{ .file_stat = .{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 = .{
1222 .file_offset = file_off,1221 .file_offset = file_off,
1223 .err = if (actual_is_directory) error.IsDir else error.NotDir,1222 .err = e,
1224 } });1223 } }),
1224
1225 else => |e| return fail(diag, .{ .file_stat = .{
1226 .file_offset = file_off,
1227 .err = e,
1228 } }),
1225 }1229 }
1226 try input_file.setStat(m, .init(actual_stat));
1227 }1230 }
12281231
1229 if (input_file.flags.metadata_only) {1232 if (input_file.flags.metadata_only) {
...@@ -1369,6 +1372,9 @@ pub const Manifest = struct {...@@ -1369,6 +1372,9 @@ pub const Manifest = struct {
1369 // space to compute the digest in case stat changed, in order to compare with `disk_file.digest`.1372 // space to compute the digest in case stat changed, in order to compare with `disk_file.digest`.
13701373
1371 if (input_path.have_stat) {1374 if (input_path.have_stat) {
1375 if (!disk_file.flags.exists) {
1376 return .{ .path_created = file_off };
1377 }
1372 const changed = try disk_file.setStatChanged(m, input_file.stat());1378 const changed = try disk_file.setStatChanged(m, input_file.stat());
1373 if (!changed) return .hit;1379 if (!changed) return .hit;
1374 }1380 }
...@@ -1376,29 +1382,28 @@ pub const Manifest = struct {...@@ -1376,29 +1382,28 @@ pub const Manifest = struct {
1376 if (disk_file.flags.metadata_only) {1382 if (disk_file.flags.metadata_only) {
1377 if (!input_path.have_stat) {1383 if (!input_path.have_stat) {
1378 // Since this is an input file, FileNotFound counts as a failure, not a cache miss.1384 // 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)) {1385 const stat_result = switch (input_path.getHandle(input_file)) {
1380 .none => parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) {1386 .none => parent_dir.statFile(io, file_path, .{}),
1381 error.Canceled => |e| return e,1387 .file => |opened_file| opened_file.stat(io),
1382 else => |e| return fail(diag, .{ .file_stat = .{1388 .dir => |opened_dir| opened_dir.stat(io),
1383 .file_offset = file_off,1389 };
1384 .err = e,1390 const actual_stat = stat_result catch |err| switch (err) {
1385 } }),1391 error.Canceled => |e| return e,
1386 },1392 error.FileNotFound => |e| switch (disk_file.flags.exists) {
1387 .file => |opened_file| opened_file.stat(io) catch |err| switch (err) {1393 false => return .hit,
1388 error.Canceled => |e| return e,1394 true => return fail(diag, .{ .file_stat = .{
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 = .{
1397 .file_offset = file_off,1395 .file_offset = file_off,
1398 .err = e,1396 .err = e,
1399 } }),1397 } }),
1400 },1398 },
1399 else => |e| return fail(diag, .{ .file_stat = .{
1400 .file_offset = file_off,
1401 .err = e,
1402 } }),
1401 };1403 };
1404 if (!disk_file.flags.exists) {
1405 return .{ .path_created = file_off };
1406 }
1402 // In the other case (have_stat=true), this check is redundant since we already asserted the flags are1407 // In the other case (have_stat=true), this check is redundant since we already asserted the flags are
1403 // identical, which contains the prefix.1408 // identical, which contains the prefix.
1404 const actual_is_directory = actual_stat.kind == .directory;1409 const actual_is_directory = actual_stat.kind == .directory;
...@@ -1559,7 +1564,10 @@ pub const Manifest = struct {...@@ -1559,7 +1564,10 @@ pub const Manifest = struct {
15591564
1560 if (file.flags.metadata_only) {1565 if (file.flags.metadata_only) {
1561 const actual_stat = parent_dir.statFile(io, file_path, .{}) catch |err| switch (err) {1566 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 },
1563 error.Canceled => |e| return e,1571 error.Canceled => |e| return e,
1564 else => |e| return fail(diag, .{ .file_stat = .{1572 else => |e| return fail(diag, .{ .file_stat = .{
1565 .file_offset = file_off,1573 .file_offset = file_off,
...@@ -1567,6 +1575,10 @@ pub const Manifest = struct {...@@ -1567,6 +1575,10 @@ pub const Manifest = struct {
1567 } }),1575 } }),
1568 };1576 };
15691577
1578 if (!file.flags.exists) {
1579 return .{ .path_created = file_off };
1580 }
1581
1570 const actual_is_directory = actual_stat.kind == .directory;1582 const actual_is_directory = actual_stat.kind == .directory;
1571 if (actual_is_directory != file.flags.is_directory) return .{ .directory_status_changed = file_off };1583 if (actual_is_directory != file.flags.is_directory) return .{ .directory_status_changed = file_off };
15721584
...@@ -1576,6 +1588,8 @@ pub const Manifest = struct {...@@ -1576,6 +1588,8 @@ pub const Manifest = struct {
1576 return .hit;1588 return .hit;
1577 }1589 }
15781590
1591 assert(file.flags.exists); // file not existing can only be tracked with metadata_only
1592
1579 if (file.flags.is_directory) {1593 if (file.flags.is_directory) {
1580 const opened_dir = parent_dir.openDir(io, file_path, .{1594 const opened_dir = parent_dir.openDir(io, file_path, .{
1581 .iterate = true,1595 .iterate = true,
...@@ -1727,7 +1741,14 @@ pub const Manifest = struct {...@@ -1727,7 +1741,14 @@ pub const Manifest = struct {
1727 pub fn format(this: @This(), w: *Io.Writer) Io.Writer.Error!void {1741 pub fn format(this: @This(), w: *Io.Writer) Io.Writer.Error!void {
1728 switch (this) {1742 switch (this) {
1729 .none => return w.writeAll("none"),1743 .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 }),
1731 }1752 }
1732 }1753 }
1733 };1754 };
...@@ -1767,6 +1788,7 @@ pub const Manifest = struct {...@@ -1767,6 +1788,7 @@ pub const Manifest = struct {
1767 .prefix = new_prefix,1788 .prefix = new_prefix,
1768 .is_directory = is_directory,1789 .is_directory = is_directory,
1769 .metadata_only = options.metadata_only,1790 .metadata_only = options.metadata_only,
1791 .exists = true, // modified later if necessary
1770 };1792 };
17711793
1772 const gop = m.files.getOrPutAssumeCapacityContext(new_file_offset, .{ .contents = m.contents.items });1794 const gop = m.files.getOrPutAssumeCapacityContext(new_file_offset, .{ .contents = m.contents.items });
...@@ -1810,18 +1832,25 @@ pub const Manifest = struct {...@@ -1810,18 +1832,25 @@ pub const Manifest = struct {
1810 } else {1832 } else {
1811 const dir = cache.prefixes()[prefix].handle;1833 const dir = cache.prefixes()[prefix].handle;
1812 const sub_path = file_offset.path(m.contents.items);1834 const sub_path = file_offset.path(m.contents.items);
1813 const handle = dir.openDir(io, sub_path, .{1835 if (dir.openDir(io, sub_path, .{
1814 .access_sub_paths = false,1836 .access_sub_paths = false,
1815 .iterate = true,1837 .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) {
1817 error.Canceled => |e| return e,1842 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 },
1818 else => |e| {1849 else => |e| {
1819 if (options.diagnostic) |d| d.* = .{ .open_dir = e };1850 if (options.diagnostic) |d| d.* = .{ .open_dir = e };
1820 return error.FileSystemFailure;1851 return error.FileSystemFailure;
1821 },1852 },
1822 };1853 }
1823 defer handle.close(io);
1824 try populateDirectory(m, header, need_stat, handle, options.contents, metadata_only, options.diagnostic);
1825 },1854 },
18261855
1827 .file => |opt_handle| if (opt_handle) |handle| {1856 .file => |opt_handle| if (opt_handle) |handle| {
...@@ -1829,15 +1858,22 @@ pub const Manifest = struct {...@@ -1829,15 +1858,22 @@ pub const Manifest = struct {
1829 } else {1858 } else {
1830 const dir = cache.prefixes()[prefix].handle;1859 const dir = cache.prefixes()[prefix].handle;
1831 const sub_path = file_offset.path(m.contents.items);1860 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) {
1833 error.Canceled => |e| return e,1865 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 },
1834 else => |e| {1872 else => |e| {
1835 if (options.diagnostic) |d| d.* = .{ .open_file = e };1873 if (options.diagnostic) |d| d.* = .{ .open_file = e };
1836 return error.FileSystemFailure;1874 return error.FileSystemFailure;
1837 },1875 },
1838 };1876 }
1839 defer handle.close(io);
1840 try populateFile(m, header, need_stat, handle, options.contents, metadata_only, options.diagnostic);
1841 },1877 },
1842 }1878 }
1843 }1879 }
src/link/Coff.zig+1-1
...@@ -6100,7 +6100,7 @@ pub fn idle(coff: *Coff) !bool {...@@ -6100,7 +6100,7 @@ pub fn idle(coff: *Coff) !bool {
6100 break :task;6100 break :task;
6101 }6101 }
6102 while (coff.mf.updates.pop()) |ni| : (coff.mf.update_prog_node.completeOne()) {6102 while (coff.mf.updates.pop()) |ni| : (coff.mf.update_prog_node.completeOne()) {
6103 if (ni.pendingDelete(&coff.mf)) continue;6103 ni.cleanUpdated(&coff.mf);
6104 const clean_moved = ni.cleanMoved(&coff.mf);6104 const clean_moved = ni.cleanMoved(&coff.mf);
6105 const clean_resized = ni.cleanResized(&coff.mf);6105 const clean_resized = ni.cleanResized(&coff.mf);
6106 const clean_next_moved = ni.cleanNextMoved(&coff.mf);6106 const clean_next_moved = ni.cleanNextMoved(&coff.mf);
src/link/Dwarf.zig+37-16
...@@ -2662,7 +2662,6 @@ fn initWipNavInner(...@@ -2662,7 +2662,6 @@ fn initWipNavInner(
2662 try wip_nav.strp(@"extern".name.toSlice(ip));2662 try wip_nav.strp(@"extern".name.toSlice(ip));
2663 try wip_nav.refType(.fromInterned(if (maybe_func_type) |func_type| func_type.return_type else @"extern".ty));2663 try wip_nav.refType(.fromInterned(if (maybe_func_type) |func_type| func_type.return_type else @"extern".ty));
2664 if (maybe_func_type) |func_type| {2664 if (maybe_func_type) |func_type| {
2665 try wip_nav.infoAddrSym(sym_index, 0);
2666 try diw.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));2665 try diw.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));
2667 if (func_type.param_types.len > 0 or func_type.is_var_args) {2666 if (func_type.param_types.len > 0 or func_type.is_var_args) {
2668 for (func_type.param_types.get(ip)) |param_type| {2667 for (func_type.param_types.get(ip)) |param_type| {
...@@ -2672,7 +2671,7 @@ fn initWipNavInner(...@@ -2672,7 +2671,7 @@ fn initWipNavInner(
2672 if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args);2671 if (func_type.is_var_args) try wip_nav.abbrevCode(.is_var_args);
2673 try diw.writeUleb128(@backingInt(AbbrevCode.null));2672 try diw.writeUleb128(@backingInt(AbbrevCode.null));
2674 }2673 }
2675 } else try wip_nav.infoExprLoc(.{ .addr_reloc = sym_index });2674 }
2676 },2675 },
2677 .syntax => switch (ip.isFunctionType(@"extern".ty)) {2676 .syntax => switch (ip.isFunctionType(@"extern".ty)) {
2678 false => continue :nav_val .{ .undef = @"extern".ty },2677 false => continue :nav_val .{ .undef = @"extern".ty },
...@@ -2690,7 +2689,6 @@ fn initWipNavInner(...@@ -2690,7 +2689,6 @@ fn initWipNavInner(
2690 }, &nav, inst_info.file, &decl);2689 }, &nav, inst_info.file, &decl);
2691 try wip_nav.strp(@"extern".name.toSlice(ip));2690 try wip_nav.strp(@"extern".name.toSlice(ip));
2692 try wip_nav.refType(.fromInterned(func_type.return_type));2691 try wip_nav.refType(.fromInterned(func_type.return_type));
2693 try wip_nav.infoAddrSym(sym_index, 0);
2694 try diw.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));2692 try diw.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));
2695 if (func_type.param_types.len > 0 or func_type.is_var_args) {2693 if (func_type.param_types.len > 0 or func_type.is_var_args) {
2696 for (func_type.param_types.get(ip)) |param_type| {2694 for (func_type.param_types.get(ip)) |param_type| {
...@@ -2817,13 +2815,19 @@ fn initWipNavInner(...@@ -2817,13 +2815,19 @@ fn initWipNavInner(
2817 else => {2815 else => {
2818 const diw = &wip_nav.debug_info.writer;2816 const diw = &wip_nav.debug_info.writer;
2819 try wip_nav.declCommon(.{2817 try wip_nav.declCommon(.{
2820 .decl = .decl_var,2818 .decl = switch (decl.linkage) {
2819 .@"extern" => .decl_extern_var,
2820 else => .decl_var,
2821 },
2821 .decl_specification = switch (decl.kind) {2822 .decl_specification = switch (decl.kind) {
2822 .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable,2823 .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable,
2823 .@"const" => .decl_specification_const,2824 .@"const" => .decl_specification_const,
2824 .@"var" => .decl_specification_var,2825 .@"var" => .decl_specification_var,
2825 },2826 },
2826 .decl_instance = .decl_instance_var,2827 .decl_instance = switch (decl.linkage) {
2828 .@"extern" => .decl_instance_extern_var,
2829 else => .decl_instance_var,
2830 },
2827 }, &nav, inst_info.file, &decl);2831 }, &nav, inst_info.file, &decl);
2828 try wip_nav.strp(switch (decl.linkage) {2832 try wip_nav.strp(switch (decl.linkage) {
2829 .normal => nav.fqn,2833 .normal => nav.fqn,
...@@ -2842,20 +2846,24 @@ fn initWipNavInner(...@@ -2842,20 +2846,24 @@ fn initWipNavInner(
2842 .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable,2846 .unnamed_test, .@"test", .decltest, .@"comptime" => unreachable,
2843 .@"const" => {2847 .@"const" => {
2844 const const_ty_reloc_index = try wip_nav.refForward();2848 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);
2846 try diw.writeUleb128(nav.resolved.?.@"align".toByteUnits() orelse2850 try diw.writeUleb128(nav.resolved.?.@"align".toByteUnits() orelse
2847 ty.abiAlignment(zcu).toByteUnits().?);2851 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 }
2849 wip_nav.finishForward(const_ty_reloc_index);2855 wip_nav.finishForward(const_ty_reloc_index);
2850 try wip_nav.abbrevCode(.is_const);2856 try wip_nav.abbrevCode(.is_const);
2851 try wip_nav.refType(ty);2857 try wip_nav.refType(ty);
2852 },2858 },
2853 .@"var" => {2859 .@"var" => {
2854 try wip_nav.refType(ty);2860 try wip_nav.refType(ty);
2855 try wip_nav.infoExprLoc(loc);2861 if (decl.linkage != .@"extern") try wip_nav.infoExprLoc(loc);
2856 try diw.writeUleb128(nav.resolved.?.@"align".toByteUnits() orelse2862 try diw.writeUleb128(nav.resolved.?.@"align".toByteUnits() orelse
2857 ty.abiAlignment(zcu).toByteUnits().?);2863 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 }
2859 },2867 },
2860 }2868 }
2861 },2869 },
...@@ -5138,6 +5146,7 @@ const AbbrevCode = enum {...@@ -5138,6 +5146,7 @@ const AbbrevCode = enum {
5138 decl_union,5146 decl_union,
5139 decl_packed_union,5147 decl_packed_union,
5140 decl_var,5148 decl_var,
5149 decl_extern_var,
5141 decl_const,5150 decl_const,
5142 decl_const_runtime_bits,5151 decl_const_runtime_bits,
5143 decl_const_comptime_state,5152 decl_const_comptime_state,
...@@ -5160,6 +5169,7 @@ const AbbrevCode = enum {...@@ -5160,6 +5169,7 @@ const AbbrevCode = enum {
5160 decl_instance_union,5169 decl_instance_union,
5161 decl_instance_packed_union,5170 decl_instance_packed_union,
5162 decl_instance_var,5171 decl_instance_var,
5172 decl_instance_extern_var,
5163 decl_instance_const,5173 decl_instance_const,
5164 decl_instance_const_runtime_bits,5174 decl_instance_const_runtime_bits,
5165 decl_instance_const_comptime_state,5175 decl_instance_const_comptime_state,
...@@ -5360,6 +5370,15 @@ const AbbrevCode = enum {...@@ -5360,6 +5370,15 @@ const AbbrevCode = enum {
5360 .{ .external, .flag },5370 .{ .external, .flag },
5361 },5371 },
5362 },5372 },
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 },
5363 .decl_const = .{5382 .decl_const = .{
5364 .tag = .constant,5383 .tag = .constant,
5365 .attrs = decl_abbrev_common_attrs ++ .{5384 .attrs = decl_abbrev_common_attrs ++ .{
...@@ -5443,7 +5462,6 @@ const AbbrevCode = enum {...@@ -5443,7 +5462,6 @@ const AbbrevCode = enum {
5443 .attrs = decl_abbrev_common_attrs ++ .{5462 .attrs = decl_abbrev_common_attrs ++ .{
5444 .{ .linkage_name, .strp },5463 .{ .linkage_name, .strp },
5445 .{ .type, .ref_addr },5464 .{ .type, .ref_addr },
5446 .{ .low_pc, .addr },
5447 .{ .external, .flag_present },5465 .{ .external, .flag_present },
5448 .{ .noreturn, .flag },5466 .{ .noreturn, .flag },
5449 },5467 },
...@@ -5454,7 +5472,6 @@ const AbbrevCode = enum {...@@ -5454,7 +5472,6 @@ const AbbrevCode = enum {
5454 .attrs = decl_abbrev_common_attrs ++ .{5472 .attrs = decl_abbrev_common_attrs ++ .{
5455 .{ .linkage_name, .strp },5473 .{ .linkage_name, .strp },
5456 .{ .type, .ref_addr },5474 .{ .type, .ref_addr },
5457 .{ .low_pc, .addr },
5458 .{ .external, .flag_present },5475 .{ .external, .flag_present },
5459 .{ .noreturn, .flag },5476 .{ .noreturn, .flag },
5460 },5477 },
...@@ -5536,6 +5553,15 @@ const AbbrevCode = enum {...@@ -5536,6 +5553,15 @@ const AbbrevCode = enum {
5536 .{ .external, .flag },5553 .{ .external, .flag },
5537 },5554 },
5538 },5555 },
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 },
5539 .decl_instance_const = .{5565 .decl_instance_const = .{
5540 .tag = .constant,5566 .tag = .constant,
5541 .attrs = decl_instance_abbrev_common_attrs ++ .{5567 .attrs = decl_instance_abbrev_common_attrs ++ .{
...@@ -5619,7 +5645,6 @@ const AbbrevCode = enum {...@@ -5619,7 +5645,6 @@ const AbbrevCode = enum {
5619 .attrs = decl_instance_abbrev_common_attrs ++ .{5645 .attrs = decl_instance_abbrev_common_attrs ++ .{
5620 .{ .linkage_name, .strp },5646 .{ .linkage_name, .strp },
5621 .{ .type, .ref_addr },5647 .{ .type, .ref_addr },
5622 .{ .low_pc, .addr },
5623 .{ .external, .flag_present },5648 .{ .external, .flag_present },
5624 .{ .noreturn, .flag },5649 .{ .noreturn, .flag },
5625 },5650 },
...@@ -5630,7 +5655,6 @@ const AbbrevCode = enum {...@@ -5630,7 +5655,6 @@ const AbbrevCode = enum {
5630 .attrs = decl_instance_abbrev_common_attrs ++ .{5655 .attrs = decl_instance_abbrev_common_attrs ++ .{
5631 .{ .linkage_name, .strp },5656 .{ .linkage_name, .strp },
5632 .{ .type, .ref_addr },5657 .{ .type, .ref_addr },
5633 .{ .low_pc, .addr },
5634 .{ .external, .flag_present },5658 .{ .external, .flag_present },
5635 .{ .noreturn, .flag },5659 .{ .noreturn, .flag },
5636 },5660 },
...@@ -6048,7 +6072,6 @@ const AbbrevCode = enum {...@@ -6048,7 +6072,6 @@ const AbbrevCode = enum {
6048 .{ .ZIG_parent, .ref_addr },6072 .{ .ZIG_parent, .ref_addr },
6049 .{ .linkage_name, .strp },6073 .{ .linkage_name, .strp },
6050 .{ .type, .ref_addr },6074 .{ .type, .ref_addr },
6051 .{ .low_pc, .addr },
6052 .{ .external, .flag_present },6075 .{ .external, .flag_present },
6053 .{ .noreturn, .flag },6076 .{ .noreturn, .flag },
6054 },6077 },
...@@ -6060,7 +6083,6 @@ const AbbrevCode = enum {...@@ -6060,7 +6083,6 @@ const AbbrevCode = enum {
6060 .{ .ZIG_parent, .ref_addr },6083 .{ .ZIG_parent, .ref_addr },
6061 .{ .linkage_name, .strp },6084 .{ .linkage_name, .strp },
6062 .{ .type, .ref_addr },6085 .{ .type, .ref_addr },
6063 .{ .low_pc, .addr },
6064 .{ .external, .flag_present },6086 .{ .external, .flag_present },
6065 .{ .noreturn, .flag },6087 .{ .noreturn, .flag },
6066 },6088 },
...@@ -6071,7 +6093,6 @@ const AbbrevCode = enum {...@@ -6071,7 +6093,6 @@ const AbbrevCode = enum {
6071 .{ .ZIG_parent, .ref_addr },6093 .{ .ZIG_parent, .ref_addr },
6072 .{ .linkage_name, .strp },6094 .{ .linkage_name, .strp },
6073 .{ .type, .ref_addr },6095 .{ .type, .ref_addr },
6074 .{ .location, .exprloc },
6075 .{ .external, .flag_present },6096 .{ .external, .flag_present },
6076 },6097 },
6077 },6098 },
src/link/Dwarf2.zig+13-24
...@@ -2101,11 +2101,9 @@ pub fn updateExtern(...@@ -2101,11 +2101,9 @@ pub fn updateExtern(
2101 dwarf: *Dwarf,2101 dwarf: *Dwarf,
2102 pt: Zcu.PerThread,2102 pt: Zcu.PerThread,
2103 di_nw: *link.MappedFile.Node.Writer,2103 di_nw: *link.MappedFile.Node.Writer,
2104 arch: std.Target.Cpu.Arch,
2105 si: link.File.SymbolId,
2106 global: InternPool.Nav.Index,2104 global: InternPool.Nav.Index,
2107) link.Error!void {2105) 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) {
2109 else => |e| return e,2107 else => |e| return e,
2110 error.WriteFailed => return dwarf.reportWriteError(di_nw),2108 error.WriteFailed => return dwarf.reportWriteError(di_nw),
2111 };2109 };
...@@ -2114,20 +2112,17 @@ fn updateExternInner(...@@ -2114,20 +2112,17 @@ fn updateExternInner(
2114 dwarf: *Dwarf,2112 dwarf: *Dwarf,
2115 pt: Zcu.PerThread,2113 pt: Zcu.PerThread,
2116 di_nw: *link.MappedFile.Node.Writer,2114 di_nw: *link.MappedFile.Node.Writer,
2117 arch: std.Target.Cpu.Arch,
2118 si: link.File.SymbolId,
2119 global: InternPool.Nav.Index,2115 global: InternPool.Nav.Index,
2120) link.EmitError!void {2116) link.EmitError!void {
2121 const zcu = pt.zcu;2117 const zcu = pt.zcu;
2122 const ip = &zcu.intern_pool;2118 const ip = &zcu.intern_pool;
2123 const nav = ip.getNav(global);2119 const nav = ip.getNav(global);
2124 log.debug("updateExtern({f})", .{nav.fqn.fmt(ip)});2120 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: {
2126 const nav_resolved = nav.resolved.?;2122 const nav_resolved = nav.resolved.?;
2127 break :nav_resolved .{2123 break :nav_resolved .{
2128 nav_resolved.@"align",2124 nav_resolved.@"align",
2129 nav_resolved.@"const",2125 nav_resolved.@"const",
2130 nav_resolved.@"threadlocal",
2131 ip.indexToKey(nav_resolved.value).@"extern",2126 ip.indexToKey(nav_resolved.value).@"extern",
2132 };2127 };
2133 };2128 };
...@@ -2162,7 +2157,7 @@ fn updateExternInner(...@@ -2162,7 +2157,7 @@ fn updateExternInner(
2162 try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private);2157 try di_w.writeByte(if (decl.is_pub) DW.ACCESS.public else DW.ACCESS.private);
2163 try dwarf.strx(di_nw, nav.name.toSlice(ip));2158 try dwarf.strx(di_nw, nav.name.toSlice(ip));
2164 } else {2159 } else {
2165 try dwarf.abbrevCode(di_nw, .decl_var);2160 try dwarf.abbrevCode(di_nw, .decl_extern_var);
2166 try dwarf.refType(pt, di_nw, .fromInterned(zcu.fileRootType(inst_info.file)));2161 try dwarf.refType(pt, di_nw, .fromInterned(zcu.fileRootType(inst_info.file)));
2167 try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian);2162 try di_w.writeInt(u32, decl.src_line + 1, dwarf.endian);
2168 try di_w.writeUleb128(decl.src_column + 1);2163 try di_w.writeUleb128(decl.src_column + 1);
...@@ -2173,7 +2168,6 @@ fn updateExternInner(...@@ -2173,7 +2168,6 @@ fn updateExternInner(
2173 try dwarf.strx(di_nw, nav_extern.name.toSlice(ip));2168 try dwarf.strx(di_nw, nav_extern.name.toSlice(ip));
2174 if (maybe_func_type) |func_type| {2169 if (maybe_func_type) |func_type| {
2175 try dwarf.refType(pt, di_nw, .fromInterned(func_type.return_type));2170 try dwarf.refType(pt, di_nw, .fromInterned(func_type.return_type));
2176 try dwarf.addrSym(di_nw, si, 0);
2177 try di_w.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));2171 try di_w.writeByte(@intFromBool(Type.fromInterned(func_type.return_type).isNoReturn(zcu)));
2178 for (func_type.param_types.get(ip)) |param_type| {2172 for (func_type.param_types.get(ip)) |param_type| {
2179 try dwarf.abbrevCode(di_nw, .extern_param);2173 try dwarf.abbrevCode(di_nw, .extern_param);
...@@ -2182,23 +2176,13 @@ fn updateExternInner(...@@ -2182,23 +2176,13 @@ fn updateExternInner(
2182 if (func_type.is_var_args) try dwarf.abbrevCode(di_nw, .is_var_args);2176 if (func_type.is_var_args) try dwarf.abbrevCode(di_nw, .is_var_args);
2183 if (!is_empty) try di_w.writeUleb128(@backingInt(AbbrevCode.null));2177 if (!is_empty) try di_w.writeUleb128(@backingInt(AbbrevCode.null));
2184 } else {2178 } 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;
2190 const type_offset = di_w.end;2179 const type_offset = di_w.end;
2191 if (nav_const)2180 if (nav_const)
2192 try dwarf.secOffsetPlaceholder(di_w)2181 try dwarf.secOffsetPlaceholder(di_w)
2193 else2182 else
2194 try dwarf.refType(pt, di_nw, .fromInterned(nav_extern.ty));2183 try dwarf.refType(pt, di_nw, .fromInterned(nav_extern.ty));
2195 try dwarf.exprLoc(di_nw, loc);
2196 try di_w.writeUleb128(nav_align.toByteUnits() orelse2184 try di_w.writeUleb128(nav_align.toByteUnits() orelse
2197 Type.fromInterned(nav_extern.ty).abiAlignment(zcu).toByteUnits().?);2185 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 }
2202 if (nav_const) {2186 if (nav_const) {
2203 try dwarf.secOffsetFinish(di_nw, type_offset, di_nw.ni, di_w.end);2187 try dwarf.secOffsetFinish(di_nw, type_offset, di_nw.ni, di_w.end);
2204 try dwarf.abbrevCode(di_nw, .is_const);2188 try dwarf.abbrevCode(di_nw, .is_const);
...@@ -4800,6 +4784,7 @@ pub const AbbrevCode = enum {...@@ -4800,6 +4784,7 @@ pub const AbbrevCode = enum {
4800 decl_func_generic,4784 decl_func_generic,
4801 decl_extern_empty_func,4785 decl_extern_empty_func,
4802 decl_extern_func,4786 decl_extern_func,
4787 decl_extern_var,
4803 decl_specification_empty_struct,4788 decl_specification_empty_struct,
4804 decl_specification_struct,4789 decl_specification_struct,
4805 type_decl_specification_empty_struct,4790 type_decl_specification_empty_struct,
...@@ -5285,7 +5270,6 @@ pub const AbbrevCode = enum {...@@ -5285,7 +5270,6 @@ pub const AbbrevCode = enum {
5285 .attrs = decl_attrs ++ .{5270 .attrs = decl_attrs ++ .{
5286 .{ .linkage_name, .strx },5271 .{ .linkage_name, .strx },
5287 .{ .type, .ref_addr },5272 .{ .type, .ref_addr },
5288 .{ .low_pc, .addr },
5289 .{ .external, .flag_present },5273 .{ .external, .flag_present },
5290 .{ .noreturn, .flag },5274 .{ .noreturn, .flag },
5291 },5275 },
...@@ -5296,11 +5280,19 @@ pub const AbbrevCode = enum {...@@ -5296,11 +5280,19 @@ pub const AbbrevCode = enum {
5296 .attrs = decl_attrs ++ .{5280 .attrs = decl_attrs ++ .{
5297 .{ .linkage_name, .strx },5281 .{ .linkage_name, .strx },
5298 .{ .type, .ref_addr },5282 .{ .type, .ref_addr },
5299 .{ .low_pc, .addr },
5300 .{ .external, .flag_present },5283 .{ .external, .flag_present },
5301 .{ .noreturn, .flag },5284 .{ .noreturn, .flag },
5302 },5285 },
5303 },5286 },
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 },
5304 .decl_specification_empty_struct = .{5296 .decl_specification_empty_struct = .{
5305 .tag = .structure_type,5297 .tag = .structure_type,
5306 .attrs = decl_specification_attrs,5298 .attrs = decl_specification_attrs,
...@@ -5995,7 +5987,6 @@ pub const AbbrevCode = enum {...@@ -5995,7 +5987,6 @@ pub const AbbrevCode = enum {
5995 .{ .ZIG_parent, .ref_addr },5987 .{ .ZIG_parent, .ref_addr },
5996 .{ .linkage_name, .strx },5988 .{ .linkage_name, .strx },
5997 .{ .type, .ref_addr },5989 .{ .type, .ref_addr },
5998 .{ .low_pc, .addr },
5999 .{ .external, .flag_present },5990 .{ .external, .flag_present },
6000 .{ .noreturn, .flag },5991 .{ .noreturn, .flag },
6001 },5992 },
...@@ -6007,7 +5998,6 @@ pub const AbbrevCode = enum {...@@ -6007,7 +5998,6 @@ pub const AbbrevCode = enum {
6007 .{ .ZIG_parent, .ref_addr },5998 .{ .ZIG_parent, .ref_addr },
6008 .{ .linkage_name, .strx },5999 .{ .linkage_name, .strx },
6009 .{ .type, .ref_addr },6000 .{ .type, .ref_addr },
6010 .{ .low_pc, .addr },
6011 .{ .external, .flag_present },6001 .{ .external, .flag_present },
6012 .{ .noreturn, .flag },6002 .{ .noreturn, .flag },
6013 },6003 },
...@@ -6018,7 +6008,6 @@ pub const AbbrevCode = enum {...@@ -6018,7 +6008,6 @@ pub const AbbrevCode = enum {
6018 .{ .ZIG_parent, .ref_addr },6008 .{ .ZIG_parent, .ref_addr },
6019 .{ .linkage_name, .strx },6009 .{ .linkage_name, .strx },
6020 .{ .type, .ref_addr },6010 .{ .type, .ref_addr },
6021 .{ .location, .exprloc },
6022 .{ .alignment, .udata },6011 .{ .alignment, .udata },
6023 .{ .external, .flag_present },6012 .{ .external, .flag_present },
6024 },6013 },
src/link/Elf2.zig+879-770
...@@ -244,8 +244,9 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),...@@ -244,8 +244,9 @@ section_by_name: std.array_hash_map.Auto(String(.shstrtab), void),
244changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void),244changed_symtab_index: std.array_hash_map.Auto(Symbol.Global.Index, void),
245/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`245/// Counts how many relocations are currently in `.rela.dyn` which would require a `DT_TEXTREL`
246/// entry in the `.dynamic` section. This allows adding `DT_TEXTREL` to the output `.dynamic`246/// 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`.
248textrel_count: u32,248textrel_count: u32,
249entry_gsi: Symbol.Global.Index.Optional,
249250
250dwarf: Dwarf,251dwarf: Dwarf,
251dwarf_shared: std.enums.EnumArray(Dwarf.SharedSection, dwarf_relocs.Shared),252dwarf_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),...@@ -259,6 +260,8 @@ dwarf_decls: std.array_hash_map.Auto(Dwarf.Decl.Index, dwarf_relocs.Decl),
259260
260overflowed_reloc_count: u32,261overflowed_reloc_count: u32,
261misaligned_reloc_count: u32,262misaligned_reloc_count: u32,
263illegal_dynamic_type_reloc_count: u32,
264illegal_dynamic_section_reloc_count: u32,
262265
263const_prog_node: std.Progress.Node,266const_prog_node: std.Progress.Node,
264input_prog_node: std.Progress.Node,267input_prog_node: std.Progress.Node,
...@@ -308,9 +311,6 @@ const Node = union(enum) {...@@ -308,9 +311,6 @@ const Node = union(enum) {
308 /// emitted a copy relocation.311 /// emitted a copy relocation.
309 ///312 ///
310 /// TODO it would be better to emit these into `.bss` or `.bss.rel.ro`, once we support those.313 /// 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`.
314 copied_global: Symbol.Global.Index,314 copied_global: Symbol.Global.Index,
315 /// May contain relocations.315 /// May contain relocations.
316 nav: NavMapIndex,316 nav: NavMapIndex,
...@@ -464,6 +464,135 @@ const Node = union(enum) {...@@ -464,6 +464,135 @@ const Node = union(enum) {
464 fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index {464 fn fromAtom(atom: link.File.AtomId) MappedFile.Node.Index {
465 return @fromBackingInt(@backingInt(atom));465 return @fromBackingInt(@backingInt(atom));
466 }466 }
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 };
467};596};
468597
469const InputSection = struct {598const InputSection = struct {
...@@ -947,10 +1076,14 @@ const dwarf_relocs = struct {...@@ -947,10 +1076,14 @@ const dwarf_relocs = struct {
947 const Addr = struct {1076 const Addr = struct {
948 first_target_reloc: NodeReloc.Index,1077 first_target_reloc: NodeReloc.Index,
949 symbol_relocs: std.ArrayList(SymbolReloc.Index),1078 symbol_relocs: std.ArrayList(SymbolReloc.Index),
1079 /// Index into `symbol_relocs`.
1080 pending_reloc_index: usize,
950 };1081 };
951 const StrOffsets = struct {1082 const StrOffsets = struct {
952 first_target_reloc: NodeReloc.Index,1083 first_target_reloc: NodeReloc.Index,
953 node_relocs: std.ArrayList(NodeReloc.Index),1084 node_relocs: std.ArrayList(NodeReloc.Index),
1085 /// Index into `node_relocs`.
1086 pending_reloc_index: usize,
954 };1087 };
955 const Unit = struct {1088 const Unit = struct {
956 frame_cie_first_target_reloc: NodeReloc.Index,1089 frame_cie_first_target_reloc: NodeReloc.Index,
...@@ -1279,7 +1412,13 @@ const SymbolReloc = struct {...@@ -1279,7 +1412,13 @@ const SymbolReloc = struct {
1279 /// relocation entry. The entry will be removed if we discover a definition which allows us to1412 /// relocation entry. The entry will be removed if we discover a definition which allows us to
1280 /// statically resolve the relocation.1413 /// statically resolve the relocation.
1281 rela_index: Section.RelaIndex.Optional,1414 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
1284 /// Determines the section in which this relocation will be placed if it is outstanding.1423 /// Determines the section in which this relocation will be placed if it is outstanding.
1285 ///1424 ///
...@@ -1813,32 +1952,28 @@ const SymbolReloc = struct {...@@ -1813,32 +1952,28 @@ const SymbolReloc = struct {
1813 }1952 }
1814 };1953 };
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
1830 fn apply(reloc: *SymbolReloc, elf: *Elf) void {1955 fn apply(reloc: *SymbolReloc, elf: *Elf) void {
1831 assert(elf.ehdrType() != .REL);
1832 const node = reloc.node.unwrap() orelse return; // deleted1956 const node = reloc.node.unwrap() orelse return; // deleted
1833 if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {1957 if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(elf)) {
1834 // There's no point applying the relocation now, because it will be re-applied by1958 // There's no point applying the relocation now, because it will be re-applied by
1835 // `flushMoved` at some point anyway.1959 // `flushMoved` at some point anyway.
1836 return;1960 return;
1837 }1961 }
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 }
1838 switch (reloc.result) {1969 switch (reloc.result) {
1839 .ok => {},1970 .ok => {},
1840 .overflowed => elf.overflowed_reloc_count -= 1,1971 .overflowed => elf.overflowed_reloc_count -= 1,
1841 .misaligned => elf.misaligned_reloc_count -= 1,1972 .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 },
1842 }1977 }
1843 if (reloc.applyInner(elf)) {1978 if (reloc.applyInner(elf)) {
1844 @branchHint(.likely);1979 @branchHint(.likely);
...@@ -1900,7 +2035,11 @@ const SymbolReloc = struct {...@@ -1900,7 +2035,11 @@ const SymbolReloc = struct {
1900 // is required, meaning we can handle it now and return early.2035 // is required, meaning we can handle it now and return early.
1901 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {2036 if (reloc.rela_index.unwrap()) |rela_index| switch (elf.classifySymbolValue(reloc.target)) {
1902 .static => unreachable,2037 .static => unreachable,
1903 .dynamic => return, // the relocation happens at runtime2038 .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 },
1904 .static_relative => {2043 .static_relative => {
1905 // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address2044 // We have emitted an R_*_RELATIVE relocation to help lower an absolute-address
1906 // relocation. The value computed above is valid, but instead of writing it to the2045 // relocation. The value computed above is valid, but instead of writing it to the
...@@ -1912,6 +2051,7 @@ const SymbolReloc = struct {...@@ -1912,6 +2051,7 @@ const SymbolReloc = struct {
1912 }2051 }
1913 assert(reloc.type.action.simple.cast == .unsigned);2052 assert(reloc.type.action.simple.cast == .unsigned);
1914 assert(reloc.type.action.simple.shift == .@"0");2053 assert(reloc.type.action.simple.shift == .@"0");
2054 elf.shndx.rela_dyn.relaSetOffset(elf, rela_index, dest_vaddr);
1915 elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val);2055 elf.shndx.rela_dyn.relaSetAddend(elf, rela_index, target_val);
1916 return;2056 return;
1917 },2057 },
...@@ -1944,6 +2084,8 @@ const SymbolReloc = struct {...@@ -1944,6 +2084,8 @@ const SymbolReloc = struct {
1944 .ok => {},2084 .ok => {},
1945 .overflowed => elf.overflowed_reloc_count -= 1,2085 .overflowed => elf.overflowed_reloc_count -= 1,
1946 .misaligned => elf.misaligned_reloc_count -= 1,2086 .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,
1947 }2089 }
19482090
1949 reloc.* = undefined;2091 reloc.* = undefined;
...@@ -1957,14 +2099,211 @@ const SymbolReloc = struct {...@@ -1957,14 +2099,211 @@ const SymbolReloc = struct {
1957 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);2099 reloc.relaSection(elf).relaDeleteOne(elf, rela_index);
1958 switch (elf.ehdrType()) {2100 switch (elf.ehdrType()) {
1959 .REL => {},2101 .REL => {},
1960 .EXEC, .DYN => switch (elf.nodeWantsDsoRelocation(reloc.node.unwrap().?)) {2102 .EXEC, .DYN => switch (elf.nodeAllowsDynamicReloc(reloc.node.unwrap().?)) {
1961 .no => unreachable, // there *was* a dynamic relocation!2103 .not_alloc => unreachable, // there *was* a dynamic relocation!
1962 .yes => {},2104 .ok => {},
1963 .yes_textrel => elf.textrel_count -= 1,2105 .ok_textrel => elf.textrel_count -= 1,
1964 },2106 },
1965 }2107 }
1966 reloc.rela_index = .none;2108 reloc.rela_index = .none;
1967 }2109 }
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 }
1968};2307};
19692308
1970/// A relocation targeting an arbitrary node (within a section) with a fixed addend.2309/// A relocation targeting an arbitrary node (within a section) with a fixed addend.
...@@ -1992,34 +2331,14 @@ const NodeReloc = struct {...@@ -1992,34 +2331,14 @@ const NodeReloc = struct {
1992 }2331 }
1993 };2332 };
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
2018 fn apply(reloc: *NodeReloc, elf: *Elf) void {2334 fn apply(reloc: *NodeReloc, elf: *Elf) void {
2019 const node = reloc.node.unwrap() orelse return; // deleted2335 const node = reloc.node.unwrap() orelse return; // deleted
2020 if (reloc.rela_index.unwrap()) |rela_index| {2336 if (reloc.rela_index.unwrap()) |rela_index| {
2021 assert(elf.ehdrType() == .REL);2337 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)));
2023 } else {2342 } else {
2024 assert(elf.ehdrType() != .REL);2343 assert(elf.ehdrType() != .REL);
2025 if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(&elf.mf)) {2344 if (node.hasMoved(&elf.mf) or reloc.target.hasMoved(&elf.mf)) {
...@@ -2755,6 +3074,8 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{...@@ -2755,6 +3074,8 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2755} || Error)!Symbol.Global.Index {3074} || Error)!Symbol.Global.Index {
2756 _ = opts.lib_name; // TODO3075 _ = opts.lib_name; // TODO
27573076
3077 log.debug("addGlobalSymbol({q})", .{opts.name});
3078
2758 const gpa = elf.base.comp.gpa;3079 const gpa = elf.base.comp.gpa;
27593080
2760 try elf.symtab.ensureUnusedCapacity(gpa, 1);3081 try elf.symtab.ensureUnusedCapacity(gpa, 1);
...@@ -2802,9 +3123,7 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{...@@ -2802,9 +3123,7 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2802 }3123 }
2803 }3124 }
28043125
2805 if (elf.mergeGlobalSymbolVisibility(gsi, opts.visibility)) {3126 elf.mergeGlobalSymbolVisibility(gsi, opts.visibility);
2806 force_apply_relocs = true;
2807 }
28083127
2809 // There's already a symbol with this name, we just need to potentially set its value, and3128 // There's already a symbol with this name, we just need to potentially set its value, and
2810 // to update its bind and visibility.3129 // to update its bind and visibility.
...@@ -2988,10 +3307,15 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{...@@ -2988,10 +3307,15 @@ fn addGlobalSymbol(elf: *Elf, opts: Symbol.Global.AddOptions) (error{
2988/// some operation which requires relocations to be re-applied, it can pass `force_apply_relocs` as3307/// some operation which requires relocations to be re-applied, it can pass `force_apply_relocs` as
2989/// `true` to guarantee this function to do so.3308/// `true` to guarantee this function to do so.
2990fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_relocs: bool) Error!void {3309fn 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
2991 const gpa = elf.base.comp.gpa;3313 const gpa = elf.base.comp.gpa;
29923314
2993 const gsi = orig_gsi.resolveAlias(elf);3315 const gsi = orig_gsi.resolveAlias(elf);
29943316
3317 trace.addText(gsi.rawName(elf).slice(elf));
3318
2995 const maybe_alias_gsi = gsi.findAliaser(elf);3319 const maybe_alias_gsi = gsi.findAliaser(elf);
29963320
2997 var apply_relocs: bool = force_apply_relocs;3321 var apply_relocs: bool = force_apply_relocs;
...@@ -3055,14 +3379,23 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel...@@ -3055,14 +3379,23 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
3055 },3379 },
3056 };3380 };
30573381
3058 const omit_dynsym: bool = omit: {3382 const have_dynsym: bool = switch (elf.shndx.dynsym) {
3059 if (elf.shndx.dynsym == .UNDEF) break :omit true;3383 .UNDEF => false,
3060 break :omit switch (visibility) {3384 else => switch (visibility) {
3061 .HIDDEN, .INTERNAL => true,3385 .HIDDEN, .INTERNAL => false,
3062 .DEFAULT, .PROTECTED => false,3386 // An undefined protected dynamic symbol would be pointless, because protected symbols
3063 };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 },
3064 };3397 };
3065 if (omit_dynsym) {3398 if (!have_dynsym) {
3066 if (try elf.deleteOwnedDynsym(gsi)) {3399 if (try elf.deleteOwnedDynsym(gsi)) {
3067 apply_relocs = true;3400 apply_relocs = true;
3068 }3401 }
...@@ -3089,9 +3422,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel...@@ -3089,9 +3422,14 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
3089 if (elf.ehdrType() != .REL) {3422 if (elf.ehdrType() != .REL) {
3090 assert(elf.classifySymbolValue(.global(gsi)) != .dynamic);3423 assert(elf.classifySymbolValue(.global(gsi)) != .dynamic);
3091 }3424 }
3092 gsi.deleteDynamicTargetRelocs(elf);3425
3426 if (try gsi.updateDynamicTargetRelocs(elf)) {
3427 apply_relocs = true;
3428 }
3093 if (maybe_alias_gsi) |alias_gsi| {3429 if (maybe_alias_gsi) |alias_gsi| {
3094 alias_gsi.deleteDynamicTargetRelocs(elf);3430 if (try alias_gsi.updateDynamicTargetRelocs(elf)) {
3431 apply_relocs = true;
3432 }
3095 }3433 }
30963434
3097 if (apply_relocs) {3435 if (apply_relocs) {
...@@ -3335,10 +3673,8 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel...@@ -3335,10 +3673,8 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
3335 } else {3673 } else {
3336 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {3674 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
3337 // We previously made a copy relocation, but now have deemed it unnecessary.3675 // We previously made a copy relocation, but now have deemed it unnecessary.
3676 try elf.deleteNode(copied_global_kv.value.node);
3338 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);3677 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.
3342 apply_relocs = true;3678 apply_relocs = true;
3343 }3679 }
3344 }3680 }
...@@ -3352,10 +3688,12 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel...@@ -3352,10 +3688,12 @@ fn updateGlobalDynamic(elf: *Elf, orig_gsi: Symbol.Global.Index, force_apply_rel
3352 _ = elf.unknown_globals.swapRemove(gsi);3688 _ = elf.unknown_globals.swapRemove(gsi);
3353 }3689 }
33543690
3355 if (elf.classifySymbolValue(.global(gsi)) != .dynamic) {3691 if (try gsi.updateDynamicTargetRelocs(elf)) {
3356 gsi.deleteDynamicTargetRelocs(elf);3692 apply_relocs = true;
3357 if (maybe_alias_gsi) |alias_gsi| {3693 }
3358 alias_gsi.deleteDynamicTargetRelocs(elf);3694 if (maybe_alias_gsi) |alias_gsi| {
3695 if (try alias_gsi.updateDynamicTargetRelocs(elf)) {
3696 apply_relocs = true;
3359 }3697 }
3360 }3698 }
33613699
...@@ -3478,15 +3816,12 @@ fn setGlobalSymbolBind(...@@ -3478,15 +3816,12 @@ fn setGlobalSymbolBind(
3478/// undefined---their visibility values are combined to determine the resulting visibility.3816/// undefined---their visibility values are combined to determine the resulting visibility.
3479///3817///
3480/// Every call to this function *must* be followed by a call to `updateGlobalDynamic` since changes3818/// 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`,3819/// to symbol visibility can affect the dynamic symbol table entry.
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.
3485fn mergeGlobalSymbolVisibility(3820fn mergeGlobalSymbolVisibility(
3486 elf: *Elf,3821 elf: *Elf,
3487 gsi: Symbol.Global.Index,3822 gsi: Symbol.Global.Index,
3488 other_visibility: std.elf.STV,3823 other_visibility: std.elf.STV,
3489) bool {3824) void {
3490 const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {3825 const old_visibility: std.elf.STV = switch (elf.symPtr(gsi.ptr(elf).symtab_index)) {
3491 inline else => |sym| elf.targetLoad(&sym.other).visibility,3826 inline else => |sym| elf.targetLoad(&sym.other).visibility,
3492 };3827 };
...@@ -3518,13 +3853,13 @@ fn mergeGlobalSymbolVisibility(...@@ -3518,13 +3853,13 @@ fn mergeGlobalSymbolVisibility(
3518 ),3853 ),
3519 }3854 }
35203855
3521 if (elf.ehdrType() == .REL) {3856 if (elf.ehdrType() == .REL) return;
3522 return false;
3523 }
35243857
3525 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or3858 // We also need to deal with a concept I call "symbol demotion". If a symbol is `STV_HIDDEN` or
3526 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to3859 // `STV_INTERNAL`, and we're emitting an ELF module (executable or shared object, as opposed to
3527 // a relocatable), then the symbol should be "demoted" to binding `STB_LOCAL` in the output.3860 // 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.
3528 const old_demoted: bool = switch (old_visibility) {3863 const old_demoted: bool = switch (old_visibility) {
3529 .INTERNAL, .HIDDEN => true,3864 .INTERNAL, .HIDDEN => true,
3530 .DEFAULT, .PROTECTED => false,3865 .DEFAULT, .PROTECTED => false,
...@@ -3542,16 +3877,6 @@ fn mergeGlobalSymbolVisibility(...@@ -3542,16 +3877,6 @@ fn mergeGlobalSymbolVisibility(
3542 }3877 }
3543 elf.moveDemotedGlobal(gsi);3878 elf.moveDemotedGlobal(gsi);
3544 }3879 }
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;
3555}3880}
3556/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),3881/// If a symbol which was STB_GLOBAL/STB_WEAK becomes STB_LOCAL (see `mergeGlobalSymbolVisibility`),
3557/// the symbol must be moved from the "globals" part of the symtab to the "locals" part, because ELF3882/// 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...@@ -3613,10 +3938,8 @@ fn deleteOwnedDynsym(elf: *Elf, gsi: Symbol.Global.Index) std.mem.Allocator.Erro
36133938
3614 var apply_relocs: bool = false;3939 var apply_relocs: bool = false;
3615 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {3940 if (elf.copied_globals.fetchSwapRemove(gsi)) |copied_global_kv| {
3941 try elf.deleteNode(copied_global_kv.value.node);
3616 elf.shndx.rela_dyn.relaDeleteOne(elf, copied_global_kv.value.rela_index);3942 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.
3620 apply_relocs = true;3943 apply_relocs = true;
3621 }3944 }
3622 if (elf.plt.getIndex(gsi)) |plt_index| {3945 if (elf.plt.getIndex(gsi)) |plt_index| {
...@@ -3930,85 +4253,52 @@ const Symbol = struct {...@@ -3930,85 +4253,52 @@ const Symbol = struct {
3930 return null;4253 return null;
3931 }4254 }
39324255
3933 /// Scans through all relocations targeting `gsi`, deletes their dynamic relocation4256 /// Updates the dynamic relocations associated with relocations targeting `gsi`, adding
3934 /// entries, and adds `R_*_RELATIVE` relocation entries as needed.4257 /// or removing them as needed based on the current classification of `gsi`.
3935 ///4258 ///
3936 /// Asserts we are creating a DSO.4259 /// Returns `true` if any relocations were updated, in which case relocations targeting
3937 fn deleteDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) void {4260 /// `gsi` must be re-applied by the caller.
3938 if (elf.shndx.dynamic == .UNDEF) return;4261 fn updateDynamicTargetRelocs(gsi: Symbol.Global.Index, elf: *Elf) Error!bool {
4262 if (elf.shndx.dynamic == .UNDEF) return false;
3939 const sym_id: Symbol.Id = .global(gsi);4263 const sym_id: Symbol.Id = .global(gsi);
4264
3940 switch (elf.classifySymbolValue(sym_id)) {4265 switch (elf.classifySymbolValue(sym_id)) {
3941 .static => {4266 .static => {
3942 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and4267 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
3943 !gsi.ptr(elf).status.any_static_relative_target_relocs)4268 !gsi.ptr(elf).status.any_static_relative_target_relocs)
3944 {4269 {
3945 return;4270 return false;
3946 }4271 }
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;
3950 },4272 },
3951 .static_relative => {4273 .static_relative => {
3952 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and4274 if (!gsi.ptr(elf).status.any_dynamic_target_relocs and
3953 !gsi.ptr(elf).status.any_static_target_relocs)4275 !gsi.ptr(elf).status.any_static_target_relocs)
3954 {4276 {
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;
3956 }4285 }
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;
3960 },4286 },
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,
3965 }4287 }
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
3966 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;4293 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
3967 while (ri != .none) {4294 while (ri != .none) {
3968 const reloc = ri.get(elf);4295 const reloc = ri.get(elf);
3969 assert(reloc.target == sym_id);4296 assert(reloc.target == sym_id);
3970 reloc.deleteOutputRel(elf);4297 try reloc.updateDynamicReloc(elf);
3971 ri = reloc.next;4298 ri = reloc.next;
3972 }4299 }
3973 switch (elf.classifySymbolValue(sym_id)) {4300
3974 .static => return,4301 return true;
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 }
4012 }4302 }
40134303
4014 const Optional = enum(u32) {4304 const Optional = enum(u32) {
...@@ -4139,7 +4429,20 @@ const Symbol = struct {...@@ -4139,7 +4429,20 @@ const Symbol = struct {
4139 }4429 }
41404430
4141 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {4431 fn flushMoved(sym_id: Symbol.Id, elf: *Elf, new_value: u64) void {
4432 const trace = tracy.trace(@src());
4433 defer trace.end();
4434
4142 const sym_index = sym_id.index(elf);4435 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
4143 switch (elf.symPtr(sym_index)) {4446 switch (elf.symPtr(sym_index)) {
4144 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),4447 inline else => |sym| elf.targetStore(&sym.value, @intCast(new_value)),
4145 }4448 }
...@@ -4164,14 +4467,12 @@ const Symbol = struct {...@@ -4164,14 +4467,12 @@ const Symbol = struct {
4164 }4467 }
41654468
4166 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {4469 fn applyTargetRelocs(sym_id: Symbol.Id, elf: *Elf) void {
4167 if (elf.ehdrType() != .REL) {4470 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;
4168 var ri = sym_id.index(elf).ptr(elf).first_target_reloc;4471 while (ri != .none) {
4169 while (ri != .none) {4472 const reloc = ri.get(elf);
4170 const reloc = ri.get(elf);4473 assert(reloc.target == sym_id);
4171 assert(reloc.target == sym_id);4474 reloc.apply(elf);
4172 reloc.apply(elf);4475 ri = reloc.next;
4173 ri = reloc.next;
4174 }
4175 }4476 }
41764477
4177 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {4478 if (elf.got.getIndex(.{ .symbol = sym_id })) |got_index| {
...@@ -4251,61 +4552,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {...@@ -4251,61 +4552,53 @@ fn classifySymbolValue(elf: *Elf, sym: Symbol.Id) enum {
4251 .EXEC => false,4552 .EXEC => false,
4252 };4553 };
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
4259 const resolved_sym = sym.resolveAlias(elf);4555 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))) {
4262 inline else => |sym_ptr| .{4558 inline else => |sym_ptr| .{
4263 .fromSection(elf.targetLoad(&sym_ptr.shndx)),4559 .fromSection(elf.targetLoad(&sym_ptr.shndx)),
4264 elf.targetLoad(&sym_ptr.other).visibility,4560 elf.targetLoad(&sym_ptr.other).visibility,
4265 },4561 },
4266 };4562 };
42674563
4268 switch (resolved_sym.unwrap()) {4564 const shndx: Section.Index = switch (resolved_sym.unwrap()) {
4269 .local => {4565 .local => shndx: {
4270 assert(shndx != .UNDEF);4566 assert(symtab_shndx != .UNDEF);
4271 assert(visibility == .DEFAULT);4567 assert(visibility == .DEFAULT);
4568 break :shndx symtab_shndx;
4272 },4569 },
4273 .global => |gsi| if (visibility == .DEFAULT and comp.config.output_mode != .Exe) {4570 .global => |gsi| shndx: {
4274 // An unprotected symbol in a DSO which is not an executable is subject to runtime4571 const shndx: Section.Index = switch (symtab_shndx) {
4275 // preemption, so a dynamic relocation is required for it even if we have a definition.4572 .UNDEF => if (elf.copied_globals.contains(gsi)) .data else .UNDEF,
4276 return .dynamic;4573 else => |shndx| shndx,
4277 } else if (elf.copied_globals.contains(gsi)) {4574 };
4278 // This becomes a locally-defined symbol in `.data`.
4279 return if (runtime_load_addr) .static_relative else .static;
4280 },
4281 }
42824575
4283 return switch (shndx) {4576 // Check for dynamic symbols before falling into the static path.
4284 .UNDEF => switch (visibility) {4577 if (gsi.dynsymIndex(elf) != null and visibility == .DEFAULT) {
4285 .DEFAULT => if (comp.config.link_mode == .static and comp.config.output_mode == .Exe) {4578 if (shndx == .UNDEF) {
4286 assert(comp.config.pie); // non-PIE static exe should not have a `.dynamic` section4579 // There is no local definition of this symbol.
4287 // This is a static PIE---we'll see a definition at some point, so there'd be no4580 return .dynamic;
4288 // point in adding an invalid dynamic relocation (the only valid dynamic relation4581 } else if (comp.config.output_mode != .Exe) {
4289 // type in a static PIE is `R_*_RELATIVE`).4582 // There is a local definition, but it is preemptible at runtime.
4290 return .static;4583 return .dynamic;
4291 } else .dynamic, // external symbol4584 }
4585 }
42924586
4293 // If the symbol *cannot* be external, then there's no point making a dynamic relocation4587 break :shndx shndx;
4294 // now---if linking succeeds we won't need anything more than perhaps an `R_*_RELATIVE`.
4295 .INTERNAL, .HIDDEN, .PROTECTED => .static,
4296 },4588 },
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 and4601 return .static;
4301 shndx.flags(elf).ALLOC and
4302 !shndx.flags(elf).TLS)
4303 {
4304 return .static_relative;
4305 } else {
4306 return .static;
4307 },
4308 };
4309}4602}
43104603
4311pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {4604pub fn symbolForAtom(elf: *Elf, atom: link.File.AtomId) link.File.SymbolId {
...@@ -4848,6 +5141,7 @@ fn create(...@@ -4848,6 +5141,7 @@ fn create(
4848 .section_by_name = .empty,5141 .section_by_name = .empty,
4849 .changed_symtab_index = .empty,5142 .changed_symtab_index = .empty,
4850 .textrel_count = 0,5143 .textrel_count = 0,
5144 .entry_gsi = undefined,
48515145
4852 .dwarf = .init(&elf.base, switch (comp.config.debug_format) {5146 .dwarf = .init(&elf.base, switch (comp.config.debug_format) {
4853 .strip => .@"32", // for .eh_frame5147 .strip => .@"32", // for .eh_frame
...@@ -4860,10 +5154,12 @@ fn create(...@@ -4860,10 +5154,12 @@ fn create(
4860 .dwarf_addr = .{5154 .dwarf_addr = .{
4861 .first_target_reloc = .none,5155 .first_target_reloc = .none,
4862 .symbol_relocs = .empty,5156 .symbol_relocs = .empty,
5157 .pending_reloc_index = 0,
4863 },5158 },
4864 .dwarf_str_offsets = .{5159 .dwarf_str_offsets = .{
4865 .first_target_reloc = .none,5160 .first_target_reloc = .none,
4866 .node_relocs = .empty,5161 .node_relocs = .empty,
5162 .pending_reloc_index = 0,
4867 },5163 },
4868 .dwarf_units = &.{},5164 .dwarf_units = &.{},
4869 .dwarf_consts = .empty,5165 .dwarf_consts = .empty,
...@@ -4873,6 +5169,8 @@ fn create(...@@ -4873,6 +5169,8 @@ fn create(
48735169
4874 .overflowed_reloc_count = 0,5170 .overflowed_reloc_count = 0,
4875 .misaligned_reloc_count = 0,5171 .misaligned_reloc_count = 0,
5172 .illegal_dynamic_type_reloc_count = 0,
5173 .illegal_dynamic_section_reloc_count = 0,
48765174
4877 .const_prog_node = .none,5175 .const_prog_node = .none,
4878 .input_prog_node = .none,5176 .input_prog_node = .none,
...@@ -5078,9 +5376,10 @@ fn initHeaders(...@@ -5078,9 +5376,10 @@ fn initHeaders(
5078 defer phnum += 1;5376 defer phnum += 1;
5079 break :phndx phnum;5377 break :phndx phnum;
5080 } else undefined,5378 } else undefined,
5081 // `data` must be assigned after all other loadable segments so that it has the greatest5379 // `data` must be assigned after all other loadable segments so that it has the last
5082 // phndx of any loadable segment. This is so that `targetSegmentLoadAddressRestrictions`5380 // phndx of the loadable segments. This causes `updateSegmentLoadAddresses` to put
5083 // can be obeyed (specifically, the `.data_last` restriction, needed on SPARC).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`.
5084 .data = phndx: {5383 .data = phndx: {
5085 defer phnum += 1;5384 defer phnum += 1;
5086 break :phndx phnum;5385 break :phndx phnum;
...@@ -5214,7 +5513,7 @@ fn initHeaders(...@@ -5214,7 +5513,7 @@ fn initHeaders(
5214 // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node5513 // Must be at least `addr_align` for `elf.ni.phdr` to be placed inside this node
5215 .alignment = node_block_align.max(addr_align),5514 .alignment = node_block_align.max(addr_align),
5216 .moved = true,5515 .moved = true,
5217 .bubbles_moved = false,5516 .bubbles_moved = false, // see loadable segment handling in `flushMoved`
5218 }), .{ .segment = phndx.rodata });5517 }), .{ .segment = phndx.rodata });
5219 elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata);5518 elf.phdrs.items[phndx.rodata] = .wrap(elf.ni.rodata);
52205519
...@@ -5223,14 +5522,13 @@ fn initHeaders(...@@ -5223,14 +5522,13 @@ fn initHeaders(
5223 .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above5522 .alignment = addr_align, // keep in sync with `elf.ni.rodata` alignment above
5224 .moved = true,5523 .moved = true,
5225 .resized = true,5524 .resized = true,
5226 .bubbles_moved = false,
5227 }), .{ .segment = phndx.phdr });5525 }), .{ .segment = phndx.phdr });
5228 elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr);5526 elf.phdrs.items[phndx.phdr] = .wrap(elf.ni.phdr);
52295527
5230 elf.ni.text = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{5528 elf.ni.text = elf.addNodeAssumeCapacity(try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{
5231 .alignment = node_block_align,5529 .alignment = node_block_align,
5232 .moved = true,5530 .moved = true,
5233 .bubbles_moved = false,5531 .bubbles_moved = false, // see loadable segment handling in `flushMoved`
5234 }), .{ .segment = phndx.text });5532 }), .{ .segment = phndx.text });
5235 elf.phdrs.items[phndx.text] = .wrap(elf.ni.text);5533 elf.phdrs.items[phndx.text] = .wrap(elf.ni.text);
52365534
...@@ -5238,7 +5536,7 @@ fn initHeaders(...@@ -5238,7 +5536,7 @@ fn initHeaders(
5238 // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node5536 // Must be at least `addr_align` for `elf.ni.data_rel_ro` to be placed inside this node
5239 .alignment = node_block_align.max(addr_align),5537 .alignment = node_block_align.max(addr_align),
5240 .moved = true,5538 .moved = true,
5241 .bubbles_moved = false,5539 .bubbles_moved = false, // see loadable segment handling in `flushMoved`
5242 }), .{ .segment = phndx.data });5540 }), .{ .segment = phndx.data });
5243 elf.phdrs.items[phndx.data] = .wrap(elf.ni.data);5541 elf.phdrs.items[phndx.data] = .wrap(elf.ni.data);
52445542
...@@ -5246,7 +5544,7 @@ fn initHeaders(...@@ -5246,7 +5544,7 @@ fn initHeaders(
5246 try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{5544 try elf.ni.elf.addFloatingChild(gpa, &elf.mf, .{
5247 .alignment = node_block_align,5545 .alignment = node_block_align,
5248 .moved = true,5546 .moved = true,
5249 .bubbles_moved = false,5547 .bubbles_moved = false, // see loadable segment handling in `flushMoved`
5250 }),5548 }),
5251 .{ .segment = phndx.plt },5549 .{ .segment = phndx.plt },
5252 ));5550 ));
...@@ -5256,7 +5554,6 @@ fn initHeaders(...@@ -5256,7 +5554,6 @@ fn initHeaders(
5256 // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above.5554 // later (if `have_dynamic_section`). Keep in sync with `elf.ni.data` alignment above.
5257 .alignment = node_block_align.max(addr_align),5555 .alignment = node_block_align.max(addr_align),
5258 .moved = true,5556 .moved = true,
5259 .bubbles_moved = false,
5260 }), .{ .segment = phndx.relro });5557 }), .{ .segment = phndx.relro });
5261 elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro);5558 elf.phdrs.items[phndx.relro] = .wrap(elf.ni.data_rel_ro);
52625559
...@@ -5265,7 +5562,7 @@ fn initHeaders(...@@ -5265,7 +5562,7 @@ fn initHeaders(
5265 try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{5562 try elf.ni.rodata.addFloatingChild(gpa, &elf.mf, .{
5266 .alignment = node_block_align,5563 .alignment = node_block_align,
5267 .moved = true,5564 .moved = true,
5268 .bubbles_moved = false,5565 .bubbles_moved = false, // see TLS segment handling in `flushMoved`
5269 }),5566 }),
5270 .{ .segment = phndx.tls },5567 .{ .segment = phndx.tls },
5271 ));5568 ));
...@@ -5358,25 +5655,10 @@ fn initHeaders(...@@ -5358,25 +5655,10 @@ fn initHeaders(
5358 const ElfN = ct_class.ElfN();5655 const ElfN = ct_class.ElfN();
5359 const target_endian = elf.targetEndian();5656 const target_endian = elf.targetEndian();
53605657
5361 populate_phdrs: {5658 if (@"type" != .REL) {
5362 // Initially we will give every `PT_LOAD` segment this address. When we re-allocate5659 // Initially we will give every `PT_LOAD` segment the base address. They will be
5363 // segments in the virtual address space in `flushMoved` and `flushResized`, we will5660 // moved to non-overlapping addresses by `updateSegmentLoadAddresses`.
5364 // move some segments to higher addresses to prevent overlap. This address therefore5661 const base_vaddr: u64 = elf.baseLoadableSegmentVAddr();
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 };
53805662
5381 // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the5663 // All `PT_LOAD` segments are given this `.@"align"`. However, to avoid bloating the
5382 // binary, their *nodes* are not aligned to this boundary---ELF only requires that5664 // binary, their *nodes* are not aligned to this boundary---ELF only requires that
...@@ -5683,7 +5965,6 @@ fn initHeaders(...@@ -5683,7 +5965,6 @@ fn initHeaders(
5683 .size = interp.len + 1,5965 .size = interp.len + 1,
5684 .moved = true,5966 .moved = true,
5685 .resized = true,5967 .resized = true,
5686 .bubbles_moved = false,
5687 }),5968 }),
5688 .{ .segment = phndx.interp },5969 .{ .segment = phndx.interp },
5689 );5970 );
...@@ -5705,7 +5986,6 @@ fn initHeaders(...@@ -5705,7 +5986,6 @@ fn initHeaders(
5705 try elf.ni.data_rel_ro.addFloatingChild(gpa, &elf.mf, .{5986 try elf.ni.data_rel_ro.addFloatingChild(gpa, &elf.mf, .{
5706 .alignment = addr_align,5987 .alignment = addr_align,
5707 .moved = true,5988 .moved = true,
5708 .bubbles_moved = false,
5709 }),5989 }),
5710 .{ .segment = phndx.dynamic },5990 .{ .segment = phndx.dynamic },
5711 );5991 );
...@@ -5959,7 +6239,6 @@ fn initHeaders(...@@ -5959,7 +6239,6 @@ fn initHeaders(
5959 .size = @sizeOf(Dwarf.EhFrameHdr),6239 .size = @sizeOf(Dwarf.EhFrameHdr),
5960 .alignment = .@"4",6240 .alignment = .@"4",
5961 .moved = true,6241 .moved = true,
5962 .bubbles_moved = false,
5963 }),6242 }),
5964 .{ .segment = phndx.gnu_eh_frame },6243 .{ .segment = phndx.gnu_eh_frame },
5965 );6244 );
...@@ -6272,6 +6551,31 @@ fn initHeaders(...@@ -6272,6 +6551,31 @@ fn initHeaders(
6272 .code_view => unreachable,6551 .code_view => unreachable,
6273 }6552 }
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
6275 assert(elf.nodes.len == expected_nodes_len);6579 assert(elf.nodes.len == expected_nodes_len);
6276 assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF6580 assert(elf.shdrs.items.len == shnum - 1); // -1 to exclude SHN_UNDEF
62776581
...@@ -6303,21 +6607,7 @@ fn initHeaders(...@@ -6303,21 +6607,7 @@ fn initHeaders(
6303 },6607 },
6304 };6608 };
63056609
6306 if (@"type" != .REL) switch (elf.targetSegmentLoadAddressRestrictions()) {6610 try elf.updateSegmentLoadAddresses();
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 };
6321}6611}
63226612
6323pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {6613pub fn startProgress(elf: *Elf, prog_node: std.Progress.Node) void {
...@@ -6482,43 +6772,6 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {...@@ -6482,43 +6772,6 @@ fn computeNodeVAddr(elf: *Elf, ni: MappedFile.Node.Index) u64 {
6482 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);6772 const offset, _ = ni.location(&elf.mf).resolve(&elf.mf);
6483 return parent_vaddr + offset;6773 return parent_vaddr + offset;
6484}6774}
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}
6522fn computeNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 {6775fn computeNodeElfOffset(elf: *Elf, ni: MappedFile.Node.Index) u64 {
6523 return ni.fileLocation(&elf.mf, false).offset - elf.ni.elf.fileLocation(&elf.mf, false).offset;6776 return ni.fileLocation(&elf.mf, false).offset - elf.ni.elf.fileLocation(&elf.mf, false).offset;
6524}6777}
...@@ -6660,7 +6913,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -6660,7 +6913,6 @@ pub fn resetNodeRelocs(elf: *Elf, ni: MappedFile.Node.Index) void {
6660fn flushMovedNodeRelocs(6913fn flushMovedNodeRelocs(
6661 elf: *Elf,6914 elf: *Elf,
6662 node: MappedFile.Node.Index,6915 node: MappedFile.Node.Index,
6663 node_vaddr: u64,
6664 opts: struct {6916 opts: struct {
6665 first_symbol_reloc: SymbolReloc.Index = .none,6917 first_symbol_reloc: SymbolReloc.Index = .none,
6666 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,6918 skip_symbol_relocs: MappedFile.Node.Index.Optional = .none,
...@@ -6676,7 +6928,7 @@ fn flushMovedNodeRelocs(...@@ -6676,7 +6928,7 @@ fn flushMovedNodeRelocs(
6676 if (reloc.node == opts.skip_symbol_relocs) continue;6928 if (reloc.node == opts.skip_symbol_relocs) continue;
6677 break;6929 break;
6678 }6930 }
6679 reloc.flushMovedNode(elf, node_vaddr);6931 reloc.apply(elf);
6680 }6932 }
6681 }6933 }
66826934
...@@ -6687,7 +6939,7 @@ fn flushMovedNodeRelocs(...@@ -6687,7 +6939,7 @@ fn flushMovedNodeRelocs(
6687 if (reloc.node == opts.skip_node_relocs) continue;6939 if (reloc.node == opts.skip_node_relocs) continue;
6688 break;6940 break;
6689 }6941 }
6690 reloc.flushMovedNode(elf, node_vaddr);6942 reloc.apply(elf);
6691 }6943 }
6692 }6944 }
66936945
...@@ -6755,6 +7007,21 @@ fn ehdrType(elf: *const Elf) EhdrType {...@@ -6755,6 +7007,21 @@ fn ehdrType(elf: *const Elf) EhdrType {
6755 }7007 }
6756}7008}
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
6758fn targetPtrSize(elf: *const Elf) u8 {7025fn targetPtrSize(elf: *const Elf) u8 {
6759 return elf.identClass().size();7026 return elf.identClass().size();
6760}7027}
...@@ -6873,30 +7140,6 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo {...@@ -6873,30 +7140,6 @@ fn targetDynsymHashInfo(elf: *const Elf) DynsymHashInfo {
6873 // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`.7140 // TODO: Alpha and S390x will need to use either `."@4"` or `.@"8"` depending on `elf.identClass()`.
6874 };7141 };
6875}7142}
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}
6900fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {7143fn targetLoad(elf: *const Elf, ptr: anytype) @typeInfo(@TypeOf(ptr)).pointer.child {
6901 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;7144 const pointer_ty = @typeInfo(@TypeOf(ptr)).pointer;
6902 const Child = pointer_ty.child;7145 const Child = pointer_ty.child;
...@@ -7326,6 +7569,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!v...@@ -7326,6 +7569,7 @@ pub fn loadInput(elf: *Elf, input: link.Input) (link.Error || error{BadMagic})!v
7326 .{elf.mf.io_err.?},7569 .{elf.mf.io_err.?},
7327 ),7570 ),
7328 };7571 };
7572 try elf.updateSegmentLoadAddresses();
7329}7573}
7330fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void {7574fn loadInputInner(elf: *Elf, input: link.Input) (Error || error{BadMagic})!void {
7331 const comp = elf.base.comp;7575 const comp = elf.base.comp;
...@@ -8605,6 +8849,7 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {...@@ -8605,6 +8849,7 @@ pub fn prelink(elf: *Elf, prog_node: std.Progress.Node) link.Error!void {
8605 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),8849 error.MappedFileIo => return diags.fail("failed to write output file: {t}", .{elf.mf.io_err.?}),
8606 else => |e| return e,8850 else => |e| return e,
8607 };8851 };
8852 try elf.updateSegmentLoadAddresses();
8608}8853}
8609fn prelinkInner(elf: *Elf) Error!void {8854fn prelinkInner(elf: *Elf) Error!void {
8610 const comp = elf.base.comp;8855 const comp = elf.base.comp;
...@@ -8659,24 +8904,27 @@ fn prelinkInner(elf: *Elf) Error!void {...@@ -8659,24 +8904,27 @@ fn prelinkInner(elf: *Elf) Error!void {
8659 if (comp.zcu) |_| self_hosted_codegen: {8904 if (comp.zcu) |_| self_hosted_codegen: {
8660 if (comp.config.use_llvm) break :self_hosted_codegen;8905 if (comp.config.use_llvm) break :self_hosted_codegen;
86618906
8662 // We're using self-hosted codegen---add an input representing the Zig "object".8907 if (elf.archive == null) {
8663 try elf.inputs.ensureUnusedCapacity(gpa, 1);8908 // Self-hosted codegen is logically emitting a relocatable which is then immediately
8664 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});8909 // being linked into the output file, so add that fictional Zig object file input.
8665 defer gpa.free(zcu_name);8910 try elf.inputs.ensureUnusedCapacity(gpa, 1);
8666 const zcu_file_symbol = try elf.addLocalSymbol(.{8911 const zcu_name = try std.fmt.allocPrint(gpa, "{s}_zcu", .{comp.root_name});
8667 .node = .none,8912 defer gpa.free(zcu_name);
8668 .name = zcu_name,8913 const zcu_file_symbol = try elf.addLocalSymbol(.{
8669 .value = 0,8914 .node = .none,
8670 .size = 0,8915 .name = zcu_name,
8671 .type = .FILE,8916 .value = 0,
8672 .shndx = .ABS,8917 .size = 0,
8673 });8918 .type = .FILE,
8674 elf.inputs.addOneAssumeCapacity().* = .{8919 .shndx = .ABS,
8675 .path = elf.base.emit,8920 });
8676 .member = null,8921 elf.inputs.addOneAssumeCapacity().* = .{
8677 .extra = .{ .file_symbol = zcu_file_symbol },8922 .path = elf.base.emit,
8678 };8923 .member = null,
8679 elf.input_pending_index += 1;8924 .extra = .{ .file_symbol = zcu_file_symbol },
8925 };
8926 elf.input_pending_index += 1;
8927 }
86808928
8681 switch (elf.shndx.debug_addr) {8929 switch (elf.shndx.debug_addr) {
8682 .UNDEF => {},8930 .UNDEF => {},
...@@ -8758,6 +9006,7 @@ pub fn zcuFilesReady(elf: *Elf, zcu: *Zcu) link.Error!void {...@@ -8758,6 +9006,7 @@ pub fn zcuFilesReady(elf: *Elf, zcu: *Zcu) link.Error!void {
8758 .{elf.mf.io_err.?},9006 .{elf.mf.io_err.?},
8759 ),9007 ),
8760 };9008 };
9009 try elf.updateSegmentLoadAddresses();
8761}9010}
8762fn zcuFilesReadyInner(elf: *Elf, zcu: *Zcu) Error!void {9011fn zcuFilesReadyInner(elf: *Elf, zcu: *Zcu) Error!void {
8763 const gpa = zcu.gpa;9012 const gpa = zcu.gpa;
...@@ -9126,7 +9375,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {...@@ -9126,7 +9375,7 @@ fn addSection(elf: *Elf, segment_ni: MappedFile.Node.Index, opts: struct {
9126 .size = opts.node_align.forward(opts.size),9375 .size = opts.node_align.forward(opts.size),
9127 .alignment = opts.addralign.max(opts.node_align),9376 .alignment = opts.addralign.max(opts.node_align),
9128 .resized = opts.size > 0,9377 .resized = opts.size > 0,
9129 .bubbles_moved = opts.flags.ALLOC,9378 .bubbles_moved = opts.flags.ALLOC, // see handling in `flushMoved`
9130 }),9379 }),
9131 if (opts.manual_size) .{ .section_manual_size = shndx } else .{ .section = shndx },9380 if (opts.manual_size) .{ .section_manual_size = shndx } else .{ .section = shndx },
9132 )),9381 )),
...@@ -9265,6 +9514,7 @@ fn addRelocAssumeCapacity(...@@ -9265,6 +9514,7 @@ fn addRelocAssumeCapacity(
9265 .rela_index = rela_index.toOptional(),9514 .rela_index = rela_index.toOptional(),
9266 .result = .ok,9515 .result = .ok,
9267 });9516 });
9517 ri.get(elf).apply(elf);
9268 },9518 },
9269 .DYN, .EXEC => switch (elf.ehdrMachine()) {9519 .DYN, .EXEC => switch (elf.ehdrMachine()) {
9270 .AARCH64 => switch (@"type".AARCH64) {9520 .AARCH64 => switch (@"type".AARCH64) {
...@@ -9622,18 +9872,21 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9622,18 +9872,21 @@ fn addSymbolRelocAssumeCapacityInner(
9622) Error!void {9872) Error!void {
9623 assert(elf.ehdrType() != .REL);9873 assert(elf.ehdrType() != .REL);
96249874
9625 const rela_index: Section.RelaIndex.Optional = r: {9875 request_static_target: {
9626 if (elf.shndx.dynamic == .UNDEF) break :r .none;9876 if (elf.shndx.dynamic == .UNDEF) break :request_static_target;
96279877 const gsi: Symbol.Global.Index = switch (target.unwrap()) {
9628 // If we emit a runtime relocation entry, its `offset` is a virtual address, so we need to9878 .local => break :request_static_target,
9629 // determine the vaddr of `node`.9879 .global => |gsi| gsi,
9630 const node_vaddr = elf.getNodeVAddr(node);9880 };
96319881 // The target symbol may be dynamic, in which case its value cannot be resolved statically.
9632 // If this is `true`, we will try to create a copy relocation for the target symbol if it is9882 // However, there are strategies (such as copy relocations) the linker can employ which can
9633 // not locally defined. If the relocation value is always computed from the target symbol's9883 // effectively convert dynamic symbols into static symbols. If statically resolving this
9634 // value (even for an external target symbol), and if the target symbol might be of type9884 // relocation would require knowing the symbol value, we can set a flag so that those
9635 // STT_OBJECT, this should probably be `true`.9885 // strategies are used when possible to make this relocation statically resolvable.
9636 const try_copy_reloc: bool = switch (@"type".target) {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) {
9637 .rel, .abs => true,9890 .rel, .abs => true,
96389891
9639 .pltrel,9892 .pltrel,
...@@ -9662,92 +9915,13 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9662,92 +9915,13 @@ fn addSymbolRelocAssumeCapacityInner(
9662 => false,9915 => false,
9663 },9916 },
9664 };9917 };
9665 if (try_copy_reloc) switch (target.unwrap()) {9918 if (!want_static_value) break :request_static_target;
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,
96969919
9697 .abs, .pltabs => {},9920 if (!gsi.ptr(elf).status.want_static_value) {
9698 }9921 gsi.ptr(elf).status.want_static_value = true;
9699 if (!@"type".action.simple.dest.isAddr(elf)) break :r .none;9922 try elf.updateGlobalDynamic(gsi, false);
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 },
9749 }9923 }
9750 };9924 }
97519925
9752 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));9926 const ri: SymbolReloc.Index = @fromBackingInt(@intCast(elf.symbol_relocs.items.len));
9753 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;9927 const first_target_reloc = &target.index(elf).ptr(elf).first_target_reloc;
...@@ -9762,12 +9936,13 @@ fn addSymbolRelocAssumeCapacityInner(...@@ -9762,12 +9936,13 @@ fn addSymbolRelocAssumeCapacityInner(
9762 .type = @"type",9936 .type = @"type",
9763 .next = next,9937 .next = next,
9764 .prev = .none,9938 .prev = .none,
9765 .rela_index = rela_index,9939 .rela_index = .none,
9766 .result = .ok,9940 .result = .ok,
9767 });9941 });
9768 if (@"type".dependsOnTlsSize(elf)) {9942 if (@"type".dependsOnTlsSize(elf)) {
9769 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});9943 elf.tls_size_symbol_relocs.putAssumeCapacityNoClobber(ri, {});
9770 }9944 }
9945 try ri.get(elf).updateDynamicReloc(elf);
97719946
9772 // Actually apply the new relocation!9947 // Actually apply the new relocation!
9773 ri.get(elf).apply(elf);9948 ri.get(elf).apply(elf);
...@@ -10098,18 +10273,19 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {...@@ -10098,18 +10273,19 @@ fn updateGotEntry(elf: *Elf, got_index: usize) void {
10098 };10273 };
10099}10274}
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`.
10102///10278///
10103/// If `node` can contain runtime relocations, returns `.yes_textrel` if such a relocation requires10279/// Otherwise, returns `.ok_textrel` if placing a dynamic relocation in this node requires the
10104/// the presence of a `DT_TEXTREL` dynamic entry, or `.yes` otherwise.10280/// presence of a `DT_TEXTREL` dynamic entry, or `.ok` otherwise.
10105fn nodeWantsDsoRelocation(elf: *Elf, node: MappedFile.Node.Index) enum { yes, yes_textrel, no } {10281fn nodeAllowsDynamicReloc(elf: *Elf, node: MappedFile.Node.Index) enum { ok, ok_textrel, not_alloc } {
10106 const shndx = elf.getNodeShndx(node);10282 const shndx = elf.getNodeShndx(node);
10107 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {10283 const shf: std.elf.SHF = switch (elf.shdrPtr(shndx)) {
10108 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,10284 inline else => |shdr| elf.targetLoad(&shdr.flags).shf,
10109 };10285 };
10110 if (!shf.ALLOC) return .no;10286 if (!shf.ALLOC) return .not_alloc;
10111 if (!shf.WRITE) return .yes_textrel;10287 if (!shf.WRITE) return .ok_textrel;
10112 return .yes;10288 return .ok;
10113}10289}
1011410290
10115pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.Error!void {10291pub 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)...@@ -10120,6 +10296,7 @@ pub fn updateNav(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
10120 .{elf.mf.io_err.?},10296 .{elf.mf.io_err.?},
10121 ),10297 ),
10122 };10298 };
10299 try elf.updateSegmentLoadAddresses();
10123}10300}
10124fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {10301fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) Error!void {
10125 const comp = elf.base.comp;10302 const comp = elf.base.comp;
...@@ -10130,15 +10307,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)...@@ -10130,15 +10307,8 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
10130 const nav = ip.getNav(nav_index);10307 const nav = ip.getNav(nav_index);
10131 const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?;10308 const mod = zcu.fileByIndex(nav.srcInst(ip).resolveFile(ip)).mod.?;
10132 switch (ip.indexToKey(nav.resolved.?.value)) {10309 switch (ip.indexToKey(nav.resolved.?.value)) {
10133 .@"extern" => |@"extern"| {10310 .@"extern" => {
10134 if (mod.strip or elf.ehdrMachine() != .X86_64) return;10311 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 });
10142 const dwarf_gi = try elf.dwarf.getGlobal(nav_index);10312 const dwarf_gi = try elf.dwarf.getGlobal(nav_index);
10143 const dwarf_global = dwarf_gi.get(&elf.dwarf);10313 const dwarf_global = dwarf_gi.get(&elf.dwarf);
10144 const debug_info_ni = dwarf_global.debug_info_ni.unwrap().?;10314 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)...@@ -10147,10 +10317,7 @@ fn updateNavInner(elf: *Elf, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index)
10147 debug_info_ni.writer(gpa, &elf.mf, &di_nw);10317 debug_info_ni.writer(gpa, &elf.mf, &di_nw);
10148 defer di_nw.deinit();10318 defer di_nw.deinit();
10149 elf.resetNodeRelocs(debug_info_ni);10319 elf.resetNodeRelocs(debug_info_ni);
10150 try elf.dwarf.updateExtern(pt, &di_nw, switch (elf.ehdrMachine()) {10320 try elf.dwarf.updateExtern(pt, &di_nw, nav_index);
10151 else => unreachable,
10152 .X86_64 => .x86_64,
10153 }, si, nav_index);
10154 },10321 },
10155 else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {10322 else => if (Type.fromInterned(nav.resolved.?.type).hasRuntimeBits(zcu)) {
10156 const nmi = try elf.navMapIndex(zcu, nav_index);10323 const nmi = try elf.navMapIndex(zcu, nav_index);
...@@ -10219,6 +10386,7 @@ pub fn updateContainerType(...@@ -10219,6 +10386,7 @@ pub fn updateContainerType(
10219 .{elf.mf.io_err.?},10386 .{elf.mf.io_err.?},
10220 ),10387 ),
10221 };10388 };
10389 try elf.updateSegmentLoadAddresses();
10222}10390}
10223fn updateContainerTypeInner(10391fn updateContainerTypeInner(
10224 elf: *Elf,10392 elf: *Elf,
...@@ -10360,6 +10528,7 @@ pub fn updateFunc(...@@ -10360,6 +10528,7 @@ pub fn updateFunc(
10360 .{elf.mf.io_err.?},10528 .{elf.mf.io_err.?},
10361 ),10529 ),
10362 };10530 };
10531 try elf.updateSegmentLoadAddresses();
10363}10532}
10364fn updateFuncInner(10533fn updateFuncInner(
10365 elf: *Elf,10534 elf: *Elf,
...@@ -10607,6 +10776,7 @@ pub fn lostTracking(...@@ -10607,6 +10776,7 @@ pub fn lostTracking(
10607 elf.mf.io_err.?,10776 elf.mf.io_err.?,
10608 }),10777 }),
10609 };10778 };
10779 try elf.updateSegmentLoadAddresses();
10610}10780}
1061110781
10612pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void {10782pub 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 {...@@ -10616,6 +10786,7 @@ pub fn updateErrorData(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
10616 });10786 });
10617 if (elf.dwarf.const_pool.getIfExists(.anyerror_type)) |cpi|10787 if (elf.dwarf.const_pool.getIfExists(.anyerror_type)) |cpi|
10618 try elf.updateConstInner(pt, cpi, .anyerror_type, .complete);10788 try elf.updateConstInner(pt, cpi, .anyerror_type, .complete);
10789 try elf.updateSegmentLoadAddresses();
10619}10790}
1062010791
10621pub fn flush(10792pub fn flush(
...@@ -10663,6 +10834,8 @@ fn flushInner(...@@ -10663,6 +10834,8 @@ fn flushInner(
1066310834
10664 try elf.prepareDynamic();10835 try elf.prepareDynamic();
1066510836
10837 try elf.updateSegmentLoadAddresses();
10838
10666 while (try elf.idle()) {}10839 while (try elf.idle()) {}
1066710840
10668 assert(elf.input_pending_index == elf.inputs.items.len);10841 assert(elf.input_pending_index == elf.inputs.items.len);
...@@ -10678,10 +10851,28 @@ fn flushInner(...@@ -10678,10 +10851,28 @@ fn flushInner(
10678 defer elf.mf.nodes_lock.unlock();10851 defer elf.mf.nodes_lock.unlock();
1067910852
10680 if (elf.overflowed_reloc_count > 0) {10853 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 );
10682 }10858 }
10683 if (elf.misaligned_reloc_count > 0) {10859 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 );
10685 }10876 }
1068610877
10687 if (elf.archive) |*archive| {10878 if (elf.archive) |*archive| {
...@@ -10698,20 +10889,22 @@ fn flushInner(...@@ -10698,20 +10889,22 @@ fn flushInner(
10698 elf.flushDynamic();10889 elf.flushDynamic();
1069910890
10700 const entry_addr: u64 = entry: {10891 const entry_addr: u64 = entry: {
10701 const sym_name: []const u8 = name: switch (elf.options.entry) {10892 const entry_gsi = elf.entry_gsi.unwrap() orelse break :entry 0;
10702 .default => switch (comp.config.output_mode) {10893
10703 .Exe => continue :name .enabled,10894 // Even though we already reported undefined globals earlier, we also need to check here
10704 .Lib, .Obj => continue :name .disabled,10895 // whether the entry point is defined. There are two reasons for this:
10705 },10896 //
10706 .disabled => break :entry 0,10897 // * If we are emitting something other than an executable, we skipped the undefined globals
10707 .enabled => "_start",10898 // check, but there could still be an entry point (e.g. due to `-fentry=foo` on the CLI).
10708 .named => |named| named,10899 //
10709 };10900 // * The entry point might be defined in an input DSO, which would satisfy the undefined
10710 const gsi = elf.globalByName(.{10901 // symbol reference but is insufficient here: the entry point needs a local definition.
10711 .name = sym_name,10902
10712 .version = null,10903 if (!entry_gsi.resolveAlias(elf).defined(elf)) {
10713 }) orelse break :entry 0;10904 return diags.fail("undefined entry point symbol '{s}'", .{entry_gsi.name(elf)});
10714 break :entry Symbol.Id.global(gsi).value(elf);10905 }
10906
10907 break :entry Symbol.Id.global(entry_gsi).value(elf);
10715 };10908 };
10716 switch (elf.ehdrPtr()) {10909 switch (elf.ehdrPtr()) {
10717 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),10910 inline else => |ehdr| elf.targetStore(&ehdr.entry, @intCast(entry_addr)),
...@@ -10719,9 +10912,12 @@ fn flushInner(...@@ -10719,9 +10912,12 @@ fn flushInner(
1071910912
10720 try elf.mf.flush();10913 try elf.mf.flush();
1072110914
10722 if (elf.options.enable_link_snapshots)10915 if (elf.options.enable_link_snapshots) {
10723 elf.dumpStderr() catch |err|10916 elf.dumpStderr() catch |err| switch (err) {
10724 return diags.fail("dumping link snapshot failed: {t}", .{err});10917 error.Canceled => |e| return e,
10918 else => |e| return diags.fail("dumping link snapshot failed: {t}", .{e}),
10919 };
10920 }
10725}10921}
1072610922
10727pub fn idle(elf: *Elf) link.Error!bool {10923pub fn idle(elf: *Elf) link.Error!bool {
...@@ -10729,6 +10925,9 @@ pub fn idle(elf: *Elf) link.Error!bool {...@@ -10729,6 +10925,9 @@ pub fn idle(elf: *Elf) link.Error!bool {
10729 elf.mf.nodes_lock.lock();10925 elf.mf.nodes_lock.lock();
10730 defer elf.mf.nodes_lock.unlock();10926 defer elf.mf.nodes_lock.unlock();
1073110927
10928 const trace = tracy.trace(@src());
10929 defer trace.end();
10930
10732 const comp = elf.base.comp;10931 const comp = elf.base.comp;
10733 const diags = &comp.link_diags;10932 const diags = &comp.link_diags;
1073410933
...@@ -10857,7 +11056,7 @@ pub fn idle(elf: *Elf) link.Error!bool {...@@ -10857,7 +11056,7 @@ pub fn idle(elf: *Elf) link.Error!bool {
10857 break :task;11056 break :task;
10858 }11057 }
10859 while (elf.mf.updates.pop()) |ni| : (elf.mf.update_prog_node.completeOne()) {11058 while (elf.mf.updates.pop()) |ni| : (elf.mf.update_prog_node.completeOne()) {
10860 if (ni.pendingDelete(&elf.mf)) continue;11059 ni.cleanUpdated(&elf.mf);
10861 const clean_moved = ni.cleanMoved(&elf.mf);11060 const clean_moved = ni.cleanMoved(&elf.mf);
10862 const clean_resized = ni.cleanResized(&elf.mf);11061 const clean_resized = ni.cleanResized(&elf.mf);
10863 const clean_next_moved = ni.cleanNextMoved(&elf.mf);11062 const clean_next_moved = ni.cleanNextMoved(&elf.mf);
...@@ -10872,6 +11071,9 @@ pub fn idle(elf: *Elf) link.Error!bool {...@@ -10872,6 +11071,9 @@ pub fn idle(elf: *Elf) link.Error!bool {
10872 if (elf.bss_moved) {11071 if (elf.bss_moved) {
10873 elf.bss_moved = false;11072 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
10875 const data_phndx = elf.getNode(elf.ni.data).segment;11077 const data_phndx = elf.getNode(elf.ni.data).segment;
10876 const data_offset: u64, const data_vaddr: u64, const data_filesz: u64 = switch (elf.phdrSlice()) {11078 const data_offset: u64, const data_vaddr: u64, const data_filesz: u64 = switch (elf.phdrSlice()) {
10877 inline else => |phdr| .{11079 inline else => |phdr| .{
...@@ -10902,6 +11104,30 @@ pub fn idle(elf: *Elf) link.Error!bool {...@@ -10902,6 +11104,30 @@ pub fn idle(elf: *Elf) link.Error!bool {
1090211104
10903 break :task;11105 break :task;
10904 }11106 }
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 }
10905 }11131 }
10906 if (elf.inputs.items.len - elf.input_pending_index > 0) return true;11132 if (elf.inputs.items.len - elf.input_pending_index > 0) return true;
10907 if (elf.input_sections.items.len - elf.input_section_pending_index > 0) return true;11133 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 {...@@ -10909,6 +11135,8 @@ pub fn idle(elf: *Elf) link.Error!bool {
10909 if (elf.changed_symtab_index.count() > 0) return true;11135 if (elf.changed_symtab_index.count() > 0) return true;
10910 if (elf.mf.updates.items.len > 0) return true;11136 if (elf.mf.updates.items.len > 0) return true;
10911 if (elf.bss_moved) return true;11137 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;
10912 return false;11140 return false;
10913}11141}
1091411142
...@@ -11039,7 +11267,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {...@@ -11039,7 +11267,6 @@ fn genPending(elf: *Elf, pt: Zcu.PerThread) link.Error!void {
11039fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {11267fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {
11040 while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) {11268 while (elf.dwarf.debug_addr.map.count() - elf.dwarf.debug_addr.pending_index > 0) {
11041 const debug_addr_ni = elf.dwarf.debug_addr.ni.unwrap().?;11269 const debug_addr_ni = elf.dwarf.debug_addr.ni.unwrap().?;
11042 try debug_addr_ni.moved(gpa, &elf.mf);
11043 var da_nw: link.MappedFile.Node.Writer = undefined;11270 var da_nw: link.MappedFile.Node.Writer = undefined;
11044 debug_addr_ni.writer(gpa, &elf.mf, &da_nw);11271 debug_addr_ni.writer(gpa, &elf.mf, &da_nw);
11045 defer da_nw.deinit();11272 defer da_nw.deinit();
...@@ -11052,7 +11279,6 @@ fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {...@@ -11052,7 +11279,6 @@ fn genPendingDebug(elf: *Elf, gpa: std.mem.Allocator) link.Error!void {
11052 }11279 }
11053 while (elf.dwarf.debug_str_offsets.map.count() - elf.dwarf.debug_str_offsets.pending_index > 0) {11280 while (elf.dwarf.debug_str_offsets.map.count() - elf.dwarf.debug_str_offsets.pending_index > 0) {
11054 const debug_str_offsets_ni = elf.dwarf.debug_str_offsets.ni.unwrap().?;11281 const debug_str_offsets_ni = elf.dwarf.debug_str_offsets.ni.unwrap().?;
11055 try debug_str_offsets_ni.moved(gpa, &elf.mf);
11056 var dso_nw: link.MappedFile.Node.Writer = undefined;11282 var dso_nw: link.MappedFile.Node.Writer = undefined;
11057 debug_str_offsets_ni.writer(gpa, &elf.mf, &dso_nw);11283 debug_str_offsets_ni.writer(gpa, &elf.mf, &dso_nw);
11058 defer dso_nw.deinit();11284 defer dso_nw.deinit();
...@@ -11297,6 +11523,7 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void {...@@ -11297,6 +11523,7 @@ fn flushElfOffset(elf: *Elf, ni: MappedFile.Node.Index) void {
11297fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {11523fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
11298 const trace = tracy.trace(@src());11524 const trace = tracy.trace(@src());
11299 defer trace.end();11525 defer trace.end();
11526 trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)});
1130011527
11301 switch (elf.getNode(ni)) {11528 switch (elf.getNode(ni)) {
11302 .deleted => unreachable,11529 .deleted => unreachable,
...@@ -11315,7 +11542,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11315,7 +11542,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11315 else => unreachable,11542 else => unreachable,
1131611543
11317 .NULL, .LOAD => {11544 .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;
11319 },11550 },
1132011551
11321 .DYNAMIC,11552 .DYNAMIC,
...@@ -11343,6 +11574,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11343,6 +11574,12 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11343 elf,11574 elf,
11344 tbss_addr,11575 tbss_addr,
11345 );11576 );
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;
11346 },11583 },
11347 }11584 }
11348 },11585 },
...@@ -11382,7 +11619,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11382,7 +11619,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11382 reloc.apply(elf);11619 reloc.apply(elf);
11383 }11620 }
11384 } else if (shndx == elf.shndx.plt) {11621 } else if (shndx == elf.shndx.plt) {
11385 elf.flushMovedNodeRelocs(ni, addr, .{11622 elf.flushMovedNodeRelocs(ni, .{
11386 .first_symbol_reloc = elf.plt_first_symbol_reloc,11623 .first_symbol_reloc = elf.plt_first_symbol_reloc,
11387 });11624 });
11388 elf.flushMovedPltSection(.plt, old_addr, addr);11625 elf.flushMovedPltSection(.plt, old_addr, addr);
...@@ -11391,10 +11628,16 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11391,10 +11628,16 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11391 } else if (shndx == elf.shndx.plt_sec) {11628 } else if (shndx == elf.shndx.plt_sec) {
11392 elf.flushMovedPltSection(.plt_sec, old_addr, addr);11629 elf.flushMovedPltSection(.plt_sec, old_addr, addr);
11393 } else if (shndx == elf.shndx.eh_frame_hdr) {11630 } else if (shndx == elf.shndx.eh_frame_hdr) {
11394 elf.flushMovedNodeRelocs(ni, addr, .{11631 elf.flushMovedNodeRelocs(ni, .{
11395 .first_symbol_reloc = elf.eh_frame_hdr_first_symbol_reloc,11632 .first_symbol_reloc = elf.eh_frame_hdr_first_symbol_reloc,
11396 });11633 });
11397 }11634 }
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 }
11398 },11641 },
11399 .input_section => |isi| {11642 .input_section => |isi| {
11400 const old_section_addr = isi.ptr(elf).vaddr;11643 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...@@ -11438,17 +11681,13 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11438 }11681 }
11439 }11682 }
1144011683
11441 elf.flushMovedNodeRelocs(ni, new_section_addr, .{11684 elf.flushMovedNodeRelocs(ni, .{
11442 .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc,11685 .first_symbol_reloc = isi.ptrConst(elf).first_symbol_reloc,
11443 .first_got_reloc = isi.ptrConst(elf).first_got_reloc,11686 .first_got_reloc = isi.ptrConst(elf).first_got_reloc,
11444 });11687 });
11445 },11688 },
11446 .copied_global => |global_name| {11689 .copied_global => |global_name| {
11447 const copied_global = elf.copied_globals.getPtr(global_name) orelse {11690 const copied_global = elf.copied_globals.getPtr(global_name).?;
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 };
11452 assert(copied_global.node == ni);11691 assert(copied_global.node == ni);
1145311692
11454 const new_addr = elf.computeNodeVAddr(ni);11693 const new_addr = elf.computeNodeVAddr(ni);
...@@ -11465,7 +11704,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11465,7 +11704,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11465 Symbol.Id.global(gsi).flushMoved(elf, new_addr);11704 Symbol.Id.global(gsi).flushMoved(elf, new_addr);
11466 }11705 }
11467 }11706 }
11468 elf.flushMovedNodeRelocs(ni, new_addr, .{11707 elf.flushMovedNodeRelocs(ni, .{
11469 .first_symbol_reloc = mi.firstSymbolReloc(elf),11708 .first_symbol_reloc = mi.firstSymbolReloc(elf),
11470 .skip_symbol_relocs = switch (tag) {11709 .skip_symbol_relocs = switch (tag) {
11471 else => comptime unreachable,11710 else => comptime unreachable,
...@@ -11479,12 +11718,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11479,12 +11718,11 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11479 });11718 });
11480 },11719 },
11481 .debug_shared => |ss| {11720 .debug_shared => |ss| {
11482 const target_section_offset = elf.computeNodeSectionOffset(ni);
11483 var target_ri = elf.dwarf_shared.getPtr(ss).first_target_reloc;11721 var target_ri = elf.dwarf_shared.getPtr(ss).first_target_reloc;
11484 while (target_ri != .none) {11722 while (target_ri != .none) {
11485 const target_reloc = target_ri.get(elf);11723 const target_reloc = target_ri.get(elf);
11486 assert(target_reloc.target == ni);11724 assert(target_reloc.target == ni);
11487 target_reloc.flushMovedTarget(elf, target_section_offset);11725 target_reloc.apply(elf);
11488 target_ri = target_reloc.next;11726 target_ri = target_reloc.next;
11489 }11727 }
11490 },11728 },
...@@ -11495,119 +11733,110 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11495,119 +11733,110 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11495 .unit_debug_line,11733 .unit_debug_line,
11496 => {},11734 => {},
11497 .debug_addr => {11735 .debug_addr => {
11498 const target_section_offset = elf.computeNodeSectionOffset(ni);
11499 var target_ri = elf.dwarf_addr.first_target_reloc;11736 var target_ri = elf.dwarf_addr.first_target_reloc;
11500 while (target_ri != .none) {11737 while (target_ri != .none) {
11501 const target_reloc = target_ri.get(elf);11738 const target_reloc = target_ri.get(elf);
11502 assert(target_reloc.target == ni);11739 assert(target_reloc.target == ni);
11503 target_reloc.flushMovedTarget(elf, target_section_offset);11740 target_reloc.apply(elf);
11504 target_ri = target_reloc.next;11741 target_ri = target_reloc.next;
11505 }11742 }
11506 const node_vaddr = elf.computeNodeVAddr(ni);
11507 for (elf.dwarf_addr.symbol_relocs.items) |symbol_ri| {11743 for (elf.dwarf_addr.symbol_relocs.items) |symbol_ri| {
11508 const symbol_reloc = symbol_ri.get(elf);11744 const symbol_reloc = symbol_ri.get(elf);
11509 assert(symbol_reloc.node.unwrap().? == ni);11745 assert(symbol_reloc.node.unwrap().? == ni);
11510 symbol_reloc.flushMovedNode(elf, node_vaddr);11746 symbol_reloc.apply(elf);
11511 }11747 }
11748 elf.dwarf_addr.pending_reloc_index = elf.dwarf_addr.symbol_relocs.items.len;
11512 },11749 },
11513 .debug_str_offsets => {11750 .debug_str_offsets => {
11514 const target_section_offset = elf.computeNodeSectionOffset(ni);
11515 var target_ri = elf.dwarf_str_offsets.first_target_reloc;11751 var target_ri = elf.dwarf_str_offsets.first_target_reloc;
11516 while (target_ri != .none) {11752 while (target_ri != .none) {
11517 const target_reloc = target_ri.get(elf);11753 const target_reloc = target_ri.get(elf);
11518 assert(target_reloc.target == ni);11754 assert(target_reloc.target == ni);
11519 target_reloc.flushMovedTarget(elf, target_section_offset);11755 target_reloc.apply(elf);
11520 target_ri = target_reloc.next;11756 target_ri = target_reloc.next;
11521 }11757 }
11522 const node_vaddr = elf.computeNodeVAddr(ni);
11523 for (elf.dwarf_str_offsets.node_relocs.items) |node_ri| {11758 for (elf.dwarf_str_offsets.node_relocs.items) |node_ri| {
11524 const node_reloc = node_ri.get(elf);11759 const node_reloc = node_ri.get(elf);
11525 assert(node_reloc.node.unwrap().? == ni);11760 assert(node_reloc.node.unwrap().? == ni);
11526 node_reloc.flushMovedNode(elf, node_vaddr);11761 node_reloc.apply(elf);
11527 }11762 }
11763 elf.dwarf_str_offsets.pending_reloc_index = elf.dwarf_str_offsets.node_relocs.items.len;
11528 },11764 },
11529 .unit_frame_cie => |ui| {11765 .unit_frame_cie => |ui| {
11530 const target_section_offset = elf.computeNodeSectionOffset(ni);
11531 var target_ri = elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc;11766 var target_ri = elf.dwarf_units[@backingInt(ui)].frame_cie_first_target_reloc;
11532 while (target_ri != .none) {11767 while (target_ri != .none) {
11533 const target_reloc = target_ri.get(elf);11768 const target_reloc = target_ri.get(elf);
11534 assert(target_reloc.target == ni);11769 assert(target_reloc.target == ni);
11535 target_reloc.flushMovedTarget(elf, target_section_offset);11770 target_reloc.apply(elf);
11536 target_ri = target_reloc.next;11771 target_ri = target_reloc.next;
11537 }11772 }
11538 },11773 },
11539 .unit_debug_info_header => |ui| {11774 .unit_debug_info_header => |ui| {
11540 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];11775 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];
11541 const target_section_offset = elf.computeNodeSectionOffset(ni);
11542 var target_ri = dwarf_unit.debug_info_header_first_target_reloc;11776 var target_ri = dwarf_unit.debug_info_header_first_target_reloc;
11543 while (target_ri != .none) {11777 while (target_ri != .none) {
11544 const target_reloc = target_ri.get(elf);11778 const target_reloc = target_ri.get(elf);
11545 assert(target_reloc.target == ni);11779 assert(target_reloc.target == ni);
11546 target_reloc.flushMovedTarget(elf, target_section_offset);11780 target_reloc.apply(elf);
11547 target_ri = target_reloc.next;11781 target_ri = target_reloc.next;
11548 }11782 }
11549 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{11783 elf.flushMovedNodeRelocs(ni, .{
11550 .first_node_reloc = dwarf_unit.debug_info_header_first_node_reloc,11784 .first_node_reloc = dwarf_unit.debug_info_header_first_node_reloc,
11551 });11785 });
11552 },11786 },
11553 .unit_debug_info_footer => {},11787 .unit_debug_info_footer => {},
11554 .unit_debug_line_header => |ui| {11788 .unit_debug_line_header => |ui| {
11555 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];11789 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];
11556 const target_section_offset = elf.computeNodeSectionOffset(ni);
11557 var target_ri = dwarf_unit.debug_line_header_first_target_reloc;11790 var target_ri = dwarf_unit.debug_line_header_first_target_reloc;
11558 while (target_ri != .none) {11791 while (target_ri != .none) {
11559 const target_reloc = target_ri.get(elf);11792 const target_reloc = target_ri.get(elf);
11560 assert(target_reloc.target == ni);11793 assert(target_reloc.target == ni);
11561 target_reloc.flushMovedTarget(elf, target_section_offset);11794 target_reloc.apply(elf);
11562 target_ri = target_reloc.next;11795 target_ri = target_reloc.next;
11563 }11796 }
11564 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{11797 elf.flushMovedNodeRelocs(ni, .{
11565 .first_node_reloc = dwarf_unit.debug_line_header_first_node_reloc,11798 .first_node_reloc = dwarf_unit.debug_line_header_first_node_reloc,
11566 });11799 });
11567 },11800 },
11568 .unit_debug_rnglists => |ui| {11801 .unit_debug_rnglists => |ui| {
11569 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];11802 const dwarf_unit = &elf.dwarf_units[@backingInt(ui)];
11570 const target_section_offset = elf.computeNodeSectionOffset(ni);
11571 var target_ri = dwarf_unit.debug_rnglists_first_target_reloc;11803 var target_ri = dwarf_unit.debug_rnglists_first_target_reloc;
11572 while (target_ri != .none) {11804 while (target_ri != .none) {
11573 const target_reloc = target_ri.get(elf);11805 const target_reloc = target_ri.get(elf);
11574 assert(target_reloc.target == ni);11806 assert(target_reloc.target == ni);
11575 target_reloc.flushMovedTarget(elf, target_section_offset);11807 target_reloc.apply(elf);
11576 target_ri = target_reloc.next;11808 target_ri = target_reloc.next;
11577 }11809 }
11578 const node_vaddr = elf.computeNodeVAddr(ni);
11579 for (dwarf_unit.debug_rnglists_symbol_relocs.items) |symbol_ri| {11810 for (dwarf_unit.debug_rnglists_symbol_relocs.items) |symbol_ri| {
11580 const symbol_reloc = symbol_ri.get(elf);11811 const symbol_reloc = symbol_ri.get(elf);
11581 assert(symbol_reloc.node.unwrap().? == ni);11812 assert(symbol_reloc.node.unwrap().? == ni);
11582 symbol_reloc.flushMovedNode(elf, node_vaddr);11813 symbol_reloc.apply(elf);
11583 }11814 }
11584 },11815 },
11585 .const_debug_info => |cpi| {11816 .const_debug_info => |cpi| {
11586 const dwarf_const = &elf.dwarf_consts.get(cpi).?;11817 const dwarf_const = &elf.dwarf_consts.get(cpi).?;
11587 const target_section_offset = elf.computeNodeSectionOffset(ni);
11588 var target_ri = dwarf_const.debug_info_first_target_reloc;11818 var target_ri = dwarf_const.debug_info_first_target_reloc;
11589 while (target_ri != .none) {11819 while (target_ri != .none) {
11590 const target_reloc = target_ri.get(elf);11820 const target_reloc = target_ri.get(elf);
11591 assert(target_reloc.target == ni);11821 assert(target_reloc.target == ni);
11592 target_reloc.flushMovedTarget(elf, target_section_offset);11822 target_reloc.apply(elf);
11593 target_ri = target_reloc.next;11823 target_ri = target_reloc.next;
11594 }11824 }
11595 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{11825 elf.flushMovedNodeRelocs(ni, .{
11596 .first_symbol_reloc = dwarf_const.debug_info_first_symbol_reloc,11826 .first_symbol_reloc = dwarf_const.debug_info_first_symbol_reloc,
11597 .first_node_reloc = dwarf_const.debug_info_first_node_reloc,11827 .first_node_reloc = dwarf_const.debug_info_first_node_reloc,
11598 });11828 });
11599 },11829 },
11600 .global_debug_info => |gi| {11830 .global_debug_info => |gi| {
11601 const dwarf_global = &elf.dwarf_globals.items[@backingInt(gi)];11831 const dwarf_global = &elf.dwarf_globals.items[@backingInt(gi)];
11602 const target_section_offset = elf.computeNodeSectionOffset(ni);
11603 var target_ri = dwarf_global.debug_info_first_target_reloc;11832 var target_ri = dwarf_global.debug_info_first_target_reloc;
11604 while (target_ri != .none) {11833 while (target_ri != .none) {
11605 const target_reloc = target_ri.get(elf);11834 const target_reloc = target_ri.get(elf);
11606 assert(target_reloc.target == ni);11835 assert(target_reloc.target == ni);
11607 target_reloc.flushMovedTarget(elf, target_section_offset);11836 target_reloc.apply(elf);
11608 target_ri = target_reloc.next;11837 target_ri = target_reloc.next;
11609 }11838 }
11610 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{11839 elf.flushMovedNodeRelocs(ni, .{
11611 .first_symbol_reloc = dwarf_global.debug_info_first_symbol_reloc,11840 .first_symbol_reloc = dwarf_global.debug_info_first_symbol_reloc,
11612 .first_node_reloc = dwarf_global.debug_info_first_node_reloc,11841 .first_node_reloc = dwarf_global.debug_info_first_node_reloc,
11613 });11842 });
...@@ -11623,22 +11852,21 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11623,22 +11852,21 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11623 elf.dwarf.updateEhFrameFde(ni.slice(&elf.mf), offset);11852 elf.dwarf.updateEhFrameFde(ni.slice(&elf.mf), offset);
11624 },11853 },
11625 }11854 }
11626 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{11855 elf.flushMovedNodeRelocs(ni, .{
11627 .first_symbol_reloc = dwarf_func.frame_fde_first_symbol_reloc,11856 .first_symbol_reloc = dwarf_func.frame_fde_first_symbol_reloc,
11628 .first_node_reloc = dwarf_func.frame_fde_first_node_reloc,11857 .first_node_reloc = dwarf_func.frame_fde_first_node_reloc,
11629 });11858 });
11630 },11859 },
11631 .func_debug_info => |fi| {11860 .func_debug_info => |fi| {
11632 const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)];11861 const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)];
11633 const target_section_offset = elf.computeNodeSectionOffset(ni);
11634 var target_ri = dwarf_func.debug_info_first_target_reloc;11862 var target_ri = dwarf_func.debug_info_first_target_reloc;
11635 while (target_ri != .none) {11863 while (target_ri != .none) {
11636 const target_reloc = target_ri.get(elf);11864 const target_reloc = target_ri.get(elf);
11637 assert(target_reloc.target == ni);11865 assert(target_reloc.target == ni);
11638 target_reloc.flushMovedTarget(elf, target_section_offset);11866 target_reloc.apply(elf);
11639 target_ri = target_reloc.next;11867 target_ri = target_reloc.next;
11640 }11868 }
11641 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{11869 elf.flushMovedNodeRelocs(ni, .{
11642 .first_symbol_reloc = dwarf_func.debug_info_first_symbol_reloc,11870 .first_symbol_reloc = dwarf_func.debug_info_first_symbol_reloc,
11643 .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav|11871 .skip_symbol_relocs = if (elf.navs.getPtr(fi.nav(&elf.dwarf))) |nav|
11644 nav.lsi.index().ptr(elf).node11872 nav.lsi.index().ptr(elf).node
...@@ -11650,7 +11878,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void...@@ -11650,7 +11878,7 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11650 },11878 },
11651 .func_debug_line => |fi| {11879 .func_debug_line => |fi| {
11652 const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)];11880 const dwarf_func = &elf.dwarf_funcs.items[@backingInt(fi)];
11653 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{11881 elf.flushMovedNodeRelocs(ni, .{
11654 .first_symbol_reloc = dwarf_func.debug_line_first_symbol_reloc,11882 .first_symbol_reloc = dwarf_func.debug_line_first_symbol_reloc,
11655 .first_node_reloc = dwarf_func.debug_line_first_node_reloc,11883 .first_node_reloc = dwarf_func.debug_line_first_node_reloc,
11656 .skip_node_relocs = fi.get(&elf.dwarf).debug_info_ni,11884 .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...@@ -11658,15 +11886,14 @@ fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void
11658 },11886 },
11659 .decl_debug_info => |di| {11887 .decl_debug_info => |di| {
11660 const dwarf_decl = &elf.dwarf_decls.get(di).?;11888 const dwarf_decl = &elf.dwarf_decls.get(di).?;
11661 const target_section_offset = elf.computeNodeSectionOffset(ni);
11662 var target_ri = dwarf_decl.debug_info_first_target_reloc;11889 var target_ri = dwarf_decl.debug_info_first_target_reloc;
11663 while (target_ri != .none) {11890 while (target_ri != .none) {
11664 const target_reloc = target_ri.get(elf);11891 const target_reloc = target_ri.get(elf);
11665 assert(target_reloc.target == ni);11892 assert(target_reloc.target == ni);
11666 target_reloc.flushMovedTarget(elf, target_section_offset);11893 target_reloc.apply(elf);
11667 target_ri = target_reloc.next;11894 target_ri = target_reloc.next;
11668 }11895 }
11669 elf.flushMovedNodeRelocs(ni, elf.computeNodeVAddr(ni), .{11896 elf.flushMovedNodeRelocs(ni, .{
11670 .first_node_reloc = dwarf_decl.debug_info_first_node_reloc,11897 .first_node_reloc = dwarf_decl.debug_info_first_node_reloc,
11671 });11898 });
11672 },11899 },
...@@ -11737,166 +11964,156 @@ fn flushMovedNobitsSymbols(...@@ -11737,166 +11964,156 @@ fn flushMovedNobitsSymbols(
11737 }11964 }
11738}11965}
1173911966
11740/// Updates `vaddr` and `paddr` of the segment header, and marks everything in the segment as moved.11967/// If any loadable segments have invalid virtual addresses, assigns them new valid addresses and
11741fn updateSegmentLoadAddress(11968/// queues the necessary idle tasks (e.g. re-applying relocations targeting those segments).
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.
11763///11969///
11764/// TODO: this function is currently a source of non-determinism in the linker, because handling the11970/// This function must be called at the end of every link task in case tghe task invalidated segment
11765/// moving or resizing of a segment could reorder them and thereby affect how we handle *future*11971/// addresses (which can happen in response to any node movement/resize, or any section resize). It
11766/// changes to segments.11972/// cannot be called from `idle` as this function does not have the required idempotency guarantee
11767fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Error!void {11973/// so calling it non-deterministically it would lead to unreproducible output files.
11768 const segment_ni = elf.phdrs.items[orig_phndx].unwrap().?;11974fn updateSegmentLoadAddresses(elf: *Elf) std.mem.Allocator.Error!void {
11769 assert(elf.getNode(segment_ni).segment == orig_phndx);11975 if (elf.ehdrType() == .REL) return;
11976
11770 const page_align = elf.targetPageAlign();11977 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
11785 switch (elf.phdrSlice()) {11979 switch (elf.phdrSlice()) {
11786 inline else => |phdr| {11980 inline else => |phdr| {
11787 const offset = elf.targetLoad(&phdr[orig_phndx].offset);11981 // Somewhere in the phdrs is a contiguous sequence of all "loadable" segments (segments
11788 const filesz = elf.targetLoad(&phdr[orig_phndx].filesz);11982 // with a `type` of `PT_NULL` or `PT_LOAD`); find the start of that sequence. In theory
1178911983 // we could memoize this, but there's no real need since the number of phdrs is tiny.
11790 const orig_vaddr = elf.targetLoad(&phdr[orig_phndx].vaddr);11984 const load_phdrs_start = for (phdr, 0..) |*ph, phndx| {
11791 assert(elf.targetLoad(&phdr[orig_phndx].paddr) == orig_vaddr);11985 switch (elf.targetLoad(&ph.type)) {
1179211986 .NULL, .LOAD => break phndx,
11793 const bss_size = if (segment_ni == elf.ni.data)11987 else => continue,
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);
11819 }11988 }
11820 }11989 } else unreachable; // we always create loadable (`PT_NULL`/`PT_LOAD`) segments
1182111990
11822 // If our size has changed, or if the address shift above caused our "end" address to11991 // Update these fields for each loadable segment:
11823 // cross a page boundary, then we might be overlapping with the next segment's pages. In11992 //
11824 // that case, we will jump past that segment and give ourselves a new address after it.11993 // * `memsz`
11825 // We'll need to repeat this for every loadable phdr after us, until we're no longer11994 // * `type` (`PT_NULL` vs `PT_LOAD`)
11826 // overlapping anything.11995 // * `align`
11827 var phndx = orig_phndx;11996 // * `offset` (in theory this should happen in `updateElfOffset`, but this logic needs
11828 for (phdr[orig_phndx + 1 ..], orig_phndx + 1..) |*next_ph, next_phndx| {11997 // to track that value, so it's easier to just populate it here instead)
11829 switch (elf.targetLoad(&next_ph.type)) {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)) {
11830 .NULL, .LOAD => {},12009 .NULL, .LOAD => {},
11831 else => {12010 else => break phndx, // reached end of loadable segments
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 },
11837 }12011 }
1183812012 const segment_ni = segment_oni.unwrap().?; // all loadable segments have nodes
11839 const next_vaddr = elf.targetLoad(&next_ph.vaddr);12013
11840 // Find the first virtual address which the next phdr "owns" by aligning its vaddr12014 // We need to know the segment's file offset here anyway, so we may as well just
11841 // backwards to the start of the page.12015 // populate the phdr `offset` field now instead of in `flushElfOffset`.
11842 const next_page_vaddr = std.mem.alignBackward(u64, next_vaddr, page_align.toByteUnits());12016 const ph_offset = elf.computeNodeElfOffset(segment_ni);
1184312017 elf.targetStore(&ph.offset, @intCast(ph_offset));
11844 // Check if the segment fits here. We apply `reserve_size_multiplier`, but only if12018
11845 // the segment is already known to be moving---making it easier to grow in-place is12019 // The alignment of the segment affects which virtual addresses are valid.
11846 // the whole point of the multiplier!12020 const ph_align = segment_ni.alignment(&elf.mf).max(page_align);
11847 {12021 elf.targetStore(&ph.@"align", @intCast(ph_align.toByteUnits()));
11848 const target_size = if (vaddr == orig_vaddr) memsz else memsz * reserve_size_multiplier;12022
11849 if (vaddr + target_size <= next_page_vaddr) {12023 // The size of the segment in memory is equal to the size of the segment in the file
11850 break; // hooray, we fit here!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;
11851 }12031 }
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;
11852 }12044 }
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 to12050 // There should be no more loadable segments after the contiguous range.
11857 // consider any target-specific restrictions we are subject to.12051 for (phdr[load_phdrs_end..]) |*ph| switch (elf.targetLoad(&ph.type)) {
11858 switch (elf.targetSegmentLoadAddressRestrictions()) {12052 .NULL, .LOAD => unreachable, // non-contiguous loadable segment
11859 .none => {},12053 else => {},
11860 .data_last => if (next_ni == elf.ni.data) {12054 };
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 };
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));12061 // It's time to actually allocate virtual addresses for all segments. Our current
11873 const next_offset = elf.targetLoad(&next_ph.offset);12062 // strategy for this preserves the order of segments in memory, but this isn't a hard
11874 const next_new_vaddr = next_align.forward(free_vaddr) + next_offset % next_align.toByteUnits();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;12073 // If we determine that a segment's virtual address needs to move, then it's a good idea
11879 },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;
11880 }12103 }
1188112104
11882 // We don't fit here, so shift ourselves forward (i.e. swap with `next_phndx`). But12105 // Otherwise, put ourselves at a new address just after the previous segment.
11883 // first we need to adjust `vaddr` to come after it.12106 const new_vaddr = ph_align.forward(preferred_vaddr) + ph_offset % ph_align.toByteUnits();
11884 const next_size = elf.targetLoad(&next_ph.memsz);12107 assert(new_vaddr != ph_vaddr); // `ph_vaddr` was not valid
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();
1188812108
11889 // Now just swap the phdrs and update our `phndx`.12109 elf.targetStore(&phdr[phndx].vaddr, @intCast(new_vaddr));
11890 std.mem.swap(@TypeOf(next_ph.*), &phdr[phndx], next_ph);12110 elf.targetStore(&phdr[phndx].paddr, @intCast(new_vaddr));
11891 elf.phdrs.items[phndx] = .wrap(next_ni);12111 const segment_ni = elf.phdrs.items[phndx].unwrap().?;
11892 elf.nodes.items(.data)[@backingInt(next_ni)] = .{ .segment = phndx };12112 try segment_ni.childrenMoved(gpa, &elf.mf);
11893 elf.phdrs.items[next_phndx] = .wrap(segment_ni);12113 if (segment_ni == elf.ni.data) elf.bss_moved = true;
11894 elf.nodes.items(.data)[@backingInt(segment_ni)] = .{ .segment = @intCast(next_phndx) };
11895 phndx = @intCast(next_phndx);
11896 }
1189712114
11898 if (vaddr != orig_vaddr) {12115 first_free_vaddr = page_align.forward(new_vaddr + ph_memsz);
11899 try elf.updateSegmentLoadAddress(segment_ni, vaddr);12116 preferred_vaddr = page_align.forward(new_vaddr + ph_memsz * reserve_size_multiplier);
11900 }12117 }
11901 },12118 },
11902 }12119 }
...@@ -11905,6 +12122,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro...@@ -11905,6 +12122,7 @@ fn allocateSegmentLoadAddress(elf: *Elf, orig_phndx: u32) std.mem.Allocator.Erro
11905fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {12122fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
11906 const trace = tracy.trace(@src());12123 const trace = tracy.trace(@src());
11907 defer trace.end();12124 defer trace.end();
12125 trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)});
1190812126
11909 _, const size = ni.location(&elf.mf).resolve(&elf.mf);12127 _, const size = ni.location(&elf.mf).resolve(&elf.mf);
11910 switch (elf.getNode(ni)) {12128 switch (elf.getNode(ni)) {
...@@ -11933,7 +12151,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo...@@ -11933,7 +12151,6 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
11933 else => unreachable,12151 else => unreachable,
11934 .NULL, .LOAD => {12152 .NULL, .LOAD => {
11935 elf.targetStore(&ph.filesz, @intCast(size));12153 elf.targetStore(&ph.filesz, @intCast(size));
11936 try elf.allocateSegmentLoadAddress(phndx);
11937 if (ni == elf.ni.data) elf.bss_moved = true;12154 if (ni == elf.ni.data) elf.bss_moved = true;
11938 },12155 },
11939 .DYNAMIC, .INTERP, .PHDR, .GNU_EH_FRAME, .GNU_RELRO => {12156 .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...@@ -11946,7 +12163,11 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
1194612163
11947 elf.targetStore(&ph.filesz, @intCast(size));12164 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.
11950 const tls_align: Alignment = .fromByteUnits(elf.targetLoad(&ph.@"align"));12171 const tls_align: Alignment = .fromByteUnits(elf.targetLoad(&ph.@"align"));
11951 elf.targetStore(&ph.memsz, @intCast(12172 elf.targetStore(&ph.memsz, @intCast(
11952 tls_align.forward(size + elf.shndx.tbss.size(elf)),12173 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...@@ -12040,6 +12261,7 @@ fn flushResized(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!vo
12040fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {12261fn flushPadding(elf: *Elf, ni: MappedFile.Node.Index) std.mem.Allocator.Error!void {
12041 const trace = tracy.trace(@src());12262 const trace = tracy.trace(@src());
12042 defer trace.end();12263 defer trace.end();
12264 trace.addTextFmt("{f}", .{Node.fmtDebug(ni, elf)});
1204312265
12044 const node = elf.getNode(ni);12266 const node = elf.getNode(ni);
12045 switch (node) {12267 switch (node) {
...@@ -12480,7 +12702,6 @@ fn ensurePltEntry(elf: *Elf, gsi: Symbol.Global.Index) Error!bool {...@@ -12480,7 +12702,6 @@ fn ensurePltEntry(elf: *Elf, gsi: Symbol.Global.Index) Error!bool {
1248012702
12481 // relocate the PLT entry to point to the .GOT.PLT entry12703 // relocate the PLT entry to point to the .GOT.PLT entry
12482 const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset;12704 const got_plt_abs = got_plt_section.vaddr(elf) + got_plt_offset;
12483 // TODO: handle overflow gracefully
12484 const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]);12705 const inst0: *align(1) link.loongarch.J20 = @ptrCast(plt_slice[0..4]);
12485 const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]);12706 const inst1: *align(1) link.loongarch.K12 = @ptrCast(plt_slice[4..8]);
12486 elf.targetStore(inst0, .{12707 elf.targetStore(inst0, .{
...@@ -12646,7 +12867,6 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad...@@ -12646,7 +12867,6 @@ fn flushMovedPltSection(elf: *Elf, which: enum { plt, plt_sec, got_plt }, old_ad
12646 const target_slice = plt_slice[plt_offset..];12867 const target_slice = plt_slice[plt_offset..];
1264712868
12648 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;12869 const got_plt_abs: u64 = got_plt_addr + got_plt_offset;
12649 // TODO: handle overflow gracefully
12650 const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]);12870 const inst0: *align(1) link.loongarch.J20 = @ptrCast(target_slice[0..4]);
12651 const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]);12871 const inst1: *align(1) link.loongarch.K12 = @ptrCast(target_slice[4..8]);
1265212872
...@@ -12698,6 +12918,7 @@ pub fn updateExports(...@@ -12698,6 +12918,7 @@ pub fn updateExports(
12698 };12918 };
12699 }12919 }
12700 try elf.genPending(pt);12920 try elf.genPending(pt);
12921 try elf.updateSegmentLoadAddresses();
12701}12922}
12702fn updateExportInner(elf: *Elf, export_index: Zcu.Export.Index) Error!void {12923fn updateExportInner(elf: *Elf, export_index: Zcu.Export.Index) Error!void {
12703 const zcu = elf.base.comp.zcu.?;12924 const zcu = elf.base.comp.zcu.?;
...@@ -12801,111 +13022,8 @@ pub fn printNode(...@@ -12801,111 +13022,8 @@ pub fn printNode(
12801 ni: MappedFile.Node.Index,13022 ni: MappedFile.Node.Index,
12802 indent: usize,13023 indent: usize,
12803) Io.Writer.Error!void {13024) Io.Writer.Error!void {
12804 const node = elf.getNode(ni);
12805 try w.splatByteAll(' ', indent);13025 try w.splatByteAll(' ', indent);
12806 try w.writeAll(@tagName(node));13026 try w.print("{f}", .{Node.fmtDebug(ni, elf)});
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 }
12909 {13027 {
12910 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];13028 const mf_node = &elf.mf.nodes.items[@backingInt(ni)];
12911 const off, const size = mf_node.location().resolve(&elf.mf);13029 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...@@ -12975,15 +13093,8 @@ fn ensureSegmentAligned(elf: *Elf, start_phndx: u32, min_align: Alignment) Error
12975 switch (elf.phdrSlice()) {13093 switch (elf.phdrSlice()) {
12976 inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) {13094 inline else => |phdr| switch (elf.targetLoad(&phdr[phndx].type)) {
12977 .NULL, .LOAD => {13095 .NULL, .LOAD => {
12978 // The `@"align"` field is managed by `allocateSegmentLoadAddress`.13096 // The `@"align"` field is managed by `updateSegmentLoadAddresses`, which will
12979 //13097 // be called at the end of the current link task.
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 }
12987 },13098 },
12988 else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max(13099 else => elf.targetStore(&phdr[phndx].@"align", @intCast(@max(
12989 elf.targetLoad(&phdr[phndx].@"align"),13100 elf.targetLoad(&phdr[phndx].@"align"),
...@@ -13008,11 +13119,9 @@ pub fn addNodeAssumeCapacity(elf: *Elf, ni: MappedFile.Node.Index, node: Node) M...@@ -13008,11 +13119,9 @@ pub fn addNodeAssumeCapacity(elf: *Elf, ni: MappedFile.Node.Index, node: Node) M
13008 return ni;13119 return ni;
13009}13120}
1301013121
13011fn deleteNode(elf: *Elf, node: *MappedFile.Node.Index.Optional) std.mem.Allocator.Error!void {13122fn deleteNode(elf: *Elf, node: MappedFile.Node.Index) std.mem.Allocator.Error!void {
13012 const ni = node.unwrap().?;13123 try node.delete(elf.base.comp.gpa, &elf.mf);
13013 try ni.delete(elf.base.comp.gpa, &elf.mf);13124 elf.nodes.set(@backingInt(node), .deleted);
13014 elf.nodes.set(@backingInt(ni), .deleted);
13015 node.* = .none;
13016}13125}
1301713126
13018/// If `sym` has a PLT entry, returns the address of that entry (specifically, the address which a13127/// 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 {...@@ -3342,6 +3342,7 @@ pub fn idle(macho: *MachO) link.Error!bool {
3342 }3342 }
33433343
3344 while (macho.mf.updates.pop()) |ni| {3344 while (macho.mf.updates.pop()) |ni| {
3345 ni.cleanUpdated(&macho.mf);
3345 const clean_moved = ni.cleanMoved(&macho.mf);3346 const clean_moved = ni.cleanMoved(&macho.mf);
3346 const clean_resized = ni.cleanResized(&macho.mf);3347 const clean_resized = ni.cleanResized(&macho.mf);
3347 const clean_next_moved = ni.cleanNextMoved(&macho.mf);3348 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...@@ -207,6 +207,7 @@ pub fn init(file: Io.File, gpa: Allocator, io: Io) (Allocator.Error || Io.Cancel
207 .resized = false,207 .resized = false,
208 .next_moved = false,208 .next_moved = false,
209 .has_content = false,209 .has_content = false,
210 .updated = false,
210 },211 },
211 .location_payload = switch (root_location) {212 .location_payload = switch (root_location) {
212 .small => |small| .{ .small = small },213 .small => |small| .{ .small = small },
...@@ -287,7 +288,14 @@ pub const Node = extern struct {...@@ -287,7 +288,14 @@ pub const Node = extern struct {
287 next_moved: bool,288 next_moved: bool,
288 /// Whether this node might contain initialized bytes.289 /// Whether this node might contain initialized bytes.
289 has_content: bool,290 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,
291 };299 };
292300
293 pub const Location = union(enum(u1)) {301 pub const Location = union(enum(u1)) {
...@@ -494,6 +502,11 @@ pub const Node = extern struct {...@@ -494,6 +502,11 @@ pub const Node = extern struct {
494 }502 }
495 }503 }
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
497 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {510 pub fn hasMoved(ni: Node.Index, mf: *const MappedFile) bool {
498 var cur_ni = ni;511 var cur_ni = ni;
499 while (cur_ni != .root) {512 while (cur_ni != .root) {
...@@ -518,9 +531,11 @@ pub const Node = extern struct {...@@ -518,9 +531,11 @@ pub const Node = extern struct {
518 if (node.prev.unwrap()) |prev_ni| prev_ni.nextMovedAssumeCapacity(mf);531 if (node.prev.unwrap()) |prev_ni| prev_ni.nextMovedAssumeCapacity(mf);
519 if (ni.hasMoved(mf)) return;532 if (ni.hasMoved(mf)) return;
520 node.flags.moved = true;533 node.flags.moved = true;
521 if (node.flags.resized or node.flags.next_moved) return;534 if (!node.flags.updated) {
522 mf.updates.appendAssumeCapacity(ni);535 node.flags.updated = true;
523 mf.update_prog_node.increaseEstimatedTotalItems(1);536 mf.updates.appendAssumeCapacity(ni);
537 mf.update_prog_node.increaseEstimatedTotalItems(1);
538 }
524 }539 }
525540
526 pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {541 pub fn hasResized(ni: Node.Index, mf: *const MappedFile) bool {
...@@ -539,9 +554,11 @@ pub const Node = extern struct {...@@ -539,9 +554,11 @@ pub const Node = extern struct {
539 const node = ni.get(mf);554 const node = ni.get(mf);
540 if (node.flags.resized) return;555 if (node.flags.resized) return;
541 node.flags.resized = true;556 node.flags.resized = true;
542 if (node.flags.moved or node.flags.next_moved) return;557 if (!node.flags.updated) {
543 mf.updates.appendAssumeCapacity(ni);558 node.flags.updated = true;
544 mf.update_prog_node.increaseEstimatedTotalItems(1);559 mf.updates.appendAssumeCapacity(ni);
560 mf.update_prog_node.increaseEstimatedTotalItems(1);
561 }
545 }562 }
546563
547 pub fn hasNextMoved(ni: Node.Index, mf: *const MappedFile) bool {564 pub fn hasNextMoved(ni: Node.Index, mf: *const MappedFile) bool {
...@@ -560,9 +577,11 @@ pub const Node = extern struct {...@@ -560,9 +577,11 @@ pub const Node = extern struct {
560 const node = ni.get(mf);577 const node = ni.get(mf);
561 if (!node.flags.enable_next_moved or node.flags.next_moved) return;578 if (!node.flags.enable_next_moved or node.flags.next_moved) return;
562 node.flags.next_moved = true;579 node.flags.next_moved = true;
563 if (node.flags.moved or node.flags.resized) return;580 if (!node.flags.updated) {
564 mf.updates.appendAssumeCapacity(ni);581 node.flags.updated = true;
565 mf.update_prog_node.increaseEstimatedTotalItems(1);582 mf.updates.appendAssumeCapacity(ni);
583 mf.update_prog_node.increaseEstimatedTotalItems(1);
584 }
566 }585 }
567586
568 pub fn alignment(ni: Node.Index, mf: *const MappedFile) Alignment {587 pub fn alignment(ni: Node.Index, mf: *const MappedFile) Alignment {
...@@ -647,19 +666,30 @@ pub const Node = extern struct {...@@ -647,19 +666,30 @@ pub const Node = extern struct {
647 pub fn delete(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {666 pub fn delete(ni: Node.Index, gpa: Allocator, mf: *MappedFile) Allocator.Error!void {
648 const node = ni.get(mf);667 const node = ni.get(mf);
649 assert(node.first == .none and node.last == .none); // has children668 assert(node.first == .none and node.last == .none); // has children
650 mf.removeNodesFromChildList(gpa, ni, ni);669 try mf.removeNodesFromChildList(gpa, ni, ni);
651 const updated = node.flags.moved or node.flags.resized or node.flags.next_moved;670 node.* = .{
652 node.* = undefined;671 .parent = undefined,
653 node.next = ni.toOptional();672 .prev = undefined,
654 if (!updated) assert(ni.pendingDelete(mf));673 .next = mf.free_ni,
655 }674 .first = undefined,
656675 .last = undefined,
657 pub fn pendingDelete(ni: Node.Index, mf: *MappedFile) bool {676 .flags = .{
658 const node = ni.get(mf);677 .position = .floating,
659 if (node.next != ni.toOptional()) return false;678 .alignment = .@"1",
660 node.next = mf.free_ni;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 };
661 mf.free_ni = ni.toOptional();692 mf.free_ni = ni.toOptional();
662 return true;
663 }693 }
664694
665 /// Ensures that the size of `ni` is at least `min_size`. Valid for any node.695 /// 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 {...@@ -946,6 +976,7 @@ fn addNode(mf: *MappedFile, gpa: Allocator, opts: struct {
946 .resized = false,976 .resized = false,
947 .next_moved = false,977 .next_moved = false,
948 .has_content = false,978 .has_content = false,
979 .updated = false,
949 },980 },
950 .location_payload = switch (location) {981 .location_payload = switch (location) {
951 .small => |small| .{ .small = small },982 .small => |small| .{ .small = small },
src/link/Queue.zig+1
...@@ -196,6 +196,7 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) Io.Cancelable!void {...@@ -196,6 +196,7 @@ fn runLinkTasks(q: *Queue, comp: *Compilation) Io.Cancelable!void {
196 };196 };
197 }197 }
198198
199 have_idle_tasks = true;
199 zcu_tasks: while (true) {200 zcu_tasks: while (true) {
200 var task_buf: [128]ZcuTask = undefined;201 var task_buf: [128]ZcuTask = undefined;
201 const limit: usize = if (have_idle_tasks) 0 else 1;202 const limit: usize = if (have_idle_tasks) 0 else 1;
test/debugger/storage-4
...@@ -67,12 +67,8 @@ Process #... stopped...@@ -67,12 +67,8 @@ Process #... stopped
67(u64) global_threadlocal1 = 0xb4d643528c04212167(u64) global_threadlocal1 = 0xb4d643528c042121
68(u64) global_threadlocal2 = 0x43faea1cf5ad7a2268(u64) global_threadlocal2 = 0x43faea1cf5ad7a22
69(u64) extern_const = 0x1b0c91ea0470b0b269(u64) extern_const = 0x1b0c91ea0470b0b2
70(u64) extern_const = 0x1b0c91ea0470b0b2
71(u64) extern_var = 0x19bc17b3f0b61ebc
72(u64) extern_var = 0x19bc17b3f0b61ebc70(u64) extern_var = 0x19bc17b3f0b61ebc
73(u64) extern_threadlocal1 = 0x3034c4ce967ed64d71(u64) extern_threadlocal1 = 0x3034c4ce967ed64d
74(u64) extern_threadlocal1 = 0x3034c4ce967ed64d
75(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb
76(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb72(u64) extern_threadlocal2 = 0xfd330ab00b4bc5eb
77(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_var73(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
78(u64) param1 = 0x6a607e08125c7e0074(u64) param1 = 0x6a607e08125c7e00