authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-15 18:48:41+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-07-15 18:49:48+02:00
logf519e781c6f952b3646cb646880ab460ad7a6cce
treed8229fdb5adec0eb2a438f3cb5048d681cfb7fb9
parentc47ce310716de806b3d95c328a9c2f9735221806

zld: move TextBlock into standalone file

which should make managing the logic of parsing and resolving relocs that much simpler to parse.

5 files changed, 288 insertions(+), 278 deletions(-)

CMakeLists.txt+1
...@@ -582,6 +582,7 @@ set(ZIG_STAGE2_SOURCES...@@ -582,6 +582,7 @@ set(ZIG_STAGE2_SOURCES
582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"582 "${CMAKE_SOURCE_DIR}/src/link/MachO/Dylib.zig"
583 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"583 "${CMAKE_SOURCE_DIR}/src/link/MachO/Object.zig"
584 "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"584 "${CMAKE_SOURCE_DIR}/src/link/MachO/Symbol.zig"
585 "${CMAKE_SOURCE_DIR}/src/link/MachO/TextBlock.zig"
585 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Trie.zig"
586 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"587 "${CMAKE_SOURCE_DIR}/src/link/MachO/Zld.zig"
587 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"588 "${CMAKE_SOURCE_DIR}/src/link/MachO/bind.zig"
src/link/MachO/Object.zig+1-1
...@@ -16,7 +16,7 @@ const Allocator = mem.Allocator;...@@ -16,7 +16,7 @@ const Allocator = mem.Allocator;
16const Arch = std.Target.Cpu.Arch;16const Arch = std.Target.Cpu.Arch;
17const Relocation = reloc.Relocation;17const Relocation = reloc.Relocation;
18const Symbol = @import("Symbol.zig");18const Symbol = @import("Symbol.zig");
19const TextBlock = Zld.TextBlock;19const TextBlock = @import("TextBlock.zig");
20const Zld = @import("Zld.zig");20const Zld = @import("Zld.zig");
2121
22usingnamespace @import("commands.zig");22usingnamespace @import("commands.zig");
src/link/MachO/TextBlock.zig created+284
...@@ -0,0 +1,284 @@
1const TextBlock = @This();
2
3const std = @import("std");
4const commands = @import("commands.zig");
5const log = std.log.scoped(.text_block);
6const macho = std.macho;
7const mem = std.mem;
8const reloc = @import("reloc.zig");
9
10const Allocator = mem.Allocator;
11const Relocation = reloc.Relocation;
12const Zld = @import("Zld.zig");
13
14allocator: *Allocator,
15local_sym_index: u32,
16stab: ?Stab = null,
17aliases: std.ArrayList(u32),
18references: std.AutoArrayHashMap(u32, void),
19contained: ?[]SymbolAtOffset = null,
20code: []u8,
21relocs: std.ArrayList(Relocation),
22size: u64,
23alignment: u32,
24rebases: std.ArrayList(u64),
25bindings: std.ArrayList(SymbolAtOffset),
26dices: std.ArrayList(macho.data_in_code_entry),
27next: ?*TextBlock = null,
28prev: ?*TextBlock = null,
29
30pub const SymbolAtOffset = struct {
31 local_sym_index: u32,
32 offset: u64,
33 stab: ?Stab = null,
34};
35
36pub const Stab = union(enum) {
37 function: u64,
38 static,
39 global,
40
41 pub fn asNlists(stab: Stab, local_sym_index: u32, zld: *Zld) ![]macho.nlist_64 {
42 var nlists = std.ArrayList(macho.nlist_64).init(zld.allocator);
43 defer nlists.deinit();
44
45 const sym = zld.locals.items[local_sym_index];
46 const reg = sym.payload.regular;
47
48 switch (stab) {
49 .function => |size| {
50 try nlists.ensureUnusedCapacity(4);
51 const section_id = reg.sectionId(zld);
52 nlists.appendAssumeCapacity(.{
53 .n_strx = 0,
54 .n_type = macho.N_BNSYM,
55 .n_sect = section_id,
56 .n_desc = 0,
57 .n_value = reg.address,
58 });
59 nlists.appendAssumeCapacity(.{
60 .n_strx = sym.strx,
61 .n_type = macho.N_FUN,
62 .n_sect = section_id,
63 .n_desc = 0,
64 .n_value = reg.address,
65 });
66 nlists.appendAssumeCapacity(.{
67 .n_strx = 0,
68 .n_type = macho.N_FUN,
69 .n_sect = 0,
70 .n_desc = 0,
71 .n_value = size,
72 });
73 nlists.appendAssumeCapacity(.{
74 .n_strx = 0,
75 .n_type = macho.N_ENSYM,
76 .n_sect = section_id,
77 .n_desc = 0,
78 .n_value = size,
79 });
80 },
81 .global => {
82 try nlists.append(.{
83 .n_strx = sym.strx,
84 .n_type = macho.N_GSYM,
85 .n_sect = 0,
86 .n_desc = 0,
87 .n_value = 0,
88 });
89 },
90 .static => {
91 try nlists.append(.{
92 .n_strx = sym.strx,
93 .n_type = macho.N_STSYM,
94 .n_sect = reg.sectionId(zld),
95 .n_desc = 0,
96 .n_value = reg.address,
97 });
98 },
99 }
100
101 return nlists.toOwnedSlice();
102 }
103};
104
105pub fn init(allocator: *Allocator) TextBlock {
106 return .{
107 .allocator = allocator,
108 .local_sym_index = undefined,
109 .aliases = std.ArrayList(u32).init(allocator),
110 .references = std.AutoArrayHashMap(u32, void).init(allocator),
111 .code = undefined,
112 .relocs = std.ArrayList(Relocation).init(allocator),
113 .size = undefined,
114 .alignment = undefined,
115 .rebases = std.ArrayList(u64).init(allocator),
116 .bindings = std.ArrayList(SymbolAtOffset).init(allocator),
117 .dices = std.ArrayList(macho.data_in_code_entry).init(allocator),
118 };
119}
120
121pub fn deinit(self: *TextBlock) void {
122 self.aliases.deinit();
123 self.references.deinit();
124 if (self.contained) |contained| {
125 self.allocator.free(contained);
126 }
127 self.allocator.free(self.code);
128 self.relocs.deinit();
129 self.rebases.deinit();
130 self.bindings.deinit();
131 self.dices.deinit();
132}
133
134pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
135 for (self.relocs.items) |rel| {
136 log.debug("relocating {}", .{rel});
137
138 const source_addr = blk: {
139 const sym = zld.locals.items[self.local_sym_index];
140 break :blk sym.payload.regular.address + rel.offset;
141 };
142 const target_addr = blk: {
143 const is_via_got = switch (rel.payload) {
144 .pointer_to_got => true,
145 .page => |page| page.kind == .got,
146 .page_off => |page_off| page_off.kind == .got,
147 .load => |load| load.kind == .got,
148 else => false,
149 };
150
151 if (is_via_got) {
152 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
153 const got = dc_seg.sections.items[zld.got_section_index.?];
154 const got_index = rel.target.got_index orelse {
155 log.err("expected GOT entry for symbol '{s}'", .{zld.getString(rel.target.strx)});
156 log.err(" this is an internal linker error", .{});
157 return error.FailedToResolveRelocationTarget;
158 };
159 break :blk got.addr + got_index * @sizeOf(u64);
160 }
161
162 switch (rel.target.payload) {
163 .regular => |reg| {
164 const is_tlv = is_tlv: {
165 const sym = zld.locals.items[self.local_sym_index];
166 const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
167 const sect = seg.sections.items[sym.payload.regular.section_id];
168 break :is_tlv commands.sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
169 };
170 if (is_tlv) {
171 // For TLV relocations, the value specified as a relocation is the displacement from the
172 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
173 // defined TLV template init section in the following order:
174 // * wrt to __thread_data if defined, then
175 // * wrt to __thread_bss
176 const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
177 const base_address = inner: {
178 if (zld.tlv_data_section_index) |i| {
179 break :inner seg.sections.items[i].addr;
180 } else if (zld.tlv_bss_section_index) |i| {
181 break :inner seg.sections.items[i].addr;
182 } else {
183 log.err("threadlocal variables present but no initializer sections found", .{});
184 log.err(" __thread_data not found", .{});
185 log.err(" __thread_bss not found", .{});
186 return error.FailedToResolveRelocationTarget;
187 }
188 };
189 break :blk reg.address - base_address;
190 }
191
192 break :blk reg.address;
193 },
194 .proxy => {
195 if (mem.eql(u8, zld.getString(rel.target.strx), "__tlv_bootstrap")) {
196 break :blk 0; // Dynamically bound by dyld.
197 }
198
199 const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
200 const stubs = segment.sections.items[zld.stubs_section_index.?];
201 const stubs_index = rel.target.stubs_index orelse {
202 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
203 break :blk 0; // Dynamically bound by dyld.
204 };
205 break :blk stubs.addr + stubs_index * stubs.reserved2;
206 },
207 else => {
208 log.err("failed to resolve symbol '{s}' as a relocation target", .{
209 zld.getString(rel.target.strx),
210 });
211 log.err(" this is an internal linker error", .{});
212 return error.FailedToResolveRelocationTarget;
213 },
214 }
215 };
216
217 log.debug(" | source_addr = 0x{x}", .{source_addr});
218 log.debug(" | target_addr = 0x{x}", .{target_addr});
219
220 try rel.resolve(self, source_addr, target_addr);
221 }
222}
223
224pub fn print_this(self: *const TextBlock, zld: *Zld) void {
225 log.warn("TextBlock", .{});
226 log.warn(" {}: {}", .{ self.local_sym_index, zld.locals.items[self.local_sym_index] });
227 if (self.stab) |stab| {
228 log.warn(" stab: {}", .{stab});
229 }
230 if (self.aliases.items.len > 0) {
231 log.warn(" aliases:", .{});
232 for (self.aliases.items) |index| {
233 log.warn(" {}: {}", .{ index, zld.locals.items[index] });
234 }
235 }
236 if (self.references.count() > 0) {
237 log.warn(" references:", .{});
238 for (self.references.keys()) |index| {
239 log.warn(" {}: {}", .{ index, zld.locals.items[index] });
240 }
241 }
242 if (self.contained) |contained| {
243 log.warn(" contained symbols:", .{});
244 for (contained) |sym_at_off| {
245 if (sym_at_off.stab) |stab| {
246 log.warn(" {}: {}, stab: {}\n", .{
247 sym_at_off.offset,
248 zld.locals.items[sym_at_off.local_sym_index],
249 stab,
250 });
251 } else {
252 log.warn(" {}: {}\n", .{
253 sym_at_off.offset,
254 zld.locals.items[sym_at_off.local_sym_index],
255 });
256 }
257 }
258 }
259 log.warn(" code.len = {}", .{self.code.len});
260 if (self.relocs.items.len > 0) {
261 log.warn(" relocations:", .{});
262 for (self.relocs.items) |rel| {
263 log.warn(" {}", .{rel});
264 }
265 }
266 if (self.rebases.items.len > 0) {
267 log.warn(" rebases: {any}", .{self.rebases.items});
268 }
269 if (self.bindings.items.len > 0) {
270 log.warn(" bindings: {any}", .{self.bindings.items});
271 }
272 if (self.dices.items.len > 0) {
273 log.warn(" dices: {any}", .{self.dices.items});
274 }
275 log.warn(" size = {}", .{self.size});
276 log.warn(" align = {}", .{self.alignment});
277}
278
279pub fn print(self: *const TextBlock, zld: *Zld) void {
280 if (self.prev) |prev| {
281 prev.print(zld);
282 }
283 self.print_this(zld);
284}
src/link/MachO/Zld.zig+1-276
...@@ -10,15 +10,14 @@ const macho = std.macho;...@@ -10,15 +10,14 @@ const macho = std.macho;
10const math = std.math;10const math = std.math;
11const log = std.log.scoped(.zld);11const log = std.log.scoped(.zld);
12const aarch64 = @import("../../codegen/aarch64.zig");12const aarch64 = @import("../../codegen/aarch64.zig");
13const reloc = @import("reloc.zig");
1413
15const Allocator = mem.Allocator;14const Allocator = mem.Allocator;
16const Archive = @import("Archive.zig");15const Archive = @import("Archive.zig");
17const CodeSignature = @import("CodeSignature.zig");16const CodeSignature = @import("CodeSignature.zig");
18const Dylib = @import("Dylib.zig");17const Dylib = @import("Dylib.zig");
19const Object = @import("Object.zig");18const Object = @import("Object.zig");
20const Relocation = reloc.Relocation;
21const Symbol = @import("Symbol.zig");19const Symbol = @import("Symbol.zig");
20const TextBlock = @import("TextBlock.zig");
22const Trie = @import("Trie.zig");21const Trie = @import("Trie.zig");
2322
24usingnamespace @import("commands.zig");23usingnamespace @import("commands.zig");
...@@ -123,280 +122,6 @@ pub const Output = struct {...@@ -123,280 +122,6 @@ pub const Output = struct {
123 install_name: ?[]const u8 = null,122 install_name: ?[]const u8 = null,
124};123};
125124
126pub const TextBlock = struct {
127 allocator: *Allocator,
128 local_sym_index: u32,
129 stab: ?Stab = null,
130 aliases: std.ArrayList(u32),
131 references: std.AutoArrayHashMap(u32, void),
132 contained: ?[]SymbolAtOffset = null,
133 code: []u8,
134 relocs: std.ArrayList(Relocation),
135 size: u64,
136 alignment: u32,
137 rebases: std.ArrayList(u64),
138 bindings: std.ArrayList(SymbolAtOffset),
139 dices: std.ArrayList(macho.data_in_code_entry),
140 next: ?*TextBlock = null,
141 prev: ?*TextBlock = null,
142
143 pub const SymbolAtOffset = struct {
144 local_sym_index: u32,
145 offset: u64,
146 stab: ?Stab = null,
147 };
148
149 pub const Stab = union(enum) {
150 function: u64,
151 static,
152 global,
153
154 pub fn asNlists(stab: Stab, local_sym_index: u32, zld: *Zld) ![]macho.nlist_64 {
155 var nlists = std.ArrayList(macho.nlist_64).init(zld.allocator);
156 defer nlists.deinit();
157
158 const sym = zld.locals.items[local_sym_index];
159 const reg = sym.payload.regular;
160
161 switch (stab) {
162 .function => |size| {
163 try nlists.ensureUnusedCapacity(4);
164 const section_id = reg.sectionId(zld);
165 nlists.appendAssumeCapacity(.{
166 .n_strx = 0,
167 .n_type = macho.N_BNSYM,
168 .n_sect = section_id,
169 .n_desc = 0,
170 .n_value = reg.address,
171 });
172 nlists.appendAssumeCapacity(.{
173 .n_strx = sym.strx,
174 .n_type = macho.N_FUN,
175 .n_sect = section_id,
176 .n_desc = 0,
177 .n_value = reg.address,
178 });
179 nlists.appendAssumeCapacity(.{
180 .n_strx = 0,
181 .n_type = macho.N_FUN,
182 .n_sect = 0,
183 .n_desc = 0,
184 .n_value = size,
185 });
186 nlists.appendAssumeCapacity(.{
187 .n_strx = 0,
188 .n_type = macho.N_ENSYM,
189 .n_sect = section_id,
190 .n_desc = 0,
191 .n_value = size,
192 });
193 },
194 .global => {
195 try nlists.append(.{
196 .n_strx = sym.strx,
197 .n_type = macho.N_GSYM,
198 .n_sect = 0,
199 .n_desc = 0,
200 .n_value = 0,
201 });
202 },
203 .static => {
204 try nlists.append(.{
205 .n_strx = sym.strx,
206 .n_type = macho.N_STSYM,
207 .n_sect = reg.sectionId(zld),
208 .n_desc = 0,
209 .n_value = reg.address,
210 });
211 },
212 }
213
214 return nlists.toOwnedSlice();
215 }
216 };
217
218 pub fn init(allocator: *Allocator) TextBlock {
219 return .{
220 .allocator = allocator,
221 .local_sym_index = undefined,
222 .aliases = std.ArrayList(u32).init(allocator),
223 .references = std.AutoArrayHashMap(u32, void).init(allocator),
224 .code = undefined,
225 .relocs = std.ArrayList(Relocation).init(allocator),
226 .size = undefined,
227 .alignment = undefined,
228 .rebases = std.ArrayList(u64).init(allocator),
229 .bindings = std.ArrayList(SymbolAtOffset).init(allocator),
230 .dices = std.ArrayList(macho.data_in_code_entry).init(allocator),
231 };
232 }
233
234 pub fn deinit(self: *TextBlock) void {
235 self.aliases.deinit();
236 self.references.deinit();
237 if (self.contained) |contained| {
238 self.allocator.free(contained);
239 }
240 self.allocator.free(self.code);
241 self.relocs.deinit();
242 self.rebases.deinit();
243 self.bindings.deinit();
244 self.dices.deinit();
245 }
246
247 pub fn resolveRelocs(self: *TextBlock, zld: *Zld) !void {
248 for (self.relocs.items) |rel| {
249 log.debug("relocating {}", .{rel});
250
251 const source_addr = blk: {
252 const sym = zld.locals.items[self.local_sym_index];
253 break :blk sym.payload.regular.address + rel.offset;
254 };
255 const target_addr = blk: {
256 const is_via_got = switch (rel.payload) {
257 .pointer_to_got => true,
258 .page => |page| page.kind == .got,
259 .page_off => |page_off| page_off.kind == .got,
260 .load => |load| load.kind == .got,
261 else => false,
262 };
263
264 if (is_via_got) {
265 const dc_seg = zld.load_commands.items[zld.data_const_segment_cmd_index.?].Segment;
266 const got = dc_seg.sections.items[zld.got_section_index.?];
267 const got_index = rel.target.got_index orelse {
268 log.err("expected GOT entry for symbol '{s}'", .{zld.getString(rel.target.strx)});
269 log.err(" this is an internal linker error", .{});
270 return error.FailedToResolveRelocationTarget;
271 };
272 break :blk got.addr + got_index * @sizeOf(u64);
273 }
274
275 switch (rel.target.payload) {
276 .regular => |reg| {
277 const is_tlv = is_tlv: {
278 const sym = zld.locals.items[self.local_sym_index];
279 const seg = zld.load_commands.items[sym.payload.regular.segment_id].Segment;
280 const sect = seg.sections.items[sym.payload.regular.section_id];
281 break :is_tlv sectionType(sect) == macho.S_THREAD_LOCAL_VARIABLES;
282 };
283 if (is_tlv) {
284 // For TLV relocations, the value specified as a relocation is the displacement from the
285 // TLV initializer (either value in __thread_data or zero-init in __thread_bss) to the first
286 // defined TLV template init section in the following order:
287 // * wrt to __thread_data if defined, then
288 // * wrt to __thread_bss
289 const seg = zld.load_commands.items[zld.data_segment_cmd_index.?].Segment;
290 const base_address = inner: {
291 if (zld.tlv_data_section_index) |i| {
292 break :inner seg.sections.items[i].addr;
293 } else if (zld.tlv_bss_section_index) |i| {
294 break :inner seg.sections.items[i].addr;
295 } else {
296 log.err("threadlocal variables present but no initializer sections found", .{});
297 log.err(" __thread_data not found", .{});
298 log.err(" __thread_bss not found", .{});
299 return error.FailedToResolveRelocationTarget;
300 }
301 };
302 break :blk reg.address - base_address;
303 }
304
305 break :blk reg.address;
306 },
307 .proxy => {
308 if (mem.eql(u8, zld.getString(rel.target.strx), "__tlv_bootstrap")) {
309 break :blk 0; // Dynamically bound by dyld.
310 }
311
312 const segment = zld.load_commands.items[zld.text_segment_cmd_index.?].Segment;
313 const stubs = segment.sections.items[zld.stubs_section_index.?];
314 const stubs_index = rel.target.stubs_index orelse {
315 // TODO verify in TextBlock that the symbol is indeed dynamically bound.
316 break :blk 0; // Dynamically bound by dyld.
317 };
318 break :blk stubs.addr + stubs_index * stubs.reserved2;
319 },
320 else => {
321 log.err("failed to resolve symbol '{s}' as a relocation target", .{
322 zld.getString(rel.target.strx),
323 });
324 log.err(" this is an internal linker error", .{});
325 return error.FailedToResolveRelocationTarget;
326 },
327 }
328 };
329
330 log.debug(" | source_addr = 0x{x}", .{source_addr});
331 log.debug(" | target_addr = 0x{x}", .{target_addr});
332
333 try rel.resolve(self, source_addr, target_addr);
334 }
335 }
336
337 pub fn print_this(self: *const TextBlock, zld: *Zld) void {
338 log.warn("TextBlock", .{});
339 log.warn(" {}: {}", .{ self.local_sym_index, zld.locals.items[self.local_sym_index] });
340 if (self.stab) |stab| {
341 log.warn(" stab: {}", .{stab});
342 }
343 if (self.aliases.items.len > 0) {
344 log.warn(" aliases:", .{});
345 for (self.aliases.items) |index| {
346 log.warn(" {}: {}", .{ index, zld.locals.items[index] });
347 }
348 }
349 if (self.references.count() > 0) {
350 log.warn(" references:", .{});
351 for (self.references.keys()) |index| {
352 log.warn(" {}: {}", .{ index, zld.locals.items[index] });
353 }
354 }
355 if (self.contained) |contained| {
356 log.warn(" contained symbols:", .{});
357 for (contained) |sym_at_off| {
358 if (sym_at_off.stab) |stab| {
359 log.warn(" {}: {}, stab: {}\n", .{
360 sym_at_off.offset,
361 zld.locals.items[sym_at_off.local_sym_index],
362 stab,
363 });
364 } else {
365 log.warn(" {}: {}\n", .{
366 sym_at_off.offset,
367 zld.locals.items[sym_at_off.local_sym_index],
368 });
369 }
370 }
371 }
372 log.warn(" code.len = {}", .{self.code.len});
373 if (self.relocs.items.len > 0) {
374 log.warn(" relocations:", .{});
375 for (self.relocs.items) |rel| {
376 log.warn(" {}", .{rel});
377 }
378 }
379 if (self.rebases.items.len > 0) {
380 log.warn(" rebases: {any}", .{self.rebases.items});
381 }
382 if (self.bindings.items.len > 0) {
383 log.warn(" bindings: {any}", .{self.bindings.items});
384 }
385 if (self.dices.items.len > 0) {
386 log.warn(" dices: {any}", .{self.dices.items});
387 }
388 log.warn(" size = {}", .{self.size});
389 log.warn(" align = {}", .{self.alignment});
390 }
391
392 pub fn print(self: *const TextBlock, zld: *Zld) void {
393 if (self.prev) |prev| {
394 prev.print(zld);
395 }
396 self.print_this(zld);
397 }
398};
399
400/// Default path to dyld125/// Default path to dyld
401const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";126const DEFAULT_DYLD_PATH: [*:0]const u8 = "/usr/lib/dyld";
402127
src/link/MachO/reloc.zig+1-1
...@@ -12,7 +12,7 @@ const Allocator = mem.Allocator;...@@ -12,7 +12,7 @@ const Allocator = mem.Allocator;
12const Arch = std.Target.Cpu.Arch;12const Arch = std.Target.Cpu.Arch;
13const Object = @import("Object.zig");13const Object = @import("Object.zig");
14const Symbol = @import("Symbol.zig");14const Symbol = @import("Symbol.zig");
15const TextBlock = Zld.TextBlock;15const TextBlock = @import("TextBlock.zig");
16const Zld = @import("Zld.zig");16const Zld = @import("Zld.zig");
1717
18pub const Relocation = struct {18pub const Relocation = struct {