| ... | ... | @@ -128,18 +128,28 @@ const Entry = struct { |
| 128 | 128 | pub const Reloc = struct { |
| 129 | 129 | @"type": enum { |
| 130 | 130 | // x86, x86_64 |
| 131 | /// RIP-relative displacement to a GOT pointer |
| 131 | 132 | got, |
| 133 | /// RIP-relative displacement to an import pointer |
| 134 | import, |
| 132 | 135 | |
| 133 | 136 | // aarch64 |
| 134 | | branch_26, |
| 137 | /// PC-relative distance to target page in GOT section |
| 135 | 138 | got_page, |
| 139 | /// Offset to a GOT pointer relative to the start of a page in GOT section |
| 136 | 140 | got_pageoff, |
| 141 | /// PC-relative distance to target page in a section (e.g., .rdata) |
| 137 | 142 | page, |
| 143 | /// Offset to a pointer relative to the start of a page in a section (e.g., .rdata) |
| 138 | 144 | pageoff, |
| 145 | /// PC-relative distance to target page in a import section |
| 146 | import_page, |
| 147 | /// Offset to a pointer relative to the start of a page in an import section (e.g., .rdata) |
| 148 | import_pageoff, |
| 139 | 149 | |
| 140 | 150 | // common |
| 141 | | import, |
| 142 | | direct, // as unsigned, TODO split into signed for x86 |
| 151 | /// Absolute pointer value |
| 152 | direct, |
| 143 | 153 | }, |
| 144 | 154 | target: SymbolWithLoc, |
| 145 | 155 | offset: u32, |
| ... | ... | @@ -157,12 +167,14 @@ pub const Reloc = struct { |
| 157 | 167 | => return coff_file.getGotAtomForSymbol(self.target), |
| 158 | 168 | |
| 159 | 169 | .direct, |
| 160 | | .branch_26, |
| 161 | 170 | .page, |
| 162 | 171 | .pageoff, |
| 163 | 172 | => return coff_file.getAtomForSymbol(self.target), |
| 164 | 173 | |
| 165 | | .import => return coff_file.getImportAtomForSymbol(self.target), |
| 174 | .import, |
| 175 | .import_page, |
| 176 | .import_pageoff, |
| 177 | => return coff_file.getImportAtomForSymbol(self.target), |
| 166 | 178 | } |
| 167 | 179 | } |
| 168 | 180 | }; |
| ... | ... | @@ -172,8 +184,6 @@ const BaseRelocationTable = std.AutoHashMapUnmanaged(*Atom, std.ArrayListUnmanag |
| 172 | 184 | const UnnamedConstTable = std.AutoHashMapUnmanaged(Module.Decl.Index, std.ArrayListUnmanaged(*Atom)); |
| 173 | 185 | |
| 174 | 186 | const default_file_alignment: u16 = 0x200; |
| 175 | | const default_image_base_dll: u64 = 0x10000000; |
| 176 | | const default_image_base_exe: u64 = 0x400000; |
| 177 | 187 | const default_size_of_stack_reserve: u32 = 0x1000000; |
| 178 | 188 | const default_size_of_stack_commit: u32 = 0x1000; |
| 179 | 189 | const default_size_of_heap_reserve: u32 = 0x100000; |
| ... | ... | @@ -891,6 +901,7 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 891 | 901 | const target_atom = reloc.getTargetAtom(self) orelse continue; |
| 892 | 902 | const target_vaddr = target_atom.getSymbol(self).value; |
| 893 | 903 | const target_vaddr_with_addend = target_vaddr + reloc.addend; |
| 904 | const image_base = self.getImageBase(); |
| 894 | 905 | |
| 895 | 906 | log.debug(" ({x}: [() => 0x{x} ({s})) ({s}) (in file at 0x{x})", .{ |
| 896 | 907 | source_vaddr, |
| ... | ... | @@ -902,31 +913,23 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 902 | 913 | |
| 903 | 914 | reloc.dirty = false; |
| 904 | 915 | |
| 905 | | var buffer: [@sizeOf(u32)]u8 = undefined; |
| 906 | | switch (reloc.@"type") { |
| 907 | | .branch_26, |
| 908 | | .got_page, |
| 909 | | .got_pageoff, |
| 910 | | .page, |
| 911 | | .pageoff, |
| 912 | | => { |
| 913 | | const amt = try self.base.file.?.preadAll(&buffer, file_offset + reloc.offset); |
| 914 | | if (amt != buffer.len) return error.InputOutput; |
| 916 | switch (self.base.options.target.cpu.arch) { |
| 917 | .aarch64 => { |
| 918 | var buffer: [@sizeOf(u64)]u8 = undefined; |
| 919 | switch (reloc.length) { |
| 920 | 2 => { |
| 921 | const amt = try self.base.file.?.preadAll(buffer[0..4], file_offset + reloc.offset); |
| 922 | if (amt != 4) return error.InputOutput; |
| 923 | }, |
| 924 | 3 => { |
| 925 | const amt = try self.base.file.?.preadAll(&buffer, file_offset + reloc.offset); |
| 926 | if (amt != 8) return error.InputOutput; |
| 927 | }, |
| 928 | else => unreachable, |
| 929 | } |
| 915 | 930 | |
| 916 | 931 | switch (reloc.@"type") { |
| 917 | | .branch_26 => { |
| 918 | | const displacement = math.cast(i28, @intCast(i64, target_vaddr_with_addend) - @intCast(i64, source_vaddr)) orelse |
| 919 | | unreachable; // TODO generate thunks |
| 920 | | var inst = aarch64.Instruction{ |
| 921 | | .unconditional_branch_immediate = mem.bytesToValue(meta.TagPayload( |
| 922 | | aarch64.Instruction, |
| 923 | | aarch64.Instruction.unconditional_branch_immediate, |
| 924 | | ), &buffer), |
| 925 | | }; |
| 926 | | inst.unconditional_branch_immediate.imm26 = @truncate(u26, @bitCast(u28, displacement >> 2)); |
| 927 | | mem.writeIntLittle(u32, &buffer, inst.toU32()); |
| 928 | | }, |
| 929 | | .got_page, .page => { |
| 932 | .got_page, .import_page, .page => { |
| 930 | 933 | const source_page = @intCast(i32, source_vaddr >> 12); |
| 931 | 934 | const target_page = @intCast(i32, target_vaddr_with_addend >> 12); |
| 932 | 935 | const pages = @bitCast(u21, @intCast(i21, target_page - source_page)); |
| ... | ... | @@ -934,31 +937,31 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 934 | 937 | .pc_relative_address = mem.bytesToValue(meta.TagPayload( |
| 935 | 938 | aarch64.Instruction, |
| 936 | 939 | aarch64.Instruction.pc_relative_address, |
| 937 | | ), &buffer), |
| 940 | ), buffer[0..4]), |
| 938 | 941 | }; |
| 939 | 942 | inst.pc_relative_address.immhi = @truncate(u19, pages >> 2); |
| 940 | 943 | inst.pc_relative_address.immlo = @truncate(u2, pages); |
| 941 | | mem.writeIntLittle(u32, &buffer, inst.toU32()); |
| 944 | mem.writeIntLittle(u32, buffer[0..4], inst.toU32()); |
| 942 | 945 | }, |
| 943 | | .got_pageoff, .pageoff => { |
| 946 | .got_pageoff, .import_pageoff, .pageoff => { |
| 944 | 947 | assert(!reloc.pcrel); |
| 945 | 948 | |
| 946 | 949 | const narrowed = @truncate(u12, @intCast(u64, target_vaddr_with_addend)); |
| 947 | | if (isArithmeticOp(&buffer)) { |
| 950 | if (isArithmeticOp(buffer[0..4])) { |
| 948 | 951 | var inst = aarch64.Instruction{ |
| 949 | 952 | .add_subtract_immediate = mem.bytesToValue(meta.TagPayload( |
| 950 | 953 | aarch64.Instruction, |
| 951 | 954 | aarch64.Instruction.add_subtract_immediate, |
| 952 | | ), &buffer), |
| 955 | ), buffer[0..4]), |
| 953 | 956 | }; |
| 954 | 957 | inst.add_subtract_immediate.imm12 = narrowed; |
| 955 | | mem.writeIntLittle(u32, &buffer, inst.toU32()); |
| 958 | mem.writeIntLittle(u32, buffer[0..4], inst.toU32()); |
| 956 | 959 | } else { |
| 957 | 960 | var inst = aarch64.Instruction{ |
| 958 | 961 | .load_store_register = mem.bytesToValue(meta.TagPayload( |
| 959 | 962 | aarch64.Instruction, |
| 960 | 963 | aarch64.Instruction.load_store_register, |
| 961 | | ), &buffer), |
| 964 | ), buffer[0..4]), |
| 962 | 965 | }; |
| 963 | 966 | const offset: u12 = blk: { |
| 964 | 967 | if (inst.load_store_register.size == 0) { |
| ... | ... | @@ -974,55 +977,73 @@ fn resolveRelocs(self: *Coff, atom: *Atom) !void { |
| 974 | 977 | } |
| 975 | 978 | }; |
| 976 | 979 | inst.load_store_register.offset = offset; |
| 977 | | mem.writeIntLittle(u32, &buffer, inst.toU32()); |
| 980 | mem.writeIntLittle(u32, buffer[0..4], inst.toU32()); |
| 981 | } |
| 982 | }, |
| 983 | .direct => { |
| 984 | assert(!reloc.pcrel); |
| 985 | switch (reloc.length) { |
| 986 | 2 => mem.writeIntLittle( |
| 987 | u32, |
| 988 | buffer[0..4], |
| 989 | @truncate(u32, target_vaddr_with_addend + image_base), |
| 990 | ), |
| 991 | 3 => mem.writeIntLittle(u64, &buffer, target_vaddr_with_addend + image_base), |
| 992 | else => unreachable, |
| 978 | 993 | } |
| 979 | 994 | }, |
| 980 | 995 | |
| 981 | | else => unreachable, |
| 996 | .got => unreachable, |
| 997 | .import => unreachable, |
| 982 | 998 | } |
| 983 | 999 | |
| 984 | | try self.base.file.?.pwriteAll(&buffer, file_offset + reloc.offset); |
| 985 | | |
| 986 | | return; |
| 1000 | switch (reloc.length) { |
| 1001 | 2 => try self.base.file.?.pwriteAll(buffer[0..4], file_offset + reloc.offset), |
| 1002 | 3 => try self.base.file.?.pwriteAll(&buffer, file_offset + reloc.offset), |
| 1003 | else => unreachable, |
| 1004 | } |
| 987 | 1005 | }, |
| 988 | 1006 | |
| 989 | | else => {}, |
| 990 | | } |
| 991 | | |
| 992 | | switch (reloc.@"type") { |
| 993 | | .branch_26 => unreachable, |
| 994 | | .got_page => unreachable, |
| 995 | | .got_pageoff => unreachable, |
| 996 | | .page => unreachable, |
| 997 | | .pageoff => unreachable, |
| 998 | | |
| 999 | | .got, .import => { |
| 1000 | | assert(reloc.pcrel); |
| 1001 | | const disp = @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; |
| 1002 | | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); |
| 1003 | | }, |
| 1004 | | .direct => { |
| 1005 | | if (reloc.pcrel) { |
| 1006 | | const disp = @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; |
| 1007 | | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); |
| 1008 | | } else switch (self.ptr_width) { |
| 1009 | | .p32 => try self.base.file.?.pwriteAll( |
| 1010 | | mem.asBytes(&@intCast(u32, target_vaddr_with_addend + default_image_base_exe)), |
| 1011 | | file_offset + reloc.offset, |
| 1012 | | ), |
| 1013 | | .p64 => switch (reloc.length) { |
| 1014 | | 2 => try self.base.file.?.pwriteAll( |
| 1015 | | mem.asBytes(&@truncate(u32, target_vaddr_with_addend + default_image_base_exe)), |
| 1016 | | file_offset + reloc.offset, |
| 1017 | | ), |
| 1018 | | 3 => try self.base.file.?.pwriteAll( |
| 1019 | | mem.asBytes(&(target_vaddr_with_addend + default_image_base_exe)), |
| 1020 | | file_offset + reloc.offset, |
| 1021 | | ), |
| 1022 | | else => unreachable, |
| 1007 | .x86_64, .i386 => { |
| 1008 | switch (reloc.@"type") { |
| 1009 | .got_page => unreachable, |
| 1010 | .got_pageoff => unreachable, |
| 1011 | .page => unreachable, |
| 1012 | .pageoff => unreachable, |
| 1013 | .import_page => unreachable, |
| 1014 | .import_pageoff => unreachable, |
| 1015 | |
| 1016 | .got, .import => { |
| 1017 | assert(reloc.pcrel); |
| 1018 | const disp = @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; |
| 1019 | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); |
| 1020 | }, |
| 1021 | .direct => { |
| 1022 | if (reloc.pcrel) { |
| 1023 | const disp = @intCast(i32, target_vaddr_with_addend) - @intCast(i32, source_vaddr) - 4; |
| 1024 | try self.base.file.?.pwriteAll(mem.asBytes(&disp), file_offset + reloc.offset); |
| 1025 | } else switch (self.ptr_width) { |
| 1026 | .p32 => try self.base.file.?.pwriteAll( |
| 1027 | mem.asBytes(&@intCast(u32, target_vaddr_with_addend + image_base)), |
| 1028 | file_offset + reloc.offset, |
| 1029 | ), |
| 1030 | .p64 => switch (reloc.length) { |
| 1031 | 2 => try self.base.file.?.pwriteAll( |
| 1032 | mem.asBytes(&@truncate(u32, target_vaddr_with_addend + image_base)), |
| 1033 | file_offset + reloc.offset, |
| 1034 | ), |
| 1035 | 3 => try self.base.file.?.pwriteAll( |
| 1036 | mem.asBytes(&(target_vaddr_with_addend + image_base)), |
| 1037 | file_offset + reloc.offset, |
| 1038 | ), |
| 1039 | else => unreachable, |
| 1040 | }, |
| 1041 | } |
| 1023 | 1042 | }, |
| 1024 | 1043 | } |
| 1025 | 1044 | }, |
| 1045 | |
| 1046 | else => unreachable, // unhandled target architecture |
| 1026 | 1047 | } |
| 1027 | 1048 | } |
| 1028 | 1049 | } |
| ... | ... | @@ -1950,11 +1971,7 @@ fn writeHeader(self: *Coff) !void { |
| 1950 | 1971 | const subsystem: coff.Subsystem = .WINDOWS_CUI; |
| 1951 | 1972 | const size_of_image: u32 = self.getSizeOfImage(); |
| 1952 | 1973 | const size_of_headers: u32 = mem.alignForwardGeneric(u32, self.getSizeOfHeaders(), default_file_alignment); |
| 1953 | | const image_base = self.base.options.image_base_override orelse switch (self.base.options.output_mode) { |
| 1954 | | .Exe => default_image_base_exe, |
| 1955 | | .Lib => default_image_base_dll, |
| 1956 | | else => unreachable, |
| 1957 | | }; |
| 1974 | const image_base = self.getImageBase(); |
| 1958 | 1975 | |
| 1959 | 1976 | const base_of_code = self.sections.get(self.text_section_index.?).header.virtual_address; |
| 1960 | 1977 | const base_of_data = self.sections.get(self.data_section_index.?).header.virtual_address; |
| ... | ... | @@ -2161,6 +2178,19 @@ pub fn getEntryPoint(self: Coff) ?SymbolWithLoc { |
| 2161 | 2178 | return self.globals.items[global_index]; |
| 2162 | 2179 | } |
| 2163 | 2180 | |
| 2181 | pub fn getImageBase(self: Coff) u64 { |
| 2182 | const image_base: u64 = self.base.options.image_base_override orelse switch (self.base.options.output_mode) { |
| 2183 | .Exe => switch (self.base.options.target.cpu.arch) { |
| 2184 | .aarch64 => 0x140000000, |
| 2185 | .x86_64, .i386 => 0x400000, |
| 2186 | else => unreachable, // unsupported target architecture |
| 2187 | }, |
| 2188 | .Lib => 0x10000000, |
| 2189 | else => unreachable, |
| 2190 | }; |
| 2191 | return image_base; |
| 2192 | } |
| 2193 | |
| 2164 | 2194 | /// Returns pointer-to-symbol described by `sym_loc` descriptor. |
| 2165 | 2195 | pub fn getSymbolPtr(self: *Coff, sym_loc: SymbolWithLoc) *coff.Symbol { |
| 2166 | 2196 | assert(sym_loc.file == null); // TODO linking object files |