Commit 8d3cca7fc2

Andrew Kelley <andrew@ziglang.org>
2020-05-19 19:51:46
stage2: function calls using the global offset table
so far they don't support parameters or return values
1 parent 8d812db
Changed files (3)
src-self-hosted
test
stage2
src-self-hosted/codegen.zig
@@ -19,24 +19,6 @@ pub const Result = union(enum) {
     fail: *Module.ErrorMsg,
 };
 
-pub fn pltEntrySize(target: Target) u16 {
-    return switch (target.cpu.arch) {
-        .i386, .x86_64 => 5,
-        else => @panic("TODO implement pltEntrySize for more architectures"),
-    };
-}
-
-pub fn writePltEntry(target: Target, buf: []u8, addr: u32) void {
-    switch (target.cpu.arch) {
-        .i386, .x86_64 => {
-            // 9a xx xx xx xx    call addr
-            buf[0] = 0x9a;
-            mem.writeIntLittle(u32, buf[1..5], addr);
-        },
-        else => @panic("TODO implement pltEntrySize for more architectures"),
-    }
-}
-
 pub fn generateSymbol(
     bin_file: *link.ElfFile,
     src: usize,
@@ -221,14 +203,14 @@ const Function = struct {
 
             if (func_inst.val.cast(Value.Payload.Function)) |func_val| {
                 const func = func_val.func;
-                const plt_index = func.owner_decl.link.offset_table_index.plt;
-                const plt = &self.bin_file.program_headers.items[self.bin_file.phdr_got_plt_index.?];
-                const plt_entry_size = pltEntrySize(self.target.*);
-                const plt_addr = @intCast(u32, plt.p_vaddr + func.owner_decl.link.offset_table_index.plt * plt_entry_size);
-                // ea xx xx xx xx    jmp addr
-                try self.code.resize(self.code.items.len + 5);
-                self.code.items[self.code.items.len - 5] = 0xea;
-                mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], plt_addr);
+                const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
+                const ptr_bits = self.target.cpu.arch.ptrBitWidth();
+                const ptr_bytes: u64 = @divExact(ptr_bits, 8);
+                const got_addr = @intCast(u32, got.p_vaddr + func.owner_decl.link.offset_table_index * ptr_bytes);
+                // ff 14 25 xx xx xx xx    call [addr]
+                try self.code.resize(self.code.items.len + 7);
+                self.code.items[self.code.items.len - 7 ..][0..3].* = [3]u8{ 0xff, 0x14, 0x25 };
+                mem.writeIntLittle(u32, self.code.items[self.code.items.len - 4 ..][0..4], got_addr);
                 const return_type = func.fn_type.fnReturnType();
                 switch (return_type.zigTypeTag()) {
                     .Void => return MCValue{ .none = {} },
@@ -606,7 +588,7 @@ const Function = struct {
                 if (typed_value.val.cast(Value.Payload.DeclRef)) |payload| {
                     const got = &self.bin_file.program_headers.items[self.bin_file.phdr_got_index.?];
                     const decl = payload.decl;
-                    const got_addr = got.p_vaddr + decl.link.offset_table_index.got * ptr_bytes;
+                    const got_addr = got.p_vaddr + decl.link.offset_table_index * ptr_bytes;
                     return MCValue{ .memory = got_addr };
                 }
                 return self.fail(src, "TODO codegen more kinds of const pointers", .{});
src-self-hosted/link.zig
@@ -110,7 +110,6 @@ pub const ElfFile = struct {
     /// The index into the program headers of the global offset table.
     /// It needs PT_LOAD and Read flags.
     phdr_got_index: ?u16 = null,
-    phdr_got_plt_index: ?u16 = null,
     entry_addr: ?u64 = null,
 
     shstrtab: std.ArrayListUnmanaged(u8) = std.ArrayListUnmanaged(u8){},
@@ -119,7 +118,6 @@ pub const ElfFile = struct {
     text_section_index: ?u16 = null,
     symtab_section_index: ?u16 = null,
     got_section_index: ?u16 = null,
-    got_plt_section_index: ?u16 = null,
 
     /// The same order as in the file. ELF requires global symbols to all be after the
     /// local symbols, they cannot be mixed. So we must buffer all the global symbols and
@@ -132,16 +130,11 @@ pub const ElfFile = struct {
     /// If the vaddr of the executable program header changes, the entire
     /// offset table needs to be rewritten.
     offset_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
-    /// Same order as in the file. The value is the absolute vaddr value.
-    /// If the vaddr of the executable program header changes, the entire
-    /// fn trampoline table needs to be rewritten.
-    fn_trampoline_table: std.ArrayListUnmanaged(u64) = std.ArrayListUnmanaged(u64){},
 
     phdr_table_dirty: bool = false,
     shdr_table_dirty: bool = false,
     shstrtab_dirty: bool = false,
     offset_table_count_dirty: bool = false,
-    fn_trampoline_table_count_dirty: bool = false,
 
     error_flags: ErrorFlags = ErrorFlags{},
 
@@ -157,18 +150,12 @@ pub const ElfFile = struct {
         /// If this field is 0, it means the codegen size = 0 and there is no symbol or
         /// offset table entry.
         local_sym_index: u32,
-        /// when size = 0 and there is no offset table index
-        offset_table_index: union {
-            unallocated: void,
-            /// This is an index into offset_table
-            got: u32,
-            /// This is an index into fn_trampoline_table
-            plt: u32,
-        },
+        /// This field is undefined for symbols with size = 0.
+        offset_table_index: u32,
 
         pub const empty = Decl{
             .local_sym_index = 0,
-            .offset_table_index = .{ .unallocated = {} },
+            .offset_table_index = undefined,
         };
     };
 
@@ -183,7 +170,6 @@ pub const ElfFile = struct {
         self.local_symbols.deinit(self.allocator);
         self.global_symbols.deinit(self.allocator);
         self.offset_table.deinit(self.allocator);
-        self.fn_trampoline_table.deinit(self.allocator);
         if (self.owns_file_handle) {
             if (self.file) |f| f.close();
         }
@@ -357,30 +343,6 @@ pub const ElfFile = struct {
             });
             self.phdr_table_dirty = true;
         }
-        if (self.phdr_got_plt_index == null) {
-            self.phdr_got_plt_index = @intCast(u16, self.program_headers.items.len);
-            const file_size = @as(u64, ptr_size) * self.options.symbol_count_hint;
-            // We really only need ptr alignment but since we are using PROGBITS, linux requires
-            // page align.
-            const p_align = 0x1000;
-            const off = self.findFreeSpace(file_size, p_align);
-            //std.debug.warn("found PT_LOAD free space 0x{x} to 0x{x}\n", .{ off, off + file_size });
-            // TODO instead of hard coding the vaddr, make a function to find a vaddr to put things at.
-            // we'll need to re-use that function anyway, in case the GOT grows and overlaps something
-            // else in virtual memory.
-            const default_got_plt_addr = 0x6000000;
-            try self.program_headers.append(self.allocator, .{
-                .p_type = elf.PT_LOAD,
-                .p_offset = off,
-                .p_filesz = file_size,
-                .p_vaddr = default_got_plt_addr,
-                .p_paddr = default_got_plt_addr,
-                .p_memsz = file_size,
-                .p_align = p_align,
-                .p_flags = elf.PF_R,
-            });
-            self.phdr_table_dirty = true;
-        }
         if (self.shstrtab_index == null) {
             self.shstrtab_index = @intCast(u16, self.sections.items.len);
             assert(self.shstrtab.items.len == 0);
@@ -438,24 +400,6 @@ pub const ElfFile = struct {
             });
             self.shdr_table_dirty = true;
         }
-        if (self.got_plt_section_index == null) {
-            self.got_plt_section_index = @intCast(u16, self.sections.items.len);
-            const phdr = &self.program_headers.items[self.phdr_got_plt_index.?];
-
-            try self.sections.append(self.allocator, .{
-                .sh_name = try self.makeString(".got.plt"),
-                .sh_type = elf.SHT_PROGBITS,
-                .sh_flags = elf.SHF_ALLOC,
-                .sh_addr = phdr.p_vaddr,
-                .sh_offset = phdr.p_offset,
-                .sh_size = phdr.p_filesz,
-                .sh_link = 0,
-                .sh_info = 0,
-                .sh_addralign = phdr.p_align,
-                .sh_entsize = 0,
-            });
-            self.shdr_table_dirty = true;
-        }
         if (self.symtab_section_index == null) {
             self.symtab_section_index = @intCast(u16, self.sections.items.len);
             const min_align: u16 = if (small_ptr) @alignOf(elf.Elf32_Sym) else @alignOf(elf.Elf64_Sym);
@@ -640,7 +584,6 @@ pub const ElfFile = struct {
         assert(!self.shdr_table_dirty);
         assert(!self.shstrtab_dirty);
         assert(!self.offset_table_count_dirty);
-        assert(!self.fn_trampoline_table_count_dirty);
         const syms_sect = &self.sections.items[self.symtab_section_index.?];
         assert(syms_sect.sh_info == self.local_symbols.items.len);
     }
@@ -836,14 +779,10 @@ pub const ElfFile = struct {
     pub fn allocateDeclIndexes(self: *ElfFile, decl: *Module.Decl) !void {
         if (decl.link.local_sym_index != 0) return;
 
-        const is_fn = (decl.typed_value.most_recent.typed_value.ty.zigTypeTag() == .Fn);
-
         try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
         try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
-        try self.fn_trampoline_table.ensureCapacity(self.allocator, self.fn_trampoline_table.items.len + 1);
         const local_sym_index = self.local_symbols.items.len;
         const offset_table_index = self.offset_table.items.len;
-        const fn_trampoline_table_index = self.fn_trampoline_table.items.len;
         const phdr = &self.program_headers.items[self.phdr_load_re_index.?];
 
         self.local_symbols.appendAssumeCapacity(.{
@@ -854,20 +793,15 @@ pub const ElfFile = struct {
             .st_value = phdr.p_vaddr,
             .st_size = 0,
         });
-        if (is_fn) {
-            self.fn_trampoline_table.appendAssumeCapacity(0);
-            self.fn_trampoline_table_count_dirty = true;
-        } else {
-            self.offset_table.appendAssumeCapacity(0);
-            self.offset_table_count_dirty = true;
-        }
+        errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
+        self.offset_table.appendAssumeCapacity(0);
+        errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
+
+        self.offset_table_count_dirty = true;
 
         decl.link = .{
             .local_sym_index = @intCast(u32, local_sym_index),
-            .offset_table_index = if (is_fn)
-                .{ .plt = @intCast(u32, fn_trampoline_table_index) }
-            else
-                .{ .got = @intCast(u32, offset_table_index) },
+            .offset_table_index = @intCast(u32, offset_table_index),
         };
     }
 
@@ -885,7 +819,6 @@ pub const ElfFile = struct {
                 return;
             },
         };
-        const is_fn = (typed_value.ty.zigTypeTag() == .Fn);
 
         const required_alignment = typed_value.ty.abiAlignment(self.options.target);
 
@@ -905,13 +838,14 @@ pub const ElfFile = struct {
                 const file_offset = if (need_realloc) fo: {
                     const new_block = try self.allocateTextBlock(code.len, required_alignment);
                     local_sym.st_value = new_block.vaddr;
-                    if (is_fn) {
-                        self.fn_trampoline_table.items[decl.link.offset_table_index.plt] = new_block.vaddr;
-                        try self.writeFnTrampolineEntry(decl.link.offset_table_index.plt);
-                    } else {
-                        self.offset_table.items[decl.link.offset_table_index.got] = new_block.vaddr;
-                        try self.writeOffsetTableEntry(decl.link.offset_table_index.got);
-                    }
+                    self.offset_table.items[decl.link.offset_table_index] = new_block.vaddr;
+
+                    //std.debug.warn("{}: writing got index {}=0x{x}\n", .{
+                    //    decl.name,
+                    //    decl.link.offset_table_index,
+                    //    self.offset_table.items[decl.link.offset_table_index],
+                    //});
+                    try self.writeOffsetTableEntry(decl.link.offset_table_index);
 
                     break :fo new_block.file_offset;
                 } else existing_block.file_offset;
@@ -928,13 +862,11 @@ pub const ElfFile = struct {
             } else {
                 try self.local_symbols.ensureCapacity(self.allocator, self.local_symbols.items.len + 1);
                 try self.offset_table.ensureCapacity(self.allocator, self.offset_table.items.len + 1);
-                try self.fn_trampoline_table.ensureCapacity(self.allocator, self.fn_trampoline_table.items.len + 1);
                 const decl_name = mem.spanZ(decl.name);
                 const name_str_index = try self.makeString(decl_name);
                 const new_block = try self.allocateTextBlock(code.len, required_alignment);
                 const local_sym_index = self.local_symbols.items.len;
                 const offset_table_index = self.offset_table.items.len;
-                const fn_trampoline_table_index = self.fn_trampoline_table.items.len;
 
                 //std.debug.warn("add symbol for {} at vaddr 0x{x}, size {}\n", .{ decl.name, new_block.vaddr, code.len });
                 self.local_symbols.appendAssumeCapacity(.{
@@ -946,32 +878,17 @@ pub const ElfFile = struct {
                     .st_size = code.len,
                 });
                 errdefer self.local_symbols.shrink(self.allocator, self.local_symbols.items.len - 1);
-                if (is_fn) {
-                    self.fn_trampoline_table.appendAssumeCapacity(new_block.vaddr);
-                } else {
-                    self.offset_table.appendAssumeCapacity(new_block.vaddr);
-                }
-                errdefer if (is_fn) {
-                    self.fn_trampoline_table.shrink(self.allocator, self.fn_trampoline_table.items.len - 1);
-                } else {
-                    self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
-                };
+                self.offset_table.appendAssumeCapacity(new_block.vaddr);
+                errdefer self.offset_table.shrink(self.allocator, self.offset_table.items.len - 1);
+
+                self.offset_table_count_dirty = true;
 
                 try self.writeSymbol(local_sym_index);
-                if (is_fn) {
-                    try self.writeFnTrampolineEntry(fn_trampoline_table_index);
-                    self.fn_trampoline_table_count_dirty = true;
-                } else {
-                    try self.writeOffsetTableEntry(offset_table_index);
-                    self.offset_table_count_dirty = true;
-                }
+                try self.writeOffsetTableEntry(offset_table_index);
 
                 decl.link = .{
                     .local_sym_index = @intCast(u32, local_sym_index),
-                    .offset_table_index = if (is_fn)
-                        .{ .plt = @intCast(u32, fn_trampoline_table_index) }
-                    else
-                        .{ .got = @intCast(u32, offset_table_index) },
+                    .offset_table_index = @intCast(u32, offset_table_index),
                 };
 
                 //std.debug.warn("writing new {} at vaddr 0x{x}\n", .{ decl.name, new_block.vaddr });
@@ -1101,40 +1018,6 @@ pub const ElfFile = struct {
         }
     }
 
-    fn writeFnTrampolineEntry(self: *ElfFile, index: usize) !void {
-        const shdr = &self.sections.items[self.got_plt_section_index.?];
-        const phdr = &self.program_headers.items[self.phdr_got_plt_index.?];
-        const entry_size = codegen.pltEntrySize(self.options.target);
-        var entry_buf: [16]u8 = undefined;
-        assert(entry_size <= entry_buf.len);
-
-        if (self.fn_trampoline_table_count_dirty) {
-            // TODO Also detect virtual address collisions.
-            const allocated_size = self.allocatedSize(shdr.sh_offset);
-            const needed_size = self.local_symbols.items.len * entry_size;
-            if (needed_size > allocated_size) {
-                // Must move the entire .got.plt section.
-                const new_offset = self.findFreeSpace(needed_size, entry_size);
-                const amt = try self.file.?.copyRangeAll(shdr.sh_offset, self.file.?, new_offset, shdr.sh_size);
-                if (amt != shdr.sh_size) return error.InputOutput;
-                shdr.sh_offset = new_offset;
-                phdr.p_offset = new_offset;
-            }
-            shdr.sh_size = needed_size;
-            phdr.p_memsz = needed_size;
-            phdr.p_filesz = needed_size;
-
-            self.shdr_table_dirty = true; // TODO look into making only the one section dirty
-            self.phdr_table_dirty = true; // TODO look into making only the one program header dirty
-
-            self.fn_trampoline_table_count_dirty = false;
-        }
-        const off = shdr.sh_offset + @as(u64, entry_size) * index;
-        const vaddr = @intCast(u32, self.fn_trampoline_table.items[index]);
-        codegen.writePltEntry(self.options.target, &entry_buf, vaddr);
-        try self.file.?.pwriteAll(entry_buf[0..entry_size], off);
-    }
-
     fn writeOffsetTableEntry(self: *ElfFile, index: usize) !void {
         const shdr = &self.sections.items[self.got_section_index.?];
         const phdr = &self.program_headers.items[self.phdr_got_index.?];
test/stage2/zir.zig
@@ -209,4 +209,57 @@ pub fn addCases(ctx: *TestContext) void {
             \\
         },
     );
+
+    ctx.addZIRCompareOutput(
+        "function call with no args no return value",
+        &[_][]const u8{
+            \\@noreturn = primitive(noreturn)
+            \\@void = primitive(void)
+            \\@usize = primitive(usize)
+            \\@0 = int(0)
+            \\@1 = int(1)
+            \\@2 = int(2)
+            \\@3 = int(3)
+            \\
+            \\@syscall_array = str("syscall")
+            \\@sysoutreg_array = str("={rax}")
+            \\@rax_array = str("{rax}")
+            \\@rdi_array = str("{rdi}")
+            \\@rcx_array = str("rcx")
+            \\@r11_array = str("r11")
+            \\@memory_array = str("memory")
+            \\
+            \\@exit0_fnty = fntype([], @noreturn)
+            \\@exit0 = fn(@exit0_fnty, {
+            \\  %SYS_exit_group = int(231)
+            \\  %exit_code = as(@usize, @0)
+            \\
+            \\  %syscall = ref(@syscall_array)
+            \\  %sysoutreg = ref(@sysoutreg_array)
+            \\  %rax = ref(@rax_array)
+            \\  %rdi = ref(@rdi_array)
+            \\  %rcx = ref(@rcx_array)
+            \\  %r11 = ref(@r11_array)
+            \\  %memory = ref(@memory_array)
+            \\
+            \\  %rc = asm(%syscall, @usize,
+            \\    volatile=1,
+            \\    output=%sysoutreg,
+            \\    inputs=[%rax, %rdi],
+            \\    clobbers=[%rcx, %r11, %memory],
+            \\    args=[%SYS_exit_group, %exit_code])
+            \\
+            \\  %99 = unreachable()
+            \\});
+            \\
+            \\@start_fnty = fntype([], @noreturn, cc=Naked)
+            \\@start = fn(@start_fnty, {
+            \\  %0 = call(@exit0, [])
+            \\})
+            \\@9 = str("_start")
+            \\@10 = ref(@9)
+            \\@11 = export(@10, @start)
+        },
+        &[_][]const u8{""},
+    );
 }