authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-02-25 22:04:28+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-03-17 19:59:13+01:00
log586c704212b5453281cc27626f1ae46982a62d16
tree665ae83451630d493e8595263da349643b7c6681
parente825a15b0588d2ae4d3c6ab28fb8d8fd2ae05288

zld: pass stage2 tests linked with zld!


2 files changed, 120 insertions(+), 79 deletions(-)

src/link/MachO/Zld.zig+97-75
...@@ -252,9 +252,9 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {...@@ -252,9 +252,9 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8) !void {
252 try self.populateMetadata();252 try self.populateMetadata();
253 try self.parseInputFiles(files);253 try self.parseInputFiles(files);
254 try self.resolveImports();254 try self.resolveImports();
255 self.allocateTextSegment();255 try self.allocateTextSegment();
256 self.allocateDataSegment();256 try self.allocateDataSegment();
257 self.allocateLinkeditSegment();257 try self.allocateLinkeditSegment();
258 try self.writeStubHelperCommon();258 try self.writeStubHelperCommon();
259 try self.resolveSymbols();259 try self.resolveSymbols();
260 try self.doRelocs();260 try self.doRelocs();
...@@ -317,13 +317,20 @@ fn parseObjectFile(self: *Zld, object: *const Object) !void {...@@ -317,13 +317,20 @@ fn parseObjectFile(self: *Zld, object: *const Object) !void {
317 if (mem.eql(u8, sectname, "__thread_vars")) {317 if (mem.eql(u8, sectname, "__thread_vars")) {
318 self.tlv_section_index = sect_index;318 self.tlv_section_index = sect_index;
319 }319 }
320 log.warn("{s} align 0x{x}", .{ sectname, sect.@"align" });
321 const alignment = switch (sect.flags) {
322 macho.S_4BYTE_LITERALS => 2,
323 macho.S_8BYTE_LITERALS => 3,
324 macho.S_16BYTE_LITERALS => 4,
325 else => sect.@"align",
326 };
320 try seg.append(self.allocator, .{327 try seg.append(self.allocator, .{
321 .sectname = makeStaticString(&sect.sectname),328 .sectname = makeStaticString(&sect.sectname),
322 .segname = makeStaticString(&sect.segname),329 .segname = makeStaticString(&sect.segname),
323 .addr = 0,330 .addr = 0,
324 .size = 0,331 .size = 0,
325 .offset = 0,332 .offset = 0,
326 .@"align" = sect.@"align",333 .@"align" = alignment,
327 .reloff = 0,334 .reloff = 0,
328 .nreloc = 0,335 .nreloc = 0,
329 .flags = sect.flags,336 .flags = sect.flags,
...@@ -429,7 +436,7 @@ fn resolveImports(self: *Zld) !void {...@@ -429,7 +436,7 @@ fn resolveImports(self: *Zld) !void {
429 });436 });
430}437}
431438
432fn allocateTextSegment(self: *Zld) void {439fn allocateTextSegment(self: *Zld) !void {
433 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;440 const seg = &self.load_commands.items[self.text_segment_cmd_index.?].Segment;
434 const nexterns = @intCast(u32, self.lazy_imports.items().len);441 const nexterns = @intCast(u32, self.lazy_imports.items().len);
435442
...@@ -450,10 +457,16 @@ fn allocateTextSegment(self: *Zld) void {...@@ -450,10 +457,16 @@ fn allocateTextSegment(self: *Zld) void {
450 sizeofcmds += lc.cmdsize();457 sizeofcmds += lc.cmdsize();
451 }458 }
452459
453 self.allocateSegment(self.text_segment_cmd_index.?, 0, sizeofcmds, true);460 try self.allocateSegment(
461 self.text_segment_cmd_index.?,
462 0,
463 // sizeofcmds + 10 * 4 * @sizeOf(u32),
464 3140,
465 true,
466 );
454}467}
455468
456fn allocateDataSegment(self: *Zld) void {469fn allocateDataSegment(self: *Zld) !void {
457 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;470 const seg = &self.load_commands.items[self.data_segment_cmd_index.?].Segment;
458 const nonlazy = @intCast(u32, self.nonlazy_imports.items().len);471 const nonlazy = @intCast(u32, self.nonlazy_imports.items().len);
459 const lazy = @intCast(u32, self.lazy_imports.items().len);472 const lazy = @intCast(u32, self.lazy_imports.items().len);
...@@ -470,16 +483,16 @@ fn allocateDataSegment(self: *Zld) void {...@@ -470,16 +483,16 @@ fn allocateDataSegment(self: *Zld) void {
470483
471 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;484 const text_seg = self.load_commands.items[self.text_segment_cmd_index.?].Segment;
472 const offset = text_seg.inner.fileoff + text_seg.inner.filesize;485 const offset = text_seg.inner.fileoff + text_seg.inner.filesize;
473 self.allocateSegment(self.data_segment_cmd_index.?, offset, 0, false);486 try self.allocateSegment(self.data_segment_cmd_index.?, offset, 0, false);
474}487}
475488
476fn allocateLinkeditSegment(self: *Zld) void {489fn allocateLinkeditSegment(self: *Zld) !void {
477 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;490 const data_seg = self.load_commands.items[self.data_segment_cmd_index.?].Segment;
478 const offset = data_seg.inner.fileoff + data_seg.inner.filesize;491 const offset = data_seg.inner.fileoff + data_seg.inner.filesize;
479 self.allocateSegment(self.linkedit_segment_cmd_index.?, offset, 0, false);492 try self.allocateSegment(self.linkedit_segment_cmd_index.?, offset, 0, false);
480}493}
481494
482fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: bool) void {495fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: bool) !void {
483 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;496 const base_vmaddr = self.load_commands.items[self.pagezero_segment_cmd_index.?].Segment.inner.vmsize;
484 const seg = &self.load_commands.items[index].Segment;497 const seg = &self.load_commands.items[index].Segment;
485498
...@@ -495,23 +508,27 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: boo...@@ -495,23 +508,27 @@ fn allocateSegment(self: *Zld, index: u16, offset: u64, start: u64, reverse: boo
495 seg.inner.filesize = aligned_size;508 seg.inner.filesize = aligned_size;
496509
497 // Allocate section offsets510 // Allocate section offsets
498 if (reverse) {511 // if (reverse) {
499 var end_off: u64 = seg.inner.fileoff + seg.inner.filesize;512 // var end_off: u64 = seg.inner.fileoff + seg.inner.filesize;
500 var count: usize = seg.sections.items.len;513 // var count: usize = seg.sections.items.len;
501 while (count > 0) : (count -= 1) {514 // while (count > 0) : (count -= 1) {
502 const sec = &seg.sections.items[count - 1];515 // const sec = &seg.sections.items[count - 1];
503 end_off -= mem.alignForwardGeneric(u64, sec.size, @sizeOf(u32)); // TODO Should we always align to 4?516 // const alignment = math.max(@alignOf(u32), try std.math.powi(u32, 2, sec.@"align"));
504 sec.offset = @intCast(u32, end_off);517 // log.warn("{s} 0x{x} alignment = 0x{x}", .{ parseName(&sec.sectname), sec.@"align", alignment });
505 sec.addr = base_vmaddr + end_off;518 // end_off -= mem.alignForwardGeneric(u64, sec.size, alignment);
506 }519 // sec.offset = @intCast(u32, end_off);
507 } else {520 // sec.addr = base_vmaddr + end_off;
508 var next_off: u64 = seg.inner.fileoff;521 // }
509 for (seg.sections.items) |*sect| {522 // } else {
510 sect.offset = @intCast(u32, next_off);523 var next_off: u64 = seg.inner.fileoff + start;
511 sect.addr = base_vmaddr + next_off;524 for (seg.sections.items) |*sect| {
512 next_off += mem.alignForwardGeneric(u64, sect.size, @sizeOf(u32)); // TODO Should we always align to 4?525 const alignment = math.max(@alignOf(u32), try std.math.powi(u32, 2, sect.@"align"));
513 }526 log.warn("{s} 0x{x} alignment = 0x{x}", .{ parseName(&sect.sectname), sect.@"align", alignment });
514 }527 sect.offset = @intCast(u32, next_off);
528 sect.addr = base_vmaddr + next_off;
529 next_off += mem.alignForwardGeneric(u64, sect.size, alignment);
530 }
531 // }
515}532}
516533
517fn writeStubHelperCommon(self: *Zld) !void {534fn writeStubHelperCommon(self: *Zld) !void {
...@@ -552,33 +569,48 @@ fn writeStubHelperCommon(self: *Zld) !void {...@@ -552,33 +569,48 @@ fn writeStubHelperCommon(self: *Zld) !void {
552 break :blk stub_helper.offset + code_size;569 break :blk stub_helper.offset + code_size;
553 },570 },
554 .aarch64 => {571 .aarch64 => {
555 var code: [4 * @sizeOf(u32)]u8 = undefined;572 var code: [6 * @sizeOf(u32)]u8 = undefined;
556 {573 {
557 const target_addr = data.addr + data.size - @sizeOf(u64);574 const target_addr = data.addr + data.size - @sizeOf(u64);
558 const displacement = @bitCast(u21, try math.cast(i21, target_addr - stub_helper.addr));575 const displacement = @bitCast(u21, try math.cast(i21, target_addr - stub_helper.addr));
559 // adr x17, disp576 // adr x17, disp
560 mem.writeIntLittle(u32, code[0..4], Arm64.adr(17, displacement).toU32());577 mem.writeIntLittle(u32, code[0..4], Arm64.adr(17, displacement).toU32());
578 // TODO check if adr is enough and expand into adrp + add if not.
579 // nop in case we need to expand adr for adrp followed by add.
580 mem.writeIntLittle(u32, code[4..8], aarch64.Instruction.nop().toU32());
561 }581 }
562 // stp x16, x17, [sp, #-16]!582 // stp x16, x17, [sp, #-16]!
563 code[4] = 0xf0;583 code[8] = 0xf0;
564 code[5] = 0x47;584 code[9] = 0x47;
565 code[6] = 0xbf;585 code[10] = 0xbf;
566 code[7] = 0xa9;586 code[11] = 0xa9;
567 {587 binder: {
568 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;588 const dyld_stub_binder = self.nonlazy_imports.get("dyld_stub_binder").?;
569 const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));589 const addr = (got.addr + dyld_stub_binder.index * @sizeOf(u64));
570 const displacement = try math.divExact(u64, addr - stub_helper.addr - 2 * @sizeOf(u32), 4);590 const displacement = math.divExact(u64, addr - stub_helper.addr - 3 * @sizeOf(u32), 4) catch |_| {
571 const literal = try math.cast(u19, displacement);591 log.warn("0x{x}", .{addr - stub_helper.addr - 3 * @sizeOf(u32)});
592 // Pad with nop to please division.
593 // nop
594 mem.writeIntLittle(u32, code[12..16], aarch64.Instruction.nop().toU32());
595 // ldr x16, label
596 const disp = try math.divExact(u64, addr - stub_helper.addr - 4 * @sizeOf(u32), 4);
597 const literal = try math.cast(u19, disp); // TODO use adrp + add if we exceed the range.
598 mem.writeIntLittle(u32, code[16..20], Arm64.ldr(16, literal, 1).toU32());
599 break :binder;
600 };
601 const literal = try math.cast(u19, displacement); // TODO use adrp + add if we exceed the range.
572 // ldr x16, label602 // ldr x16, label
573 mem.writeIntLittle(u32, code[8..12], Arm64.ldr(16, literal, 1).toU32());603 mem.writeIntLittle(u32, code[12..16], Arm64.ldr(16, literal, 1).toU32());
604 // nop
605 mem.writeIntLittle(u32, code[16..20], aarch64.Instruction.nop().toU32());
574 }606 }
575 // br x16607 // br x16
576 code[12] = 0x00;608 code[20] = 0x00;
577 code[13] = 0x02;609 code[21] = 0x02;
578 code[14] = 0x1f;610 code[22] = 0x1f;
579 code[15] = 0xd6;611 code[23] = 0xd6;
580 try self.file.?.pwriteAll(&code, stub_helper.offset);612 try self.file.?.pwriteAll(&code, stub_helper.offset);
581 break :blk stub_helper.offset + 4 * @sizeOf(u32);613 break :blk stub_helper.offset + 6 * @sizeOf(u32);
582 },614 },
583 else => unreachable,615 else => unreachable,
584 }616 }
...@@ -635,12 +667,14 @@ fn writeStub(self: *Zld, index: u32) !void {...@@ -635,12 +667,14 @@ fn writeStub(self: *Zld, index: u32) !void {
635 },667 },
636 .aarch64 => {668 .aarch64 => {
637 assert(la_ptr_addr >= stub_addr);669 assert(la_ptr_addr >= stub_addr);
638 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr, 4);670 const displacement = try math.divExact(u64, la_ptr_addr - stub_addr - @sizeOf(u32), 4);
639 const literal = try math.cast(u19, displacement);671 const literal = try math.cast(u19, displacement);
672 // nop
673 mem.writeIntLittle(u32, code[0..4], aarch64.Instruction.nop().toU32());
640 // ldr x16, literal674 // ldr x16, literal
641 mem.writeIntLittle(u32, code[0..4], Arm64.ldr(16, literal, 1).toU32());675 mem.writeIntLittle(u32, code[4..8], Arm64.ldr(16, literal, 1).toU32());
642 // br x16676 // br x16
643 mem.writeIntLittle(u32, code[4..8], Arm64.br(16).toU32());677 mem.writeIntLittle(u32, code[8..12], Arm64.br(16).toU32());
644 },678 },
645 else => unreachable,679 else => unreachable,
646 }680 }
...@@ -722,7 +756,8 @@ fn resolveSymbols(self: *Zld) !void {...@@ -722,7 +756,8 @@ fn resolveSymbols(self: *Zld) !void {
722 const sym_name = object.getString(sym.n_strx);756 const sym_name = object.getString(sym.n_strx);
723757
724 if (isLocal(&sym) and self.locals.get(sym_name) != null) {758 if (isLocal(&sym) and self.locals.get(sym_name) != null) {
725 log.warn("symbol '{s}' already exists; skipping", .{sym_name});759 log.warn("local symbol '{s}' defined multiple times; removing", .{sym_name});
760 self.locals.swapRemoveAssertDiscard(sym_name);
726 continue;761 continue;
727 }762 }
728763
...@@ -978,12 +1013,6 @@ fn doRelocs(self: *Zld) !void {...@@ -978,12 +1013,6 @@ fn doRelocs(self: *Zld) !void {
978 const this_page = @intCast(i32, this_addr >> 12);1013 const this_page = @intCast(i32, this_addr >> 12);
979 const target_page = @intCast(i32, ta >> 12);1014 const target_page = @intCast(i32, ta >> 12);
980 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));1015 const pages = @bitCast(u21, @intCast(i21, target_page - this_page));
981 if (pages == 0) {
982 // No need to execute adrp. Instead, replace with a nop.
983 log.warn(" | replacing ADRP with NOP", .{});
984 mem.writeIntLittle(u32, inst, aarch64.Instruction.nop().toU32());
985 continue;
986 }
987 log.warn(" | moving by {} pages", .{pages});1016 log.warn(" | moving by {} pages", .{pages});
988 var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Address), inst);1017 var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Address), inst);
989 parsed.immhi = @truncate(u19, pages >> 2);1018 parsed.immhi = @truncate(u19, pages >> 2);
...@@ -1000,12 +1029,6 @@ fn doRelocs(self: *Zld) !void {...@@ -1000,12 +1029,6 @@ fn doRelocs(self: *Zld) !void {
1000 var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Add), inst);1029 var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.Add), inst);
1001 const ta = if (addend) |a| target_addr + a else target_addr;1030 const ta = if (addend) |a| target_addr + a else target_addr;
1002 const narrowed = @truncate(u12, ta);1031 const narrowed = @truncate(u12, ta);
1003 if (narrowed == 0) {
1004 // No need to execute add. Instead, replace with a nop.
1005 log.warn(" | replacing ADD with NOP", .{});
1006 mem.writeIntLittle(u32, inst, aarch64.Instruction.nop().toU32());
1007 continue;
1008 }
1009 parsed.offset = narrowed;1032 parsed.offset = narrowed;
1010 } else {1033 } else {
1011 log.warn(" | detected LDR/STR opcode", .{});1034 log.warn(" | detected LDR/STR opcode", .{});
...@@ -1013,20 +1036,18 @@ fn doRelocs(self: *Zld) !void {...@@ -1013,20 +1036,18 @@ fn doRelocs(self: *Zld) !void {
1013 var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst);1036 var parsed = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst);
1014 const ta = if (addend) |a| target_addr + a else target_addr;1037 const ta = if (addend) |a| target_addr + a else target_addr;
1015 const narrowed = @truncate(u12, ta);1038 const narrowed = @truncate(u12, ta);
1016 if (narrowed == 0) {1039 const offset: u12 = blk: {
1017 // No need to execute ldr/str. Instead, replace with a nop.1040 if (parsed.size == 0) {
1018 log.warn(" | replacing LDR/STR with NOP", .{});1041 break :blk narrowed;
1019 mem.writeIntLittle(u32, inst, aarch64.Instruction.nop().toU32());1042 } else {
1020 continue;1043 const denom: u4 = try math.powi(u4, 2, parsed.size);
1021 }1044 const offf = math.divExact(u12, narrowed, denom) catch |_| {
1022 const denom: u12 = if (parsed.size == 1) 8 else 4;1045 log.warn(" | narrowed 0x{x}", .{narrowed});
1023 const offset = math.divExact(u12, narrowed, denom) catch |_| {1046 log.warn(" | denom 0x{x}", .{denom});
1024 // If we are here, then this means we are not able to divide the offset1047 continue;
1025 // exactly by the required denominator. Therefore, we will use add instead of1048 };
1026 // ldr as we expect ldr to follow this instruction nonetheless.1049 break :blk offf;
1027 // TODO I believe ldr/str can only occur for GOT_LOAD_PAGEOFF12.1050 }
1028 mem.writeIntLittle(u32, inst, Arm64.add(parsed.rn, parsed.rn, narrowed, parsed.size).toU32());
1029 continue;
1030 };1051 };
1031 parsed.offset = offset;1052 parsed.offset = offset;
1032 }1053 }
...@@ -1046,7 +1067,7 @@ fn doRelocs(self: *Zld) !void {...@@ -1046,7 +1067,7 @@ fn doRelocs(self: *Zld) !void {
1046 break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = curr.size };1067 break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = curr.size };
1047 } else {1068 } else {
1048 const curr = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst);1069 const curr = mem.bytesAsValue(meta.TagPayload(Arm64, Arm64.LoadRegister), inst);
1049 break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = curr.size };1070 break :blk .{ .rt = curr.rt, .rn = curr.rn, .size = @truncate(u1, curr.size) };
1050 }1071 }
1051 };1072 };
1052 const ta = if (addend) |a| target_addr + a else target_addr;1073 const ta = if (addend) |a| target_addr + a else target_addr;
...@@ -1145,6 +1166,7 @@ fn relocTargetAddr(self: *Zld, object: Object, rel: macho.relocation_info, next_...@@ -1145,6 +1166,7 @@ fn relocTargetAddr(self: *Zld, object: Object, rel: macho.relocation_info, next_
1145 .segname = source_sect.segname,1166 .segname = source_sect.segname,
1146 .sectname = source_sect.sectname,1167 .sectname = source_sect.sectname,
1147 }).?;1168 }).?;
1169 log.warn(" | symbol local to object", .{});
1148 break :blk target_space.address + sym.n_value - source_sect.addr;1170 break :blk target_space.address + sym.n_value - source_sect.addr;
1149 } else if (isImport(&sym)) {1171 } else if (isImport(&sym)) {
1150 // Relocate to either the artifact's local symbol, or an import from1172 // Relocate to either the artifact's local symbol, or an import from
...@@ -1267,7 +1289,7 @@ fn populateMetadata(self: *Zld) !void {...@@ -1267,7 +1289,7 @@ fn populateMetadata(self: *Zld) !void {
1267 };1289 };
1268 const stub_size: u4 = switch (self.arch.?) {1290 const stub_size: u4 = switch (self.arch.?) {
1269 .x86_64 => 6,1291 .x86_64 => 6,
1270 .aarch64 => 2 * @sizeOf(u32),1292 .aarch64 => 3 * @sizeOf(u32),
1271 else => unreachable, // unhandled architecture type1293 else => unreachable, // unhandled architecture type
1272 };1294 };
1273 try text_seg.append(self.allocator, .{1295 try text_seg.append(self.allocator, .{
...@@ -1298,7 +1320,7 @@ fn populateMetadata(self: *Zld) !void {...@@ -1298,7 +1320,7 @@ fn populateMetadata(self: *Zld) !void {
1298 .aarch64 => 2,1320 .aarch64 => 2,
1299 else => unreachable, // unhandled architecture type1321 else => unreachable, // unhandled architecture type
1300 };1322 };
1301 const stub_helper_size: u5 = switch (self.arch.?) {1323 const stub_helper_size: u6 = switch (self.arch.?) {
1302 .x86_64 => 15,1324 .x86_64 => 15,
1303 .aarch64 => 6 * @sizeOf(u32),1325 .aarch64 => 6 * @sizeOf(u32),
1304 else => unreachable,1326 else => unreachable,
src/link/MachO/reloc.zig+23-4
...@@ -26,8 +26,7 @@ pub const Arm64 = union(enum) {...@@ -26,8 +26,7 @@ pub const Arm64 = union(enum) {
26 rn: u5,26 rn: u5,
27 offset: u12,27 offset: u12,
28 _1: u8 = 0b111_0_01_01,28 _1: u8 = 0b111_0_01_01,
29 size: u1,29 size: u2,
30 _2: u1 = 0b1,
31 },30 },
32 LoadLiteral: packed struct {31 LoadLiteral: packed struct {
33 reg: u5,32 reg: u5,
...@@ -135,13 +134,33 @@ pub const Arm64 = union(enum) {...@@ -135,13 +134,33 @@ pub const Arm64 = union(enum) {
135 };134 };
136 }135 }
137136
138 pub fn ldrr(rt: u5, rn: u5, offset: u12, size: u1) Arm64 {137 pub fn ldrq(rt: u5, rn: u5, offset: u12) Arm64 {
139 return Arm64{138 return Arm64{
140 .LoadRegister = .{139 .LoadRegister = .{
141 .rt = rt,140 .rt = rt,
142 .rn = rn,141 .rn = rn,
143 .offset = offset,142 .offset = offset,
144 .size = size,143 .size = 0b11,
144 },
145 };
146 }
147 pub fn ldrh(rt: u5, rn: u5, offset: u12) Arm64 {
148 return Arm64{
149 .LoadRegister = .{
150 .rt = rt,
151 .rn = rn,
152 .offset = offset,
153 .size = 0b01,
154 },
155 };
156 }
157 pub fn ldrb(rt: u5, rn: u5, offset: u12) Arm64 {
158 return Arm64{
159 .LoadRegister = .{
160 .rt = rt,
161 .rn = rn,
162 .offset = offset,
163 .size = 0b00,
145 },164 },
146 };165 };
147 }166 }