| ... | @@ -433,7 +433,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype | ... | @@ -433,7 +433,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype |
| 433 | | 433 | |
| 434 | // While traversing relocations, mark symbols that require special handling such as | 434 | // While traversing relocations, mark symbols that require special handling such as |
| 435 | // pointer indirection via GOT, or a stub trampoline via PLT. | 435 | // pointer indirection via GOT, or a stub trampoline via PLT. |
| 436 | switch (elf_file.getTarget().cpu.arch) { | 436 | switch (cpu_arch) { |
| 437 | .x86_64 => x86_64.scanReloc(self, elf_file, rel, symbol, code, &it) catch |err| switch (err) { | 437 | .x86_64 => x86_64.scanReloc(self, elf_file, rel, symbol, code, &it) catch |err| switch (err) { |
| 438 | error.RelocFailure => has_reloc_errors = true, | 438 | error.RelocFailure => has_reloc_errors = true, |
| 439 | else => |e| return e, | 439 | else => |e| return e, |
| ... | @@ -765,6 +765,7 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi | ... | @@ -765,6 +765,7 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi |
| 765 | | 765 | |
| 766 | switch (cpu_arch) { | 766 | switch (cpu_arch) { |
| 767 | .x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) { | 767 | .x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) { |
| | 768 | error.RelocFailure, |
| 768 | error.RelaxFailure, | 769 | error.RelaxFailure, |
| 769 | error.InvalidInstruction, | 770 | error.InvalidInstruction, |
| 770 | error.CannotEncode, | 771 | error.CannotEncode, |
| ... | @@ -880,10 +881,83 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void { | ... | @@ -880,10 +881,83 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void { |
| 880 | pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { | 881 | pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void { |
| 881 | relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) }); | 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 | const cpu_arch = elf_file.getTarget().cpu.arch; |
| 884 | .x86_64 => try x86_64.resolveRelocsNonAlloc(self, elf_file, code, undefs), | 885 | const file_ptr = self.file(elf_file).?; |
| 885 | else => return error.UnsupportedCpuArch, | 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 | pub fn format( | 963 | pub fn format( |
| ... | @@ -1210,98 +1284,48 @@ const x86_64 = struct { | ... | @@ -1210,98 +1284,48 @@ const x86_64 = struct { |
| 1210 | Elf.R_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little), | 1284 | Elf.R_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little), |
| 1211 | Elf.R_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little), | 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 { | 1292 | fn resolveRelocNonAlloc( |
| 1219 | const file_ptr = atom.file(elf_file).?; | 1293 | atom: Atom, |
| 1220 | var stream = std.io.fixedBufferStream(code); | 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 | const cwriter = stream.writer(); | 1305 | const cwriter = stream.writer(); |
| 1222 | | 1306 | |
| 1223 | const rels = atom.relocs(elf_file); | 1307 | _, const A, const S, const GOT, _, _, const DTP, _ = args; |
| 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 | } | | |
| 1249 | | 1308 | |
| 1250 | // Report an undefined symbol. | 1309 | switch (r_type) { |
| 1251 | if (try atom.reportUndefined(elf_file, target, target_index, rel, undefs)) continue; | 1310 | .NONE => unreachable, |
| 1252 | | 1311 | .@"8" => try cwriter.writeInt(u8, @as(u8, @bitCast(@as(i8, @intCast(S + A)))), .little), |
| 1253 | // We will use equation format to resolve relocations: | 1312 | .@"16" => try cwriter.writeInt(u16, @as(u16, @bitCast(@as(i16, @intCast(S + A)))), .little), |
| 1254 | // https://intezer.com/blog/malware-analysis/executable-and-linkable-format-101-part-3-relocations/ | 1313 | .@"32" => try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(S + A)))), .little), |
| 1255 | // | 1314 | .@"32S" => try cwriter.writeInt(i32, @as(i32, @intCast(S + A)), .little), |
| 1256 | const P = @as(i64, @intCast(atom.address(elf_file) + rel.r_offset)); | 1315 | .@"64" => try cwriter.writeInt(i64, S + A, .little), |
| 1257 | // Addend from the relocation. | 1316 | .DTPOFF32 => try cwriter.writeInt(i32, @as(i32, @intCast(S + A - DTP)), .little), |
| 1258 | const A = rel.r_addend; | 1317 | .DTPOFF64 => try cwriter.writeInt(i64, S + A - DTP, .little), |
| 1259 | // Address of the target symbol - can be address of the symbol within an atom or address of PLT stub. | 1318 | .GOTOFF64 => try cwriter.writeInt(i64, S + A - GOT, .little), |
| 1260 | const S = @as(i64, @intCast(target.address(.{}, elf_file))); | 1319 | .GOTPC64 => try cwriter.writeInt(i64, GOT + A, .little), |
| 1261 | // Address of the global offset table. | 1320 | .SIZE32 => { |
| 1262 | const GOT = blk: { | 1321 | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1263 | const shndx = if (elf_file.got_plt_section_index) |shndx| | 1322 | try cwriter.writeInt(u32, @as(u32, @bitCast(@as(i32, @intCast(size + A)))), .little); |
| 1264 | shndx | 1323 | }, |
| 1265 | else if (elf_file.got_section_index) |shndx| | 1324 | .SIZE64 => { |
| 1266 | shndx | 1325 | const size = @as(i64, @intCast(target.elfSym(elf_file).st_size)); |
| 1267 | else | 1326 | try cwriter.writeInt(i64, @as(i64, @intCast(size + A)), .little); |
| 1268 | null; | 1327 | }, |
| 1269 | break :blk if (shndx) |index| @as(i64, @intCast(elf_file.shdrs.items[index].sh_addr)) else 0; | 1328 | else => try atom.reportUnhandledRelocError(rel, elf_file), |
| 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 | } | | |
| 1305 | } | 1329 | } |
| 1306 | } | 1330 | } |
| 1307 | | 1331 | |