authorgravatar for jacoblevgw@gmail.comJacob G-W <jacoblevgw@gmail.com> 2023-07-08 09:14:04-04:00
committergravatar for jacoblevgw@gmail.comJacob G-W <jacoblevgw@gmail.com> 2023-08-02 17:39:52-04:00
log6293a646e33d2cfe064d4475f42f7d77a3831cf4
treeb273837b11397d58f711adac042407cbfbf1c451
parent4d22bae27e2ccad4fdacb2fee053f31f815cf405

Plan9: support linking to external 'special' symbols


2 files changed, 149 insertions(+), 54 deletions(-)

src/arch/x86_64/Emit.zig+1-1
......@@ -124,7 +124,7 @@ pub fn emitMir(emit: *Emit) Error!void {
124124 .target = symbol.sym_index, // we set sym_index to just be the atom index
125125 .offset = @as(u32, @intCast(end_offset - 4)),
126126 .addend = 0,
127 .pcrel = true,
127 .type = .pcrel,
128128 });
129129 } else return emit.fail("TODO implement linker reloc for {s}", .{
130130 @tagName(emit.bin_file.tag),
src/link/Plan9.zig+148-53
......@@ -100,11 +100,23 @@ syms_index_free_list: std.ArrayListUnmanaged(usize) = .{},
100100atoms: std.ArrayListUnmanaged(Atom) = .{},
101101decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},
102102
103/// Indices of the three "special" symbols into atoms
104etext_edata_end_atom_indices: [3]?Atom.Index = .{ null, null, null },
105
103106const Reloc = struct {
104107 target: Atom.Index,
105108 offset: u64,
106109 addend: u32,
107 pcrel: bool = false,
110 type: enum {
111 pcrel,
112 nonpcrel,
113 // for getting the value of the etext symbol; we ignore target
114 special_etext,
115 // for getting the value of the edata symbol; we ignore target
116 special_edata,
117 // for getting the value of the end symbol; we ignore target
118 special_end,
119 } = .nonpcrel,
108120};
109121
110122const Bases = struct {
......@@ -467,15 +479,10 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I
467479pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void {
468480 const decl = mod.declPtr(decl_index);
469481
470 if (decl.val.getExternFunc(mod)) |_| {
471 return; // TODO Should we do more when front-end analyzed extern decl?
472 }
473 if (decl.val.getVariable(mod)) |variable| {
474 if (variable.is_extern) {
475 return; // TODO Should we do more when front-end analyzed extern decl?
476 }
482 if (decl.isExtern(mod)) {
483 log.debug("found extern decl: {s}", .{mod.intern_pool.stringToSlice(decl.name)});
484 return;
477485 }
478
479486 const atom_idx = try self.seeDecl(decl_index);
480487
481488 var code_buffer = std.ArrayList(u8).init(self.base.allocator);
......@@ -574,6 +581,13 @@ pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void {
574581 }
575582}
576583
584fn externCount(self: *Plan9) usize {
585 var extern_atom_count: usize = 0;
586 for (self.etext_edata_end_atom_indices) |idx| {
587 if (idx != null) extern_atom_count += 1;
588 }
589 return extern_atom_count;
590}
577591// counts decls, unnamed consts, and lazy syms
578592fn atomCount(self: *Plan9) usize {
579593 var fn_decl_count: usize = 0;
......@@ -594,7 +608,8 @@ fn atomCount(self: *Plan9) usize {
594608 while (it_lazy.next()) |kv| {
595609 lazy_atom_count += kv.value_ptr.numberOfAtoms();
596610 }
597 return data_decl_count + fn_decl_count + unnamed_const_count + lazy_atom_count;
611 const extern_atom_count = self.externCount();
612 return data_decl_count + fn_decl_count + unnamed_const_count + lazy_atom_count + extern_atom_count;
598613}
599614
600615pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {
......@@ -647,7 +662,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
647662 defer self.base.allocator.free(got_table);
648663
649664 // + 4 for header, got, symbols, linecountinfo
650 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.atomCount() + 4);
665 var iovecs = try self.base.allocator.alloc(std.os.iovec_const, self.atomCount() + 4 - self.externCount());
651666 defer self.base.allocator.free(iovecs);
652667
653668 const file = self.base.file.?;
......@@ -729,8 +744,17 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
729744 self.syms.items[text_atom.sym_index.?].value = off;
730745 }
731746 }
732 // etext symbol
733 self.syms.items[2].value = self.getAddr(text_i, .t);
747 // fix the sym for etext
748 if (self.etext_edata_end_atom_indices[0]) |etext_atom_idx| {
749 const etext_atom = self.getAtom(etext_atom_idx);
750 const val = self.getAddr(text_i, .t);
751 self.syms.items[etext_atom.sym_index.?].value = val;
752 if (!self.sixtyfour_bit) {
753 mem.writeInt(u32, got_table[etext_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), self.base.options.target.cpu.arch.endian());
754 } else {
755 mem.writeInt(u64, got_table[etext_atom.got_index.? * 8 ..][0..8], val, self.base.options.target.cpu.arch.endian());
756 }
757 }
734758 // global offset table is in data
735759 iovecs[iovecs_i] = .{ .iov_base = got_table.ptr, .iov_len = got_table.len };
736760 iovecs_i += 1;
......@@ -800,15 +824,34 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
800824 self.syms.items[data_atom.sym_index.?].value = off;
801825 }
802826 // edata symbol
803 self.syms.items[0].value = self.getAddr(data_i, .b);
804 // end
805 self.syms.items[1].value = self.getAddr(data_i, .b);
827 if (self.etext_edata_end_atom_indices[1]) |edata_atom_idx| {
828 const edata_atom = self.getAtom(edata_atom_idx);
829 const val = self.getAddr(data_i, .b);
830 self.syms.items[edata_atom.sym_index.?].value = val;
831 if (!self.sixtyfour_bit) {
832 mem.writeInt(u32, got_table[edata_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), self.base.options.target.cpu.arch.endian());
833 } else {
834 mem.writeInt(u64, got_table[edata_atom.got_index.? * 8 ..][0..8], val, self.base.options.target.cpu.arch.endian());
835 }
836 }
837 // end symbol (same as edata because native backends don't do .bss yet)
838 if (self.etext_edata_end_atom_indices[2]) |end_atom_idx| {
839 const end_atom = self.getAtom(end_atom_idx);
840 const val = self.getAddr(data_i, .b);
841 self.syms.items[end_atom.sym_index.?].value = val;
842 if (!self.sixtyfour_bit) {
843 mem.writeInt(u32, got_table[end_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), self.base.options.target.cpu.arch.endian());
844 } else {
845 log.debug("write end (got_table[0x{x}] = 0x{x})", .{ end_atom.got_index.? * 8, val });
846 mem.writeInt(u64, got_table[end_atom.got_index.? * 8 ..][0..8], val, self.base.options.target.cpu.arch.endian());
847 }
848 }
806849 }
807850 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
808851 try self.writeSyms(&sym_buf);
809852 const syms = try sym_buf.toOwnedSlice();
810853 defer self.base.allocator.free(syms);
811 assert(2 + self.atomCount() == iovecs_i); // we didn't write all the decls
854 assert(2 + self.atomCount() - self.externCount() == iovecs_i); // we didn't write all the decls
812855 iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len };
813856 iovecs_i += 1;
814857 iovecs[iovecs_i] = .{ .iov_base = linecountinfo.items.ptr, .iov_len = linecountinfo.items.len };
......@@ -836,28 +879,46 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
836879 const source_atom_index = kv.key_ptr.*;
837880 const source_atom = self.getAtom(source_atom_index);
838881 const source_atom_symbol = self.syms.items[source_atom.sym_index.?];
882 const code = source_atom.code.getCode(self);
883 const endian = self.base.options.target.cpu.arch.endian();
839884 for (kv.value_ptr.items) |reloc| {
840 const target_atom_index = reloc.target;
841 const target_atom = self.getAtomPtr(target_atom_index);
842 const target_symbol = self.syms.items[target_atom.sym_index.?];
843 const target_offset = target_atom.offset.?;
844
845885 const offset = reloc.offset;
846886 const addend = reloc.addend;
847
848 const code = source_atom.code.getCode(self);
849
850 if (reloc.pcrel) {
851 const disp = @as(i32, @intCast(target_offset)) - @as(i32, @intCast(source_atom.offset.?)) - 4 - @as(i32, @intCast(offset));
852 mem.writeInt(i32, code[@as(usize, @intCast(offset))..][0..4], @as(i32, @intCast(disp)), self.base.options.target.cpu.arch.endian());
887 if (reloc.type == .pcrel or reloc.type == .nonpcrel) {
888 const target_atom_index = reloc.target;
889 const target_atom = self.getAtomPtr(target_atom_index);
890 const target_symbol = self.syms.items[target_atom.sym_index.?];
891 const target_offset = target_atom.offset.?;
892
893 switch (reloc.type) {
894 .pcrel => {
895 const disp = @as(i32, @intCast(target_offset)) - @as(i32, @intCast(source_atom.offset.?)) - 4 - @as(i32, @intCast(offset));
896 mem.writeInt(i32, code[@as(usize, @intCast(offset))..][0..4], @as(i32, @intCast(disp)), endian);
897 },
898 .nonpcrel => {
899 if (!self.sixtyfour_bit) {
900 mem.writeInt(u32, code[@intCast(offset)..][0..4], @as(u32, @intCast(target_offset + addend)), endian);
901 } else {
902 mem.writeInt(u64, code[@intCast(offset)..][0..8], target_offset + addend, endian);
903 }
904 },
905 else => unreachable,
906 }
907 log.debug("relocating the address of '{s}' + {d} into '{s}' + {d} (({s}[{d}] = 0x{x} + 0x{x})", .{ target_symbol.name, addend, source_atom_symbol.name, offset, source_atom_symbol.name, offset, target_offset, addend });
853908 } else {
909 const addr = switch (reloc.type) {
910 .special_etext => self.syms.items[self.getAtom(self.etext_edata_end_atom_indices[0].?).sym_index.?].value,
911 .special_edata => self.syms.items[self.getAtom(self.etext_edata_end_atom_indices[1].?).sym_index.?].value,
912 .special_end => self.syms.items[self.getAtom(self.etext_edata_end_atom_indices[2].?).sym_index.?].value,
913 else => unreachable,
914 };
854915 if (!self.sixtyfour_bit) {
855 mem.writeInt(u32, code[@as(usize, @intCast(offset))..][0..4], @as(u32, @intCast(target_offset + addend)), self.base.options.target.cpu.arch.endian());
916 mem.writeInt(u32, code[@intCast(offset)..][0..4], @as(u32, @intCast(addr + addend)), endian);
856917 } else {
857 mem.writeInt(u64, code[@as(usize, @intCast(offset))..][0..8], target_offset + addend, self.base.options.target.cpu.arch.endian());
918 mem.writeInt(u64, code[@intCast(offset)..][0..8], addr + addend, endian);
858919 }
920 log.debug("relocating the address of '{s}' + {d} into '{s}' + {d} (({s}[{d}] = 0x{x} + 0x{x})", .{ @tagName(reloc.type), addend, source_atom_symbol.name, offset, source_atom_symbol.name, offset, addr, addend });
859921 }
860 log.debug("relocating the address of '{s}' + {d} into '{s}' + {d} (({s}[{d}] = 0x{x} + 0x{x})", .{ target_symbol.name, addend, source_atom_symbol.name, offset, source_atom_symbol.name, offset, target_offset, addend });
861922 }
862923 }
863924 }
......@@ -983,7 +1044,24 @@ pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !Atom.Index {
9831044 .exports = .{},
9841045 };
9851046 }
986 return gop.value_ptr.index;
1047 const atom_idx = gop.value_ptr.index;
1048 // handle externs here because they might not get updateDecl called on them
1049 const mod = self.base.options.module.?;
1050 const decl = mod.declPtr(decl_index);
1051 const name = mod.intern_pool.stringToSlice(decl.name);
1052 if (decl.isExtern(mod)) {
1053 // this is a "phantom atom" - it is never actually written to disk, just convenient for us to store stuff about externs
1054 if (std.mem.eql(u8, name, "etext")) {
1055 self.etext_edata_end_atom_indices[0] = atom_idx;
1056 } else if (std.mem.eql(u8, name, "edata")) {
1057 self.etext_edata_end_atom_indices[1] = atom_idx;
1058 } else if (std.mem.eql(u8, name, "end")) {
1059 self.etext_edata_end_atom_indices[2] = atom_idx;
1060 }
1061 try self.updateFinish(decl_index);
1062 log.debug("seeDecl(extern) for {s} (got_addr=0x{x})", .{ name, self.getAtom(atom_idx).getOffsetTableAddress(self) });
1063 } else log.debug("seeDecl for {s}", .{name});
1064 return atom_idx;
9871065}
9881066
9891067pub fn updateDeclExports(
......@@ -1157,23 +1235,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
11571235
11581236 self.bases = defaultBaseAddrs(options.target.cpu.arch);
11591237
1160 // first 4 symbols in our table are edata, end, etext, and got
11611238 try self.syms.appendSlice(self.base.allocator, &.{
1162 .{
1163 .value = 0xcafebabe,
1164 .type = .B,
1165 .name = "edata",
1166 },
1167 .{
1168 .value = 0xcafebabe,
1169 .type = .B,
1170 .name = "end",
1171 },
1172 .{
1173 .value = 0xcafebabe,
1174 .type = .T,
1175 .name = "etext",
1176 },
11771239 // we include the global offset table to make it easier for debugging
11781240 .{
11791241 .value = self.getAddr(0, .d), // the global offset table starts at 0
......@@ -1202,11 +1264,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
12021264 const mod = self.base.options.module.?;
12031265 const ip = &mod.intern_pool;
12041266 const writer = buf.writer();
1205 // write the first four symbols (edata, etext, end, __GOT)
1267 // write __GOT
12061268 try self.writeSym(writer, self.syms.items[0]);
1207 try self.writeSym(writer, self.syms.items[1]);
1208 try self.writeSym(writer, self.syms.items[2]);
1209 try self.writeSym(writer, self.syms.items[3]);
12101269 // write the f symbols
12111270 {
12121271 var it = self.file_segments.iterator();
......@@ -1296,6 +1355,14 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
12961355 }
12971356 }
12981357 }
1358 // special symbols
1359 for (self.etext_edata_end_atom_indices) |idx| {
1360 if (idx) |atom_idx| {
1361 const atom = self.getAtom(atom_idx);
1362 const sym = self.syms.items[atom.sym_index.?];
1363 try self.writeSym(writer, sym);
1364 }
1365 }
12991366}
13001367
13011368/// Must be called only after a successful call to `updateDecl`.
......@@ -1312,6 +1379,34 @@ pub fn getDeclVAddr(
13121379) !u64 {
13131380 const mod = self.base.options.module.?;
13141381 const decl = mod.declPtr(decl_index);
1382 log.debug("getDeclVAddr for {s}", .{mod.intern_pool.stringToSlice(decl.name)});
1383 if (decl.isExtern(mod)) {
1384 const extern_name = mod.intern_pool.stringToSlice(decl.name);
1385 if (std.mem.eql(u8, extern_name, "etext")) {
1386 try self.addReloc(reloc_info.parent_atom_index, .{
1387 .target = undefined,
1388 .offset = reloc_info.offset,
1389 .addend = reloc_info.addend,
1390 .type = .special_etext,
1391 });
1392 } else if (std.mem.eql(u8, extern_name, "edata")) {
1393 try self.addReloc(reloc_info.parent_atom_index, .{
1394 .target = undefined,
1395 .offset = reloc_info.offset,
1396 .addend = reloc_info.addend,
1397 .type = .special_edata,
1398 });
1399 } else if (std.mem.eql(u8, extern_name, "end")) {
1400 try self.addReloc(reloc_info.parent_atom_index, .{
1401 .target = undefined,
1402 .offset = reloc_info.offset,
1403 .addend = reloc_info.addend,
1404 .type = .special_end,
1405 });
1406 }
1407 // TODO handle other extern variables and functions
1408 return undefined;
1409 }
13151410 // we might already know the vaddr
13161411 if (decl.ty.zigTypeTag(mod) == .Fn) {
13171412 var start = self.bases.text;
......@@ -1339,7 +1434,7 @@ pub fn getDeclVAddr(
13391434 .offset = reloc_info.offset,
13401435 .addend = reloc_info.addend,
13411436 });
1342 return 0xcafebabe;
1437 return undefined;
13431438}
13441439
13451440pub fn addReloc(self: *Plan9, parent_index: Atom.Index, reloc: Reloc) !void {