Commit 8d3cca7fc2
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{""},
+ );
}