authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-19 13:51:46-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-19 13:51:46-04:00
log8d3cca7fc2e7b34486cc56951bb0dadb75e498a0
tree089289335a3e6374c88ccb54c5879806132503a6
parent8d812dba30fe3c17e9d64607fdf813977eaf0496

stage2: function calls using the global offset table

so far they don't support parameters or return values

3 files changed, 85 insertions(+), 167 deletions(-)

src-self-hosted/codegen.zig+9-27
...@@ -19,24 +19,6 @@ pub const Result = union(enum) {...@@ -19,24 +19,6 @@ pub const Result = union(enum) {
19 fail: *Module.ErrorMsg,19 fail: *Module.ErrorMsg,
20};20};
2121
22pub fn pltEntrySize(target: Target) u16 {
23 return switch (target.cpu.arch) {
24 .i386, .x86_64 => 5,
25 else => @panic("TODO implement pltEntrySize for more architectures"),
26 };
27}
28
29pub fn writePltEntry(target: Target, buf: []u8, addr: u32) void {
30 switch (target.cpu.arch) {
31 .i386, .x86_64 => {
32 // 9a xx xx xx xx call addr
33 buf[0] = 0x9a;
34 mem.writeIntLittle(u32, buf[1..5], addr);
35 },
36 else => @panic("TODO implement pltEntrySize for more architectures"),
37 }
38}
39
40pub fn generateSymbol(22pub fn generateSymbol(
41 bin_file: *link.ElfFile,23 bin_file: *link.ElfFile,
42 src: usize,24 src: usize,
...@@ -221,14 +203,14 @@ const Function = struct {...@@ -221,14 +203,14 @@ const Function = struct {
221203
222 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {204 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
223 const func = func_val.func;205 const func = func_val.func;
224 const plt_index = func.owner_decl.link.offset_table_index.plt;206 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
225 const plt = &self.bin_file.program_headers.items[self.bin_file.phdr_got_plt_index.?];207 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
226 const plt_entry_size = pltEntrySize(self.target.*);208 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
227 const plt_addr = @intCast(u32, plt.p_vaddr + func.owner_decl.link.offset_table_index.plt * plt_entry_size);209 const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
228 // ea xx xx xx xx jmp addr210 // ff 14 25 xx xx xx xx call [addr]
229 try self.code.resize(self.code.items.len + 5);211 try self.code.resize(self.code.items.len + 7);
230 self.code.items[self.code.items.len - 5] = 0xea;212 self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 };
231 mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], plt_addr);213 mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr);
232 const return_type = func.fn_type.fnReturnType();214 const return_type = func.fn_type.fnReturnType();
233 switch (return_type.zigTypeTag()) {215 switch (return_type.zigTypeTag()) {
234 .Void => return MCValue{ .none = {} },216 .Void => return MCValue{ .none = {} },
...@@ -606,7 +588,7 @@ const Function = struct {...@@ -606,7 +588,7 @@ const Function = struct {
606 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {588 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
607 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];589 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
608 const decl = payload.decl;590 const decl = payload.decl;
609 const got_addr = got.p_vaddr + decl.link.offset_table_index.got * ptr_bytes;591 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
610 return MCValue{ .memory = got_addr };592 return MCValue{ .memory = got_addr };
611 }593 }
612 return self.fail(src, "TODO codegen more kinds of const pointers", .{});594 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
src-self-hosted/link.zig+23-140
...@@ -110,7 +110,6 @@ pub const ElfFile = struct {...@@ -110,7 +110,6 @@ pub const ElfFile = struct {
110 /// The index into the program headers of the global offset table.110 /// The index into the program headers of the global offset table.
111 /// It needs PT_LOAD and Read flags.111 /// It needs PT_LOAD and Read flags.
112 phdr_got_index: ?u16 = null,112 phdr_got_index: ?u16 = null,
113 phdr_got_plt_index: ?u16 = null,
114 entry_addr: ?u64 = null,113 entry_addr: ?u64 = null,
115114
116 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},115 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
...@@ -119,7 +118,6 @@ pub const ElfFile = struct {...@@ -119,7 +118,6 @@ pub const ElfFile = struct {
119 text_section_index: ?u16 = null,118 text_section_index: ?u16 = null,
120 symtab_section_index: ?u16 = null,119 symtab_section_index: ?u16 = null,
121 got_section_index: ?u16 = null,120 got_section_index: ?u16 = null,
122 got_plt_section_index: ?u16 = null,
123121
124 /// The same order as in the file. ELF requires global symbols to all be after the122 /// The same order as in the file. ELF requires global symbols to all be after the
125 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and123 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
...@@ -132,16 +130,11 @@ pub const ElfFile = struct {...@@ -132,16 +130,11 @@ pub const ElfFile = struct {
132 /// If the vaddr of the executable program header changes, the entire130 /// If the vaddr of the executable program header changes, the entire
133 /// offset table needs to be rewritten.131 /// offset table needs to be rewritten.
134 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},132 offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
135 /// Same order as in the file. The value is the absolute vaddr value.
136 /// If the vaddr of the executable program header changes, the entire
137 /// fn trampoline table needs to be rewritten.
138 fn_trampoline_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
139133
140 phdr_table_dirty: bool = false,134 phdr_table_dirty: bool = false,
141 shdr_table_dirty: bool = false,135 shdr_table_dirty: bool = false,
142 shstrtab_dirty: bool = false,136 shstrtab_dirty: bool = false,
143 offset_table_count_dirty: bool = false,137 offset_table_count_dirty: bool = false,
144 fn_trampoline_table_count_dirty: bool = false,
145138
146 error_flags: ErrorFlags = ErrorFlags{},139 error_flags: ErrorFlags = ErrorFlags{},
147140
...@@ -157,18 +150,12 @@ pub const ElfFile = struct {...@@ -157,18 +150,12 @@ pub const ElfFile = struct {
157 /// If this field is 0, it means the codegen size = 0 and there is no symbol or150 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
158 /// offset table entry.151 /// offset table entry.
159 local_sym_index: u32,152 local_sym_index: u32,
160 /// when size = 0 and there is no offset table index153 /// This field is undefined for symbols with size = 0.
161 offset_table_index: union {154 offset_table_index: u32,
162 unallocated: void,
163 /// This is an index into offset_table
164 got: u32,
165 /// This is an index into fn_trampoline_table
166 plt: u32,
167 },
168155
169 pub const empty = Decl{156 pub const empty = Decl{
170 .local_sym_index = 0,157 .local_sym_index = 0,
171 .offset_table_index = .{ .unallocated = {} },158 .offset_table_index = undefined,
172 };159 };
173 };160 };
174161
...@@ -183,7 +170,6 @@ pub const ElfFile = struct {...@@ -183,7 +170,6 @@ pub const ElfFile = struct {
183 self.local_symbols.deinit(self.allocator);170 self.local_symbols.deinit(self.allocator);
184 self.global_symbols.deinit(self.allocator);171 self.global_symbols.deinit(self.allocator);
185 self.offset_table.deinit(self.allocator);172 self.offset_table.deinit(self.allocator);
186 self.fn_trampoline_table.deinit(self.allocator);
187 if (self.owns_file_handle) {173 if (self.owns_file_handle) {
188 if (self.file) |f| f.close();174 if (self.file) |f| f.close();
189 }175 }
...@@ -357,30 +343,6 @@ pub const ElfFile = struct {...@@ -357,30 +343,6 @@ pub const ElfFile = struct {
357 });343 });
358 self.phdr_table_dirty = true;344 self.phdr_table_dirty = true;
359 }345 }
360 if (self.phdr_got_plt_index == null) {
361 self.phdr_got_plt_index = @intCast(u16, self.program_headers.items.len);
362 const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
363 // We really only need ptr alignment but since we are using PROGBITS, linux requires
364 // page align.
365 const p_align = 0x1000;
366 const off = self.findFreeSpace(file_size, p_align);
367 //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
368 // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
369 // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
370 // else in virtual memory.
371 const default_got_plt_addr = 0x6000000;
372 try self.program_headers.append(self.allocator, .{
373 .p_type = elf.PT_LOAD,
374 .p_offset = off,
375 .p_filesz = file_size,
376 .p_vaddr = default_got_plt_addr,
377 .p_paddr = default_got_plt_addr,
378 .p_memsz = file_size,
379 .p_align = p_align,
380 .p_flags = elf.PF_R,
381 });
382 self.phdr_table_dirty = true;
383 }
384 if (self.shstrtab_index == null) {346 if (self.shstrtab_index == null) {
385 self.shstrtab_index = @intCast(u16, self.sections.items.len);347 self.shstrtab_index = @intCast(u16, self.sections.items.len);
386 assert(self.shstrtab.items.len == 0);348 assert(self.shstrtab.items.len == 0);
...@@ -438,24 +400,6 @@ pub const ElfFile = struct {...@@ -438,24 +400,6 @@ pub const ElfFile = struct {
438 });400 });
439 self.shdr_table_dirty = true;401 self.shdr_table_dirty = true;
440 }402 }
441 if (self.got_plt_section_index == null) {
442 self.got_plt_section_index = @intCast(u16, self.sections.items.len);
443 const phdr = &self.program_headers.items[self.phdr_got_plt_index.?];
444
445 try self.sections.append(self.allocator, .{
446 .sh_name = try self.makeString(".got.plt"),
447 .sh_type = elf.SHT_PROGBITS,
448 .sh_flags = elf.SHF_ALLOC,
449 .sh_addr = phdr.p_vaddr,
450 .sh_offset = phdr.p_offset,
451 .sh_size = phdr.p_filesz,
452 .sh_link = 0,
453 .sh_info = 0,
454 .sh_addralign = phdr.p_align,
455 .sh_entsize = 0,
456 });
457 self.shdr_table_dirty = true;
458 }
459 if (self.symtab_section_index == null) {403 if (self.symtab_section_index == null) {
460 self.symtab_section_index = @intCast(u16, self.sections.items.len);404 self.symtab_section_index = @intCast(u16, self.sections.items.len);
461 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);405 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
...@@ -640,7 +584,6 @@ pub const ElfFile = struct {...@@ -640,7 +584,6 @@ pub const ElfFile = struct {
640 assert(!self.shdr_table_dirty);584 assert(!self.shdr_table_dirty);
641 assert(!self.shstrtab_dirty);585 assert(!self.shstrtab_dirty);
642 assert(!self.offset_table_count_dirty);586 assert(!self.offset_table_count_dirty);
643 assert(!self.fn_trampoline_table_count_dirty);
644 const syms_sect = &self.sections.items[self.symtab_section_index.?];587 const syms_sect = &self.sections.items[self.symtab_section_index.?];
645 assert(syms_sect.sh_info == self.local_symbols.items.len);588 assert(syms_sect.sh_info == self.local_symbols.items.len);
646 }589 }
...@@ -836,14 +779,10 @@ pub const ElfFile = struct {...@@ -836,14 +779,10 @@ pub const ElfFile = struct {
836 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {779 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
837 if (decl.link.local_sym_index != 0) return;780 if (decl.link.local_sym_index != 0) return;
838781
839 const is_fn = (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
840
841 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);782 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
842 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);783 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
843 try self.fn_trampoline_table.ensureCapacity(self.allocator, self.fn_trampoline_table.items.len + 1);
844 const local_sym_index = self.local_symbols.items.len;784 const local_sym_index = self.local_symbols.items.len;
845 const offset_table_index = self.offset_table.items.len;785 const offset_table_index = self.offset_table.items.len;
846 const fn_trampoline_table_index = self.fn_trampoline_table.items.len;
847 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];786 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
848787
849 self.local_symbols.appendAssumeCapacity(.{788 self.local_symbols.appendAssumeCapacity(.{
...@@ -854,20 +793,15 @@ pub const ElfFile = struct {...@@ -854,20 +793,15 @@ pub const ElfFile = struct {
854 .st_value = phdr.p_vaddr,793 .st_value = phdr.p_vaddr,
855 .st_size = 0,794 .st_size = 0,
856 });795 });
857 if (is_fn) {796 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
858 self.fn_trampoline_table.appendAssumeCapacity(0);797 self.offset_table.appendAssumeCapacity(0);
859 self.fn_trampoline_table_count_dirty = true;798 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
860 } else {799
861 self.offset_table.appendAssumeCapacity(0);800 self.offset_table_count_dirty = true;
862 self.offset_table_count_dirty = true;
863 }
864801
865 decl.link = .{802 decl.link = .{
866 .local_sym_index = @intCast(u32, local_sym_index),803 .local_sym_index = @intCast(u32, local_sym_index),
867 .offset_table_index = if (is_fn)804 .offset_table_index = @intCast(u32, offset_table_index),
868 .{ .plt = @intCast(u32, fn_trampoline_table_index) }
869 else
870 .{ .got = @intCast(u32, offset_table_index) },
871 };805 };
872 }806 }
873807
...@@ -885,7 +819,6 @@ pub const ElfFile = struct {...@@ -885,7 +819,6 @@ pub const ElfFile = struct {
885 return;819 return;
886 },820 },
887 };821 };
888 const is_fn = (typed_value.ty.zigTypeTag() == .Fn);
889822
890 const required_alignment = typed_value.ty.abiAlignment(self.options.target);823 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
891824
...@@ -905,13 +838,14 @@ pub const ElfFile = struct {...@@ -905,13 +838,14 @@ pub const ElfFile = struct {
905 const file_offset = if (need_realloc) fo: {838 const file_offset = if (need_realloc) fo: {
906 const new_block = try self.allocateTextBlock(code.len, required_alignment);839 const new_block = try self.allocateTextBlock(code.len, required_alignment);
907 local_sym.st_value = new_block.vaddr;840 local_sym.st_value = new_block.vaddr;
908 if (is_fn) {841 self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr;
909 self.fn_trampoline_table.items[decl.link.offset_table_index.plt] = new_block.vaddr;842
910 try self.writeFnTrampolineEntry(decl.link.offset_table_index.plt);843 //std.debug.warn("{}: writing got index {}=0x{x}\n", .{
911 } else {844 // decl.name,
912 self.offset_table.items[decl.link.offset_table_index.got] = new_block.vaddr;845 // decl.link.offset_table_index,
913 try self.writeOffsetTableEntry(decl.link.offset_table_index.got);846 // self.offset_table.items[decl.link.offset_table_index],
914 }847 //});
848 try self.writeOffsetTableEntry(decl.link.offset_table_index);
915849
916 break :fo new_block.file_offset;850 break :fo new_block.file_offset;
917 } else existing_block.file_offset;851 } else existing_block.file_offset;
...@@ -928,13 +862,11 @@ pub const ElfFile = struct {...@@ -928,13 +862,11 @@ pub const ElfFile = struct {
928 } else {862 } else {
929 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);863 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
930 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);864 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
931 try self.fn_trampoline_table.ensureCapacity(self.allocator, self.fn_trampoline_table.items.len + 1);
932 const decl_name = mem.spanZ(decl.name);865 const decl_name = mem.spanZ(decl.name);
933 const name_str_index = try self.makeString(decl_name);866 const name_str_index = try self.makeString(decl_name);
934 const new_block = try self.allocateTextBlock(code.len, required_alignment);867 const new_block = try self.allocateTextBlock(code.len, required_alignment);
935 const local_sym_index = self.local_symbols.items.len;868 const local_sym_index = self.local_symbols.items.len;
936 const offset_table_index = self.offset_table.items.len;869 const offset_table_index = self.offset_table.items.len;
937 const fn_trampoline_table_index = self.fn_trampoline_table.items.len;
938870
939 //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len });871 //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len });
940 self.local_symbols.appendAssumeCapacity(.{872 self.local_symbols.appendAssumeCapacity(.{
...@@ -946,32 +878,17 @@ pub const ElfFile = struct {...@@ -946,32 +878,17 @@ pub const ElfFile = struct {
946 .st_size = code.len,878 .st_size = code.len,
947 });879 });
948 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);880 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
949 if (is_fn) {881 self.offset_table.appendAssumeCapacity(new_block.vaddr);
950 self.fn_trampoline_table.appendAssumeCapacity(new_block.vaddr);882 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
951 } else {883
952 self.offset_table.appendAssumeCapacity(new_block.vaddr);884 self.offset_table_count_dirty = true;
953 }
954 errdefer if (is_fn) {
955 self.fn_trampoline_table.shrink(self.allocator, self.fn_trampoline_table.items.len - 1);
956 } else {
957 self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
958 };
959885
960 try self.writeSymbol(local_sym_index);886 try self.writeSymbol(local_sym_index);
961 if (is_fn) {887 try self.writeOffsetTableEntry(offset_table_index);
962 try self.writeFnTrampolineEntry(fn_trampoline_table_index);
963 self.fn_trampoline_table_count_dirty = true;
964 } else {
965 try self.writeOffsetTableEntry(offset_table_index);
966 self.offset_table_count_dirty = true;
967 }
968888
969 decl.link = .{889 decl.link = .{
970 .local_sym_index = @intCast(u32, local_sym_index),890 .local_sym_index = @intCast(u32, local_sym_index),
971 .offset_table_index = if (is_fn)891 .offset_table_index = @intCast(u32, offset_table_index),
972 .{ .plt = @intCast(u32, fn_trampoline_table_index) }
973 else
974 .{ .got = @intCast(u32, offset_table_index) },
975 };892 };
976893
977 //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr });894 //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr });
...@@ -1101,40 +1018,6 @@ pub const ElfFile = struct {...@@ -1101,40 +1018,6 @@ pub const ElfFile = struct {
1101 }1018 }
1102 }1019 }
11031020
1104 fn writeFnTrampolineEntry(self: *ElfFile, index: usize) !void {
1105 const shdr = &self.sections.items[self.got_plt_section_index.?];
1106 const phdr = &self.program_headers.items[self.phdr_got_plt_index.?];
1107 const entry_size = codegen.pltEntrySize(self.options.target);
1108 var entry_buf: [16]u8 = undefined;
1109 assert(entry_size <= entry_buf.len);
1110
1111 if (self.fn_trampoline_table_count_dirty) {
1112 // TODO Also detect virtual address collisions.
1113 const allocated_size = self.allocatedSize(shdr.sh_offset);
1114 const needed_size = self.local_symbols.items.len * entry_size;
1115 if (needed_size > allocated_size) {
1116 // Must move the entire .got.plt section.
1117 const new_offset = self.findFreeSpace(needed_size, entry_size);
1118 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
1119 if (amt != shdr.sh_size) return error.InputOutput;
1120 shdr.sh_offset = new_offset;
1121 phdr.p_offset = new_offset;
1122 }
1123 shdr.sh_size = needed_size;
1124 phdr.p_memsz = needed_size;
1125 phdr.p_filesz = needed_size;
1126
1127 self.shdr_table_dirty = true; // TODO look into making only the one section dirty
1128 self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
1129
1130 self.fn_trampoline_table_count_dirty = false;
1131 }
1132 const off = shdr.sh_offset + @as(u64, entry_size) * index;
1133 const vaddr = @intCast(u32, self.fn_trampoline_table.items[index]);
1134 codegen.writePltEntry(self.options.target, &entry_buf, vaddr);
1135 try self.file.?.pwriteAll(entry_buf[0..entry_size], off);
1136 }
1137
1138 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {1021 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {
1139 const shdr = &self.sections.items[self.got_section_index.?];1022 const shdr = &self.sections.items[self.got_section_index.?];
1140 const phdr = &self.program_headers.items[self.phdr_got_index.?];1023 const phdr = &self.program_headers.items[self.phdr_got_index.?];
test/stage2/zir.zig+53
...@@ -209,4 +209,57 @@ pub fn addCases(ctx: *TestContext) void {...@@ -209,4 +209,57 @@ pub fn addCases(ctx: *TestContext) void {
209 \\209 \\
210 },210 },
211 );211 );
212
213 ctx.addZIRCompareOutput(
214 "function call with no args no return value",
215 &[_][]const u8{
216 \\@noreturn = primitive(noreturn)
217 \\@void = primitive(void)
218 \\@usize = primitive(usize)
219 \\@0 = int(0)
220 \\@1 = int(1)
221 \\@2 = int(2)
222 \\@3 = int(3)
223 \\
224 \\@syscall_array = str("syscall")
225 \\@sysoutreg_array = str("={rax}")
226 \\@rax_array = str("{rax}")
227 \\@rdi_array = str("{rdi}")
228 \\@rcx_array = str("rcx")
229 \\@r11_array = str("r11")
230 \\@memory_array = str("memory")
231 \\
232 \\@exit0_fnty = fntype([], @noreturn)
233 \\@exit0 = fn(@exit0_fnty, {
234 \\ %SYS_exit_group = int(231)
235 \\ %exit_code = as(@usize, @0)
236 \\
237 \\ %syscall = ref(@syscall_array)
238 \\ %sysoutreg = ref(@sysoutreg_array)
239 \\ %rax = ref(@rax_array)
240 \\ %rdi = ref(@rdi_array)
241 \\ %rcx = ref(@rcx_array)
242 \\ %r11 = ref(@r11_array)
243 \\ %memory = ref(@memory_array)
244 \\
245 \\ %rc = asm(%syscall, @usize,
246 \\ volatile=1,
247 \\ output=%sysoutreg,
248 \\ inputs=[%rax, %rdi],
249 \\ clobbers=[%rcx, %r11, %memory],
250 \\ args=[%SYS_exit_group, %exit_code])
251 \\
252 \\ %99 = unreachable()
253 \\});
254 \\
255 \\@start_fnty = fntype([], @noreturn, cc=Naked)
256 \\@start = fn(@start_fnty, {
257 \\ %0 = call(@exit0, [])
258 \\})
259 \\@9 = str("_start")
260 \\@10 = ref(@9)
261 \\@11 = export(@10, @start)
262 },
263 &[_][]const u8{""},
264 );
212}265}