authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-08-03 09:41:20-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-08-03 09:41:20-07:00
logf887b0251822f75dc4a3e24ca5337cb681c1eb1f
tree3746b4716751f53029dd73f4f2f541ef48de138c
parent31979b10065b6ab1d00413648daea5907639819e
parentd0fbfd3c9f419ceb7112d06d4a3500e9c13f6044
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #16359 from g-w1/plan9-more-std

Plan 9: more standard library support

9 files changed, 471 insertions(+), 119 deletions(-)

doc/langref.html.in+2-1
......@@ -8467,9 +8467,10 @@ export fn @"A function name that is a complete sentence."() void {}
84678467 {#header_close#}
84688468
84698469 {#header_open|@extern#}
8470 <pre>{#syntax#}@extern(T: type, comptime options: std.builtin.ExternOptions) *T{#endsyntax#}</pre>
8470 <pre>{#syntax#}@extern(T: type, comptime options: std.builtin.ExternOptions) T{#endsyntax#}</pre>
84718471 <p>
84728472 Creates a reference to an external symbol in the output object file.
8473 T must be a pointer type.
84738474 </p>
84748475 {#see_also|@export#}
84758476 {#header_close#}
lib/std/fs.zig+6-4
......@@ -39,7 +39,7 @@ pub const Watch = @import("fs/watch.zig").Watch;
3939/// fit into a UTF-8 encoded array of this length.
4040/// The byte count includes room for a null sentinel byte.
4141pub const MAX_PATH_BYTES = switch (builtin.os.tag) {
42 .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris => os.PATH_MAX,
42 .linux, .macos, .ios, .freebsd, .openbsd, .netbsd, .dragonfly, .haiku, .solaris, .plan9 => os.PATH_MAX,
4343 // Each UTF-16LE character may be expanded to 3 UTF-8 bytes.
4444 // If it would require 4 UTF-8 bytes, then there would be a surrogate
4545 // pair in the UTF-16LE, and we (over)account 3 bytes for it that way.
......@@ -1160,7 +1160,9 @@ pub const Dir = struct {
11601160 return self.openFileW(path_w.span(), flags);
11611161 }
11621162
1163 var os_flags: u32 = os.O.CLOEXEC;
1163 var os_flags: u32 = 0;
1164 if (@hasDecl(os.O, "CLOEXEC")) os_flags = os.O.CLOEXEC;
1165
11641166 // Use the O locking flags if the os supports them to acquire the lock
11651167 // atomically.
11661168 const has_flock_open_flags = @hasDecl(os.O, "EXLOCK");
......@@ -1180,7 +1182,7 @@ pub const Dir = struct {
11801182 if (@hasDecl(os.O, "LARGEFILE")) {
11811183 os_flags |= os.O.LARGEFILE;
11821184 }
1183 if (!flags.allow_ctty) {
1185 if (@hasDecl(os.O, "NOCTTY") and !flags.allow_ctty) {
11841186 os_flags |= os.O.NOCTTY;
11851187 }
11861188 os_flags |= switch (flags.mode) {
......@@ -1196,7 +1198,7 @@ pub const Dir = struct {
11961198
11971199 // WASI doesn't have os.flock so we intetinally check OS prior to the inner if block
11981200 // since it is not compiltime-known and we need to avoid undefined symbol in Wasm.
1199 if (builtin.target.os.tag != .wasi) {
1201 if (@hasDecl(os.system, "LOCK") and builtin.target.os.tag != .wasi) {
12001202 if (!has_flock_open_flags and flags.lock != .none) {
12011203 // TODO: integrate async I/O
12021204 const lock_nonblocking = if (flags.lock_nonblocking) os.LOCK.NB else @as(i32, 0);
lib/std/heap.zig+6
......@@ -21,6 +21,7 @@ pub const WasmAllocator = @import("heap/WasmAllocator.zig");
2121pub const WasmPageAllocator = @import("heap/WasmPageAllocator.zig");
2222pub const PageAllocator = @import("heap/PageAllocator.zig");
2323pub const ThreadSafeAllocator = @import("heap/ThreadSafeAllocator.zig");
24pub const SbrkAllocator = @import("heap/sbrk_allocator.zig").SbrkAllocator;
2425
2526const memory_pool = @import("heap/memory_pool.zig");
2627pub const MemoryPool = memory_pool.MemoryPool;
......@@ -228,6 +229,11 @@ pub const page_allocator = if (builtin.target.isWasm())
228229 .ptr = undefined,
229230 .vtable = &WasmPageAllocator.vtable,
230231 }
232else if (builtin.target.os.tag == .plan9)
233 Allocator{
234 .ptr = undefined,
235 .vtable = &SbrkAllocator(std.os.plan9.sbrk).vtable,
236 }
231237else if (builtin.target.os.tag == .freestanding)
232238 root.os.heap.page_allocator
233239else
lib/std/heap/sbrk_allocator.zig created+161
......@@ -0,0 +1,161 @@
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const math = std.math;
4const Allocator = std.mem.Allocator;
5const mem = std.mem;
6const assert = std.debug.assert;
7
8pub fn SbrkAllocator(comptime sbrk: *const fn (n: usize) usize) type {
9 return struct {
10 pub const vtable = Allocator.VTable{
11 .alloc = alloc,
12 .resize = resize,
13 .free = free,
14 };
15
16 pub const Error = Allocator.Error;
17
18 lock: std.Thread.Mutex = .{},
19
20 const max_usize = math.maxInt(usize);
21 const ushift = math.Log2Int(usize);
22 const bigpage_size = 64 * 1024;
23 const pages_per_bigpage = bigpage_size / mem.page_size;
24 const bigpage_count = max_usize / bigpage_size;
25
26 /// Because of storing free list pointers, the minimum size class is 3.
27 const min_class = math.log2(math.ceilPowerOfTwoAssert(usize, 1 + @sizeOf(usize)));
28 const size_class_count = math.log2(bigpage_size) - min_class;
29 /// 0 - 1 bigpage
30 /// 1 - 2 bigpages
31 /// 2 - 4 bigpages
32 /// etc.
33 const big_size_class_count = math.log2(bigpage_count);
34
35 var next_addrs = [1]usize{0} ** size_class_count;
36 /// For each size class, points to the freed pointer.
37 var frees = [1]usize{0} ** size_class_count;
38 /// For each big size class, points to the freed pointer.
39 var big_frees = [1]usize{0} ** big_size_class_count;
40
41 // TODO don't do the naive locking strategy
42 var lock: std.Thread.Mutex = .{};
43 fn alloc(ctx: *anyopaque, len: usize, log2_align: u8, return_address: usize) ?[*]u8 {
44 _ = ctx;
45 _ = return_address;
46 lock.lock();
47 defer lock.unlock();
48 // Make room for the freelist next pointer.
49 const alignment = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_align));
50 const actual_len = @max(len +| @sizeOf(usize), alignment);
51 const slot_size = math.ceilPowerOfTwo(usize, actual_len) catch return null;
52 const class = math.log2(slot_size) - min_class;
53 if (class < size_class_count) {
54 const addr = a: {
55 const top_free_ptr = frees[class];
56 if (top_free_ptr != 0) {
57 const node = @as(*usize, @ptrFromInt(top_free_ptr + (slot_size - @sizeOf(usize))));
58 frees[class] = node.*;
59 break :a top_free_ptr;
60 }
61
62 const next_addr = next_addrs[class];
63 if (next_addr % mem.page_size == 0) {
64 const addr = allocBigPages(1);
65 if (addr == 0) return null;
66 //std.debug.print("allocated fresh slot_size={d} class={d} addr=0x{x}\n", .{
67 // slot_size, class, addr,
68 //});
69 next_addrs[class] = addr + slot_size;
70 break :a addr;
71 } else {
72 next_addrs[class] = next_addr + slot_size;
73 break :a next_addr;
74 }
75 };
76 return @as([*]u8, @ptrFromInt(addr));
77 }
78 const bigpages_needed = bigPagesNeeded(actual_len);
79 const addr = allocBigPages(bigpages_needed);
80 return @as([*]u8, @ptrFromInt(addr));
81 }
82
83 fn resize(
84 ctx: *anyopaque,
85 buf: []u8,
86 log2_buf_align: u8,
87 new_len: usize,
88 return_address: usize,
89 ) bool {
90 _ = ctx;
91 _ = return_address;
92 lock.lock();
93 defer lock.unlock();
94 // We don't want to move anything from one size class to another, but we
95 // can recover bytes in between powers of two.
96 const buf_align = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_buf_align));
97 const old_actual_len = @max(buf.len + @sizeOf(usize), buf_align);
98 const new_actual_len = @max(new_len +| @sizeOf(usize), buf_align);
99 const old_small_slot_size = math.ceilPowerOfTwoAssert(usize, old_actual_len);
100 const old_small_class = math.log2(old_small_slot_size) - min_class;
101 if (old_small_class < size_class_count) {
102 const new_small_slot_size = math.ceilPowerOfTwo(usize, new_actual_len) catch return false;
103 return old_small_slot_size == new_small_slot_size;
104 } else {
105 const old_bigpages_needed = bigPagesNeeded(old_actual_len);
106 const old_big_slot_pages = math.ceilPowerOfTwoAssert(usize, old_bigpages_needed);
107 const new_bigpages_needed = bigPagesNeeded(new_actual_len);
108 const new_big_slot_pages = math.ceilPowerOfTwo(usize, new_bigpages_needed) catch return false;
109 return old_big_slot_pages == new_big_slot_pages;
110 }
111 }
112
113 fn free(
114 ctx: *anyopaque,
115 buf: []u8,
116 log2_buf_align: u8,
117 return_address: usize,
118 ) void {
119 _ = ctx;
120 _ = return_address;
121 lock.lock();
122 defer lock.unlock();
123 const buf_align = @as(usize, 1) << @as(Allocator.Log2Align, @intCast(log2_buf_align));
124 const actual_len = @max(buf.len + @sizeOf(usize), buf_align);
125 const slot_size = math.ceilPowerOfTwoAssert(usize, actual_len);
126 const class = math.log2(slot_size) - min_class;
127 const addr = @intFromPtr(buf.ptr);
128 if (class < size_class_count) {
129 const node = @as(*usize, @ptrFromInt(addr + (slot_size - @sizeOf(usize))));
130 node.* = frees[class];
131 frees[class] = addr;
132 } else {
133 const bigpages_needed = bigPagesNeeded(actual_len);
134 const pow2_pages = math.ceilPowerOfTwoAssert(usize, bigpages_needed);
135 const big_slot_size_bytes = pow2_pages * bigpage_size;
136 const node = @as(*usize, @ptrFromInt(addr + (big_slot_size_bytes - @sizeOf(usize))));
137 const big_class = math.log2(pow2_pages);
138 node.* = big_frees[big_class];
139 big_frees[big_class] = addr;
140 }
141 }
142
143 inline fn bigPagesNeeded(byte_count: usize) usize {
144 return (byte_count + (bigpage_size + (@sizeOf(usize) - 1))) / bigpage_size;
145 }
146
147 fn allocBigPages(n: usize) usize {
148 const pow2_pages = math.ceilPowerOfTwoAssert(usize, n);
149 const slot_size_bytes = pow2_pages * bigpage_size;
150 const class = math.log2(pow2_pages);
151
152 const top_free_ptr = big_frees[class];
153 if (top_free_ptr != 0) {
154 const node = @as(*usize, @ptrFromInt(top_free_ptr + (slot_size_bytes - @sizeOf(usize))));
155 big_frees[class] = node.*;
156 return top_free_ptr;
157 }
158 return sbrk(pow2_pages * pages_per_bigpage * mem.page_size);
159 }
160 };
161}
lib/std/os/plan9.zig+132-20
......@@ -1,6 +1,12 @@
11const std = @import("../std.zig");
22const builtin = @import("builtin");
33
4pub const fd_t = i32;
5
6pub const STDIN_FILENO = 0;
7pub const STDOUT_FILENO = 1;
8pub const STDERR_FILENO = 2;
9pub const PATH_MAX = 1023;
410pub const syscall_bits = switch (builtin.cpu.arch) {
511 .x86_64 => @import("plan9/x86_64.zig"),
612 else => @compileError("more plan9 syscall implementations (needs more inline asm in stage2"),
......@@ -12,6 +18,43 @@ pub fn getErrno(r: usize) E {
1218 const int = if (signed_r > -4096 and signed_r < 0) -signed_r else 0;
1319 return @as(E, @enumFromInt(int));
1420}
21// The max bytes that can be in the errstr buff
22pub const ERRMAX = 128;
23var errstr_buf: [ERRMAX]u8 = undefined;
24/// Gets whatever the last errstr was
25pub fn errstr() []const u8 {
26 _ = syscall_bits.syscall2(.ERRSTR, @intFromPtr(&errstr_buf), ERRMAX);
27 return std.mem.span(@as([*:0]u8, @ptrCast(&errstr_buf)));
28}
29pub const Plink = anyopaque;
30pub const Tos = extern struct {
31 /// Per process profiling
32 prof: extern struct {
33 /// known to be 0(ptr)
34 pp: *Plink,
35 /// known to be 4(ptr)
36 next: *Plink,
37 last: *Plink,
38 first: *Plink,
39 pid: u32,
40 what: u32,
41 },
42 /// cycle clock frequency if there is one, 0 otherwise
43 cyclefreq: u64,
44 /// cycles spent in kernel
45 kcycles: i64,
46 /// cycles spent in process (kernel + user)
47 pcycles: i64,
48 /// might as well put the pid here
49 pid: u32,
50 clock: u32,
51 // top of stack is here
52};
53
54pub var tos: *Tos = undefined; // set in start.zig
55pub fn getpid() u32 {
56 return tos.pid;
57}
1558pub const SIG = struct {
1659 /// hangup
1760 pub const HUP = 1;
......@@ -57,7 +100,8 @@ pub const SIG = struct {
57100};
58101pub const sigset_t = c_long;
59102pub const empty_sigset = 0;
60pub const siginfo_t = c_long; // TODO plan9 doesn't have sigaction_fn. Sigaction is not a union, but we incude it here to be compatible.
103pub const siginfo_t = c_long;
104// TODO plan9 doesn't have sigaction_fn. Sigaction is not a union, but we incude it here to be compatible.
61105pub const Sigaction = extern struct {
62106 pub const handler_fn = *const fn (c_int) callconv(.C) void;
63107 pub const sigaction_fn = *const fn (c_int, *const siginfo_t, ?*const anyopaque) callconv(.C) void;
......@@ -69,6 +113,9 @@ pub const Sigaction = extern struct {
69113 mask: sigset_t,
70114 flags: c_int,
71115};
116pub const AT = struct {
117 pub const FDCWD = -100; // we just make up a constant; FDCWD and openat don't actually exist in plan9
118};
72119// TODO implement sigaction
73120// right now it is just a shim to allow using start.zig code
74121pub fn sigaction(sig: u6, noalias act: ?*const Sigaction, noalias oact: ?*Sigaction) usize {
......@@ -132,20 +179,48 @@ pub const SYS = enum(usize) {
132179 _NSEC = 53,
133180};
134181
135pub fn pwrite(fd: usize, buf: [*]const u8, count: usize, offset: usize) usize {
136 return syscall_bits.syscall4(.PWRITE, fd, @intFromPtr(buf), count, offset);
182pub fn write(fd: i32, buf: [*]const u8, count: usize) usize {
183 return syscall_bits.syscall4(.PWRITE, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(@as(isize, -1)));
184}
185pub fn pwrite(fd: i32, buf: [*]const u8, count: usize, offset: isize) usize {
186 return syscall_bits.syscall4(.PWRITE, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(offset));
187}
188
189pub fn read(fd: i32, buf: [*]const u8, count: usize) usize {
190 return syscall_bits.syscall4(.PREAD, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(@as(isize, -1)));
191}
192pub fn pread(fd: i32, buf: [*]const u8, count: usize, offset: isize) usize {
193 return syscall_bits.syscall4(.PREAD, @bitCast(@as(isize, fd)), @intFromPtr(buf), count, @bitCast(offset));
194}
195
196pub fn open(path: [*:0]const u8, flags: u32) usize {
197 return syscall_bits.syscall2(.OPEN, @intFromPtr(path), @bitCast(@as(isize, flags)));
137198}
138199
139pub fn pread(fd: usize, buf: [*]const u8, count: usize, offset: usize) usize {
140 return syscall_bits.syscall4(.PREAD, fd, @intFromPtr(buf), count, offset);
200pub fn openat(dirfd: i32, path: [*:0]const u8, flags: u32, _: mode_t) usize {
201 // we skip perms because only create supports perms
202 if (dirfd == AT.FDCWD) { // openat(AT_FDCWD, ...) == open(...)
203 return open(path, flags);
204 }
205 var dir_path_buf: [std.fs.MAX_PATH_BYTES]u8 = undefined;
206 var total_path_buf: [std.fs.MAX_PATH_BYTES + 1]u8 = undefined;
207 const rc = fd2path(dirfd, &dir_path_buf, std.fs.MAX_PATH_BYTES);
208 if (rc != 0) return rc;
209 var fba = std.heap.FixedBufferAllocator.init(&total_path_buf);
210 var alloc = fba.allocator();
211 const dir_path = std.mem.span(@as([*:0]u8, @ptrCast(&dir_path_buf)));
212 const total_path = std.fs.path.join(alloc, &.{ dir_path, std.mem.span(path) }) catch unreachable; // the allocation shouldn't fail because it should not exceed MAX_PATH_BYTES
213 fba.reset();
214 const total_path_z = alloc.dupeZ(u8, total_path) catch unreachable; // should not exceed MAX_PATH_BYTES + 1
215 return open(total_path_z.ptr, flags);
141216}
142217
143pub fn open(path: [*:0]const u8, omode: OpenMode) usize {
144 return syscall_bits.syscall2(.OPEN, @intFromPtr(path), @intFromEnum(omode));
218pub fn fd2path(fd: i32, buf: [*]u8, nbuf: usize) usize {
219 return syscall_bits.syscall3(.FD2PATH, @bitCast(@as(isize, fd)), @intFromPtr(buf), nbuf);
145220}
146221
147pub fn create(path: [*:0]const u8, omode: OpenMode, perms: usize) usize {
148 return syscall_bits.syscall3(.CREATE, @intFromPtr(path), @intFromEnum(omode), perms);
222pub fn create(path: [*:0]const u8, omode: mode_t, perms: usize) usize {
223 return syscall_bits.syscall3(.CREATE, @intFromPtr(path), @bitCast(@as(isize, omode)), perms);
149224}
150225
151226pub fn exit(status: u8) noreturn {
......@@ -163,16 +238,53 @@ pub fn exits(status: ?[*:0]const u8) noreturn {
163238 unreachable;
164239}
165240
166pub fn close(fd: usize) usize {
167 return syscall_bits.syscall1(.CLOSE, fd);
241pub fn close(fd: i32) usize {
242 return syscall_bits.syscall1(.CLOSE, @bitCast(@as(isize, fd)));
168243}
169pub const OpenMode = enum(usize) {
170 OREAD = 0, //* open for read
171 OWRITE = 1, //* write
172 ORDWR = 2, //* read and write
173 OEXEC = 3, //* execute, == read but check execute permission
174 OTRUNC = 16, //* or'ed in (except for exec), truncate file first
175 OCEXEC = 32, //* or'ed in (per file descriptor), close on exec
176 ORCLOSE = 64, //* or'ed in, remove on close
177 OEXCL = 0x1000, //* or'ed in, exclusive create
244pub const mode_t = i32;
245pub const O = struct {
246 pub const READ = 0; // open for read
247 pub const RDONLY = 0;
248 pub const WRITE = 1; // write
249 pub const WRONLY = 1;
250 pub const RDWR = 2; // read and write
251 pub const EXEC = 3; // execute, == read but check execute permission
252 pub const TRUNC = 16; // or'ed in (except for exec), truncate file first
253 pub const CEXEC = 32; // or'ed in (per file descriptor), close on exec
254 pub const RCLOSE = 64; // or'ed in, remove on close
255 pub const EXCL = 0x1000; // or'ed in, exclusive create
178256};
257
258pub const ExecData = struct {
259 pub extern const etext: anyopaque;
260 pub extern const edata: anyopaque;
261 pub extern const end: anyopaque;
262};
263
264/// Brk sets the system's idea of the lowest bss location not
265/// used by the program (called the break) to addr rounded up to
266/// the next multiple of 8 bytes. Locations not less than addr
267/// and below the stack pointer may cause a memory violation if
268/// accessed. -9front brk(2)
269pub fn brk_(addr: usize) i32 {
270 return @intCast(syscall_bits.syscall1(.BRK_, addr));
271}
272var bloc: usize = 0;
273var bloc_max: usize = 0;
274
275pub fn sbrk(n: usize) usize {
276 if (bloc == 0) {
277 // we are at the start
278 bloc = @intFromPtr(&ExecData.end);
279 bloc_max = @intFromPtr(&ExecData.end);
280 }
281 var bl = std.mem.alignForward(usize, bloc, std.mem.page_size);
282 const n_aligned = std.mem.alignForward(usize, n, std.mem.page_size);
283 if (bl + n_aligned > bloc_max) {
284 // we need to allocate
285 if (brk_(bl + n_aligned) < 0) return 0;
286 bloc_max = bl + n_aligned;
287 }
288 bloc = bloc + n_aligned;
289 return bl;
290}
lib/std/os/plan9/errno.zig+8
......@@ -73,4 +73,12 @@ pub const E = enum(u16) {
7373 // These added in 1003.1b-1993
7474 CANCELED = 61,
7575 INPROGRESS = 62,
76
77 // We just add these to be compatible with std.os, which uses them,
78 // They should never get used.
79 DQUOT,
80 CONNRESET,
81 OVERFLOW,
82 LOOP,
83 TXTBSY,
7684};
lib/std/start.zig+8-22
......@@ -166,28 +166,7 @@ fn exit2(code: usize) noreturn {
166166 else => @compileError("TODO"),
167167 },
168168 // exits(0)
169 .plan9 => switch (builtin.cpu.arch) {
170 .x86_64 => {
171 asm volatile (
172 \\push $0
173 \\push $0
174 \\syscall
175 :
176 : [syscall_number] "{rbp}" (8),
177 : "rcx", "r11", "memory"
178 );
179 },
180 // TODO once we get stack setting with assembly on
181 // arm, exit with 0 instead of stack garbage
182 .aarch64 => {
183 asm volatile ("svc #0"
184 :
185 : [exit] "{x0}" (0x08),
186 : "memory", "cc"
187 );
188 },
189 else => @compileError("TODO"),
190 },
169 .plan9 => std.os.plan9.exits(null),
191170 .windows => {
192171 ExitProcess(@as(u32, @truncate(code)));
193172 },
......@@ -254,6 +233,13 @@ fn EfiMain(handle: uefi.Handle, system_table: *uefi.tables.SystemTable) callconv
254233}
255234
256235fn _start() callconv(.Naked) noreturn {
236 // TODO set Top of Stack on non x86_64-plan9
237 if (native_os == .plan9 and native_arch == .x86_64) {
238 // from /sys/src/libc/amd64/main9.s
239 std.os.plan9.tos = asm volatile (""
240 : [tos] "={rax}" (-> *std.os.plan9.Tos),
241 );
242 }
257243 asm volatile (switch (native_arch) {
258244 .x86_64 =>
259245 \\ xorl %%ebp, %%ebp
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+147-71
......@@ -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,26 +1379,35 @@ pub fn getDeclVAddr(
13121379) !u64 {
13131380 const mod = self.base.options.module.?;
13141381 const decl = mod.declPtr(decl_index);
1315 // we might already know the vaddr
1316 if (decl.ty.zigTypeTag(mod) == .Fn) {
1317 var start = self.bases.text;
1318 var it_file = self.fn_decl_table.iterator();
1319 while (it_file.next()) |fentry| {
1320 var symidx_and_submap = fentry.value_ptr;
1321 var submap_it = symidx_and_submap.functions.iterator();
1322 while (submap_it.next()) |entry| {
1323 if (entry.key_ptr.* == decl_index) return start;
1324 start += entry.value_ptr.code.len;
1325 }
1326 }
1327 } else {
1328 var start = self.bases.data + self.got_len * if (!self.sixtyfour_bit) @as(u32, 4) else 8;
1329 var it = self.data_decl_table.iterator();
1330 while (it.next()) |kv| {
1331 if (decl_index == kv.key_ptr.*) return start;
1332 start += kv.value_ptr.len;
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 });
13331406 }
1407 // TODO handle other extern variables and functions
1408 return undefined;
13341409 }
1410 // otherwise, we just add a relocation
13351411 const atom_index = try self.seeDecl(decl_index);
13361412 // the parent_atom_index in this case is just the decl_index of the parent
13371413 try self.addReloc(reloc_info.parent_atom_index, .{
......@@ -1339,7 +1415,7 @@ pub fn getDeclVAddr(
13391415 .offset = reloc_info.offset,
13401416 .addend = reloc_info.addend,
13411417 });
1342 return 0xcafebabe;
1418 return undefined;
13431419}
13441420
13451421pub fn addReloc(self: *Plan9, parent_index: Atom.Index, reloc: Reloc) !void {