authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-19 13:33:36-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-05-19 13:33:36-04:00
log8d812dba30fe3c17e9d64607fdf813977eaf0496
tree84dba7aad60ad2472da927d65ffffa7090386a6d
parent1cde0edff469fbe5ee62cb10a44161b4c9910f98

stage2: set up a trampoline table for functions

However there does not appear to be an x86 encoding for calling an immediate address. So there's no point of setting this up. We should just emit an indirect call to the got addr.

3 files changed, 183 insertions(+), 34 deletions(-)

src-self-hosted/Module.zig+8-9
......@@ -231,6 +231,7 @@ pub const Fn = struct {
231231 dependency_failure,
232232 success: Body,
233233 },
234 owner_decl: *Decl,
234235
235236 /// This memory is temporary and points to stack memory for the duration
236237 /// of Fn analysis.
......@@ -883,14 +884,6 @@ fn resolveDecl(
883884 };
884885 const arena_state = try decl_scope.arena.allocator.create(std.heap.ArenaAllocator.State);
885886
886 const has_codegen_bits = typed_value.ty.hasCodeGenBits();
887 if (has_codegen_bits) {
888 // We don't fully codegen the decl until later, but we do need to reserve a global
889 // offset table index for it. This allows us to codegen decls out of dependency order,
890 // increasing how many computations can be done in parallel.
891 try self.bin_file.allocateDeclIndexes(new_decl);
892 }
893
894887 arena_state.* = decl_scope.arena.state;
895888
896889 new_decl.typed_value = .{
......@@ -900,7 +893,12 @@ fn resolveDecl(
900893 },
901894 };
902895 new_decl.analysis = .complete;
903 if (has_codegen_bits) {
896 if (typed_value.ty.hasCodeGenBits()) {
897 // We don't fully codegen the decl until later, but we do need to reserve a global
898 // offset table index for it. This allows us to codegen decls out of dependency order,
899 // increasing how many computations can be done in parallel.
900 try self.bin_file.allocateDeclIndexes(new_decl);
901
904902 // We ensureCapacity when scanning for decls.
905903 self.work_queue.writeItemAssumeCapacity(.{ .codegen_decl = new_decl });
906904 }
......@@ -1329,6 +1327,7 @@ fn analyzeInstFn(self: *Module, scope: *Scope, fn_inst: *zir.Inst.Fn) InnerError
13291327 new_func.* = .{
13301328 .fn_type = fn_type,
13311329 .analysis = .{ .queued = fn_inst },
1330 .owner_decl = scope.decl(),
13321331 };
13331332 const fn_payload = try scope.arena().create(Value.Payload.Function);
13341333 fn_payload.* = .{ .func = new_func };
src-self-hosted/codegen.zig+33-2
......@@ -19,6 +19,24 @@ pub const Result = union(enum) {
1919 fail: *Module.ErrorMsg,
2020};
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
2240pub fn generateSymbol(
2341 bin_file: *link.ElfFile,
2442 src: usize,
......@@ -203,7 +221,20 @@ const Function = struct {
203221
204222 if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
205223 const func = func_val.func;
206 return self.fail(inst.base.src, "TODO implement calling function", .{});
224 const plt_index = func.owner_decl.link.offset_table_index.plt;
225 const plt = &self.bin_file.program_headers.items[self.bin_file.phdr_got_plt_index.?];
226 const plt_entry_size = pltEntrySize(self.target.*);
227 const plt_addr = @intCast(u32, plt.p_vaddr + func.owner_decl.link.offset_table_index.plt * plt_entry_size);
228 // ea xx xx xx xx jmp addr
229 try self.code.resize(self.code.items.len + 5);
230 self.code.items[self.code.items.len - 5] = 0xea;
231 mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], plt_addr);
232 const return_type = func.fn_type.fnReturnType();
233 switch (return_type.zigTypeTag()) {
234 .Void => return MCValue{ .none = {} },
235 .NoReturn => return MCValue{ .unreach = {} },
236 else => return self.fail(inst.base.src, "TODO implement fn call with non-void return value", .{}),
237 }
207238 } else {
208239 return self.fail(inst.base.src, "TODO implement calling weird function values", .{});
209240 }
......@@ -575,7 +606,7 @@ const Function = struct {
575606 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
576607 const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
577608 const decl = payload.decl;
578 const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
609 const got_addr = got.p_vaddr + decl.link.offset_table_index.got * ptr_bytes;
579610 return MCValue{ .memory = got_addr };
580611 }
581612 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
src-self-hosted/link.zig+142-23
......@@ -110,6 +110,7 @@ pub const ElfFile = struct {
110110 /// The index into the program headers of the global offset table.
111111 /// It needs PT_LOAD and Read flags.
112112 phdr_got_index: ?u16 = null,
113 phdr_got_plt_index: ?u16 = null,
113114 entry_addr: ?u64 = null,
114115
115116 shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
......@@ -118,6 +119,7 @@ pub const ElfFile = struct {
118119 text_section_index: ?u16 = null,
119120 symtab_section_index: ?u16 = null,
120121 got_section_index: ?u16 = null,
122 got_plt_section_index: ?u16 = null,
121123
122124 /// The same order as in the file. ELF requires global symbols to all be after the
123125 /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
......@@ -130,11 +132,16 @@ pub const ElfFile = struct {
130132 /// If the vaddr of the executable program header changes, the entire
131133 /// offset table needs to be rewritten.
132134 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){},
133139
134140 phdr_table_dirty: bool = false,
135141 shdr_table_dirty: bool = false,
136142 shstrtab_dirty: bool = false,
137143 offset_table_count_dirty: bool = false,
144 fn_trampoline_table_count_dirty: bool = false,
138145
139146 error_flags: ErrorFlags = ErrorFlags{},
140147
......@@ -150,12 +157,18 @@ pub const ElfFile = struct {
150157 /// If this field is 0, it means the codegen size = 0 and there is no symbol or
151158 /// offset table entry.
152159 local_sym_index: u32,
153 /// This field is undefined for symbols with size = 0.
154 offset_table_index: u32,
160 /// when size = 0 and there is no offset table index
161 offset_table_index: union {
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 },
155168
156169 pub const empty = Decl{
157170 .local_sym_index = 0,
158 .offset_table_index = undefined,
171 .offset_table_index = .{ .unallocated = {} },
159172 };
160173 };
161174
......@@ -170,6 +183,7 @@ pub const ElfFile = struct {
170183 self.local_symbols.deinit(self.allocator);
171184 self.global_symbols.deinit(self.allocator);
172185 self.offset_table.deinit(self.allocator);
186 self.fn_trampoline_table.deinit(self.allocator);
173187 if (self.owns_file_handle) {
174188 if (self.file) |f| f.close();
175189 }
......@@ -343,6 +357,30 @@ pub const ElfFile = struct {
343357 });
344358 self.phdr_table_dirty = true;
345359 }
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 }
346384 if (self.shstrtab_index == null) {
347385 self.shstrtab_index = @intCast(u16, self.sections.items.len);
348386 assert(self.shstrtab.items.len == 0);
......@@ -400,6 +438,24 @@ pub const ElfFile = struct {
400438 });
401439 self.shdr_table_dirty = true;
402440 }
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 }
403459 if (self.symtab_section_index == null) {
404460 self.symtab_section_index = @intCast(u16, self.sections.items.len);
405461 const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
......@@ -584,6 +640,7 @@ pub const ElfFile = struct {
584640 assert(!self.shdr_table_dirty);
585641 assert(!self.shstrtab_dirty);
586642 assert(!self.offset_table_count_dirty);
643 assert(!self.fn_trampoline_table_count_dirty);
587644 const syms_sect = &self.sections.items[self.symtab_section_index.?];
588645 assert(syms_sect.sh_info == self.local_symbols.items.len);
589646 }
......@@ -740,6 +797,7 @@ pub const ElfFile = struct {
740797 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, text_size);
741798 if (amt != text_size) return error.InputOutput;
742799 shdr.sh_offset = new_offset;
800 phdr.p_offset = new_offset;
743801 }
744802 // Now that we know the code size, we need to update the program header for executable code
745803 shdr.sh_size = needed_size;
......@@ -778,10 +836,14 @@ pub const ElfFile = struct {
778836 pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
779837 if (decl.link.local_sym_index != 0) return;
780838
839 const is_fn = (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
840
781841 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
782842 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);
783844 const local_sym_index = self.local_symbols.items.len;
784845 const offset_table_index = self.offset_table.items.len;
846 const fn_trampoline_table_index = self.fn_trampoline_table.items.len;
785847 const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
786848
787849 self.local_symbols.appendAssumeCapacity(.{
......@@ -792,15 +854,20 @@ pub const ElfFile = struct {
792854 .st_value = phdr.p_vaddr,
793855 .st_size = 0,
794856 });
795 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
796 self.offset_table.appendAssumeCapacity(0);
797 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
798
799 self.offset_table_count_dirty = true;
857 if (is_fn) {
858 self.fn_trampoline_table.appendAssumeCapacity(0);
859 self.fn_trampoline_table_count_dirty = true;
860 } else {
861 self.offset_table.appendAssumeCapacity(0);
862 self.offset_table_count_dirty = true;
863 }
800864
801865 decl.link = .{
802866 .local_sym_index = @intCast(u32, local_sym_index),
803 .offset_table_index = @intCast(u32, offset_table_index),
867 .offset_table_index = if (is_fn)
868 .{ .plt = @intCast(u32, fn_trampoline_table_index) }
869 else
870 .{ .got = @intCast(u32, offset_table_index) },
804871 };
805872 }
806873
......@@ -818,6 +885,7 @@ pub const ElfFile = struct {
818885 return;
819886 },
820887 };
888 const is_fn = (typed_value.ty.zigTypeTag() == .Fn);
821889
822890 const required_alignment = typed_value.ty.abiAlignment(self.options.target);
823891
......@@ -837,14 +905,13 @@ pub const ElfFile = struct {
837905 const file_offset = if (need_realloc) fo: {
838906 const new_block = try self.allocateTextBlock(code.len, required_alignment);
839907 local_sym.st_value = new_block.vaddr;
840 self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr;
841
842 //std.debug.warn("{}: writing got index {}=0x{x}\n", .{
843 // decl.name,
844 // decl.link.offset_table_index,
845 // self.offset_table.items[decl.link.offset_table_index],
846 //});
847 try self.writeOffsetTableEntry(decl.link.offset_table_index);
908 if (is_fn) {
909 self.fn_trampoline_table.items[decl.link.offset_table_index.plt] = new_block.vaddr;
910 try self.writeFnTrampolineEntry(decl.link.offset_table_index.plt);
911 } else {
912 self.offset_table.items[decl.link.offset_table_index.got] = new_block.vaddr;
913 try self.writeOffsetTableEntry(decl.link.offset_table_index.got);
914 }
848915
849916 break :fo new_block.file_offset;
850917 } else existing_block.file_offset;
......@@ -861,11 +928,13 @@ pub const ElfFile = struct {
861928 } else {
862929 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
863930 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);
864932 const decl_name = mem.spanZ(decl.name);
865933 const name_str_index = try self.makeString(decl_name);
866934 const new_block = try self.allocateTextBlock(code.len, required_alignment);
867935 const local_sym_index = self.local_symbols.items.len;
868936 const offset_table_index = self.offset_table.items.len;
937 const fn_trampoline_table_index = self.fn_trampoline_table.items.len;
869938
870939 //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len });
871940 self.local_symbols.appendAssumeCapacity(.{
......@@ -877,17 +946,32 @@ pub const ElfFile = struct {
877946 .st_size = code.len,
878947 });
879948 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
880 self.offset_table.appendAssumeCapacity(new_block.vaddr);
881 errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
882
883 self.offset_table_count_dirty = true;
949 if (is_fn) {
950 self.fn_trampoline_table.appendAssumeCapacity(new_block.vaddr);
951 } else {
952 self.offset_table.appendAssumeCapacity(new_block.vaddr);
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 };
884959
885960 try self.writeSymbol(local_sym_index);
886 try self.writeOffsetTableEntry(offset_table_index);
961 if (is_fn) {
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 }
887968
888969 decl.link = .{
889970 .local_sym_index = @intCast(u32, local_sym_index),
890 .offset_table_index = @intCast(u32, offset_table_index),
971 .offset_table_index = if (is_fn)
972 .{ .plt = @intCast(u32, fn_trampoline_table_index) }
973 else
974 .{ .got = @intCast(u32, offset_table_index) },
891975 };
892976
893977 //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr });
......@@ -1017,6 +1101,40 @@ pub const ElfFile = struct {
10171101 }
10181102 }
10191103
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
10201138 fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {
10211139 const shdr = &self.sections.items[self.got_section_index.?];
10221140 const phdr = &self.program_headers.items[self.phdr_got_index.?];
......@@ -1034,6 +1152,7 @@ pub const ElfFile = struct {
10341152 const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
10351153 if (amt != shdr.sh_size) return error.InputOutput;
10361154 shdr.sh_offset = new_offset;
1155 phdr.p_offset = new_offset;
10371156 }
10381157 shdr.sh_size = needed_size;
10391158 phdr.p_memsz = needed_size;