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 {...@@ -124,7 +124,7 @@ pub fn emitMir(emit: *Emit) Error!void {
124 .target = symbol.sym_index, // we set sym_index to just be the atom index124 .target = symbol.sym_index, // we set sym_index to just be the atom index
125 .offset = @as(u32, @intCast(end_offset - 4)),125 .offset = @as(u32, @intCast(end_offset - 4)),
126 .addend = 0,126 .addend = 0,
127 .pcrel = true,127 .type = .pcrel,
128 });128 });
129 } else return emit.fail("TODO implement linker reloc for {s}", .{129 } else return emit.fail("TODO implement linker reloc for {s}", .{
130 @tagName(emit.bin_file.tag),130 @tagName(emit.bin_file.tag),
src/link/Plan9.zig+148-53
...@@ -100,11 +100,23 @@ syms_index_free_list: std.ArrayListUnmanaged(usize) = .{},...@@ -100,11 +100,23 @@ syms_index_free_list: std.ArrayListUnmanaged(usize) = .{},
100atoms: std.ArrayListUnmanaged(Atom) = .{},100atoms: std.ArrayListUnmanaged(Atom) = .{},
101decls: std.AutoHashMapUnmanaged(Module.Decl.Index, DeclMetadata) = .{},101decls: 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
103const Reloc = struct {106const Reloc = struct {
104 target: Atom.Index,107 target: Atom.Index,
105 offset: u64,108 offset: u64,
106 addend: u32,109 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,
108};120};
109121
110const Bases = struct {122const Bases = struct {
...@@ -467,15 +479,10 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I...@@ -467,15 +479,10 @@ pub fn lowerUnnamedConst(self: *Plan9, tv: TypedValue, decl_index: Module.Decl.I
467pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void {479pub fn updateDecl(self: *Plan9, mod: *Module, decl_index: Module.Decl.Index) !void {
468 const decl = mod.declPtr(decl_index);480 const decl = mod.declPtr(decl_index);
469481
470 if (decl.val.getExternFunc(mod)) |_| {482 if (decl.isExtern(mod)) {
471 return; // TODO Should we do more when front-end analyzed extern decl?483 log.debug("found extern decl: {s}", .{mod.intern_pool.stringToSlice(decl.name)});
472 }484 return;
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 }
477 }485 }
478
479 const atom_idx = try self.seeDecl(decl_index);486 const atom_idx = try self.seeDecl(decl_index);
480487
481 var code_buffer = std.ArrayList(u8).init(self.base.allocator);488 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 {...@@ -574,6 +581,13 @@ pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void {
574 }581 }
575}582}
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}
577// counts decls, unnamed consts, and lazy syms591// counts decls, unnamed consts, and lazy syms
578fn atomCount(self: *Plan9) usize {592fn atomCount(self: *Plan9) usize {
579 var fn_decl_count: usize = 0;593 var fn_decl_count: usize = 0;
...@@ -594,7 +608,8 @@ fn atomCount(self: *Plan9) usize {...@@ -594,7 +608,8 @@ fn atomCount(self: *Plan9) usize {
594 while (it_lazy.next()) |kv| {608 while (it_lazy.next()) |kv| {
595 lazy_atom_count += kv.value_ptr.numberOfAtoms();609 lazy_atom_count += kv.value_ptr.numberOfAtoms();
596 }610 }
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;
598}613}
599614
600pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.Node) link.File.FlushError!void {615pub 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...@@ -647,7 +662,7 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
647 defer self.base.allocator.free(got_table);662 defer self.base.allocator.free(got_table);
648663
649 // + 4 for header, got, symbols, linecountinfo664 // + 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());
651 defer self.base.allocator.free(iovecs);666 defer self.base.allocator.free(iovecs);
652667
653 const file = self.base.file.?;668 const file = self.base.file.?;
...@@ -729,8 +744,17 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -729,8 +744,17 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
729 self.syms.items[text_atom.sym_index.?].value = off;744 self.syms.items[text_atom.sym_index.?].value = off;
730 }745 }
731 }746 }
732 // etext symbol747 // fix the sym for etext
733 self.syms.items[2].value = self.getAddr(text_i, .t);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 }
734 // global offset table is in data758 // global offset table is in data
735 iovecs[iovecs_i] = .{ .iov_base = got_table.ptr, .iov_len = got_table.len };759 iovecs[iovecs_i] = .{ .iov_base = got_table.ptr, .iov_len = got_table.len };
736 iovecs_i += 1;760 iovecs_i += 1;
...@@ -800,15 +824,34 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No...@@ -800,15 +824,34 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
800 self.syms.items[data_atom.sym_index.?].value = off;824 self.syms.items[data_atom.sym_index.?].value = off;
801 }825 }
802 // edata symbol826 // edata symbol
803 self.syms.items[0].value = self.getAddr(data_i, .b);827 if (self.etext_edata_end_atom_indices[1]) |edata_atom_idx| {
804 // end828 const edata_atom = self.getAtom(edata_atom_idx);
805 self.syms.items[1].value = self.getAddr(data_i, .b);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 }
806 }849 }
807 var sym_buf = std.ArrayList(u8).init(self.base.allocator);850 var sym_buf = std.ArrayList(u8).init(self.base.allocator);
808 try self.writeSyms(&sym_buf);851 try self.writeSyms(&sym_buf);
809 const syms = try sym_buf.toOwnedSlice();852 const syms = try sym_buf.toOwnedSlice();
810 defer self.base.allocator.free(syms);853 defer self.base.allocator.free(syms);
811 assert(2 + self.atomCount() == iovecs_i); // we didn't write all the decls854 assert(2 + self.atomCount() - self.externCount() == iovecs_i); // we didn't write all the decls
812 iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len };855 iovecs[iovecs_i] = .{ .iov_base = syms.ptr, .iov_len = syms.len };
813 iovecs_i += 1;856 iovecs_i += 1;
814 iovecs[iovecs_i] = .{ .iov_base = linecountinfo.items.ptr, .iov_len = linecountinfo.items.len };857 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...@@ -836,28 +879,46 @@ pub fn flushModule(self: *Plan9, comp: *Compilation, prog_node: *std.Progress.No
836 const source_atom_index = kv.key_ptr.*;879 const source_atom_index = kv.key_ptr.*;
837 const source_atom = self.getAtom(source_atom_index);880 const source_atom = self.getAtom(source_atom_index);
838 const source_atom_symbol = self.syms.items[source_atom.sym_index.?];881 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();
839 for (kv.value_ptr.items) |reloc| {884 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
845 const offset = reloc.offset;885 const offset = reloc.offset;
846 const addend = reloc.addend;886 const addend = reloc.addend;
847887 if (reloc.type == .pcrel or reloc.type == .nonpcrel) {
848 const code = source_atom.code.getCode(self);888 const target_atom_index = reloc.target;
849889 const target_atom = self.getAtomPtr(target_atom_index);
850 if (reloc.pcrel) {890 const target_symbol = self.syms.items[target_atom.sym_index.?];
851 const disp = @as(i32, @intCast(target_offset)) - @as(i32, @intCast(source_atom.offset.?)) - 4 - @as(i32, @intCast(offset));891 const target_offset = target_atom.offset.?;
852 mem.writeInt(i32, code[@as(usize, @intCast(offset))..][0..4], @as(i32, @intCast(disp)), self.base.options.target.cpu.arch.endian());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 });
853 } else {908 } 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 };
854 if (!self.sixtyfour_bit) {915 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);
856 } else {917 } 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);
858 }919 }
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 });
859 }921 }
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 });
861 }922 }
862 }923 }
863 }924 }
...@@ -983,7 +1044,24 @@ pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !Atom.Index {...@@ -983,7 +1044,24 @@ pub fn seeDecl(self: *Plan9, decl_index: Module.Decl.Index) !Atom.Index {
983 .exports = .{},1044 .exports = .{},
984 };1045 };
985 }1046 }
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;
987}1065}
9881066
989pub fn updateDeclExports(1067pub fn updateDeclExports(
...@@ -1157,23 +1235,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option...@@ -1157,23 +1235,7 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option
11571235
1158 self.bases = defaultBaseAddrs(options.target.cpu.arch);1236 self.bases = defaultBaseAddrs(options.target.cpu.arch);
11591237
1160 // first 4 symbols in our table are edata, end, etext, and got
1161 try self.syms.appendSlice(self.base.allocator, &.{1238 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 },
1177 // we include the global offset table to make it easier for debugging1239 // we include the global offset table to make it easier for debugging
1178 .{1240 .{
1179 .value = self.getAddr(0, .d), // the global offset table starts at 01241 .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 {...@@ -1202,11 +1264,8 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
1202 const mod = self.base.options.module.?;1264 const mod = self.base.options.module.?;
1203 const ip = &mod.intern_pool;1265 const ip = &mod.intern_pool;
1204 const writer = buf.writer();1266 const writer = buf.writer();
1205 // write the first four symbols (edata, etext, end, __GOT)1267 // write __GOT
1206 try self.writeSym(writer, self.syms.items[0]);1268 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]);
1210 // write the f symbols1269 // write the f symbols
1211 {1270 {
1212 var it = self.file_segments.iterator();1271 var it = self.file_segments.iterator();
...@@ -1296,6 +1355,14 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {...@@ -1296,6 +1355,14 @@ pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
1296 }1355 }
1297 }1356 }
1298 }1357 }
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 }
1299}1366}
13001367
1301/// Must be called only after a successful call to `updateDecl`.1368/// Must be called only after a successful call to `updateDecl`.
...@@ -1312,6 +1379,34 @@ pub fn getDeclVAddr(...@@ -1312,6 +1379,34 @@ pub fn getDeclVAddr(
1312) !u64 {1379) !u64 {
1313 const mod = self.base.options.module.?;1380 const mod = self.base.options.module.?;
1314 const decl = mod.declPtr(decl_index);1381 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 }
1315 // we might already know the vaddr1410 // we might already know the vaddr
1316 if (decl.ty.zigTypeTag(mod) == .Fn) {1411 if (decl.ty.zigTypeTag(mod) == .Fn) {
1317 var start = self.bases.text;1412 var start = self.bases.text;
...@@ -1339,7 +1434,7 @@ pub fn getDeclVAddr(...@@ -1339,7 +1434,7 @@ pub fn getDeclVAddr(
1339 .offset = reloc_info.offset,1434 .offset = reloc_info.offset,
1340 .addend = reloc_info.addend,1435 .addend = reloc_info.addend,
1341 });1436 });
1342 return 0xcafebabe;1437 return undefined;
1343}1438}
13441439
1345pub fn addReloc(self: *Plan9, parent_index: Atom.Index, reloc: Reloc) !void {1440pub fn addReloc(self: *Plan9, parent_index: Atom.Index, reloc: Reloc) !void {