authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-05 23:39:21+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2023-10-16 19:33:04+02:00
logf4c1b1d9ad7c90266b1da1892e9bcd1d6143f7ea
tree4b421b33a3827817a8bfed242d8f3749e57aa7a5
parent4af5caa81f2d1395857d0f64c5a454b3e9240969

elf: implement --verbose-link, and other fixes


1 files changed, 323 insertions(+), 19 deletions(-)

src/link/Elf.zig+323-19
......@@ -321,7 +321,8 @@ pub fn createEmpty(gpa: Allocator, options: link.Options) !*Elf {
321321 .sparc64 => 0x2000,
322322 else => 0x1000,
323323 };
324 const default_sym_version: elf.Elf64_Versym = if (options.output_mode == .Lib and options.link_mode == .Dynamic)
324 const is_dyn_lib = options.output_mode == .Lib and options.link_mode == .Dynamic;
325 const default_sym_version: elf.Elf64_Versym = if (is_dyn_lib or options.rdynamic)
325326 elf.VER_NDX_GLOBAL
326327 else
327328 elf.VER_NDX_LOCAL;
......@@ -1155,6 +1156,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11551156 break :blk path;
11561157 }
11571158 } else null;
1159 const gc_sections = self.base.options.gc_sections orelse false;
11581160
11591161 if (self.base.options.output_mode == .Obj and self.zig_module_index == null) {
11601162 // TODO this will become -r route I guess. For now, just copy the object file.
......@@ -1181,11 +1183,261 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11811183 return;
11821184 }
11831185
1186 var csu = try CsuObjects.init(arena, self.base.options, comp);
1187 const compiler_rt_path: ?[]const u8 = blk: {
1188 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
1189 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
1190 break :blk null;
1191 };
1192
1193 // --verbose-link
1194 if (self.base.options.verbose_link) {
1195 var argv = std.ArrayList([]const u8).init(arena);
1196
1197 try argv.append("zig");
1198 try argv.append("ld");
1199
1200 try argv.append("-o");
1201 try argv.append(full_out_path);
1202
1203 if (self.base.options.entry) |entry| {
1204 try argv.append("--entry");
1205 try argv.append(entry);
1206 }
1207
1208 if (self.base.options.dynamic_linker) |path| {
1209 try argv.append("-dynamic-linker");
1210 try argv.append(path);
1211 }
1212
1213 if (self.base.options.soname) |name| {
1214 try argv.append("-soname");
1215 try argv.append(name);
1216 }
1217
1218 for (self.base.options.rpath_list) |rpath| {
1219 try argv.append("-rpath");
1220 try argv.append(rpath);
1221 }
1222
1223 if (self.base.options.each_lib_rpath) {
1224 for (self.base.options.lib_dirs) |lib_dir_path| {
1225 try argv.append("-rpath");
1226 try argv.append(lib_dir_path);
1227 }
1228 for (self.base.options.objects) |obj| {
1229 if (Compilation.classifyFileExt(obj.path) == .shared_library) {
1230 const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue;
1231 if (obj.loption) continue;
1232
1233 try argv.append("-rpath");
1234 try argv.append(lib_dir_path);
1235 }
1236 }
1237 }
1238
1239 if (self.base.options.stack_size_override) |ss| {
1240 try argv.append("-z");
1241 try argv.append(try std.fmt.allocPrint(arena, "stack-size={d}", .{ss}));
1242 }
1243
1244 if (self.base.options.image_base_override) |image_base| {
1245 try argv.append(try std.fmt.allocPrint(arena, "--image-base={d}", .{image_base}));
1246 }
1247
1248 if (gc_sections) {
1249 try argv.append("--gc-sections");
1250 }
1251
1252 if (self.base.options.print_gc_sections) {
1253 try argv.append("--print-gc-sections");
1254 }
1255
1256 if (self.base.options.eh_frame_hdr) {
1257 try argv.append("--eh-frame-hdr");
1258 }
1259
1260 if (self.base.options.rdynamic) {
1261 try argv.append("--export-dynamic");
1262 }
1263
1264 if (self.base.options.strip) {
1265 try argv.append("-s");
1266 }
1267
1268 if (self.base.options.z_notext) {
1269 try argv.append("-z");
1270 try argv.append("notext");
1271 }
1272
1273 if (self.base.options.z_nocopyreloc) {
1274 try argv.append("-z");
1275 try argv.append("nocopyreloc");
1276 }
1277
1278 if (self.base.options.z_now) {
1279 try argv.append("-z");
1280 try argv.append("now");
1281 }
1282
1283 if (self.isStatic()) {
1284 try argv.append("-static");
1285 } else if (self.isDynLib()) {
1286 try argv.append("-shared");
1287 }
1288
1289 if (self.base.options.pie and self.isExe()) {
1290 try argv.append("-pie");
1291 }
1292
1293 // csu prelude
1294 if (csu.crt0) |v| try argv.append(v);
1295 if (csu.crti) |v| try argv.append(v);
1296 if (csu.crtbegin) |v| try argv.append(v);
1297
1298 for (self.base.options.lib_dirs) |lib_dir| {
1299 try argv.append("-L");
1300 try argv.append(lib_dir);
1301 }
1302
1303 if (self.base.options.link_libc) {
1304 if (self.base.options.libc_installation) |libc_installation| {
1305 try argv.append("-L");
1306 try argv.append(libc_installation.crt_dir.?);
1307 }
1308 }
1309
1310 var whole_archive = false;
1311 for (self.base.options.objects) |obj| {
1312 if (obj.must_link and !whole_archive) {
1313 try argv.append("-whole-archive");
1314 whole_archive = true;
1315 } else if (!obj.must_link and whole_archive) {
1316 try argv.append("-no-whole-archive");
1317 whole_archive = false;
1318 }
1319
1320 if (obj.loption) {
1321 assert(obj.path[0] == ':');
1322 try argv.append("-l");
1323 }
1324 try argv.append(obj.path);
1325 }
1326 if (whole_archive) {
1327 try argv.append("-no-whole-archive");
1328 whole_archive = false;
1329 }
1330
1331 for (comp.c_object_table.keys()) |key| {
1332 try argv.append(key.status.success.object_path);
1333 }
1334
1335 if (module_obj_path) |p| {
1336 try argv.append(p);
1337 }
1338
1339 // TSAN
1340 if (self.base.options.tsan) {
1341 try argv.append(comp.tsan_static_lib.?.full_object_path);
1342 }
1343
1344 // libc
1345 if (!self.base.options.skip_linker_dependencies and
1346 !self.base.options.link_libc)
1347 {
1348 if (comp.libc_static_lib) |lib| {
1349 try argv.append(lib.full_object_path);
1350 }
1351 }
1352
1353 // stack-protector.
1354 // Related: https://github.com/ziglang/zig/issues/7265
1355 if (comp.libssp_static_lib) |ssp| {
1356 try argv.append(ssp.full_object_path);
1357 }
1358
1359 // Shared libraries.
1360 // Worst-case, we need an --as-needed argument for every lib, as well
1361 // as one before and one after.
1362 try argv.ensureUnusedCapacity(self.base.options.system_libs.keys().len * 2 + 2);
1363 argv.appendAssumeCapacity("--as-needed");
1364 var as_needed = true;
1365
1366 for (self.base.options.system_libs.values()) |lib_info| {
1367 const lib_as_needed = !lib_info.needed;
1368 switch ((@as(u2, @intFromBool(lib_as_needed)) << 1) | @intFromBool(as_needed)) {
1369 0b00, 0b11 => {},
1370 0b01 => {
1371 argv.appendAssumeCapacity("--no-as-needed");
1372 as_needed = false;
1373 },
1374 0b10 => {
1375 argv.appendAssumeCapacity("--as-needed");
1376 as_needed = true;
1377 },
1378 }
1379 argv.appendAssumeCapacity(lib_info.path.?);
1380 }
1381
1382 if (!as_needed) {
1383 argv.appendAssumeCapacity("--as-needed");
1384 as_needed = true;
1385 }
1386
1387 // libc++ dep
1388 if (self.base.options.link_libcpp) {
1389 try argv.append(comp.libcxxabi_static_lib.?.full_object_path);
1390 try argv.append(comp.libcxx_static_lib.?.full_object_path);
1391 }
1392
1393 // libunwind dep
1394 if (self.base.options.link_libunwind) {
1395 try argv.append(comp.libunwind_static_lib.?.full_object_path);
1396 }
1397
1398 // libc dep
1399 if (self.base.options.link_libc) {
1400 if (self.base.options.libc_installation != null) {
1401 const needs_grouping = self.base.options.link_mode == .Static;
1402 if (needs_grouping) try argv.append("--start-group");
1403 try argv.appendSlice(target_util.libcFullLinkFlags(target));
1404 if (needs_grouping) try argv.append("--end-group");
1405 } else if (target.isGnuLibC()) {
1406 for (glibc.libs) |lib| {
1407 const lib_path = try std.fmt.allocPrint(arena, "{s}{c}lib{s}.so.{d}", .{
1408 comp.glibc_so_files.?.dir_path, fs.path.sep, lib.name, lib.sover,
1409 });
1410 try argv.append(lib_path);
1411 }
1412 try argv.append(try comp.get_libc_crt_file(arena, "libc_nonshared.a"));
1413 } else if (target.isMusl()) {
1414 try argv.append(try comp.get_libc_crt_file(arena, switch (self.base.options.link_mode) {
1415 .Static => "libc.a",
1416 .Dynamic => "libc.so",
1417 }));
1418 }
1419 }
1420
1421 // compiler-rt
1422 if (compiler_rt_path) |p| {
1423 try argv.append(p);
1424 }
1425
1426 // crt postlude
1427 if (csu.crtend) |v| try argv.append(v);
1428 if (csu.crtn) |v| try argv.append(v);
1429
1430 Compilation.dump_argv(argv.items);
1431 }
1432
11841433 // Here we will parse input positional and library files (if referenced).
11851434 // This will roughly match in any linker backend we support.
11861435 var positionals = std.ArrayList(Compilation.LinkObject).init(arena);
11871436
1188 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1437 // csu prelude
1438 if (csu.crt0) |v| try positionals.append(.{ .path = v });
1439 if (csu.crti) |v| try positionals.append(.{ .path = v });
1440 if (csu.crtbegin) |v| try positionals.append(.{ .path = v });
11891441
11901442 try positionals.ensureUnusedCapacity(self.base.options.objects.len);
11911443 positionals.appendSliceAssumeCapacity(self.base.options.objects);
......@@ -1197,11 +1449,60 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11971449 try positionals.append(.{ .path = key.status.success.object_path });
11981450 }
11991451
1200 // csu prelude
1201 var csu = try CsuObjects.init(arena, self.base.options, comp);
1202 if (csu.crt0) |v| try positionals.append(.{ .path = v });
1203 if (csu.crti) |v| try positionals.append(.{ .path = v });
1204 if (csu.crtbegin) |v| try positionals.append(.{ .path = v });
1452 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1453
1454 // rpaths
1455 var rpath_table = std.StringArrayHashMap(void).init(self.base.allocator);
1456 defer rpath_table.deinit();
1457 for (self.base.options.rpath_list) |rpath| {
1458 _ = try rpath_table.put(rpath, {});
1459 }
1460
1461 if (self.base.options.each_lib_rpath) {
1462 var test_path = std.ArrayList(u8).init(self.base.allocator);
1463 defer test_path.deinit();
1464 for (self.base.options.lib_dirs) |lib_dir_path| {
1465 for (self.base.options.system_libs.keys()) |link_lib| {
1466 test_path.clearRetainingCapacity();
1467 const sep = fs.path.sep_str;
1468 try test_path.writer().print("{s}" ++ sep ++ "lib{s}.so", .{
1469 lib_dir_path, link_lib,
1470 });
1471 fs.cwd().access(test_path.items, .{}) catch |err| switch (err) {
1472 error.FileNotFound => continue,
1473 else => |e| return e,
1474 };
1475 _ = try rpath_table.put(lib_dir_path, {});
1476 }
1477 }
1478 for (self.base.options.objects) |obj| {
1479 if (Compilation.classifyFileExt(obj.path) == .shared_library) {
1480 const lib_dir_path = std.fs.path.dirname(obj.path) orelse continue;
1481 if (obj.loption) continue;
1482 _ = try rpath_table.put(lib_dir_path, {});
1483 }
1484 }
1485 }
1486
1487 // TSAN
1488 if (self.base.options.tsan) {
1489 try positionals.append(.{ .path = comp.tsan_static_lib.?.full_object_path });
1490 }
1491
1492 // libc
1493 if (!self.base.options.skip_linker_dependencies and
1494 !self.base.options.link_libc)
1495 {
1496 if (comp.libc_static_lib) |lib| {
1497 try positionals.append(.{ .path = lib.full_object_path });
1498 }
1499 }
1500
1501 // stack-protector.
1502 // Related: https://github.com/ziglang/zig/issues/7265
1503 if (comp.libssp_static_lib) |ssp| {
1504 try positionals.append(.{ .path = ssp.full_object_path });
1505 }
12051506
12061507 for (positionals.items) |obj| {
12071508 const in_file = try std.fs.cwd().openFile(obj.path, .{});
......@@ -1213,6 +1514,11 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12131514
12141515 var system_libs = std.ArrayList(SystemLib).init(arena);
12151516
1517 try system_libs.ensureUnusedCapacity(self.base.options.system_libs.values().len);
1518 for (self.base.options.system_libs.values()) |lib_info| {
1519 system_libs.appendAssumeCapacity(.{ .needed = lib_info.needed, .path = lib_info.path.? });
1520 }
1521
12161522 // libc++ dep
12171523 if (self.base.options.link_libcpp) {
12181524 try system_libs.ensureUnusedCapacity(2);
......@@ -1266,11 +1572,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12661572 // compiler-rt. Since compiler_rt exports symbols like `memset`, it needs
12671573 // to be after the shared libraries, so they are picked up from the shared
12681574 // libraries, not libcompiler_rt.
1269 const compiler_rt_path: ?[]const u8 = blk: {
1270 if (comp.compiler_rt_lib) |x| break :blk x.full_object_path;
1271 if (comp.compiler_rt_obj) |x| break :blk x.full_object_path;
1272 break :blk null;
1273 };
12741575 if (compiler_rt_path) |path| try positionals.append(.{ .path = path });
12751576
12761577 // csu postlude
......@@ -1363,13 +1664,12 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
13631664 self.entry_index = if (entry) |name| self.globalByName(name) else null;
13641665 }
13651666
1366 const gc_sections = self.base.options.gc_sections orelse false;
13671667 if (gc_sections) {
13681668 try gc.gcAtoms(self);
13691669
1370 // if (self.base.options.print_gc_sections) {
1371 // try gc.dumpPrunedAtoms(self);
1372 // }
1670 if (self.base.options.print_gc_sections) {
1671 try gc.dumpPrunedAtoms(self);
1672 }
13731673 }
13741674
13751675 try self.addLinkerDefinedSymbols();
......@@ -1385,7 +1685,7 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
13851685 try self.file(index).?.object.addAtomsToOutputSections(self);
13861686 }
13871687 try self.sortInitFini();
1388 try self.setDynamicSection();
1688 try self.setDynamicSection(rpath_table.keys());
13891689 self.sortDynamicSymtab();
13901690 try self.setHashSections();
13911691 try self.setVersionSymtab();
......@@ -3879,7 +4179,7 @@ fn sortInitFini(self: *Elf) !void {
38794179 }
38804180}
38814181
3882fn setDynamicSection(self: *Elf) !void {
4182fn setDynamicSection(self: *Elf, rpaths: []const []const u8) !void {
38834183 if (self.dynamic_section_index == null) return;
38844184
38854185 for (self.shared_objects.items) |index| {
......@@ -3892,7 +4192,7 @@ fn setDynamicSection(self: *Elf) !void {
38924192 try self.dynamic.setSoname(soname, self);
38934193 }
38944194
3895 try self.dynamic.setRpath(self.base.options.rpath_list, self);
4195 try self.dynamic.setRpath(rpaths, self);
38964196}
38974197
38984198fn sortDynamicSymtab(self: *Elf) void {
......@@ -5153,6 +5453,10 @@ pub fn isStatic(self: Elf) bool {
51535453 return self.base.options.link_mode == .Static;
51545454}
51555455
5456pub fn isExe(self: Elf) bool {
5457 return self.base.options.effectiveOutputMode() == .Exe;
5458}
5459
51565460pub fn isDynLib(self: Elf) bool {
51575461 return self.base.options.effectiveOutputMode() == .Lib and self.base.options.link_mode == .Dynamic;
51585462}