authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-12 23:37:05+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-12 23:37:51+01:00
log616a8f9853e461400f6f5c49dede3bf3a0ca25b6
treebdfc5ea94fc6edad65ce560fdc3756b8dec663b6
parenta94d5895cfbc7b2e695ba1edaad8f4af3d2081ab

elf: move code paths responsible for emitting object and archive into relocatable module


3 files changed, 544 insertions(+), 527 deletions(-)

CMakeLists.txt+1
......@@ -593,6 +593,7 @@ set(ZIG_STAGE2_SOURCES
593593 "${CMAKE_SOURCE_DIR}/src/link/Elf/eh_frame.zig"
594594 "${CMAKE_SOURCE_DIR}/src/link/Elf/file.zig"
595595 "${CMAKE_SOURCE_DIR}/src/link/Elf/gc.zig"
596 "${CMAKE_SOURCE_DIR}/src/link/Elf/relocatable.zig"
596597 "${CMAKE_SOURCE_DIR}/src/link/Elf/synthetic_sections.zig"
597598 "${CMAKE_SOURCE_DIR}/src/link/MachO.zig"
598599 "${CMAKE_SOURCE_DIR}/src/link/MachO/Archive.zig"
src/link/Elf.zig+23-527
......@@ -570,7 +570,7 @@ fn detectAllocCollision(self: *Elf, start: u64, size: u64) ?u64 {
570570 return null;
571571}
572572
573fn allocatedSize(self: *Elf, start: u64) u64 {
573pub fn allocatedSize(self: *Elf, start: u64) u64 {
574574 if (start == 0) return 0;
575575 var min_pos: u64 = std.math.maxInt(u64);
576576 if (self.shdr_table_offset) |off| {
......@@ -597,7 +597,7 @@ fn allocatedVirtualSize(self: *Elf, start: u64) u64 {
597597 return min_pos - start;
598598}
599599
600fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
600pub fn findFreeSpace(self: *Elf, object_size: u64, min_alignment: u64) u64 {
601601 var start: u64 = 0;
602602 while (self.detectAllocCollision(start, object_size)) |item_end| {
603603 start = mem.alignForward(u64, item_end, min_alignment);
......@@ -1083,8 +1083,8 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
10831083 };
10841084
10851085 if (self.zigObjectPtr()) |zig_object| try zig_object.flushModule(self);
1086 if (self.base.isStaticLib()) return self.flushStaticLib(comp, module_obj_path);
1087 if (self.base.isObject()) return self.flushObject(comp, module_obj_path);
1086 if (self.base.isStaticLib()) return relocatable.flushStaticLib(self, comp, module_obj_path);
1087 if (self.base.isObject()) return relocatable.flushObject(self, comp, module_obj_path);
10881088
10891089 // Here we will parse input positional and library files (if referenced).
10901090 // This will roughly match in any linker backend we support.
......@@ -1388,222 +1388,6 @@ pub fn flushModule(self: *Elf, arena: Allocator, prog_node: *std.Progress.Node)
13881388 if (comp.link_errors.items.len > 0) return error.FlushFailure;
13891389}
13901390
1391pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
1392 const gpa = comp.gpa;
1393
1394 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
1395 defer positionals.deinit();
1396
1397 try positionals.ensureUnusedCapacity(comp.objects.len);
1398 positionals.appendSliceAssumeCapacity(comp.objects);
1399
1400 // This is a set of object files emitted by clang in a single `build-exe` invocation.
1401 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
1402 // in this set.
1403 for (comp.c_object_table.keys()) |key| {
1404 try positionals.append(.{ .path = key.status.success.object_path });
1405 }
1406
1407 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1408
1409 for (positionals.items) |obj| {
1410 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1411 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1412 else => |e| try self.reportParseError(
1413 obj.path,
1414 "unexpected error: parsing input file failed with error {s}",
1415 .{@errorName(e)},
1416 ),
1417 };
1418 }
1419
1420 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1421
1422 // First, we flush relocatable object file generated with our backends.
1423 if (self.zigObjectPtr()) |zig_object| {
1424 zig_object.resolveSymbols(self);
1425 zig_object.claimUnresolvedObject(self);
1426
1427 try self.initSymtab();
1428 try self.initShStrtab();
1429 try self.sortShdrs();
1430 try zig_object.addAtomsToRelaSections(self);
1431 try self.updateSectionSizesObject();
1432
1433 try self.allocateAllocSectionsObject();
1434 try self.allocateNonAllocSections();
1435
1436 if (build_options.enable_logging) {
1437 state_log.debug("{}", .{self.dumpState()});
1438 }
1439
1440 try self.writeSyntheticSectionsObject();
1441 try self.writeShdrTable();
1442 try self.writeElfHeader();
1443
1444 // TODO we can avoid reading in the file contents we just wrote if we give the linker
1445 // ability to write directly to a buffer.
1446 try zig_object.readFileContents(self);
1447 }
1448
1449 var files = std.ArrayList(File.Index).init(gpa);
1450 defer files.deinit();
1451 try files.ensureTotalCapacityPrecise(self.objects.items.len + 1);
1452 if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
1453 for (self.objects.items) |index| files.appendAssumeCapacity(index);
1454
1455 // Update ar symtab from parsed objects
1456 var ar_symtab: Archive.ArSymtab = .{};
1457 defer ar_symtab.deinit(gpa);
1458
1459 for (files.items) |index| {
1460 try self.file(index).?.updateArSymtab(&ar_symtab, self);
1461 }
1462
1463 ar_symtab.sort();
1464
1465 // Save object paths in filenames strtab.
1466 var ar_strtab: Archive.ArStrtab = .{};
1467 defer ar_strtab.deinit(gpa);
1468
1469 for (files.items) |index| {
1470 const file_ptr = self.file(index).?;
1471 try file_ptr.updateArStrtab(gpa, &ar_strtab);
1472 try file_ptr.updateArSize(self);
1473 }
1474
1475 // Update file offsets of contributing objects.
1476 const total_size: usize = blk: {
1477 var pos: usize = elf.ARMAG.len;
1478 pos += @sizeOf(elf.ar_hdr) + ar_symtab.size(.p64);
1479
1480 if (ar_strtab.size() > 0) {
1481 pos = mem.alignForward(usize, pos, 2);
1482 pos += @sizeOf(elf.ar_hdr) + ar_strtab.size();
1483 }
1484
1485 for (files.items) |index| {
1486 const file_ptr = self.file(index).?;
1487 const state = switch (file_ptr) {
1488 .zig_object => |x| &x.output_ar_state,
1489 .object => |x| &x.output_ar_state,
1490 else => unreachable,
1491 };
1492 pos = mem.alignForward(usize, pos, 2);
1493 state.file_off = pos;
1494 pos += @sizeOf(elf.ar_hdr) + (math.cast(usize, state.size) orelse return error.Overflow);
1495 }
1496
1497 break :blk pos;
1498 };
1499
1500 if (build_options.enable_logging) {
1501 state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(self)});
1502 state_log.debug("ar_strtab\n{}\n", .{ar_strtab});
1503 }
1504
1505 var buffer = std.ArrayList(u8).init(gpa);
1506 defer buffer.deinit();
1507 try buffer.ensureTotalCapacityPrecise(total_size);
1508
1509 // Write magic
1510 try buffer.writer().writeAll(elf.ARMAG);
1511
1512 // Write symtab
1513 try ar_symtab.write(.p64, self, buffer.writer());
1514
1515 // Write strtab
1516 if (ar_strtab.size() > 0) {
1517 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
1518 try ar_strtab.write(buffer.writer());
1519 }
1520
1521 // Write object files
1522 for (files.items) |index| {
1523 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
1524 try self.file(index).?.writeAr(self, buffer.writer());
1525 }
1526
1527 assert(buffer.items.len == total_size);
1528
1529 try self.base.file.?.setEndPos(total_size);
1530 try self.base.file.?.pwriteAll(buffer.items, 0);
1531
1532 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1533}
1534
1535pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
1536 const gpa = self.base.comp.gpa;
1537
1538 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
1539 defer positionals.deinit();
1540 try positionals.ensureUnusedCapacity(comp.objects.len);
1541 positionals.appendSliceAssumeCapacity(comp.objects);
1542
1543 // This is a set of object files emitted by clang in a single `build-exe` invocation.
1544 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
1545 // in this set.
1546 for (comp.c_object_table.keys()) |key| {
1547 try positionals.append(.{ .path = key.status.success.object_path });
1548 }
1549
1550 if (module_obj_path) |path| try positionals.append(.{ .path = path });
1551
1552 for (positionals.items) |obj| {
1553 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1554 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1555 else => |e| try self.reportParseError(
1556 obj.path,
1557 "unexpected error: parsing input file failed with error {s}",
1558 .{@errorName(e)},
1559 ),
1560 };
1561 }
1562
1563 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1564
1565 // Init all objects
1566 for (self.objects.items) |index| {
1567 try self.file(index).?.object.init(self);
1568 }
1569
1570 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1571
1572 // Now, we are ready to resolve the symbols across all input files.
1573 // We will first resolve the files in the ZigObject, next in the parsed
1574 // input Object files.
1575 self.resolveSymbols();
1576 self.markEhFrameAtomsDead();
1577 self.claimUnresolvedObject();
1578
1579 try self.initSectionsObject();
1580 try self.sortShdrs();
1581 if (self.zigObjectPtr()) |zig_object| {
1582 try zig_object.addAtomsToRelaSections(self);
1583 }
1584 for (self.objects.items) |index| {
1585 const object = self.file(index).?.object;
1586 try object.addAtomsToOutputSections(self);
1587 try object.addAtomsToRelaSections(self);
1588 }
1589 try self.updateSectionSizesObject();
1590
1591 try self.allocateAllocSectionsObject();
1592 try self.allocateNonAllocSections();
1593 self.allocateAtoms();
1594
1595 if (build_options.enable_logging) {
1596 state_log.debug("{}", .{self.dumpState()});
1597 }
1598
1599 try self.writeAtomsObject();
1600 try self.writeSyntheticSectionsObject();
1601 try self.writeShdrTable();
1602 try self.writeElfHeader();
1603
1604 if (comp.link_errors.items.len > 0) return error.FlushFailure;
1605}
1606
16071391/// --verbose-link output
16081392fn dumpArgv(self: *Elf, comp: *Compilation) !void {
16091393 const gpa = self.base.comp.gpa;
......@@ -1880,7 +1664,7 @@ const ParseError = error{
18801664 InvalidCharacter,
18811665} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
18821666
1883fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
1667pub fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
18841668 const tracy = trace(@src());
18851669 defer tracy.end();
18861670 if (try Object.isObject(path)) {
......@@ -2089,7 +1873,7 @@ fn accessLibPath(
20891873/// 4. Reset state of all resolved globals since we will redo this bit on the pruned set.
20901874/// 5. Remove references to dead objects/shared objects
20911875/// 6. Re-run symbol resolution on pruned objects and shared objects sets.
2092fn resolveSymbols(self: *Elf) void {
1876pub fn resolveSymbols(self: *Elf) void {
20931877 // Resolve symbols in the ZigObject. For now, we assume that it's always live.
20941878 if (self.zigObjectPtr()) |zig_object| zig_object.asFile().resolveSymbols(self);
20951879 // Resolve symbols on the set of all objects and shared objects (even if some are unneeded).
......@@ -2173,7 +1957,7 @@ fn markLive(self: *Elf) void {
21731957 }
21741958}
21751959
2176fn markEhFrameAtomsDead(self: *Elf) void {
1960pub fn markEhFrameAtomsDead(self: *Elf) void {
21771961 for (self.objects.items) |index| {
21781962 const file_ptr = self.file(index).?;
21791963 if (!file_ptr.isAlive()) continue;
......@@ -2239,15 +2023,6 @@ fn claimUnresolved(self: *Elf) void {
22392023 }
22402024}
22412025
2242fn claimUnresolvedObject(self: *Elf) void {
2243 if (self.zigObjectPtr()) |zig_object| {
2244 zig_object.claimUnresolvedObject(self);
2245 }
2246 for (self.objects.items) |index| {
2247 self.file(index).?.object.claimUnresolvedObject(self);
2248 }
2249}
2250
22512026/// In scanRelocs we will go over all live atoms and scan their relocs.
22522027/// This will help us work out what synthetics to emit, GOT indirection, etc.
22532028/// This is also the point where we will report undefined symbols for any
......@@ -2985,7 +2760,7 @@ fn writeDwarfAddrAssumeCapacity(self: *Elf, buf: *std.ArrayList(u8), addr: u64)
29852760 }
29862761}
29872762
2988fn writeShdrTable(self: *Elf) !void {
2763pub fn writeShdrTable(self: *Elf) !void {
29892764 const gpa = self.base.comp.gpa;
29902765 const target = self.base.comp.root_mod.resolved_target.result;
29912766 const target_endian = target.cpu.arch.endian();
......@@ -3082,7 +2857,7 @@ fn writePhdrTable(self: *Elf) !void {
30822857 }
30832858}
30842859
3085fn writeElfHeader(self: *Elf) !void {
2860pub fn writeElfHeader(self: *Elf) !void {
30862861 const comp = self.base.comp;
30872862 if (comp.link_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
30882863
......@@ -3658,61 +3433,7 @@ fn initSyntheticSections(self: *Elf) !void {
36583433 try self.initShStrtab();
36593434}
36603435
3661fn initSectionsObject(self: *Elf) !void {
3662 const ptr_size = self.ptrWidthBytes();
3663
3664 for (self.objects.items) |index| {
3665 const object = self.file(index).?.object;
3666 try object.initOutputSections(self);
3667 try object.initRelaSections(self);
3668 }
3669
3670 const needs_eh_frame = for (self.objects.items) |index| {
3671 if (self.file(index).?.object.cies.items.len > 0) break true;
3672 } else false;
3673 if (needs_eh_frame) {
3674 self.eh_frame_section_index = try self.addSection(.{
3675 .name = ".eh_frame",
3676 .type = elf.SHT_PROGBITS,
3677 .flags = elf.SHF_ALLOC,
3678 .addralign = ptr_size,
3679 .offset = std.math.maxInt(u64),
3680 });
3681 self.eh_frame_rela_section_index = try self.addRelaShdr(".rela.eh_frame", self.eh_frame_section_index.?);
3682 }
3683
3684 try self.initComdatGroups();
3685 try self.initSymtab();
3686 try self.initShStrtab();
3687}
3688
3689fn initComdatGroups(self: *Elf) !void {
3690 const gpa = self.base.comp.gpa;
3691
3692 for (self.objects.items) |index| {
3693 const object = self.file(index).?.object;
3694
3695 for (object.comdat_groups.items) |cg_index| {
3696 const cg = self.comdatGroup(cg_index);
3697 const cg_owner = self.comdatGroupOwner(cg.owner);
3698 if (cg_owner.file != index) continue;
3699
3700 const cg_sec = try self.comdat_group_sections.addOne(gpa);
3701 cg_sec.* = .{
3702 .shndx = try self.addSection(.{
3703 .name = ".group",
3704 .type = elf.SHT_GROUP,
3705 .entsize = @sizeOf(u32),
3706 .addralign = @alignOf(u32),
3707 .offset = std.math.maxInt(u64),
3708 }),
3709 .cg_index = cg_index,
3710 };
3711 }
3712 }
3713}
3714
3715fn initSymtab(self: *Elf) !void {
3436pub fn initSymtab(self: *Elf) !void {
37163437 const small_ptr = switch (self.ptr_width) {
37173438 .p32 => true,
37183439 .p64 => false,
......@@ -3737,7 +3458,7 @@ fn initSymtab(self: *Elf) !void {
37373458 }
37383459}
37393460
3740fn initShStrtab(self: *Elf) !void {
3461pub fn initShStrtab(self: *Elf) !void {
37413462 if (self.shstrtab_section_index == null) {
37423463 self.shstrtab_section_index = try self.addSection(.{
37433464 .name = ".shstrtab",
......@@ -4043,7 +3764,7 @@ fn shdrRank(self: *Elf, shndx: u16) u8 {
40433764 }
40443765}
40453766
4046fn sortShdrs(self: *Elf) !void {
3767pub fn sortShdrs(self: *Elf) !void {
40473768 const Entry = struct {
40483769 shndx: u16,
40493770
......@@ -4355,58 +4076,7 @@ fn updateSectionSizes(self: *Elf) !void {
43554076 self.updateShStrtabSize();
43564077}
43574078
4358fn updateSectionSizesObject(self: *Elf) !void {
4359 for (self.output_sections.keys(), self.output_sections.values()) |shndx, atom_list| {
4360 const shdr = &self.shdrs.items[shndx];
4361 for (atom_list.items) |atom_index| {
4362 const atom_ptr = self.atom(atom_index) orelse continue;
4363 if (!atom_ptr.flags.alive) continue;
4364 const offset = atom_ptr.alignment.forward(shdr.sh_size);
4365 const padding = offset - shdr.sh_size;
4366 atom_ptr.value = offset;
4367 shdr.sh_size += padding + atom_ptr.size;
4368 shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1));
4369 }
4370 }
4371
4372 for (self.output_rela_sections.values()) |sec| {
4373 const shdr = &self.shdrs.items[sec.shndx];
4374 for (sec.atom_list.items) |atom_index| {
4375 const atom_ptr = self.atom(atom_index) orelse continue;
4376 if (!atom_ptr.flags.alive) continue;
4377 const relocs = atom_ptr.relocs(self);
4378 shdr.sh_size += shdr.sh_entsize * relocs.len;
4379 }
4380
4381 if (shdr.sh_size == 0) shdr.sh_offset = 0;
4382 }
4383
4384 if (self.eh_frame_section_index) |index| {
4385 self.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(self);
4386 }
4387 if (self.eh_frame_rela_section_index) |index| {
4388 const shdr = &self.shdrs.items[index];
4389 shdr.sh_size = eh_frame.calcEhFrameRelocs(self) * shdr.sh_entsize;
4390 }
4391
4392 try self.updateSymtabSize();
4393 self.updateComdatGroupsSizes();
4394 self.updateShStrtabSize();
4395}
4396
4397fn updateComdatGroupsSizes(self: *Elf) void {
4398 for (self.comdat_group_sections.items) |cg| {
4399 const shdr = &self.shdrs.items[cg.shndx];
4400 shdr.sh_size = cg.size(self);
4401 shdr.sh_link = self.symtab_section_index.?;
4402
4403 const sym = self.symbol(cg.symbol(self));
4404 shdr.sh_info = sym.outputSymtabIndex(self) orelse
4405 self.sectionSymbolOutputSymtabIndex(sym.outputShndx().?);
4406 }
4407}
4408
4409fn updateShStrtabSize(self: *Elf) void {
4079pub fn updateShStrtabSize(self: *Elf) void {
44104080 if (self.shstrtab_section_index) |index| {
44114081 self.shdrs.items[index].sh_size = self.shstrtab.items.len;
44124082 }
......@@ -4491,7 +4161,7 @@ fn allocatePhdrTable(self: *Elf) error{OutOfMemory}!void {
44914161
44924162/// Allocates alloc sections and creates load segments for sections
44934163/// extracted from input object files.
4494fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
4164pub fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
44954165 // We use this struct to track maximum alignment of all TLS sections.
44964166 // According to https://github.com/rui314/mold/commit/bd46edf3f0fe9e1a787ea453c4657d535622e61f in mold,
44974167 // in-file offsets have to be aligned against the start of TLS program header.
......@@ -4638,27 +4308,8 @@ fn allocateAllocSections(self: *Elf) error{OutOfMemory}!void {
46384308 }
46394309}
46404310
4641/// Allocates alloc sections when merging relocatable objects files together.
4642fn allocateAllocSectionsObject(self: *Elf) !void {
4643 for (self.shdrs.items) |*shdr| {
4644 if (shdr.sh_type == elf.SHT_NULL) continue;
4645 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
4646 if (shdr.sh_type == elf.SHT_NOBITS) {
4647 shdr.sh_offset = 0;
4648 continue;
4649 }
4650 const needed_size = shdr.sh_size;
4651 if (needed_size > self.allocatedSize(shdr.sh_offset)) {
4652 shdr.sh_size = 0;
4653 const new_offset = self.findFreeSpace(needed_size, shdr.sh_addralign);
4654 shdr.sh_offset = new_offset;
4655 shdr.sh_size = needed_size;
4656 }
4657 }
4658}
4659
46604311/// Allocates non-alloc sections (debug info, symtabs, etc.).
4661fn allocateNonAllocSections(self: *Elf) !void {
4312pub fn allocateNonAllocSections(self: *Elf) !void {
46624313 for (self.shdrs.items, 0..) |*shdr, shndx| {
46634314 if (shdr.sh_type == elf.SHT_NULL) continue;
46644315 if (shdr.sh_flags & elf.SHF_ALLOC != 0) continue;
......@@ -4757,7 +4408,7 @@ fn allocateSpecialPhdrs(self: *Elf) void {
47574408 }
47584409}
47594410
4760fn allocateAtoms(self: *Elf) void {
4411pub fn allocateAtoms(self: *Elf) void {
47614412 if (self.zigObjectPtr()) |zig_object| {
47624413 zig_object.allocateTlvAtoms(self);
47634414 }
......@@ -4854,76 +4505,7 @@ fn writeAtoms(self: *Elf) !void {
48544505 try self.reportUndefinedSymbols(&undefs);
48554506}
48564507
4857fn writeAtomsObject(self: *Elf) !void {
4858 const gpa = self.base.comp.gpa;
4859
4860 // TODO iterate over `output_sections` directly
4861 for (self.shdrs.items, 0..) |shdr, shndx| {
4862 if (shdr.sh_type == elf.SHT_NULL) continue;
4863 if (shdr.sh_type == elf.SHT_NOBITS) continue;
4864
4865 const atom_list = self.output_sections.get(@intCast(shndx)) orelse continue;
4866 if (atom_list.items.len == 0) continue;
4867
4868 log.debug("writing atoms in '{s}' section", .{self.getShString(shdr.sh_name)});
4869
4870 // TODO really, really handle debug section separately
4871 const base_offset = if (self.isDebugSection(@intCast(shndx))) blk: {
4872 const zig_object = self.zigObjectPtr().?;
4873 if (shndx == self.debug_info_section_index.?)
4874 break :blk zig_object.debug_info_section_zig_size;
4875 if (shndx == self.debug_abbrev_section_index.?)
4876 break :blk zig_object.debug_abbrev_section_zig_size;
4877 if (shndx == self.debug_str_section_index.?)
4878 break :blk zig_object.debug_str_section_zig_size;
4879 if (shndx == self.debug_aranges_section_index.?)
4880 break :blk zig_object.debug_aranges_section_zig_size;
4881 if (shndx == self.debug_line_section_index.?)
4882 break :blk zig_object.debug_line_section_zig_size;
4883 unreachable;
4884 } else 0;
4885 const sh_offset = shdr.sh_offset + base_offset;
4886 const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow;
4887
4888 const buffer = try gpa.alloc(u8, sh_size);
4889 defer gpa.free(buffer);
4890 const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and
4891 shdr.sh_flags & elf.SHF_EXECINSTR != 0)
4892 0xcc // int3
4893 else
4894 0;
4895 @memset(buffer, padding_byte);
4896
4897 for (atom_list.items) |atom_index| {
4898 const atom_ptr = self.atom(atom_index).?;
4899 assert(atom_ptr.flags.alive);
4900
4901 const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
4902 return error.Overflow;
4903 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
4904
4905 log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{
4906 atom_index,
4907 sh_offset + offset,
4908 sh_offset + offset + size,
4909 });
4910
4911 // TODO decompress directly into provided buffer
4912 const out_code = buffer[offset..][0..size];
4913 const in_code = switch (atom_ptr.file(self).?) {
4914 .object => |x| try x.codeDecompressAlloc(self, atom_index),
4915 .zig_object => |x| try x.codeAlloc(self, atom_index),
4916 else => unreachable,
4917 };
4918 defer gpa.free(in_code);
4919 @memcpy(out_code, in_code);
4920 }
4921
4922 try self.base.file.?.pwriteAll(buffer, sh_offset);
4923 }
4924}
4925
4926fn updateSymtabSize(self: *Elf) !void {
4508pub fn updateSymtabSize(self: *Elf) !void {
49274509 var nlocals: u32 = 0;
49284510 var nglobals: u32 = 0;
49294511 var strsize: u32 = 0;
......@@ -5141,94 +4723,7 @@ fn writeSyntheticSections(self: *Elf) !void {
51414723 try self.writeShStrtab();
51424724}
51434725
5144fn writeSyntheticSectionsObject(self: *Elf) !void {
5145 const gpa = self.base.comp.gpa;
5146
5147 for (self.output_rela_sections.values()) |sec| {
5148 if (sec.atom_list.items.len == 0) continue;
5149
5150 const shdr = self.shdrs.items[sec.shndx];
5151
5152 const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse
5153 return error.Overflow;
5154 var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs);
5155 defer relocs.deinit();
5156
5157 for (sec.atom_list.items) |atom_index| {
5158 const atom_ptr = self.atom(atom_index) orelse continue;
5159 if (!atom_ptr.flags.alive) continue;
5160 try atom_ptr.writeRelocs(self, &relocs);
5161 }
5162 assert(relocs.items.len == num_relocs);
5163
5164 const SortRelocs = struct {
5165 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
5166 _ = ctx;
5167 return lhs.r_offset < rhs.r_offset;
5168 }
5169 };
5170
5171 mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
5172
5173 log.debug("writing {s} from 0x{x} to 0x{x}", .{
5174 self.getShString(shdr.sh_name),
5175 shdr.sh_offset,
5176 shdr.sh_offset + shdr.sh_size,
5177 });
5178
5179 try self.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
5180 }
5181
5182 if (self.eh_frame_section_index) |shndx| {
5183 const shdr = self.shdrs.items[shndx];
5184 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
5185 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
5186 defer buffer.deinit();
5187 try eh_frame.writeEhFrameObject(self, buffer.writer());
5188 log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{
5189 shdr.sh_offset,
5190 shdr.sh_offset + shdr.sh_size,
5191 });
5192 assert(buffer.items.len == sh_size);
5193 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5194 }
5195 if (self.eh_frame_rela_section_index) |shndx| {
5196 const shdr = self.shdrs.items[shndx];
5197 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
5198 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
5199 defer buffer.deinit();
5200 try eh_frame.writeEhFrameRelocs(self, buffer.writer());
5201 assert(buffer.items.len == sh_size);
5202 log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{
5203 shdr.sh_offset,
5204 shdr.sh_offset + shdr.sh_size,
5205 });
5206 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5207 }
5208
5209 try self.writeComdatGroups();
5210 try self.writeSymtab();
5211 try self.writeShStrtab();
5212}
5213
5214fn writeComdatGroups(self: *Elf) !void {
5215 const gpa = self.base.comp.gpa;
5216 for (self.comdat_group_sections.items) |cgs| {
5217 const shdr = self.shdrs.items[cgs.shndx];
5218 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
5219 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
5220 defer buffer.deinit();
5221 try cgs.write(self, buffer.writer());
5222 assert(buffer.items.len == sh_size);
5223 log.debug("writing COMDAT group from 0x{x} to 0x{x}", .{
5224 shdr.sh_offset,
5225 shdr.sh_offset + shdr.sh_size,
5226 });
5227 try self.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
5228 }
5229}
5230
5231fn writeShStrtab(self: *Elf) !void {
4726pub fn writeShStrtab(self: *Elf) !void {
52324727 if (self.shstrtab_section_index) |index| {
52334728 const shdr = self.shdrs.items[index];
52344729 log.debug("writing .shstrtab from 0x{x} to 0x{x}", .{ shdr.sh_offset, shdr.sh_offset + shdr.sh_size });
......@@ -5236,7 +4731,7 @@ fn writeShStrtab(self: *Elf) !void {
52364731 }
52374732}
52384733
5239fn writeSymtab(self: *Elf) !void {
4734pub fn writeSymtab(self: *Elf) !void {
52404735 const target = self.base.comp.root_mod.resolved_target.result;
52414736 const gpa = self.base.comp.gpa;
52424737 const symtab_shdr = self.shdrs.items[self.symtab_section_index.?];
......@@ -5367,7 +4862,7 @@ pub fn sectionSymbolOutputSymtabIndex(self: Elf, shndx: u32) u32 {
53674862}
53684863
53694864/// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF.
5370fn ptrWidthBytes(self: Elf) u8 {
4865pub fn ptrWidthBytes(self: Elf) u8 {
53714866 return switch (self.ptr_width) {
53724867 .p32 => 4,
53734868 .p64 => 8,
......@@ -5713,7 +5208,7 @@ fn addPhdr(self: *Elf, opts: struct {
57135208 return index;
57145209}
57155210
5716fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 {
5211pub fn addRelaShdr(self: *Elf, name: [:0]const u8, shndx: u16) !u16 {
57175212 const entsize: u64 = switch (self.ptr_width) {
57185213 .p32 => @sizeOf(elf.Elf32_Rela),
57195214 .p64 => @sizeOf(elf.Elf64_Rela),
......@@ -6340,7 +5835,7 @@ fn formatPhdr(
63405835 });
63415836}
63425837
6343fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
5838pub fn dumpState(self: *Elf) std.fmt.Formatter(fmtDumpState) {
63445839 return .{ .data = self };
63455840}
63465841
......@@ -6579,6 +6074,7 @@ const glibc = @import("../glibc.zig");
65796074const link = @import("../link.zig");
65806075const lldMain = @import("../main.zig").lldMain;
65816076const musl = @import("../musl.zig");
6077const relocatable = @import("Elf/relocatable.zig");
65826078const target_util = @import("../target.zig");
65836079const trace = @import("../tracy.zig").trace;
65846080const synthetic_sections = @import("Elf/synthetic_sections.zig");
src/link/Elf/relocatable.zig created+520
......@@ -0,0 +1,520 @@
1pub fn flushStaticLib(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
2 const gpa = comp.gpa;
3
4 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
5 defer positionals.deinit();
6
7 try positionals.ensureUnusedCapacity(comp.objects.len);
8 positionals.appendSliceAssumeCapacity(comp.objects);
9
10 // This is a set of object files emitted by clang in a single `build-exe` invocation.
11 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
12 // in this set.
13 for (comp.c_object_table.keys()) |key| {
14 try positionals.append(.{ .path = key.status.success.object_path });
15 }
16
17 if (module_obj_path) |path| try positionals.append(.{ .path = path });
18
19 for (positionals.items) |obj| {
20 elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
21 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
22 else => |e| try elf_file.reportParseError(
23 obj.path,
24 "unexpected error: parsing input file failed with error {s}",
25 .{@errorName(e)},
26 ),
27 };
28 }
29
30 if (comp.link_errors.items.len > 0) return error.FlushFailure;
31
32 // First, we flush relocatable object file generated with our backends.
33 if (elf_file.zigObjectPtr()) |zig_object| {
34 zig_object.resolveSymbols(elf_file);
35 zig_object.claimUnresolvedObject(elf_file);
36
37 try elf_file.initSymtab();
38 try elf_file.initShStrtab();
39 try elf_file.sortShdrs();
40 try zig_object.addAtomsToRelaSections(elf_file);
41 try updateSectionSizes(elf_file);
42
43 try allocateAllocSections(elf_file);
44 try elf_file.allocateNonAllocSections();
45
46 if (build_options.enable_logging) {
47 state_log.debug("{}", .{elf_file.dumpState()});
48 }
49
50 try writeSyntheticSections(elf_file);
51 try elf_file.writeShdrTable();
52 try elf_file.writeElfHeader();
53
54 // TODO we can avoid reading in the file contents we just wrote if we give the linker
55 // ability to write directly to a buffer.
56 try zig_object.readFileContents(elf_file);
57 }
58
59 var files = std.ArrayList(File.Index).init(gpa);
60 defer files.deinit();
61 try files.ensureTotalCapacityPrecise(elf_file.objects.items.len + 1);
62 if (elf_file.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
63 for (elf_file.objects.items) |index| files.appendAssumeCapacity(index);
64
65 // Update ar symtab from parsed objects
66 var ar_symtab: Archive.ArSymtab = .{};
67 defer ar_symtab.deinit(gpa);
68
69 for (files.items) |index| {
70 try elf_file.file(index).?.updateArSymtab(&ar_symtab, elf_file);
71 }
72
73 ar_symtab.sort();
74
75 // Save object paths in filenames strtab.
76 var ar_strtab: Archive.ArStrtab = .{};
77 defer ar_strtab.deinit(gpa);
78
79 for (files.items) |index| {
80 const file_ptr = elf_file.file(index).?;
81 try file_ptr.updateArStrtab(gpa, &ar_strtab);
82 try file_ptr.updateArSize(elf_file);
83 }
84
85 // Update file offsets of contributing objects.
86 const total_size: usize = blk: {
87 var pos: usize = elf.ARMAG.len;
88 pos += @sizeOf(elf.ar_hdr) + ar_symtab.size(.p64);
89
90 if (ar_strtab.size() > 0) {
91 pos = mem.alignForward(usize, pos, 2);
92 pos += @sizeOf(elf.ar_hdr) + ar_strtab.size();
93 }
94
95 for (files.items) |index| {
96 const file_ptr = elf_file.file(index).?;
97 const state = switch (file_ptr) {
98 .zig_object => |x| &x.output_ar_state,
99 .object => |x| &x.output_ar_state,
100 else => unreachable,
101 };
102 pos = mem.alignForward(usize, pos, 2);
103 state.file_off = pos;
104 pos += @sizeOf(elf.ar_hdr) + (math.cast(usize, state.size) orelse return error.Overflow);
105 }
106
107 break :blk pos;
108 };
109
110 if (build_options.enable_logging) {
111 state_log.debug("ar_symtab\n{}\n", .{ar_symtab.fmt(elf_file)});
112 state_log.debug("ar_strtab\n{}\n", .{ar_strtab});
113 }
114
115 var buffer = std.ArrayList(u8).init(gpa);
116 defer buffer.deinit();
117 try buffer.ensureTotalCapacityPrecise(total_size);
118
119 // Write magic
120 try buffer.writer().writeAll(elf.ARMAG);
121
122 // Write symtab
123 try ar_symtab.write(.p64, elf_file, buffer.writer());
124
125 // Write strtab
126 if (ar_strtab.size() > 0) {
127 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
128 try ar_strtab.write(buffer.writer());
129 }
130
131 // Write object files
132 for (files.items) |index| {
133 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
134 try elf_file.file(index).?.writeAr(elf_file, buffer.writer());
135 }
136
137 assert(buffer.items.len == total_size);
138
139 try elf_file.base.file.?.setEndPos(total_size);
140 try elf_file.base.file.?.pwriteAll(buffer.items, 0);
141
142 if (comp.link_errors.items.len > 0) return error.FlushFailure;
143}
144
145pub fn flushObject(elf_file: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
146 const gpa = elf_file.base.comp.gpa;
147
148 var positionals = std.ArrayList(Compilation.LinkObject).init(gpa);
149 defer positionals.deinit();
150 try positionals.ensureUnusedCapacity(comp.objects.len);
151 positionals.appendSliceAssumeCapacity(comp.objects);
152
153 // This is a set of object files emitted by clang in a single `build-exe` invocation.
154 // For instance, the implicit `a.o` as compiled by `zig build-exe a.c` will end up
155 // in this set.
156 for (comp.c_object_table.keys()) |key| {
157 try positionals.append(.{ .path = key.status.success.object_path });
158 }
159
160 if (module_obj_path) |path| try positionals.append(.{ .path = path });
161
162 for (positionals.items) |obj| {
163 elf_file.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
164 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
165 else => |e| try elf_file.reportParseError(
166 obj.path,
167 "unexpected error: parsing input file failed with error {s}",
168 .{@errorName(e)},
169 ),
170 };
171 }
172
173 if (comp.link_errors.items.len > 0) return error.FlushFailure;
174
175 // Init all objects
176 for (elf_file.objects.items) |index| {
177 try elf_file.file(index).?.object.init(elf_file);
178 }
179
180 if (comp.link_errors.items.len > 0) return error.FlushFailure;
181
182 // Now, we are ready to resolve the symbols across all input files.
183 // We will first resolve the files in the ZigObject, next in the parsed
184 // input Object files.
185 elf_file.resolveSymbols();
186 elf_file.markEhFrameAtomsDead();
187 claimUnresolved(elf_file);
188
189 try initSections(elf_file);
190 try elf_file.sortShdrs();
191 if (elf_file.zigObjectPtr()) |zig_object| {
192 try zig_object.addAtomsToRelaSections(elf_file);
193 }
194 for (elf_file.objects.items) |index| {
195 const object = elf_file.file(index).?.object;
196 try object.addAtomsToOutputSections(elf_file);
197 try object.addAtomsToRelaSections(elf_file);
198 }
199 try updateSectionSizes(elf_file);
200
201 try allocateAllocSections(elf_file);
202 try elf_file.allocateNonAllocSections();
203 elf_file.allocateAtoms();
204
205 if (build_options.enable_logging) {
206 state_log.debug("{}", .{elf_file.dumpState()});
207 }
208
209 try writeAtoms(elf_file);
210 try writeSyntheticSections(elf_file);
211 try elf_file.writeShdrTable();
212 try elf_file.writeElfHeader();
213
214 if (comp.link_errors.items.len > 0) return error.FlushFailure;
215}
216
217fn claimUnresolved(elf_file: *Elf) void {
218 if (elf_file.zigObjectPtr()) |zig_object| {
219 zig_object.claimUnresolvedObject(elf_file);
220 }
221 for (elf_file.objects.items) |index| {
222 elf_file.file(index).?.object.claimUnresolvedObject(elf_file);
223 }
224}
225
226fn initSections(elf_file: *Elf) !void {
227 const ptr_size = elf_file.ptrWidthBytes();
228
229 for (elf_file.objects.items) |index| {
230 const object = elf_file.file(index).?.object;
231 try object.initOutputSections(elf_file);
232 try object.initRelaSections(elf_file);
233 }
234
235 const needs_eh_frame = for (elf_file.objects.items) |index| {
236 if (elf_file.file(index).?.object.cies.items.len > 0) break true;
237 } else false;
238 if (needs_eh_frame) {
239 elf_file.eh_frame_section_index = try elf_file.addSection(.{
240 .name = ".eh_frame",
241 .type = elf.SHT_PROGBITS,
242 .flags = elf.SHF_ALLOC,
243 .addralign = ptr_size,
244 .offset = std.math.maxInt(u64),
245 });
246 elf_file.eh_frame_rela_section_index = try elf_file.addRelaShdr(".rela.eh_frame", elf_file.eh_frame_section_index.?);
247 }
248
249 try initComdatGroups(elf_file);
250 try elf_file.initSymtab();
251 try elf_file.initShStrtab();
252}
253
254fn initComdatGroups(elf_file: *Elf) !void {
255 const gpa = elf_file.base.comp.gpa;
256
257 for (elf_file.objects.items) |index| {
258 const object = elf_file.file(index).?.object;
259
260 for (object.comdat_groups.items) |cg_index| {
261 const cg = elf_file.comdatGroup(cg_index);
262 const cg_owner = elf_file.comdatGroupOwner(cg.owner);
263 if (cg_owner.file != index) continue;
264
265 const cg_sec = try elf_file.comdat_group_sections.addOne(gpa);
266 cg_sec.* = .{
267 .shndx = try elf_file.addSection(.{
268 .name = ".group",
269 .type = elf.SHT_GROUP,
270 .entsize = @sizeOf(u32),
271 .addralign = @alignOf(u32),
272 .offset = std.math.maxInt(u64),
273 }),
274 .cg_index = cg_index,
275 };
276 }
277 }
278}
279
280fn updateSectionSizes(elf_file: *Elf) !void {
281 for (elf_file.output_sections.keys(), elf_file.output_sections.values()) |shndx, atom_list| {
282 const shdr = &elf_file.shdrs.items[shndx];
283 for (atom_list.items) |atom_index| {
284 const atom_ptr = elf_file.atom(atom_index) orelse continue;
285 if (!atom_ptr.flags.alive) continue;
286 const offset = atom_ptr.alignment.forward(shdr.sh_size);
287 const padding = offset - shdr.sh_size;
288 atom_ptr.value = offset;
289 shdr.sh_size += padding + atom_ptr.size;
290 shdr.sh_addralign = @max(shdr.sh_addralign, atom_ptr.alignment.toByteUnits(1));
291 }
292 }
293
294 for (elf_file.output_rela_sections.values()) |sec| {
295 const shdr = &elf_file.shdrs.items[sec.shndx];
296 for (sec.atom_list.items) |atom_index| {
297 const atom_ptr = elf_file.atom(atom_index) orelse continue;
298 if (!atom_ptr.flags.alive) continue;
299 const relocs = atom_ptr.relocs(elf_file);
300 shdr.sh_size += shdr.sh_entsize * relocs.len;
301 }
302
303 if (shdr.sh_size == 0) shdr.sh_offset = 0;
304 }
305
306 if (elf_file.eh_frame_section_index) |index| {
307 elf_file.shdrs.items[index].sh_size = try eh_frame.calcEhFrameSize(elf_file);
308 }
309 if (elf_file.eh_frame_rela_section_index) |index| {
310 const shdr = &elf_file.shdrs.items[index];
311 shdr.sh_size = eh_frame.calcEhFrameRelocs(elf_file) * shdr.sh_entsize;
312 }
313
314 try elf_file.updateSymtabSize();
315 updateComdatGroupsSizes(elf_file);
316 elf_file.updateShStrtabSize();
317}
318
319fn updateComdatGroupsSizes(elf_file: *Elf) void {
320 for (elf_file.comdat_group_sections.items) |cg| {
321 const shdr = &elf_file.shdrs.items[cg.shndx];
322 shdr.sh_size = cg.size(elf_file);
323 shdr.sh_link = elf_file.symtab_section_index.?;
324
325 const sym = elf_file.symbol(cg.symbol(elf_file));
326 shdr.sh_info = sym.outputSymtabIndex(elf_file) orelse
327 elf_file.sectionSymbolOutputSymtabIndex(sym.outputShndx().?);
328 }
329}
330
331/// Allocates alloc sections when merging relocatable objects files together.
332fn allocateAllocSections(elf_file: *Elf) !void {
333 for (elf_file.shdrs.items) |*shdr| {
334 if (shdr.sh_type == elf.SHT_NULL) continue;
335 if (shdr.sh_flags & elf.SHF_ALLOC == 0) continue;
336 if (shdr.sh_type == elf.SHT_NOBITS) {
337 shdr.sh_offset = 0;
338 continue;
339 }
340 const needed_size = shdr.sh_size;
341 if (needed_size > elf_file.allocatedSize(shdr.sh_offset)) {
342 shdr.sh_size = 0;
343 const new_offset = elf_file.findFreeSpace(needed_size, shdr.sh_addralign);
344 shdr.sh_offset = new_offset;
345 shdr.sh_size = needed_size;
346 }
347 }
348}
349
350fn writeAtoms(elf_file: *Elf) !void {
351 const gpa = elf_file.base.comp.gpa;
352
353 // TODO iterate over `output_sections` directly
354 for (elf_file.shdrs.items, 0..) |shdr, shndx| {
355 if (shdr.sh_type == elf.SHT_NULL) continue;
356 if (shdr.sh_type == elf.SHT_NOBITS) continue;
357
358 const atom_list = elf_file.output_sections.get(@intCast(shndx)) orelse continue;
359 if (atom_list.items.len == 0) continue;
360
361 log.debug("writing atoms in '{s}' section", .{elf_file.getShString(shdr.sh_name)});
362
363 // TODO really, really handle debug section separately
364 const base_offset = if (elf_file.isDebugSection(@intCast(shndx))) blk: {
365 const zig_object = elf_file.zigObjectPtr().?;
366 if (shndx == elf_file.debug_info_section_index.?)
367 break :blk zig_object.debug_info_section_zig_size;
368 if (shndx == elf_file.debug_abbrev_section_index.?)
369 break :blk zig_object.debug_abbrev_section_zig_size;
370 if (shndx == elf_file.debug_str_section_index.?)
371 break :blk zig_object.debug_str_section_zig_size;
372 if (shndx == elf_file.debug_aranges_section_index.?)
373 break :blk zig_object.debug_aranges_section_zig_size;
374 if (shndx == elf_file.debug_line_section_index.?)
375 break :blk zig_object.debug_line_section_zig_size;
376 unreachable;
377 } else 0;
378 const sh_offset = shdr.sh_offset + base_offset;
379 const sh_size = math.cast(usize, shdr.sh_size - base_offset) orelse return error.Overflow;
380
381 const buffer = try gpa.alloc(u8, sh_size);
382 defer gpa.free(buffer);
383 const padding_byte: u8 = if (shdr.sh_type == elf.SHT_PROGBITS and
384 shdr.sh_flags & elf.SHF_EXECINSTR != 0)
385 0xcc // int3
386 else
387 0;
388 @memset(buffer, padding_byte);
389
390 for (atom_list.items) |atom_index| {
391 const atom_ptr = elf_file.atom(atom_index).?;
392 assert(atom_ptr.flags.alive);
393
394 const offset = math.cast(usize, atom_ptr.value - shdr.sh_addr - base_offset) orelse
395 return error.Overflow;
396 const size = math.cast(usize, atom_ptr.size) orelse return error.Overflow;
397
398 log.debug("writing atom({d}) from 0x{x} to 0x{x}", .{
399 atom_index,
400 sh_offset + offset,
401 sh_offset + offset + size,
402 });
403
404 // TODO decompress directly into provided buffer
405 const out_code = buffer[offset..][0..size];
406 const in_code = switch (atom_ptr.file(elf_file).?) {
407 .object => |x| try x.codeDecompressAlloc(elf_file, atom_index),
408 .zig_object => |x| try x.codeAlloc(elf_file, atom_index),
409 else => unreachable,
410 };
411 defer gpa.free(in_code);
412 @memcpy(out_code, in_code);
413 }
414
415 try elf_file.base.file.?.pwriteAll(buffer, sh_offset);
416 }
417}
418
419fn writeSyntheticSections(elf_file: *Elf) !void {
420 const gpa = elf_file.base.comp.gpa;
421
422 for (elf_file.output_rela_sections.values()) |sec| {
423 if (sec.atom_list.items.len == 0) continue;
424
425 const shdr = elf_file.shdrs.items[sec.shndx];
426
427 const num_relocs = math.cast(usize, @divExact(shdr.sh_size, shdr.sh_entsize)) orelse
428 return error.Overflow;
429 var relocs = try std.ArrayList(elf.Elf64_Rela).initCapacity(gpa, num_relocs);
430 defer relocs.deinit();
431
432 for (sec.atom_list.items) |atom_index| {
433 const atom_ptr = elf_file.atom(atom_index) orelse continue;
434 if (!atom_ptr.flags.alive) continue;
435 try atom_ptr.writeRelocs(elf_file, &relocs);
436 }
437 assert(relocs.items.len == num_relocs);
438
439 const SortRelocs = struct {
440 pub fn lessThan(ctx: void, lhs: elf.Elf64_Rela, rhs: elf.Elf64_Rela) bool {
441 _ = ctx;
442 return lhs.r_offset < rhs.r_offset;
443 }
444 };
445
446 mem.sort(elf.Elf64_Rela, relocs.items, {}, SortRelocs.lessThan);
447
448 log.debug("writing {s} from 0x{x} to 0x{x}", .{
449 elf_file.getShString(shdr.sh_name),
450 shdr.sh_offset,
451 shdr.sh_offset + shdr.sh_size,
452 });
453
454 try elf_file.base.file.?.pwriteAll(mem.sliceAsBytes(relocs.items), shdr.sh_offset);
455 }
456
457 if (elf_file.eh_frame_section_index) |shndx| {
458 const shdr = elf_file.shdrs.items[shndx];
459 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
460 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
461 defer buffer.deinit();
462 try eh_frame.writeEhFrameObject(elf_file, buffer.writer());
463 log.debug("writing .eh_frame from 0x{x} to 0x{x}", .{
464 shdr.sh_offset,
465 shdr.sh_offset + shdr.sh_size,
466 });
467 assert(buffer.items.len == sh_size);
468 try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
469 }
470 if (elf_file.eh_frame_rela_section_index) |shndx| {
471 const shdr = elf_file.shdrs.items[shndx];
472 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
473 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
474 defer buffer.deinit();
475 try eh_frame.writeEhFrameRelocs(elf_file, buffer.writer());
476 assert(buffer.items.len == sh_size);
477 log.debug("writing .rela.eh_frame from 0x{x} to 0x{x}", .{
478 shdr.sh_offset,
479 shdr.sh_offset + shdr.sh_size,
480 });
481 try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
482 }
483
484 try writeComdatGroups(elf_file);
485 try elf_file.writeSymtab();
486 try elf_file.writeShStrtab();
487}
488
489fn writeComdatGroups(elf_file: *Elf) !void {
490 const gpa = elf_file.base.comp.gpa;
491 for (elf_file.comdat_group_sections.items) |cgs| {
492 const shdr = elf_file.shdrs.items[cgs.shndx];
493 const sh_size = math.cast(usize, shdr.sh_size) orelse return error.Overflow;
494 var buffer = try std.ArrayList(u8).initCapacity(gpa, sh_size);
495 defer buffer.deinit();
496 try cgs.write(elf_file, buffer.writer());
497 assert(buffer.items.len == sh_size);
498 log.debug("writing COMDAT group from 0x{x} to 0x{x}", .{
499 shdr.sh_offset,
500 shdr.sh_offset + shdr.sh_size,
501 });
502 try elf_file.base.file.?.pwriteAll(buffer.items, shdr.sh_offset);
503 }
504}
505
506const assert = std.debug.assert;
507const build_options = @import("build_options");
508const eh_frame = @import("eh_frame.zig");
509const elf = std.elf;
510const link = @import("../../link.zig");
511const log = std.log.scoped(.link);
512const math = std.math;
513const mem = std.mem;
514const state_log = std.log.scoped(.link_state);
515const std = @import("std");
516
517const Archive = @import("Archive.zig");
518const Compilation = @import("../../Compilation.zig");
519const Elf = @import("../Elf.zig");
520const File = @import("file.zig").File;