authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-10 23:55:40-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-08-11 10:56:20-07:00
log0b3c3c02e30f61da44d6368cb89e1ecb1afb1399
treee39a2f66c4e293c572446a91dfd8796cff9796a1
parentd1917b5c3ac9b79425667dc75768e4f75389f635

linker: delete plan9 support

This experimental target was never fully completed. The operating system is not that interesting or popular anyway, and the maintainer is no longer around. Not worth the maintenance burden. This code can be resurrected later if it is worth it. In such case it will be subject to greater scrutiny.

11 files changed, 19 insertions(+), 1626 deletions(-)

CMakeLists.txt-2
......@@ -612,8 +612,6 @@ set(ZIG_STAGE2_SOURCES
612612 src/link/MachO/synthetic.zig
613613 src/link/MachO/Thunk.zig
614614 src/link/MachO/uuid.zig
615 src/link/Plan9.zig
616 src/link/Plan9/aout.zig
617615 src/link/Queue.zig
618616 src/link/StringTable.zig
619617 src/link/Wasm.zig
src/arch/riscv64/Emit.zig-3
......@@ -125,7 +125,6 @@ pub fn emitMir(emit: *Emit) Error!void {
125125 });
126126 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
127127 },
128 .plan9 => {},
129128 .none => {},
130129 }
131130 },
......@@ -142,7 +141,6 @@ pub fn emitMir(emit: *Emit) Error!void {
142141 });
143142 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
144143 },
145 .plan9 => {},
146144 .none => {},
147145 }
148146 },
......@@ -200,7 +198,6 @@ fn dbgAdvancePCAndLine(emit: *Emit, line: u32, column: u32) Error!void {
200198 emit.prev_di_column = column;
201199 emit.prev_di_pc = emit.code.items.len;
202200 },
203 .plan9 => {},
204201 .none => {},
205202 }
206203}
src/arch/sparc64/Emit.zig-2
......@@ -179,7 +179,6 @@ fn mirDebugPrologueEnd(emit: *Emit) !void {
179179 try dbg_out.setPrologueEnd();
180180 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
181181 },
182 .plan9 => {},
183182 .none => {},
184183 }
185184}
......@@ -190,7 +189,6 @@ fn mirDebugEpilogueBegin(emit: *Emit) !void {
190189 try dbg_out.setEpilogueBegin();
191190 try emit.dbgAdvancePCAndLine(emit.prev_di_line, emit.prev_di_column);
192191 },
193 .plan9 => {},
194192 .none => {},
195193 }
196194}
src/arch/x86_64/Emit.zig+2-45
......@@ -82,7 +82,6 @@ pub fn emitMir(emit: *Emit) Error!void {
8282 }),
8383 else => unreachable,
8484 },
85 .plan9 => {},
8685 .none => {},
8786 }
8887 continue;
......@@ -173,9 +172,6 @@ pub fn emitMir(emit: *Emit) Error!void {
173172 coff_file.getAtom(atom).getSymbolIndex().?
174173 else |err|
175174 return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
176 else if (emit.bin_file.cast(.plan9)) |p9_file|
177 p9_file.getOrCreateAtomForLazySymbol(emit.pt, lazy_sym) catch |err|
178 return emit.fail("{s} creating lazy symbol", .{@errorName(err)})
179175 else
180176 return emit.fail("lazy symbols unimplemented for {s}", .{@tagName(emit.bin_file.tag)}),
181177 .is_extern = false,
......@@ -418,7 +414,6 @@ pub fn emitMir(emit: *Emit) Error!void {
418414 else => unreachable,
419415 .pseudo_dbg_prologue_end_none => switch (emit.debug_output) {
420416 .dwarf => |dwarf| try dwarf.setPrologueEnd(),
421 .plan9 => {},
422417 .none => {},
423418 },
424419 .pseudo_dbg_line_stmt_line_column => try emit.dbgAdvancePCAndLine(.{
......@@ -439,7 +434,6 @@ pub fn emitMir(emit: *Emit) Error!void {
439434 });
440435 try emit.dbgAdvancePCAndLine(emit.prev_di_loc);
441436 },
442 .plan9 => {},
443437 .none => {},
444438 },
445439 .pseudo_dbg_enter_block_none => switch (emit.debug_output) {
......@@ -449,7 +443,6 @@ pub fn emitMir(emit: *Emit) Error!void {
449443 });
450444 try dwarf.enterBlock(emit.code.items.len);
451445 },
452 .plan9 => {},
453446 .none => {},
454447 },
455448 .pseudo_dbg_leave_block_none => switch (emit.debug_output) {
......@@ -459,7 +452,6 @@ pub fn emitMir(emit: *Emit) Error!void {
459452 });
460453 try dwarf.leaveBlock(emit.code.items.len);
461454 },
462 .plan9 => {},
463455 .none => {},
464456 },
465457 .pseudo_dbg_enter_inline_func => switch (emit.debug_output) {
......@@ -469,7 +461,6 @@ pub fn emitMir(emit: *Emit) Error!void {
469461 });
470462 try dwarf.enterInlineFunc(mir_inst.data.ip_index, emit.code.items.len, emit.prev_di_loc.line, emit.prev_di_loc.column);
471463 },
472 .plan9 => {},
473464 .none => {},
474465 },
475466 .pseudo_dbg_leave_inline_func => switch (emit.debug_output) {
......@@ -479,7 +470,6 @@ pub fn emitMir(emit: *Emit) Error!void {
479470 });
480471 try dwarf.leaveInlineFunc(mir_inst.data.ip_index, emit.code.items.len);
481472 },
482 .plan9 => {},
483473 .none => {},
484474 },
485475 .pseudo_dbg_arg_none,
......@@ -613,7 +603,7 @@ pub fn emitMir(emit: *Emit) Error!void {
613603 loc,
614604 );
615605 },
616 .plan9, .none => local_index += 1,
606 .none => local_index += 1,
617607 },
618608 .pseudo_dbg_arg_val, .pseudo_dbg_var_val => switch (emit.debug_output) {
619609 .dwarf => |dwarf| {
......@@ -630,11 +620,10 @@ pub fn emitMir(emit: *Emit) Error!void {
630620 .fromInterned(mir_inst.data.ip_index),
631621 );
632622 },
633 .plan9, .none => local_index += 1,
623 .none => local_index += 1,
634624 },
635625 .pseudo_dbg_var_args_none => switch (emit.debug_output) {
636626 .dwarf => |dwarf| try dwarf.genVarArgsDebugInfo(),
637 .plan9 => {},
638627 .none => {},
639628 },
640629 .pseudo_dead_none => {},
......@@ -929,38 +918,6 @@ fn dbgAdvancePCAndLine(emit: *Emit, loc: Loc) Error!void {
929918 emit.prev_di_loc = loc;
930919 emit.prev_di_pc = emit.code.items.len;
931920 },
932 .plan9 => |dbg_out| {
933 if (delta_pc <= 0) return; // only do this when the pc changes
934
935 // increasing the line number
936 try link.File.Plan9.changeLine(&dbg_out.dbg_line, @intCast(delta_line));
937 // increasing the pc
938 const d_pc_p9 = @as(i64, @intCast(delta_pc)) - dbg_out.pc_quanta;
939 if (d_pc_p9 > 0) {
940 // minus one because if its the last one, we want to leave space to change the line which is one pc quanta
941 var diff = @divExact(d_pc_p9, dbg_out.pc_quanta) - dbg_out.pc_quanta;
942 while (diff > 0) {
943 if (diff < 64) {
944 try dbg_out.dbg_line.append(@intCast(diff + 128));
945 diff = 0;
946 } else {
947 try dbg_out.dbg_line.append(@intCast(64 + 128));
948 diff -= 64;
949 }
950 }
951 if (dbg_out.pcop_change_index) |pci|
952 dbg_out.dbg_line.items[pci] += 1;
953 dbg_out.pcop_change_index = @intCast(dbg_out.dbg_line.items.len - 1);
954 } else if (d_pc_p9 == 0) {
955 // we don't need to do anything, because adding the pc quanta does it for us
956 } else unreachable;
957 if (dbg_out.start_line == null)
958 dbg_out.start_line = emit.prev_di_loc.line;
959 dbg_out.end_line = loc.line;
960 // only do this if the pc changed
961 emit.prev_di_loc = loc;
962 emit.prev_di_pc = emit.code.items.len;
963 },
964921 .none => {},
965922 }
966923}
src/arch/x86_64/Mir.zig-4
......@@ -2004,7 +2004,6 @@ pub fn emit(
20042004 const atom = try cf.getOrCreateAtomForNav(nav);
20052005 break :sym cf.getAtom(atom).getSymbolIndex().?;
20062006 }
2007 if (lf.cast(.plan9)) |p9f| break :sym try p9f.seeNav(pt, nav);
20082007 unreachable;
20092008 },
20102009 .debug_output = debug_output,
......@@ -2015,7 +2014,6 @@ pub fn emit(
20152014 .column = func.lbrace_column,
20162015 .is_stmt = switch (debug_output) {
20172016 .dwarf => |dwarf| dwarf.dwarf.debug_line.header.default_is_stmt,
2018 .plan9 => undefined,
20192017 .none => undefined,
20202018 },
20212019 },
......@@ -2067,8 +2065,6 @@ pub fn emitLazy(
20672065 return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
20682066 break :sym cf.getAtom(atom).getSymbolIndex().?;
20692067 }
2070 if (lf.cast(.plan9)) |p9f| break :sym p9f.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err|
2071 return zcu.codegenFailType(lazy_sym.ty, "{s} creating lazy symbol", .{@errorName(err)});
20722068 unreachable;
20732069 },
20742070 .debug_output = debug_output,
src/codegen.zig-2
......@@ -997,8 +997,6 @@ pub fn genNavRef(
997997 },
998998 .link_once => unreachable,
999999 }
1000 } else if (lf.cast(.plan9)) |p9| {
1001 return .{ .sym_index = try p9.seeNav(pt, nav_index) };
10021000 } else {
10031001 const msg = try ErrorMsg.create(zcu.gpa, src_loc, "TODO genNavRef for target {}", .{target});
10041002 return .{ .fail = msg };
src/link.zig+17-7
......@@ -530,6 +530,7 @@ pub const File = struct {
530530 return &lld.base;
531531 }
532532 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
533 .plan9 => return error.UnsupportedObjectFormat,
533534 inline else => |tag| {
534535 dev.check(tag.devFeature());
535536 const ptr = try tag.Type().open(arena, comp, emit, options);
......@@ -552,6 +553,7 @@ pub const File = struct {
552553 return &lld.base;
553554 }
554555 switch (Tag.fromObjectFormat(comp.root_mod.resolved_target.result.ofmt)) {
556 .plan9 => return error.UnsupportedObjectFormat,
555557 inline else => |tag| {
556558 dev.check(tag.devFeature());
557559 const ptr = try tag.Type().createEmpty(arena, comp, emit, options);
......@@ -571,7 +573,7 @@ pub const File = struct {
571573 const gpa = comp.gpa;
572574 switch (base.tag) {
573575 .lld => assert(base.file == null),
574 .coff, .elf, .macho, .plan9, .wasm, .goff, .xcoff => {
576 .coff, .elf, .macho, .wasm, .goff, .xcoff => {
575577 if (base.file != null) return;
576578 dev.checkAny(&.{ .coff_linker, .elf_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker });
577579 const emit = base.emit;
......@@ -612,6 +614,7 @@ pub const File = struct {
612614 });
613615 },
614616 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
617 .plan9 => unreachable,
615618 }
616619 }
617620
......@@ -659,7 +662,7 @@ pub const File = struct {
659662 }
660663 }
661664 },
662 .coff, .macho, .plan9, .wasm, .goff, .xcoff => if (base.file) |f| {
665 .coff, .macho, .wasm, .goff, .xcoff => if (base.file) |f| {
663666 dev.checkAny(&.{ .coff_linker, .macho_linker, .plan9_linker, .wasm_linker, .goff_linker, .xcoff_linker });
664667 f.close();
665668 base.file = null;
......@@ -675,12 +678,12 @@ pub const File = struct {
675678 }
676679 },
677680 .c, .spirv => dev.checkAny(&.{ .c_linker, .spirv_linker }),
681 .plan9 => unreachable,
678682 }
679683 }
680684
681685 pub const DebugInfoOutput = union(enum) {
682686 dwarf: *Dwarf.WipNav,
683 plan9: *Plan9.DebugInfoOutput,
684687 none,
685688 };
686689 pub const UpdateDebugInfoError = Dwarf.UpdateError;
......@@ -699,7 +702,6 @@ pub const File = struct {
699702 log.debug("getGlobalSymbol '{s}' (expected in '{?s}')", .{ name, lib_name });
700703 switch (base.tag) {
701704 .lld => unreachable,
702 .plan9 => unreachable,
703705 .spirv => unreachable,
704706 .c => unreachable,
705707 inline else => |tag| {
......@@ -717,6 +719,7 @@ pub const File = struct {
717719 assert(nav.status == .fully_resolved);
718720 switch (base.tag) {
719721 .lld => unreachable,
722 .plan9 => unreachable,
720723 inline else => |tag| {
721724 dev.check(tag.devFeature());
722725 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateNav(pt, nav_index);
......@@ -759,6 +762,7 @@ pub const File = struct {
759762 switch (base.tag) {
760763 .lld => unreachable,
761764 .spirv => unreachable, // see corresponding special case in `Zcu.PerThread.runCodegenInner`
765 .plan9 => unreachable,
762766 inline else => |tag| {
763767 dev.check(tag.devFeature());
764768 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateFunc(pt, func_index, mir);
......@@ -788,6 +792,7 @@ pub const File = struct {
788792 .lld => unreachable,
789793 .spirv => {},
790794 .goff, .xcoff => {},
795 .plan9 => unreachable,
791796 inline else => |tag| {
792797 dev.check(tag.devFeature());
793798 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateLineNumber(pt, ti_id);
......@@ -812,6 +817,7 @@ pub const File = struct {
812817 base.releaseLock();
813818 if (base.file) |f| f.close();
814819 switch (base.tag) {
820 .plan9 => unreachable,
815821 inline else => |tag| {
816822 dev.check(tag.devFeature());
817823 @as(*tag.Type(), @fieldParentPtr("base", base)).deinit();
......@@ -851,6 +857,7 @@ pub const File = struct {
851857 }
852858 assert(base.post_prelink);
853859 switch (base.tag) {
860 .plan9 => unreachable,
854861 inline else => |tag| {
855862 dev.check(tag.devFeature());
856863 return @as(*tag.Type(), @fieldParentPtr("base", base)).flush(arena, tid, prog_node);
......@@ -877,6 +884,7 @@ pub const File = struct {
877884 assert(base.comp.zcu.?.llvm_object == null);
878885 switch (base.tag) {
879886 .lld => unreachable,
887 .plan9 => unreachable,
880888 inline else => |tag| {
881889 dev.check(tag.devFeature());
882890 return @as(*tag.Type(), @fieldParentPtr("base", base)).updateExports(pt, exported, export_indices);
......@@ -911,6 +919,7 @@ pub const File = struct {
911919 .spirv => unreachable,
912920 .wasm => unreachable,
913921 .goff, .xcoff => unreachable,
922 .plan9 => unreachable,
914923 inline else => |tag| {
915924 dev.check(tag.devFeature());
916925 return @as(*tag.Type(), @fieldParentPtr("base", base)).getNavVAddr(pt, nav_index, reloc_info);
......@@ -933,6 +942,7 @@ pub const File = struct {
933942 .spirv => unreachable,
934943 .wasm => unreachable,
935944 .goff, .xcoff => unreachable,
945 .plan9 => unreachable,
936946 inline else => |tag| {
937947 dev.check(tag.devFeature());
938948 return @as(*tag.Type(), @fieldParentPtr("base", base)).lowerUav(pt, decl_val, decl_align, src_loc);
......@@ -949,6 +959,7 @@ pub const File = struct {
949959 .spirv => unreachable,
950960 .wasm => unreachable,
951961 .goff, .xcoff => unreachable,
962 .plan9 => unreachable,
952963 inline else => |tag| {
953964 dev.check(tag.devFeature());
954965 return @as(*tag.Type(), @fieldParentPtr("base", base)).getUavVAddr(decl_val, reloc_info);
......@@ -965,8 +976,8 @@ pub const File = struct {
965976 assert(base.comp.zcu.?.llvm_object == null);
966977 switch (base.tag) {
967978 .lld => unreachable,
979 .plan9 => unreachable,
968980
969 .plan9,
970981 .spirv,
971982 .goff,
972983 .xcoff,
......@@ -1116,10 +1127,10 @@ pub const File = struct {
11161127 .c => C,
11171128 .wasm => Wasm,
11181129 .spirv => SpirV,
1119 .plan9 => Plan9,
11201130 .goff => Goff,
11211131 .xcoff => Xcoff,
11221132 .lld => Lld,
1133 .plan9 => comptime unreachable,
11231134 };
11241135 }
11251136
......@@ -1211,7 +1222,6 @@ pub const File = struct {
12111222 pub const Lld = @import("link/Lld.zig");
12121223 pub const C = @import("link/C.zig");
12131224 pub const Coff = @import("link/Coff.zig");
1214 pub const Plan9 = @import("link/Plan9.zig");
12151225 pub const Elf = @import("link/Elf.zig");
12161226 pub const MachO = @import("link/MachO.zig");
12171227 pub const SpirV = @import("link/SpirV.zig");
src/link/Elf/ZigObject.zig-2
......@@ -950,7 +950,6 @@ pub fn getNavVAddr(
950950 .target_sym = this_sym_index,
951951 .target_off = reloc_info.addend,
952952 }),
953 .plan9 => unreachable,
954953 .none => unreachable,
955954 },
956955 }
......@@ -983,7 +982,6 @@ pub fn getUavVAddr(
983982 .target_sym = sym_index,
984983 .target_off = reloc_info.addend,
985984 }),
986 .plan9 => unreachable,
987985 .none => unreachable,
988986 },
989987 }
src/link/MachO/ZigObject.zig-2
......@@ -650,7 +650,6 @@ pub fn getNavVAddr(
650650 .target_sym = sym_index,
651651 .target_off = reloc_info.addend,
652652 }),
653 .plan9 => unreachable,
654653 .none => unreachable,
655654 },
656655 }
......@@ -690,7 +689,6 @@ pub fn getUavVAddr(
690689 .target_sym = sym_index,
691690 .target_off = reloc_info.addend,
692691 }),
693 .plan9 => unreachable,
694692 .none => unreachable,
695693 },
696694 }
src/link/Plan9.zig deleted-1426
......@@ -1,1426 +0,0 @@
1//! This implementation does all the linking work in flush(). A future improvement
2//! would be to add incremental linking in a similar way as ELF does.
3
4const Plan9 = @This();
5const link = @import("../link.zig");
6const Zcu = @import("../Zcu.zig");
7const InternPool = @import("../InternPool.zig");
8const Compilation = @import("../Compilation.zig");
9const aout = @import("Plan9/aout.zig");
10const codegen = @import("../codegen.zig");
11const trace = @import("../tracy.zig").trace;
12const File = link.File;
13const build_options = @import("build_options");
14const Air = @import("../Air.zig");
15const Type = @import("../Type.zig");
16const Value = @import("../Value.zig");
17const AnalUnit = InternPool.AnalUnit;
18
19const std = @import("std");
20const builtin = @import("builtin");
21const mem = std.mem;
22const Allocator = std.mem.Allocator;
23const log = std.log.scoped(.link);
24const assert = std.debug.assert;
25const Path = std.Build.Cache.Path;
26
27base: link.File,
28sixtyfour_bit: bool,
29bases: Bases,
30
31/// A symbol's value is just casted down when compiling
32/// for a 32 bit target.
33/// Does not represent the order or amount of symbols in the file
34/// it is just useful for storing symbols. Some other symbols are in
35/// file_segments.
36syms: std.ArrayListUnmanaged(aout.Sym) = .empty,
37
38/// The plan9 a.out format requires segments of
39/// filenames to be deduplicated, so we use this map to
40/// de duplicate it. The value is the value of the path
41/// component
42file_segments: std.StringArrayHashMapUnmanaged(u16) = .empty,
43/// The value of a 'f' symbol increments by 1 every time, so that no 2 'f'
44/// symbols have the same value.
45file_segments_i: u16 = 1,
46
47path_arena: std.heap.ArenaAllocator,
48
49/// maps a file scope to a hash map of decl to codegen output
50/// this is useful for line debuginfo, since it makes sense to sort by file
51/// The debugger looks for the first file (aout.Sym.Type.z) preceeding the text symbol
52/// of the function to know what file it came from.
53/// If we group the decls by file, it makes it really easy to do this (put the symbol in the correct place)
54fn_nav_table: std.AutoArrayHashMapUnmanaged(
55 Zcu.File.Index,
56 struct { sym_index: u32, functions: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, FnNavOutput) = .empty },
57) = .{},
58/// the code is modified when relocated, so that is why it is mutable
59data_nav_table: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []u8) = .empty,
60/// When `updateExports` is called, we store the export indices here, to be used
61/// during flush.
62nav_exports: std.AutoArrayHashMapUnmanaged(InternPool.Nav.Index, []Zcu.Export.Index) = .empty,
63
64lazy_syms: LazySymbolTable = .{},
65
66uavs: std.AutoHashMapUnmanaged(InternPool.Index, Atom.Index) = .empty,
67
68relocs: std.AutoHashMapUnmanaged(Atom.Index, std.ArrayListUnmanaged(Reloc)) = .empty,
69hdr: aout.ExecHdr = undefined,
70
71// relocs: std.
72magic: u32,
73
74entry_val: ?u64 = null,
75
76got_len: usize = 0,
77// A list of all the free got indexes, so when making a new decl
78// don't make a new one, just use one from here.
79got_index_free_list: std.ArrayListUnmanaged(usize) = .empty,
80
81syms_index_free_list: std.ArrayListUnmanaged(usize) = .empty,
82
83atoms: std.ArrayListUnmanaged(Atom) = .empty,
84navs: std.AutoHashMapUnmanaged(InternPool.Nav.Index, NavMetadata) = .empty,
85
86/// Indices of the three "special" symbols into atoms
87etext_edata_end_atom_indices: [3]?Atom.Index = .{ null, null, null },
88
89const Reloc = struct {
90 target: Atom.Index,
91 offset: u64,
92 addend: u32,
93 type: enum {
94 pcrel,
95 nonpcrel,
96 // for getting the value of the etext symbol; we ignore target
97 special_etext,
98 // for getting the value of the edata symbol; we ignore target
99 special_edata,
100 // for getting the value of the end symbol; we ignore target
101 special_end,
102 } = .nonpcrel,
103};
104
105const Bases = struct {
106 text: u64,
107 /// the Global Offset Table starts at the beginning of the data section
108 data: u64,
109};
110
111const LazySymbolTable = std.AutoArrayHashMapUnmanaged(InternPool.Index, LazySymbolMetadata);
112
113const LazySymbolMetadata = struct {
114 const State = enum { unused, pending_flush, flushed };
115 text_atom: Atom.Index = undefined,
116 rodata_atom: Atom.Index = undefined,
117 text_state: State = .unused,
118 rodata_state: State = .unused,
119
120 fn numberOfAtoms(self: LazySymbolMetadata) u32 {
121 var n: u32 = 0;
122 if (self.text_state != .unused) n += 1;
123 if (self.rodata_state != .unused) n += 1;
124 return n;
125 }
126};
127
128pub const PtrWidth = enum { p32, p64 };
129
130pub const Atom = struct {
131 type: aout.Sym.Type,
132 /// offset in the text or data sects
133 offset: ?u64,
134 /// offset into syms
135 sym_index: ?usize,
136 /// offset into got
137 got_index: ?usize,
138 /// We include the code here to be use in relocs
139 /// In the case of lazy_syms, this atom owns the code.
140 /// But, in the case of function and data decls, they own the code and this field
141 /// is just a pointer for convience.
142 code: CodePtr,
143
144 const CodePtr = struct {
145 code_ptr: ?[*]u8,
146 other: union {
147 code_len: usize,
148 nav_index: InternPool.Nav.Index,
149 },
150 fn fromSlice(slice: []u8) CodePtr {
151 return .{ .code_ptr = slice.ptr, .other = .{ .code_len = slice.len } };
152 }
153 fn getCode(self: CodePtr, plan9: *const Plan9) []u8 {
154 const zcu = plan9.base.comp.zcu.?;
155 const ip = &zcu.intern_pool;
156 return if (self.code_ptr) |p| p[0..self.other.code_len] else blk: {
157 const nav_index = self.other.nav_index;
158 const nav = ip.getNav(nav_index);
159 if (ip.isFunctionType(nav.typeOf(ip))) {
160 const table = plan9.fn_nav_table.get(zcu.navFileScopeIndex(nav_index)).?.functions;
161 const output = table.get(nav_index).?;
162 break :blk output.code;
163 } else {
164 break :blk plan9.data_nav_table.get(nav_index).?;
165 }
166 };
167 }
168 fn getOwnedCode(self: CodePtr) ?[]u8 {
169 return if (self.code_ptr) |p| p[0..self.other.code_len] else null;
170 }
171 };
172
173 pub const Index = u32;
174
175 pub fn getOrCreateOffsetTableEntry(self: *Atom, plan9: *Plan9) usize {
176 if (self.got_index == null) self.got_index = plan9.allocateGotIndex();
177 return self.got_index.?;
178 }
179
180 pub fn getOrCreateSymbolTableEntry(self: *Atom, plan9: *Plan9) !usize {
181 if (self.sym_index == null) self.sym_index = try plan9.allocateSymbolIndex();
182 return self.sym_index.?;
183 }
184
185 // asserts that self.got_index != null
186 pub fn getOffsetTableAddress(self: Atom, plan9: *Plan9) u64 {
187 const target = &plan9.base.comp.root_mod.resolved_target.result;
188 const ptr_bytes = @divExact(target.ptrBitWidth(), 8);
189 const got_addr = plan9.bases.data;
190 const got_index = self.got_index.?;
191 return got_addr + got_index * ptr_bytes;
192 }
193};
194
195/// the plan9 debuginfo output is a bytecode with 4 opcodes
196/// assume all numbers/variables are bytes
197/// 0 w x y z -> interpret w x y z as a big-endian i32, and add it to the line offset
198/// x when x < 65 -> add x to line offset
199/// x when x < 129 -> subtract 64 from x and subtract it from the line offset
200/// x -> subtract 129 from x, multiply it by the quanta of the instruction size
201/// (1 on x86_64), and add it to the pc
202/// after every opcode, add the quanta of the instruction size to the pc
203pub const DebugInfoOutput = struct {
204 /// the actual opcodes
205 dbg_line: std.ArrayList(u8),
206 /// what line the debuginfo starts on
207 /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
208 start_line: ?u32,
209 /// what the line count ends on after codegen
210 /// this helps because the linker might have to insert some opcodes to make sure that the line count starts at the right amount for the next decl
211 end_line: u32,
212 /// the last pc change op
213 /// This is very useful for adding quanta
214 /// to it if its not actually the last one.
215 pcop_change_index: ?u32,
216 /// cached pc quanta
217 pc_quanta: u8,
218};
219
220const NavMetadata = struct {
221 index: Atom.Index,
222 exports: std.ArrayListUnmanaged(usize) = .empty,
223
224 fn getExport(m: NavMetadata, p9: *const Plan9, name: []const u8) ?usize {
225 for (m.exports.items) |exp| {
226 const sym = p9.syms.items[exp];
227 if (mem.eql(u8, name, sym.name)) return exp;
228 }
229 return null;
230 }
231};
232
233const FnNavOutput = struct {
234 /// this code is modified when relocated so it is mutable
235 code: []u8,
236 /// this might have to be modified in the linker, so thats why its mutable
237 lineinfo: []u8,
238 start_line: u32,
239 end_line: u32,
240};
241
242fn getAddr(self: Plan9, addr: u64, t: aout.Sym.Type) u64 {
243 return addr + switch (t) {
244 .T, .t, .l, .L => self.bases.text,
245 .D, .d, .B, .b => self.bases.data,
246 else => unreachable,
247 };
248}
249
250fn getSymAddr(self: Plan9, s: aout.Sym) u64 {
251 return self.getAddr(s.value, s.type);
252}
253
254pub fn defaultBaseAddrs(arch: std.Target.Cpu.Arch) Bases {
255 return switch (arch) {
256 .x86_64 => .{
257 // header size => 40 => 0x28
258 .text = 0x200028,
259 .data = 0x400000,
260 },
261 .x86 => .{
262 // header size => 32 => 0x20
263 .text = 0x200020,
264 .data = 0x400000,
265 },
266 .aarch64 => .{
267 // header size => 40 => 0x28
268 .text = 0x10028,
269 .data = 0x20000,
270 },
271 else => std.debug.panic("find default base address for {}", .{arch}),
272 };
273}
274
275pub fn createEmpty(
276 arena: Allocator,
277 comp: *Compilation,
278 emit: Path,
279 options: link.File.OpenOptions,
280) !*Plan9 {
281 const target = &comp.root_mod.resolved_target.result;
282 const gpa = comp.gpa;
283 const optimize_mode = comp.root_mod.optimize_mode;
284 const output_mode = comp.config.output_mode;
285
286 const sixtyfour_bit: bool = switch (target.ptrBitWidth()) {
287 0...32 => false,
288 33...64 => true,
289 else => return error.UnsupportedP9Architecture,
290 };
291
292 const self = try arena.create(Plan9);
293 self.* = .{
294 .path_arena = std.heap.ArenaAllocator.init(gpa),
295 .base = .{
296 .tag = .plan9,
297 .comp = comp,
298 .emit = emit,
299 .gc_sections = options.gc_sections orelse (optimize_mode != .Debug and output_mode != .Obj),
300 .print_gc_sections = options.print_gc_sections,
301 .stack_size = options.stack_size orelse 16777216,
302 .allow_shlib_undefined = options.allow_shlib_undefined orelse false,
303 .file = null,
304 .build_id = options.build_id,
305 },
306 .sixtyfour_bit = sixtyfour_bit,
307 .bases = undefined,
308 .magic = try aout.magicFromArch(target.cpu.arch),
309 };
310 // a / will always be in a file path
311 try self.file_segments.put(gpa, "/", 1);
312 return self;
313}
314
315fn putFn(self: *Plan9, nav_index: InternPool.Nav.Index, out: FnNavOutput) !void {
316 const comp = self.base.comp;
317 const gpa = comp.gpa;
318 const zcu = comp.zcu.?;
319 const file_scope = zcu.navFileScopeIndex(nav_index);
320 const fn_map_res = try self.fn_nav_table.getOrPut(gpa, file_scope);
321 if (fn_map_res.found_existing) {
322 if (try fn_map_res.value_ptr.functions.fetchPut(gpa, nav_index, out)) |old_entry| {
323 gpa.free(old_entry.value.code);
324 gpa.free(old_entry.value.lineinfo);
325 }
326 } else {
327 const file = zcu.fileByIndex(file_scope);
328 const arena = self.path_arena.allocator();
329 // each file gets a symbol
330 fn_map_res.value_ptr.* = .{
331 .sym_index = blk: {
332 try self.syms.append(gpa, undefined);
333 try self.syms.append(gpa, undefined);
334 break :blk @as(u32, @intCast(self.syms.items.len - 1));
335 },
336 };
337 try fn_map_res.value_ptr.functions.put(gpa, nav_index, out);
338
339 var a = std.ArrayList(u8).init(arena);
340 errdefer a.deinit();
341 // every 'z' starts with 0
342 try a.append(0);
343 // path component value of '/'
344 try a.writer().writeInt(u16, 1, .big);
345
346 // getting the full file path
347 {
348 const full_path = try file.path.toAbsolute(comp.dirs, gpa);
349 defer gpa.free(full_path);
350 try self.addPathComponents(full_path, &a);
351 }
352
353 // null terminate
354 try a.append(0);
355 const final = try a.toOwnedSlice();
356 self.syms.items[fn_map_res.value_ptr.sym_index - 1] = .{
357 .type = .z,
358 .value = 1,
359 .name = final,
360 };
361 self.syms.items[fn_map_res.value_ptr.sym_index] = .{
362 .type = .z,
363 // just put a giant number, no source file will have this many newlines
364 .value = std.math.maxInt(u31),
365 .name = &.{ 0, 0 },
366 };
367 }
368}
369
370fn addPathComponents(self: *Plan9, path: []const u8, a: *std.ArrayList(u8)) !void {
371 const gpa = self.base.comp.gpa;
372 const sep = std.fs.path.sep;
373 var it = std.mem.tokenizeScalar(u8, path, sep);
374 while (it.next()) |component| {
375 if (self.file_segments.get(component)) |num| {
376 try a.writer().writeInt(u16, num, .big);
377 } else {
378 self.file_segments_i += 1;
379 try self.file_segments.put(gpa, component, self.file_segments_i);
380 try a.writer().writeInt(u16, self.file_segments_i, .big);
381 }
382 }
383}
384
385pub fn updateFunc(
386 self: *Plan9,
387 pt: Zcu.PerThread,
388 func_index: InternPool.Index,
389 mir: *const codegen.AnyMir,
390) link.File.UpdateNavError!void {
391 if (build_options.skip_non_native and builtin.object_format != .plan9) {
392 @panic("Attempted to compile for object format that was disabled by build configuration");
393 }
394
395 const zcu = pt.zcu;
396 const gpa = zcu.gpa;
397 const target = &self.base.comp.root_mod.resolved_target.result;
398 const func = zcu.funcInfo(func_index);
399
400 const atom_idx = try self.seeNav(pt, func.owner_nav);
401
402 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
403 defer code_buffer.deinit(gpa);
404 var dbg_info_output: DebugInfoOutput = .{
405 .dbg_line = std.ArrayList(u8).init(gpa),
406 .start_line = null,
407 .end_line = undefined,
408 .pcop_change_index = null,
409 // we have already checked the target in the linker to make sure it is compatable
410 .pc_quanta = aout.getPCQuant(target.cpu.arch) catch unreachable,
411 };
412 defer dbg_info_output.dbg_line.deinit();
413
414 try codegen.emitFunction(
415 &self.base,
416 pt,
417 zcu.navSrcLoc(func.owner_nav),
418 func_index,
419 mir,
420 &code_buffer,
421 .{ .plan9 = &dbg_info_output },
422 );
423 const code = try code_buffer.toOwnedSlice(gpa);
424 self.getAtomPtr(atom_idx).code = .{
425 .code_ptr = null,
426 .other = .{ .nav_index = func.owner_nav },
427 };
428 const out: FnNavOutput = .{
429 .code = code,
430 .lineinfo = try dbg_info_output.dbg_line.toOwnedSlice(),
431 .start_line = dbg_info_output.start_line.?,
432 .end_line = dbg_info_output.end_line,
433 };
434 try self.putFn(func.owner_nav, out);
435 return self.updateFinish(pt, func.owner_nav);
436}
437
438pub fn updateNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) link.File.UpdateNavError!void {
439 const zcu = pt.zcu;
440 const gpa = zcu.gpa;
441 const ip = &zcu.intern_pool;
442 const nav = ip.getNav(nav_index);
443 const nav_val = zcu.navValue(nav_index);
444 const nav_init = switch (ip.indexToKey(nav_val.toIntern())) {
445 .func => return,
446 .variable => |variable| Value.fromInterned(variable.init),
447 .@"extern" => {
448 log.debug("found extern decl: {f}", .{nav.name.fmt(ip)});
449 return;
450 },
451 else => nav_val,
452 };
453
454 if (nav_init.typeOf(zcu).hasRuntimeBits(zcu)) {
455 const atom_idx = try self.seeNav(pt, nav_index);
456
457 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
458 defer code_buffer.deinit(gpa);
459 // TODO we need the symbol index for symbol in the table of locals for the containing atom
460 try codegen.generateSymbol(
461 &self.base,
462 pt,
463 zcu.navSrcLoc(nav_index),
464 nav_init,
465 &code_buffer,
466 .{ .atom_index = @intCast(atom_idx) },
467 );
468 const code = code_buffer.items;
469 try self.data_nav_table.ensureUnusedCapacity(gpa, 1);
470 const duped_code = try gpa.dupe(u8, code);
471 self.getAtomPtr(self.navs.get(nav_index).?.index).code = .{ .code_ptr = null, .other = .{ .nav_index = nav_index } };
472 if (self.data_nav_table.fetchPutAssumeCapacity(nav_index, duped_code)) |old_entry| {
473 gpa.free(old_entry.value);
474 }
475 try self.updateFinish(pt, nav_index);
476 }
477}
478
479/// called at the end of update{Decl,Func}
480fn updateFinish(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !void {
481 const zcu = pt.zcu;
482 const gpa = zcu.gpa;
483 const ip = &zcu.intern_pool;
484 const nav = ip.getNav(nav_index);
485 const is_fn = ip.isFunctionType(nav.typeOf(ip));
486 const sym_t: aout.Sym.Type = if (is_fn) .t else .d;
487
488 const atom = self.getAtomPtr(self.navs.get(nav_index).?.index);
489 // write the internal linker metadata
490 atom.type = sym_t;
491 // write the symbol
492 // we already have the got index
493 const sym: aout.Sym = .{
494 .value = undefined, // the value of stuff gets filled in in flush
495 .type = atom.type,
496 .name = try gpa.dupe(u8, nav.name.toSlice(ip)),
497 };
498
499 if (atom.sym_index) |s| {
500 self.syms.items[s] = sym;
501 } else {
502 const s = try self.allocateSymbolIndex();
503 atom.sym_index = s;
504 self.syms.items[s] = sym;
505 }
506}
507
508fn allocateSymbolIndex(self: *Plan9) !usize {
509 const gpa = self.base.comp.gpa;
510 if (self.syms_index_free_list.pop()) |i| {
511 return i;
512 } else {
513 _ = try self.syms.addOne(gpa);
514 return self.syms.items.len - 1;
515 }
516}
517
518fn allocateGotIndex(self: *Plan9) usize {
519 if (self.got_index_free_list.pop()) |i| {
520 return i;
521 } else {
522 self.got_len += 1;
523 return self.got_len - 1;
524 }
525}
526
527pub fn changeLine(l: *std.ArrayList(u8), delta_line: i32) !void {
528 if (delta_line > 0 and delta_line < 65) {
529 const toappend = @as(u8, @intCast(delta_line));
530 try l.append(toappend);
531 } else if (delta_line < 0 and delta_line > -65) {
532 const toadd: u8 = @as(u8, @intCast(-delta_line + 64));
533 try l.append(toadd);
534 } else if (delta_line != 0) {
535 try l.append(0);
536 try l.writer().writeInt(i32, delta_line, .big);
537 }
538}
539
540fn externCount(self: *Plan9) usize {
541 var extern_atom_count: usize = 0;
542 for (self.etext_edata_end_atom_indices) |idx| {
543 if (idx != null) extern_atom_count += 1;
544 }
545 return extern_atom_count;
546}
547// counts decls, and lazy syms
548fn atomCount(self: *Plan9) usize {
549 var fn_nav_count: usize = 0;
550 var itf_files = self.fn_nav_table.iterator();
551 while (itf_files.next()) |ent| {
552 // get the submap
553 var submap = ent.value_ptr.functions;
554 fn_nav_count += submap.count();
555 }
556 const data_nav_count = self.data_nav_table.count();
557 var lazy_atom_count: usize = 0;
558 var it_lazy = self.lazy_syms.iterator();
559 while (it_lazy.next()) |kv| {
560 lazy_atom_count += kv.value_ptr.numberOfAtoms();
561 }
562 const uav_atom_count = self.uavs.count();
563 const extern_atom_count = self.externCount();
564 return data_nav_count + fn_nav_count + lazy_atom_count + extern_atom_count + uav_atom_count;
565}
566
567pub fn flush(
568 self: *Plan9,
569 arena: Allocator,
570 /// TODO: stop using this
571 tid: Zcu.PerThread.Id,
572 prog_node: std.Progress.Node,
573) link.File.FlushError!void {
574 if (build_options.skip_non_native and builtin.object_format != .plan9) {
575 @panic("Attempted to compile for object format that was disabled by build configuration");
576 }
577
578 const tracy = trace(@src());
579 defer tracy.end();
580
581 _ = arena; // Has the same lifetime as the call to Compilation.update.
582
583 const comp = self.base.comp;
584 const diags = &comp.link_diags;
585 const gpa = comp.gpa;
586 const target = &comp.root_mod.resolved_target.result;
587
588 switch (comp.config.output_mode) {
589 .Exe => {},
590 .Obj => return diags.fail("writing plan9 object files unimplemented", .{}),
591 .Lib => return diags.fail("writing plan9 lib files unimplemented", .{}),
592 }
593
594 const sub_prog_node = prog_node.start("Flush Module", 0);
595 defer sub_prog_node.end();
596
597 log.debug("flush", .{});
598
599 defer assert(self.hdr.entry != 0x0);
600
601 const pt: Zcu.PerThread = .activate(
602 self.base.comp.zcu orelse return diags.fail("linking without zig source unimplemented", .{}),
603 tid,
604 );
605 defer pt.deactivate();
606
607 // finish up the lazy syms
608 if (self.lazy_syms.getPtr(.none)) |metadata| {
609 // Most lazy symbols can be updated on first use, but
610 // anyerror needs to wait for everything to be flushed.
611 if (metadata.text_state != .unused) try self.updateLazySymbolAtom(
612 pt,
613 .{ .kind = .code, .ty = .anyerror_type },
614 metadata.text_atom,
615 );
616 if (metadata.rodata_state != .unused) try self.updateLazySymbolAtom(
617 pt,
618 .{ .kind = .const_data, .ty = .anyerror_type },
619 metadata.rodata_atom,
620 );
621 }
622 for (self.lazy_syms.values()) |*metadata| {
623 if (metadata.text_state != .unused) metadata.text_state = .flushed;
624 if (metadata.rodata_state != .unused) metadata.rodata_state = .flushed;
625 }
626 // make sure the got table is good
627 const atom_count = self.atomCount();
628 assert(self.got_len == atom_count + self.got_index_free_list.items.len);
629 const got_size = self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
630 var got_table = try gpa.alloc(u8, got_size);
631 defer gpa.free(got_table);
632
633 // + 4 for header, got, symbols, linecountinfo
634 var iovecs = try gpa.alloc(std.posix.iovec_const, self.atomCount() + 4 - self.externCount());
635 defer gpa.free(iovecs);
636
637 const file = self.base.file.?;
638
639 var hdr_buf: [40]u8 = undefined;
640 // account for the fat header
641 const hdr_size: usize = if (self.sixtyfour_bit) 40 else 32;
642 const hdr_slice: []u8 = hdr_buf[0..hdr_size];
643 var foff = hdr_size;
644 iovecs[0] = .{ .base = hdr_slice.ptr, .len = hdr_slice.len };
645 var iovecs_i: usize = 1;
646 var text_i: u64 = 0;
647
648 var linecountinfo = std.ArrayList(u8).init(gpa);
649 defer linecountinfo.deinit();
650 // text
651 {
652 var linecount: i64 = -1;
653 var it_file = self.fn_nav_table.iterator();
654 while (it_file.next()) |fentry| {
655 var it = fentry.value_ptr.functions.iterator();
656 while (it.next()) |entry| {
657 const nav_index = entry.key_ptr.*;
658 const nav = pt.zcu.intern_pool.getNav(nav_index);
659 const atom = self.getAtomPtr(self.navs.get(nav_index).?.index);
660 const out = entry.value_ptr.*;
661 {
662 // connect the previous decl to the next
663 const delta_line = @as(i32, @intCast(out.start_line)) - @as(i32, @intCast(linecount));
664
665 try changeLine(&linecountinfo, delta_line);
666 // TODO change the pc too (maybe?)
667
668 // write out the actual info that was generated in codegen now
669 try linecountinfo.appendSlice(out.lineinfo);
670 linecount = out.end_line;
671 }
672 foff += out.code.len;
673 iovecs[iovecs_i] = .{ .base = out.code.ptr, .len = out.code.len };
674 iovecs_i += 1;
675 const off = self.getAddr(text_i, .t);
676 text_i += out.code.len;
677 atom.offset = off;
678 log.debug("write text nav 0x{x} ({f}), lines {d} to {d}.;__GOT+0x{x} vaddr: 0x{x}", .{ nav_index, nav.name.fmt(&pt.zcu.intern_pool), out.start_line + 1, out.end_line, atom.got_index.? * 8, off });
679 if (!self.sixtyfour_bit) {
680 mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @intCast(off), target.cpu.arch.endian());
681 } else {
682 mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian());
683 }
684 self.syms.items[atom.sym_index.?].value = off;
685 if (self.nav_exports.get(nav_index)) |export_indices| {
686 try self.addNavExports(pt.zcu, nav_index, export_indices);
687 }
688 }
689 }
690 if (linecountinfo.items.len & 1 == 1) {
691 // just a nop to make it even, the plan9 linker does this
692 try linecountinfo.append(129);
693 }
694 }
695 // the text lazy symbols
696 {
697 var it = self.lazy_syms.iterator();
698 while (it.next()) |kv| {
699 const meta = kv.value_ptr;
700 const text_atom = if (meta.text_state != .unused) self.getAtomPtr(meta.text_atom) else continue;
701 const code = text_atom.code.getOwnedCode().?;
702 foff += code.len;
703 iovecs[iovecs_i] = .{ .base = code.ptr, .len = code.len };
704 iovecs_i += 1;
705 const off = self.getAddr(text_i, .t);
706 text_i += code.len;
707 text_atom.offset = off;
708 if (!self.sixtyfour_bit) {
709 mem.writeInt(u32, got_table[text_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian());
710 } else {
711 mem.writeInt(u64, got_table[text_atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian());
712 }
713 self.syms.items[text_atom.sym_index.?].value = off;
714 }
715 }
716 // fix the sym for etext
717 if (self.etext_edata_end_atom_indices[0]) |etext_atom_idx| {
718 const etext_atom = self.getAtom(etext_atom_idx);
719 const val = self.getAddr(text_i, .t);
720 self.syms.items[etext_atom.sym_index.?].value = val;
721 if (!self.sixtyfour_bit) {
722 mem.writeInt(u32, got_table[etext_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), target.cpu.arch.endian());
723 } else {
724 mem.writeInt(u64, got_table[etext_atom.got_index.? * 8 ..][0..8], val, target.cpu.arch.endian());
725 }
726 }
727 // global offset table is in data
728 iovecs[iovecs_i] = .{ .base = got_table.ptr, .len = got_table.len };
729 iovecs_i += 1;
730 // data
731 var data_i: u64 = got_size;
732 {
733 var it = self.data_nav_table.iterator();
734 while (it.next()) |entry| {
735 const nav_index = entry.key_ptr.*;
736 const atom = self.getAtomPtr(self.navs.get(nav_index).?.index);
737 const code = entry.value_ptr.*;
738
739 foff += code.len;
740 iovecs[iovecs_i] = .{ .base = code.ptr, .len = code.len };
741 iovecs_i += 1;
742 const off = self.getAddr(data_i, .d);
743 data_i += code.len;
744 atom.offset = off;
745 if (!self.sixtyfour_bit) {
746 mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian());
747 } else {
748 mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian());
749 }
750 self.syms.items[atom.sym_index.?].value = off;
751 if (self.nav_exports.get(nav_index)) |export_indices| {
752 try self.addNavExports(pt.zcu, nav_index, export_indices);
753 }
754 }
755 {
756 var it_uav = self.uavs.iterator();
757 while (it_uav.next()) |kv| {
758 const atom = self.getAtomPtr(kv.value_ptr.*);
759 const code = atom.code.getOwnedCode().?;
760 log.debug("write anon decl: {s}", .{self.syms.items[atom.sym_index.?].name});
761 foff += code.len;
762 iovecs[iovecs_i] = .{ .base = code.ptr, .len = code.len };
763 iovecs_i += 1;
764 const off = self.getAddr(data_i, .d);
765 data_i += code.len;
766 atom.offset = off;
767 if (!self.sixtyfour_bit) {
768 mem.writeInt(u32, got_table[atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian());
769 } else {
770 mem.writeInt(u64, got_table[atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian());
771 }
772 self.syms.items[atom.sym_index.?].value = off;
773 }
774 }
775 // the lazy data symbols
776 var it_lazy = self.lazy_syms.iterator();
777 while (it_lazy.next()) |kv| {
778 const meta = kv.value_ptr;
779 const data_atom = if (meta.rodata_state != .unused) self.getAtomPtr(meta.rodata_atom) else continue;
780 const code = data_atom.code.getOwnedCode().?; // lazy symbols must own their code
781 foff += code.len;
782 iovecs[iovecs_i] = .{ .base = code.ptr, .len = code.len };
783 iovecs_i += 1;
784 const off = self.getAddr(data_i, .d);
785 data_i += code.len;
786 data_atom.offset = off;
787 if (!self.sixtyfour_bit) {
788 mem.writeInt(u32, got_table[data_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(off)), target.cpu.arch.endian());
789 } else {
790 mem.writeInt(u64, got_table[data_atom.got_index.? * 8 ..][0..8], off, target.cpu.arch.endian());
791 }
792 self.syms.items[data_atom.sym_index.?].value = off;
793 }
794 // edata symbol
795 if (self.etext_edata_end_atom_indices[1]) |edata_atom_idx| {
796 const edata_atom = self.getAtom(edata_atom_idx);
797 const val = self.getAddr(data_i, .b);
798 self.syms.items[edata_atom.sym_index.?].value = val;
799 if (!self.sixtyfour_bit) {
800 mem.writeInt(u32, got_table[edata_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), target.cpu.arch.endian());
801 } else {
802 mem.writeInt(u64, got_table[edata_atom.got_index.? * 8 ..][0..8], val, target.cpu.arch.endian());
803 }
804 }
805 // end symbol (same as edata because native backends don't do .bss yet)
806 if (self.etext_edata_end_atom_indices[2]) |end_atom_idx| {
807 const end_atom = self.getAtom(end_atom_idx);
808 const val = self.getAddr(data_i, .b);
809 self.syms.items[end_atom.sym_index.?].value = val;
810 if (!self.sixtyfour_bit) {
811 mem.writeInt(u32, got_table[end_atom.got_index.? * 4 ..][0..4], @as(u32, @intCast(val)), target.cpu.arch.endian());
812 } else {
813 log.debug("write end (got_table[0x{x}] = 0x{x})", .{ end_atom.got_index.? * 8, val });
814 mem.writeInt(u64, got_table[end_atom.got_index.? * 8 ..][0..8], val, target.cpu.arch.endian());
815 }
816 }
817 }
818 var sym_buf = std.ArrayList(u8).init(gpa);
819 try self.writeSyms(&sym_buf);
820 const syms = try sym_buf.toOwnedSlice();
821 defer gpa.free(syms);
822 assert(2 + self.atomCount() - self.externCount() == iovecs_i); // we didn't write all the decls
823 iovecs[iovecs_i] = .{ .base = syms.ptr, .len = syms.len };
824 iovecs_i += 1;
825 iovecs[iovecs_i] = .{ .base = linecountinfo.items.ptr, .len = linecountinfo.items.len };
826 iovecs_i += 1;
827 // generate the header
828 self.hdr = .{
829 .magic = self.magic,
830 .text = @as(u32, @intCast(text_i)),
831 .data = @as(u32, @intCast(data_i)),
832 .syms = @as(u32, @intCast(syms.len)),
833 .bss = 0,
834 .spsz = 0,
835 .pcsz = @as(u32, @intCast(linecountinfo.items.len)),
836 .entry = @as(u32, @intCast(self.entry_val.?)),
837 };
838 @memcpy(hdr_slice, self.hdr.toU8s()[0..hdr_size]);
839 // write the fat header for 64 bit entry points
840 if (self.sixtyfour_bit) {
841 mem.writeInt(u64, hdr_buf[32..40], self.entry_val.?, .big);
842 }
843 // perform the relocs
844 {
845 var it = self.relocs.iterator();
846 while (it.next()) |kv| {
847 const source_atom_index = kv.key_ptr.*;
848 const source_atom = self.getAtom(source_atom_index);
849 const source_atom_symbol = self.syms.items[source_atom.sym_index.?];
850 const code = source_atom.code.getCode(self);
851 const endian = target.cpu.arch.endian();
852 for (kv.value_ptr.items) |reloc| {
853 const offset = reloc.offset;
854 const addend = reloc.addend;
855 if (reloc.type == .pcrel or reloc.type == .nonpcrel) {
856 const target_atom_index = reloc.target;
857 const target_atom = self.getAtomPtr(target_atom_index);
858 const target_symbol = self.syms.items[target_atom.sym_index.?];
859 const target_offset = target_atom.offset.?;
860
861 switch (reloc.type) {
862 .pcrel => {
863 const disp = @as(i32, @intCast(target_offset)) - @as(i32, @intCast(source_atom.offset.?)) - 4 - @as(i32, @intCast(offset));
864 mem.writeInt(i32, code[@as(usize, @intCast(offset))..][0..4], @as(i32, @intCast(disp)), endian);
865 },
866 .nonpcrel => {
867 if (!self.sixtyfour_bit) {
868 mem.writeInt(u32, code[@intCast(offset)..][0..4], @as(u32, @intCast(target_offset + addend)), endian);
869 } else {
870 mem.writeInt(u64, code[@intCast(offset)..][0..8], target_offset + addend, endian);
871 }
872 },
873 else => unreachable,
874 }
875 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 });
876 } else {
877 const addr = switch (reloc.type) {
878 .special_etext => self.syms.items[self.getAtom(self.etext_edata_end_atom_indices[0].?).sym_index.?].value,
879 .special_edata => self.syms.items[self.getAtom(self.etext_edata_end_atom_indices[1].?).sym_index.?].value,
880 .special_end => self.syms.items[self.getAtom(self.etext_edata_end_atom_indices[2].?).sym_index.?].value,
881 else => unreachable,
882 };
883 if (!self.sixtyfour_bit) {
884 mem.writeInt(u32, code[@intCast(offset)..][0..4], @as(u32, @intCast(addr + addend)), endian);
885 } else {
886 mem.writeInt(u64, code[@intCast(offset)..][0..8], addr + addend, endian);
887 }
888 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 });
889 }
890 }
891 }
892 }
893 file.pwritevAll(iovecs, 0) catch |err| return diags.fail("failed to write file: {s}", .{@errorName(err)});
894}
895fn addNavExports(
896 self: *Plan9,
897 zcu: *Zcu,
898 nav_index: InternPool.Nav.Index,
899 export_indices: []const Zcu.Export.Index,
900) !void {
901 const gpa = self.base.comp.gpa;
902 const metadata = self.navs.getPtr(nav_index).?;
903 const atom = self.getAtom(metadata.index);
904
905 for (export_indices) |export_idx| {
906 const exp = export_idx.ptr(zcu);
907 const exp_name = exp.opts.name.toSlice(&zcu.intern_pool);
908 // plan9 does not support custom sections
909 if (exp.opts.section.unwrap()) |section_name| {
910 if (!section_name.eqlSlice(".text", &zcu.intern_pool) and
911 !section_name.eqlSlice(".data", &zcu.intern_pool))
912 {
913 try zcu.failed_exports.put(zcu.gpa, export_idx, try Zcu.ErrorMsg.create(
914 gpa,
915 zcu.navSrcLoc(nav_index),
916 "plan9 does not support extra sections",
917 .{},
918 ));
919 break;
920 }
921 }
922 const sym: aout.Sym = .{
923 .value = atom.offset.?,
924 .type = atom.type.toGlobal(),
925 .name = try gpa.dupe(u8, exp_name),
926 };
927
928 if (metadata.getExport(self, exp_name)) |i| {
929 self.syms.items[i] = sym;
930 } else {
931 try self.syms.append(gpa, sym);
932 try metadata.exports.append(gpa, self.syms.items.len - 1);
933 }
934 }
935}
936
937fn createAtom(self: *Plan9) !Atom.Index {
938 const gpa = self.base.comp.gpa;
939 const index = @as(Atom.Index, @intCast(self.atoms.items.len));
940 const atom = try self.atoms.addOne(gpa);
941 atom.* = .{
942 .type = .t,
943 .offset = null,
944 .sym_index = null,
945 .got_index = null,
946 .code = undefined,
947 };
948 return index;
949}
950
951pub fn seeNav(self: *Plan9, pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) !Atom.Index {
952 const zcu = pt.zcu;
953 const ip = &zcu.intern_pool;
954 const gpa = zcu.gpa;
955 const gop = try self.navs.getOrPut(gpa, nav_index);
956 if (!gop.found_existing) {
957 const index = try self.createAtom();
958 self.getAtomPtr(index).got_index = self.allocateGotIndex();
959 gop.value_ptr.* = .{
960 .index = index,
961 .exports = .{},
962 };
963 }
964 const atom_idx = gop.value_ptr.index;
965 // handle externs here because they might not get updateDecl called on them
966 const nav = ip.getNav(nav_index);
967 if (nav.getExtern(ip) != null) {
968 // this is a "phantom atom" - it is never actually written to disk, just convenient for us to store stuff about externs
969 if (nav.name.eqlSlice("etext", ip)) {
970 self.etext_edata_end_atom_indices[0] = atom_idx;
971 } else if (nav.name.eqlSlice("edata", ip)) {
972 self.etext_edata_end_atom_indices[1] = atom_idx;
973 } else if (nav.name.eqlSlice("end", ip)) {
974 self.etext_edata_end_atom_indices[2] = atom_idx;
975 }
976 try self.updateFinish(pt, nav_index);
977 log.debug("seeNav(extern) for {f} (got_addr=0x{x})", .{
978 nav.name.fmt(ip),
979 self.getAtom(atom_idx).getOffsetTableAddress(self),
980 });
981 } else log.debug("seeNav for {f}", .{nav.name.fmt(ip)});
982 return atom_idx;
983}
984
985pub fn updateExports(
986 self: *Plan9,
987 pt: Zcu.PerThread,
988 exported: Zcu.Exported,
989 export_indices: []const Zcu.Export.Index,
990) !void {
991 const gpa = self.base.comp.gpa;
992 switch (exported) {
993 .uav => @panic("TODO: plan9 updateExports handling values"),
994 .nav => |nav| {
995 _ = try self.seeNav(pt, nav);
996 if (self.nav_exports.fetchSwapRemove(nav)) |kv| {
997 gpa.free(kv.value);
998 }
999 try self.nav_exports.ensureUnusedCapacity(gpa, 1);
1000 const duped_indices = try gpa.dupe(Zcu.Export.Index, export_indices);
1001 self.nav_exports.putAssumeCapacityNoClobber(nav, duped_indices);
1002 },
1003 }
1004 // all proper work is done in flush
1005}
1006
1007pub fn getOrCreateAtomForLazySymbol(self: *Plan9, pt: Zcu.PerThread, lazy_sym: File.LazySymbol) !Atom.Index {
1008 const gop = try self.lazy_syms.getOrPut(pt.zcu.gpa, lazy_sym.ty);
1009 errdefer _ = if (!gop.found_existing) self.lazy_syms.pop();
1010
1011 if (!gop.found_existing) gop.value_ptr.* = .{};
1012
1013 const atom_ptr, const state_ptr = switch (lazy_sym.kind) {
1014 .code => .{ &gop.value_ptr.text_atom, &gop.value_ptr.text_state },
1015 .const_data => .{ &gop.value_ptr.rodata_atom, &gop.value_ptr.rodata_state },
1016 };
1017 switch (state_ptr.*) {
1018 .unused => atom_ptr.* = try self.createAtom(),
1019 .pending_flush => return atom_ptr.*,
1020 .flushed => {},
1021 }
1022 state_ptr.* = .pending_flush;
1023 const atom = atom_ptr.*;
1024 _ = try self.getAtomPtr(atom).getOrCreateSymbolTableEntry(self);
1025 _ = self.getAtomPtr(atom).getOrCreateOffsetTableEntry(self);
1026 // anyerror needs to be deferred until flush
1027 if (lazy_sym.ty != .anyerror_type) try self.updateLazySymbolAtom(pt, lazy_sym, atom);
1028 return atom;
1029}
1030
1031fn updateLazySymbolAtom(
1032 self: *Plan9,
1033 pt: Zcu.PerThread,
1034 sym: File.LazySymbol,
1035 atom_index: Atom.Index,
1036) error{ LinkFailure, OutOfMemory }!void {
1037 const gpa = pt.zcu.gpa;
1038 const comp = self.base.comp;
1039 const diags = &comp.link_diags;
1040
1041 var required_alignment: InternPool.Alignment = .none;
1042 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1043 defer code_buffer.deinit(gpa);
1044
1045 // create the symbol for the name
1046 const name = try std.fmt.allocPrint(gpa, "__lazy_{s}_{f}", .{
1047 @tagName(sym.kind),
1048 Type.fromInterned(sym.ty).fmt(pt),
1049 });
1050
1051 const symbol: aout.Sym = .{
1052 .value = undefined,
1053 .type = if (sym.kind == .code) .t else .d,
1054 .name = name,
1055 };
1056 self.syms.items[self.getAtomPtr(atom_index).sym_index.?] = symbol;
1057
1058 // generate the code
1059 const src = Type.fromInterned(sym.ty).srcLocOrNull(pt.zcu) orelse Zcu.LazySrcLoc.unneeded;
1060 codegen.generateLazySymbol(
1061 &self.base,
1062 pt,
1063 src,
1064 sym,
1065 &required_alignment,
1066 &code_buffer,
1067 .none,
1068 .{ .atom_index = @intCast(atom_index) },
1069 ) catch |err| switch (err) {
1070 error.OutOfMemory => return error.OutOfMemory,
1071 error.CodegenFail => return error.LinkFailure,
1072 error.Overflow,
1073 error.RelocationNotByteAligned,
1074 => return diags.fail("unable to codegen: {s}", .{@errorName(err)}),
1075 };
1076 const code = code_buffer.items;
1077 // duped_code is freed when the atom is freed
1078 const duped_code = try gpa.dupe(u8, code);
1079 errdefer gpa.free(duped_code);
1080 self.getAtomPtr(atom_index).code = .{
1081 .code_ptr = duped_code.ptr,
1082 .other = .{ .code_len = duped_code.len },
1083 };
1084}
1085
1086pub fn deinit(self: *Plan9) void {
1087 const gpa = self.base.comp.gpa;
1088 {
1089 var it = self.relocs.valueIterator();
1090 while (it.next()) |relocs| {
1091 relocs.deinit(gpa);
1092 }
1093 self.relocs.deinit(gpa);
1094 }
1095 var it_lzc = self.lazy_syms.iterator();
1096 while (it_lzc.next()) |kv| {
1097 if (kv.value_ptr.text_state != .unused)
1098 gpa.free(self.syms.items[self.getAtom(kv.value_ptr.text_atom).sym_index.?].name);
1099 if (kv.value_ptr.rodata_state != .unused)
1100 gpa.free(self.syms.items[self.getAtom(kv.value_ptr.rodata_atom).sym_index.?].name);
1101 }
1102 self.lazy_syms.deinit(gpa);
1103 var itf_files = self.fn_nav_table.iterator();
1104 while (itf_files.next()) |ent| {
1105 // get the submap
1106 var submap = ent.value_ptr.functions;
1107 defer submap.deinit(gpa);
1108 var itf = submap.iterator();
1109 while (itf.next()) |entry| {
1110 gpa.free(entry.value_ptr.code);
1111 gpa.free(entry.value_ptr.lineinfo);
1112 }
1113 }
1114 self.fn_nav_table.deinit(gpa);
1115 var itd = self.data_nav_table.iterator();
1116 while (itd.next()) |entry| {
1117 gpa.free(entry.value_ptr.*);
1118 }
1119 var it_uav = self.uavs.iterator();
1120 while (it_uav.next()) |entry| {
1121 const sym_index = self.getAtom(entry.value_ptr.*).sym_index.?;
1122 gpa.free(self.syms.items[sym_index].name);
1123 }
1124 self.data_nav_table.deinit(gpa);
1125 for (self.nav_exports.values()) |export_indices| {
1126 gpa.free(export_indices);
1127 }
1128 self.nav_exports.deinit(gpa);
1129 self.syms.deinit(gpa);
1130 self.got_index_free_list.deinit(gpa);
1131 self.syms_index_free_list.deinit(gpa);
1132 self.file_segments.deinit(gpa);
1133 self.path_arena.deinit();
1134 for (self.atoms.items) |a| {
1135 if (a.code.getOwnedCode()) |c| {
1136 gpa.free(c);
1137 }
1138 }
1139 self.atoms.deinit(gpa);
1140
1141 {
1142 var it = self.navs.iterator();
1143 while (it.next()) |entry| {
1144 entry.value_ptr.exports.deinit(gpa);
1145 }
1146 self.navs.deinit(gpa);
1147 }
1148}
1149
1150pub fn open(
1151 arena: Allocator,
1152 comp: *Compilation,
1153 emit: Path,
1154 options: link.File.OpenOptions,
1155) !*Plan9 {
1156 const target = &comp.root_mod.resolved_target.result;
1157 const use_lld = build_options.have_llvm and comp.config.use_lld;
1158 const use_llvm = comp.config.use_llvm;
1159
1160 assert(!use_llvm); // Caught by Compilation.Config.resolve.
1161 assert(!use_lld); // Caught by Compilation.Config.resolve.
1162 assert(target.ofmt == .plan9);
1163
1164 const self = try createEmpty(arena, comp, emit, options);
1165 errdefer self.base.destroy();
1166
1167 const file = try emit.root_dir.handle.createFile(emit.sub_path, .{
1168 .read = true,
1169 .mode = link.File.determineMode(comp.config.output_mode, comp.config.link_mode),
1170 });
1171 errdefer file.close();
1172 self.base.file = file;
1173
1174 self.bases = defaultBaseAddrs(target.cpu.arch);
1175
1176 const gpa = comp.gpa;
1177
1178 try self.syms.appendSlice(gpa, &.{
1179 // we include the global offset table to make it easier for debugging
1180 .{
1181 .value = self.getAddr(0, .d), // the global offset table starts at 0
1182 .type = .d,
1183 .name = "__GOT",
1184 },
1185 });
1186
1187 return self;
1188}
1189
1190pub fn writeSym(self: *Plan9, w: anytype, sym: aout.Sym) !void {
1191 // log.debug("write sym{{name: {s}, value: {x}}}", .{ sym.name, sym.value });
1192 if (sym.type == .bad) return; // we don't want to write free'd symbols
1193 if (!self.sixtyfour_bit) {
1194 try w.writeInt(u32, @as(u32, @intCast(sym.value)), .big);
1195 } else {
1196 try w.writeInt(u64, sym.value, .big);
1197 }
1198 try w.writeByte(@intFromEnum(sym.type));
1199 try w.writeAll(sym.name);
1200 try w.writeByte(0);
1201}
1202
1203pub fn writeSyms(self: *Plan9, buf: *std.ArrayList(u8)) !void {
1204 const zcu = self.base.comp.zcu.?;
1205 const ip = &zcu.intern_pool;
1206 const writer = buf.writer();
1207 // write __GOT
1208 try self.writeSym(writer, self.syms.items[0]);
1209 // write the f symbols
1210 {
1211 var it = self.file_segments.iterator();
1212 while (it.next()) |entry| {
1213 try self.writeSym(writer, .{
1214 .type = .f,
1215 .value = entry.value_ptr.*,
1216 .name = entry.key_ptr.*,
1217 });
1218 }
1219 }
1220
1221 // write the data symbols
1222 {
1223 var it = self.data_nav_table.iterator();
1224 while (it.next()) |entry| {
1225 const nav_index = entry.key_ptr.*;
1226 const nav_metadata = self.navs.get(nav_index).?;
1227 const atom = self.getAtom(nav_metadata.index);
1228 const sym = self.syms.items[atom.sym_index.?];
1229 try self.writeSym(writer, sym);
1230 if (self.nav_exports.get(nav_index)) |export_indices| {
1231 for (export_indices) |export_idx| {
1232 const exp = export_idx.ptr(zcu);
1233 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
1234 try self.writeSym(writer, self.syms.items[exp_i]);
1235 }
1236 }
1237 }
1238 }
1239 }
1240 // the data lazy symbols
1241 {
1242 var it = self.lazy_syms.iterator();
1243 while (it.next()) |kv| {
1244 const meta = kv.value_ptr;
1245 const data_atom = if (meta.rodata_state != .unused) self.getAtomPtr(meta.rodata_atom) else continue;
1246 const sym = self.syms.items[data_atom.sym_index.?];
1247 try self.writeSym(writer, sym);
1248 }
1249 }
1250 // text symbols are the hardest:
1251 // the file of a text symbol is the .z symbol before it
1252 // so we have to write everything in the right order
1253 {
1254 var it_file = self.fn_nav_table.iterator();
1255 while (it_file.next()) |fentry| {
1256 var symidx_and_submap = fentry.value_ptr;
1257 // write the z symbols
1258 try self.writeSym(writer, self.syms.items[symidx_and_submap.sym_index - 1]);
1259 try self.writeSym(writer, self.syms.items[symidx_and_submap.sym_index]);
1260
1261 // write all the decls come from the file of the z symbol
1262 var submap_it = symidx_and_submap.functions.iterator();
1263 while (submap_it.next()) |entry| {
1264 const nav_index = entry.key_ptr.*;
1265 const nav_metadata = self.navs.get(nav_index).?;
1266 const atom = self.getAtom(nav_metadata.index);
1267 const sym = self.syms.items[atom.sym_index.?];
1268 try self.writeSym(writer, sym);
1269 if (self.nav_exports.get(nav_index)) |export_indices| {
1270 for (export_indices) |export_idx| {
1271 const exp = export_idx.ptr(zcu);
1272 if (nav_metadata.getExport(self, exp.opts.name.toSlice(ip))) |exp_i| {
1273 const s = self.syms.items[exp_i];
1274 if (mem.eql(u8, s.name, "_start"))
1275 self.entry_val = s.value;
1276 try self.writeSym(writer, s);
1277 }
1278 }
1279 }
1280 }
1281 }
1282 // the text lazy symbols
1283 {
1284 var it = self.lazy_syms.iterator();
1285 while (it.next()) |kv| {
1286 const meta = kv.value_ptr;
1287 const text_atom = if (meta.text_state != .unused) self.getAtomPtr(meta.text_atom) else continue;
1288 const sym = self.syms.items[text_atom.sym_index.?];
1289 try self.writeSym(writer, sym);
1290 }
1291 }
1292 }
1293 // special symbols
1294 for (self.etext_edata_end_atom_indices) |idx| {
1295 if (idx) |atom_idx| {
1296 const atom = self.getAtom(atom_idx);
1297 const sym = self.syms.items[atom.sym_index.?];
1298 try self.writeSym(writer, sym);
1299 }
1300 }
1301}
1302
1303pub fn updateLineNumber(self: *Plan9, pt: Zcu.PerThread, ti_id: InternPool.TrackedInst.Index) !void {
1304 _ = self;
1305 _ = pt;
1306 _ = ti_id;
1307}
1308
1309pub fn getNavVAddr(
1310 self: *Plan9,
1311 pt: Zcu.PerThread,
1312 nav_index: InternPool.Nav.Index,
1313 reloc_info: link.File.RelocInfo,
1314) !u64 {
1315 const ip = &pt.zcu.intern_pool;
1316 const nav = ip.getNav(nav_index);
1317 log.debug("getDeclVAddr for {f}", .{nav.name.fmt(ip)});
1318 if (nav.getExtern(ip) != null) {
1319 if (nav.name.eqlSlice("etext", ip)) {
1320 try self.addReloc(reloc_info.parent.atom_index, .{
1321 .target = undefined,
1322 .offset = reloc_info.offset,
1323 .addend = reloc_info.addend,
1324 .type = .special_etext,
1325 });
1326 } else if (nav.name.eqlSlice("edata", ip)) {
1327 try self.addReloc(reloc_info.parent.atom_index, .{
1328 .target = undefined,
1329 .offset = reloc_info.offset,
1330 .addend = reloc_info.addend,
1331 .type = .special_edata,
1332 });
1333 } else if (nav.name.eqlSlice("end", ip)) {
1334 try self.addReloc(reloc_info.parent.atom_index, .{
1335 .target = undefined,
1336 .offset = reloc_info.offset,
1337 .addend = reloc_info.addend,
1338 .type = .special_end,
1339 });
1340 }
1341 // TODO handle other extern variables and functions
1342 return undefined;
1343 }
1344 // otherwise, we just add a relocation
1345 const atom_index = try self.seeNav(pt, nav_index);
1346 // the parent_atom_index in this case is just the decl_index of the parent
1347 try self.addReloc(reloc_info.parent.atom_index, .{
1348 .target = atom_index,
1349 .offset = reloc_info.offset,
1350 .addend = reloc_info.addend,
1351 });
1352 return undefined;
1353}
1354
1355pub fn lowerUav(
1356 self: *Plan9,
1357 pt: Zcu.PerThread,
1358 uav: InternPool.Index,
1359 explicit_alignment: InternPool.Alignment,
1360 src_loc: Zcu.LazySrcLoc,
1361) !codegen.SymbolResult {
1362 _ = explicit_alignment;
1363 // example:
1364 // const ty = mod.intern_pool.typeOf(decl_val).toType();
1365 // const val = decl_val.toValue();
1366 // The symbol name can be something like `__anon_{d}` with `@intFromEnum(decl_val)`.
1367 // It doesn't have an owner decl because it's just an unnamed constant that might
1368 // be used by more than one function, however, its address is being used so we need
1369 // to put it in some location.
1370 // ...
1371 const gpa = self.base.comp.gpa;
1372 const gop = try self.uavs.getOrPut(gpa, uav);
1373 if (gop.found_existing) return .{ .sym_index = gop.value_ptr.* };
1374 const val = Value.fromInterned(uav);
1375 const name = try std.fmt.allocPrint(gpa, "__anon_{d}", .{@intFromEnum(uav)});
1376
1377 const index = try self.createAtom();
1378 const got_index = self.allocateGotIndex();
1379 gop.value_ptr.* = index;
1380 // we need to free name latex
1381 var code_buffer: std.ArrayListUnmanaged(u8) = .empty;
1382 defer code_buffer.deinit(gpa);
1383 try codegen.generateSymbol(&self.base, pt, src_loc, val, &code_buffer, .{ .atom_index = index });
1384 const atom_ptr = self.getAtomPtr(index);
1385 atom_ptr.* = .{
1386 .type = .d,
1387 .offset = undefined,
1388 .sym_index = null,
1389 .got_index = got_index,
1390 .code = Atom.CodePtr.fromSlice(try code_buffer.toOwnedSlice(gpa)),
1391 };
1392 _ = try atom_ptr.getOrCreateSymbolTableEntry(self);
1393 self.syms.items[atom_ptr.sym_index.?] = .{
1394 .type = .d,
1395 .value = undefined,
1396 .name = name,
1397 };
1398 return .{ .sym_index = index };
1399}
1400
1401pub fn getUavVAddr(self: *Plan9, uav: InternPool.Index, reloc_info: link.File.RelocInfo) !u64 {
1402 const atom_index = self.uavs.get(uav).?;
1403 try self.addReloc(reloc_info.parent.atom_index, .{
1404 .target = atom_index,
1405 .offset = reloc_info.offset,
1406 .addend = reloc_info.addend,
1407 });
1408 return undefined;
1409}
1410
1411pub fn addReloc(self: *Plan9, parent_index: Atom.Index, reloc: Reloc) !void {
1412 const gpa = self.base.comp.gpa;
1413 const gop = try self.relocs.getOrPut(gpa, parent_index);
1414 if (!gop.found_existing) {
1415 gop.value_ptr.* = .{};
1416 }
1417 try gop.value_ptr.append(gpa, reloc);
1418}
1419
1420pub fn getAtom(self: *const Plan9, index: Atom.Index) Atom {
1421 return self.atoms.items[index];
1422}
1423
1424fn getAtomPtr(self: *Plan9, index: Atom.Index) *Atom {
1425 return &self.atoms.items[index];
1426}
src/link/Plan9/aout.zig deleted-131
......@@ -1,131 +0,0 @@
1const std = @import("std");
2const assert = std.debug.assert;
3
4/// All integers are in big-endian format (needs a byteswap).
5pub const ExecHdr = extern struct {
6 magic: u32,
7 text: u32,
8 data: u32,
9 bss: u32,
10 syms: u32,
11 /// You should truncate this to 32 bits on 64 bit systems, then but the actual 8 bytes
12 /// in the fat header.
13 entry: u32,
14 spsz: u32,
15 pcsz: u32,
16 comptime {
17 assert(@sizeOf(@This()) == 32);
18 }
19 /// It is up to the caller to discard the last 8 bytes if the header is not fat.
20 pub fn toU8s(self: *@This()) [40]u8 {
21 var buf: [40]u8 = undefined;
22 var i: u8 = 0;
23 inline for (std.meta.fields(@This())) |f| {
24 std.mem.writeInt(u32, buf[i..][0..4], @field(self, f.name), .big);
25 i += 4;
26 }
27 return buf;
28 }
29};
30
31pub const Sym = struct {
32 /// Big endian in the file
33 value: u64,
34 type: Type,
35 name: []const u8,
36
37 pub const undefined_symbol: Sym = .{
38 .value = undefined,
39 .type = .bad,
40 .name = "undefined_symbol",
41 };
42
43 /// The type field is one of the following characters with the
44 /// high bit set:
45 /// T text segment symbol
46 /// t static text segment symbol
47 /// L leaf function text segment symbol
48 /// l static leaf function text segment symbol
49 /// D data segment symbol
50 /// d static data segment symbol
51 /// B bss segment symbol
52 /// b static bss segment symbol
53 /// a automatic (local) variable symbol
54 /// p function parameter symbol
55 /// f source file name components
56 /// z source file name
57 /// Z source file line offset
58 /// m for '.frame'
59 pub const Type = enum(u8) {
60 T = 0x80 | 'T',
61 t = 0x80 | 't',
62 L = 0x80 | 'L',
63 l = 0x80 | 'l',
64 D = 0x80 | 'D',
65 d = 0x80 | 'd',
66 B = 0x80 | 'B',
67 b = 0x80 | 'b',
68 a = 0x80 | 'a',
69 p = 0x80 | 'p',
70 f = 0x80 | 'f',
71 z = 0x80 | 'z',
72 Z = 0x80 | 'Z',
73 m = 0x80 | 'm',
74 /// represents an undefined symbol, to be removed in flush
75 bad = 0,
76
77 pub fn toGlobal(self: Type) Type {
78 return switch (self) {
79 .t => .T,
80 .b => .B,
81 .d => .D,
82 else => unreachable,
83 };
84 }
85 };
86};
87
88pub const HDR_MAGIC = 0x00008000;
89pub inline fn _MAGIC(f: anytype, b: anytype) @TypeOf(f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7))) {
90 return f | ((((@as(c_int, 4) * b) + @as(c_int, 0)) * b) + @as(c_int, 7));
91}
92pub const A_MAGIC = _MAGIC(0, 8); // 68020
93pub const I_MAGIC = _MAGIC(0, 11); // intel 386
94pub const J_MAGIC = _MAGIC(0, 12); // intel 960 (retired)
95pub const K_MAGIC = _MAGIC(0, 13); // sparc
96pub const V_MAGIC = _MAGIC(0, 16); // mips 3000 BE
97pub const X_MAGIC = _MAGIC(0, 17); // att dsp 3210 (retired)
98pub const M_MAGIC = _MAGIC(0, 18); // mips 4000 BE
99pub const D_MAGIC = _MAGIC(0, 19); // amd 29000 (retired)
100pub const E_MAGIC = _MAGIC(0, 20); // arm
101pub const Q_MAGIC = _MAGIC(0, 21); // powerpc
102pub const N_MAGIC = _MAGIC(0, 22); // mips 4000 LE
103pub const L_MAGIC = _MAGIC(0, 23); // dec alpha (retired)
104pub const P_MAGIC = _MAGIC(0, 24); // mips 3000 LE
105pub const U_MAGIC = _MAGIC(0, 25); // sparc64
106pub const S_MAGIC = _MAGIC(HDR_MAGIC, 26); // amd64
107pub const T_MAGIC = _MAGIC(HDR_MAGIC, 27); // powerpc64
108pub const R_MAGIC = _MAGIC(HDR_MAGIC, 28); // arm64
109
110pub fn magicFromArch(arch: std.Target.Cpu.Arch) !u32 {
111 return switch (arch) {
112 .x86 => I_MAGIC,
113 .sparc => K_MAGIC, // TODO should sparc64 go here?
114 .mips => V_MAGIC,
115 .arm => E_MAGIC,
116 .aarch64 => R_MAGIC,
117 .powerpc => Q_MAGIC,
118 .powerpc64 => T_MAGIC,
119 .x86_64 => S_MAGIC,
120 else => error.ArchNotSupportedByPlan9,
121 };
122}
123
124/// gets the quantization of pc for the arch
125pub fn getPCQuant(arch: std.Target.Cpu.Arch) !u8 {
126 return switch (arch) {
127 .x86, .x86_64 => 1,
128 .powerpc, .powerpc64, .mips, .sparc, .arm, .aarch64 => 4,
129 else => error.ArchNotSupportedByPlan9,
130 };
131}