| ... | ... | @@ -433,7 +433,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype |
| 433 | 433 | |
| 434 | 434 | // While traversing relocations, mark symbols that require special handling such as |
| 435 | 435 | // pointer indirection via GOT, or a stub trampoline via PLT. |
| 436 | | switch (elf_file.getTarget().cpu.arch) { |
| 436 | switch (cpu_arch) { |
| 437 | 437 | .x86_64 => x86_64.scanReloc(self, elf_file, rel, symbol, code, &it) catch |err| switch (err) { |
| 438 | 438 | error.RelocFailure => has_reloc_errors = true, |
| 439 | 439 | else => |e| return e, |
| ... | ... | @@ -765,6 +765,7 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi |
| 765 | 765 | |
| 766 | 766 | switch (cpu_arch) { |
| 767 | 767 | .x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) { |
| 768 | error.RelocFailure, |
| 768 | 769 | error.RelaxFailure, |
| 769 | 770 | error.InvalidInstruction, |
| 770 | 771 | error.CannotEncode, |
| ... | ... | @@ -880,10 +881,83 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void { |
| 880 | 881 | pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { |
| 881 | 882 | relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) }); |
| 882 | 883 | |
| 883 | | switch (elf_file.getTarget().cpu.arch) { |
| 884 | | .x86_64 => try x86_64.resolveRelocsNonAlloc(self, elf_file, code, undefs), |
| 885 | | else => return error.UnsupportedCpuArch, |
| 884 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 885 | const file_ptr = self.file(elf_file).?; |
| 886 | var stream = std.io.fixedBufferStream(code); |
| 887 | |
| 888 | const rels = self.relocs(elf_file); |
| 889 | var has_reloc_errors = false; |
| 890 | var it = RelocsIterator{ .relocs = rels }; |
| 891 | while (it.next()) |rel| { |
| 892 | const r_kind = relocation.decode(rel.r_type(), cpu_arch); |
| 893 | if (r_kind == .none) continue; |
| 894 | |
| 895 | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; |
| 896 | |
| 897 | const target_index = switch (file_ptr) { |
| 898 | .zig_object => |x| x.symbol(rel.r_sym()), |
| 899 | .object => |x| x.symbols.items[rel.r_sym()], |
| 900 | else => unreachable, |
| 901 | }; |
| 902 | const target = elf_file.symbol(target_index); |
| 903 | |
| 904 | // Check for violation of One Definition Rule for COMDATs. |
| 905 | if (target.file(elf_file) == null) { |
| 906 | // TODO convert into an error |
| 907 | log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ |
| 908 | file_ptr.fmtPath(), |
| 909 | self.name(elf_file), |
| 910 | target.name(elf_file), |
| 911 | }); |
| 912 | continue; |
| 913 | } |
| 914 | |
| 915 | // Report an undefined symbol. |
| 916 | if (try self.reportUndefined(elf_file, target, target_index, rel, undefs)) continue; |
| 917 | |
| 918 | // We will use equation format to resolve relocations: |
| 919 | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ |
| 920 | // |
| 921 | const P = @as(i64, @intCast(self.address(elf_file) + rel.r_offset)); |
| 922 | // Addend from the relocation. |
| 923 | const A = rel.r_addend; |
| 924 | // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. |
| 925 | const S = @as(i64, @intCast(target.address(.{}, elf_file))); |
| 926 | // Address of the global offset table. |
| 927 | const GOT = blk: { |
| 928 | const shndx = if (elf_file.got_plt_section_index) |shndx| |
| 929 | shndx |
| 930 | else if (elf_file.got_section_index) |shndx| |
| 931 | shndx |
| 932 | else |
| 933 | null; |
| 934 | break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; |
| 935 | }; |
| 936 | // Address of the dynamic thread pointer. |
| 937 | const DTP = @as(i64, @intCast(elf_file.dtpAddress())); |
| 938 | |
| 939 | const args = ResolveArgs{ P, A, S, GOT, 0, 0, DTP, 0 }; |
| 940 | |
| 941 | relocs_log.debug(" {}: {x}: [{x} => {x}] ({s})", .{ |
| 942 | relocation.fmtRelocType(rel.r_type(), cpu_arch), |
| 943 | rel.r_offset, |
| 944 | P, |
| 945 | S + A, |
| 946 | target.name(elf_file), |
| 947 | }); |
| 948 | |
| 949 | try stream.seekTo(r_offset); |
| 950 | |
| 951 | switch (cpu_arch) { |
| 952 | .x86_64 => x86_64.resolveRelocNonAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) { |
| 953 | error.RelocFailure => has_reloc_errors = true, |
| 954 | else => |e| return e, |
| 955 | }, |
| 956 | else => return error.UnsupportedCpuArch, |
| 957 | } |
| 886 | 958 | } |
| 959 | |
| 960 | if (has_reloc_errors) return error.RelocFailure; |
| 887 | 961 | } |
| 888 | 962 | |
| 889 | 963 | pub fn format( |
| ... | ... | @@ -1210,98 +1284,48 @@ const x86_64 = struct { |
| 1210 | 1284 | Elf.R_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little), |
| 1211 | 1285 | Elf.R_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little), |
| 1212 | 1286 | |
| 1213 | | else => {}, |
| 1287 | else => try atom.reportUnhandledRelocError(rel, elf_file), |
| 1214 | 1288 | }, |
| 1215 | 1289 | } |
| 1216 | 1290 | } |
| 1217 | 1291 | |
| 1218 | | fn resolveRelocsNonAlloc(atom: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { |
| 1219 | | const file_ptr = atom.file(elf_file).?; |
| 1220 | | var stream = std.io.fixedBufferStream(code); |
| 1292 | fn resolveRelocNonAlloc( |
| 1293 | atom: Atom, |
| 1294 | elf_file: *Elf, |
| 1295 | rel: elf.Elf64_Rela, |
| 1296 | target: *const Symbol, |
| 1297 | args: ResolveArgs, |
| 1298 | it: *RelocsIterator, |
| 1299 | code: []u8, |
| 1300 | stream: anytype, |
| 1301 | ) !void { |
| 1302 | _ = code; |
| 1303 | _ = it; |
| 1304 | const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type()); |
| 1221 | 1305 | const cwriter = stream.writer(); |
| 1222 | 1306 | |
| 1223 | | const rels = atom.relocs(elf_file); |
| 1224 | | var i: usize = 0; |
| 1225 | | while (i < rels.len) : (i += 1) { |
| 1226 | | const rel = rels[i]; |
| 1227 | | const r_type: elf.R_X86_64 = @enumFromInt(rel.r_type()); |
| 1228 | | if (r_type == .NONE) continue; |
| 1229 | | |
| 1230 | | const r_offset = std.math.cast(usize, rel.r_offset) orelse return error.Overflow; |
| 1231 | | |
| 1232 | | const target_index = switch (file_ptr) { |
| 1233 | | .zig_object => |x| x.symbol(rel.r_sym()), |
| 1234 | | .object => |x| x.symbols.items[rel.r_sym()], |
| 1235 | | else => unreachable, |
| 1236 | | }; |
| 1237 | | const target = elf_file.symbol(target_index); |
| 1238 | | |
| 1239 | | // Check for violation of One Definition Rule for COMDATs. |
| 1240 | | if (target.file(elf_file) == null) { |
| 1241 | | // TODO convert into an error |
| 1242 | | log.debug("{}: {s}: {s} refers to a discarded COMDAT section", .{ |
| 1243 | | file_ptr.fmtPath(), |
| 1244 | | atom.name(elf_file), |
| 1245 | | target.name(elf_file), |
| 1246 | | }); |
| 1247 | | continue; |
| 1248 | | } |
| 1307 | _, const A, const S, const GOT, _, _, const DTP, _ = args; |
| 1249 | 1308 | |
| 1250 | | // Report an undefined symbol. |
| 1251 | | if (try atom.reportUndefined(elf_file, target, target_index, rel, undefs)) continue; |
| 1252 | | |
| 1253 | | // We will use equation format to resolve relocations: |
| 1254 | | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ |
| 1255 | | // |
| 1256 | | const P = @as(i64, @intCast(atom.address(elf_file) + rel.r_offset)); |
| 1257 | | // Addend from the relocation. |
| 1258 | | const A = rel.r_addend; |
| 1259 | | // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. |
| 1260 | | const S = @as(i64, @intCast(target.address(.{}, elf_file))); |
| 1261 | | // Address of the global offset table. |
| 1262 | | const GOT = blk: { |
| 1263 | | const shndx = if (elf_file.got_plt_section_index) |shndx| |
| 1264 | | shndx |
| 1265 | | else if (elf_file.got_section_index) |shndx| |
| 1266 | | shndx |
| 1267 | | else |
| 1268 | | null; |
| 1269 | | break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; |
| 1270 | | }; |
| 1271 | | // Address of the dynamic thread pointer. |
| 1272 | | const DTP = @as(i64, @intCast(elf_file.dtpAddress())); |
| 1273 | | |
| 1274 | | relocs_log.debug(" {}: {x}: [{x} => {x}] ({s})", .{ |
| 1275 | | relocation.fmtRelocType(rel.r_type(), .x86_64), |
| 1276 | | rel.r_offset, |
| 1277 | | P, |
| 1278 | | S + A, |
| 1279 | | target.name(elf_file), |
| 1280 | | }); |
| 1281 | | |
| 1282 | | try stream.seekTo(r_offset); |
| 1283 | | |
| 1284 | | switch (r_type) { |
| 1285 | | .NONE => unreachable, |
| 1286 | | .@"8" => try cwriter.writeInt(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A)))), .little), |
| 1287 | | .@"16" => try cwriter.writeInt(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A)))), .little), |
| 1288 | | .@"32" => try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A)))), .little), |
| 1289 | | .@"32S" => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little), |
| 1290 | | .@"64" => try cwriter.writeInt(i64, S + A, .little), |
| 1291 | | .DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - DTP)), .little), |
| 1292 | | .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little), |
| 1293 | | .GOTOFF64 => try cwriter.writeInt(i64, S + A - GOT, .little), |
| 1294 | | .GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little), |
| 1295 | | .SIZE32 => { |
| 1296 | | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1297 | | try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little); |
| 1298 | | }, |
| 1299 | | .SIZE64 => { |
| 1300 | | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1301 | | try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little); |
| 1302 | | }, |
| 1303 | | else => try atom.reportUnhandledRelocError(rel, elf_file), |
| 1304 | | } |
| 1309 | switch (r_type) { |
| 1310 | .NONE => unreachable, |
| 1311 | .@"8" => try cwriter.writeInt(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A)))), .little), |
| 1312 | .@"16" => try cwriter.writeInt(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A)))), .little), |
| 1313 | .@"32" => try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A)))), .little), |
| 1314 | .@"32S" => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little), |
| 1315 | .@"64" => try cwriter.writeInt(i64, S + A, .little), |
| 1316 | .DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - DTP)), .little), |
| 1317 | .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little), |
| 1318 | .GOTOFF64 => try cwriter.writeInt(i64, S + A - GOT, .little), |
| 1319 | .GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little), |
| 1320 | .SIZE32 => { |
| 1321 | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1322 | try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little); |
| 1323 | }, |
| 1324 | .SIZE64 => { |
| 1325 | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1326 | try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little); |
| 1327 | }, |
| 1328 | else => try atom.reportUnhandledRelocError(rel, elf_file), |
| 1305 | 1329 | } |
| 1306 | 1330 | } |
| 1307 | 1331 | |