authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-21 20:29:56+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2024-02-21 20:29:56+01:00
log06c191a4ffdb977f9668d6cae90aa7e72065f362
tree406859d6ab770827b32d3a9905f9478e91926cfe
parent4cde47a169efc3d2dc70f057742e65b088e139a3

elf: simplify logic for resolving nonalloc relocs on different arches


1 files changed, 113 insertions(+), 89 deletions(-)

src/link/Elf/Atom.zig+113-89
......@@ -433,7 +433,7 @@ pub fn scanRelocs(self: Atom, elf_file: *Elf, code: ?[]const u8, undefs: anytype
433433
434434 // While traversing relocations, mark symbols that require special handling such as
435435 // pointer indirection via GOT, or a stub trampoline via PLT.
436 switch (elf_file.getTarget().cpu.arch) {
436 switch (cpu_arch) {
437437 .x86_64 => x86_64.scanReloc(self, elf_file, rel, symbol, code, &it) catch |err| switch (err) {
438438 error.RelocFailure => has_reloc_errors = true,
439439 else => |e| return e,
......@@ -765,6 +765,7 @@ pub fn resolveRelocsAlloc(self: Atom, elf_file: *Elf, code: []u8) RelocError!voi
765765
766766 switch (cpu_arch) {
767767 .x86_64 => x86_64.resolveRelocAlloc(self, elf_file, rel, target, args, &it, code, &stream) catch |err| switch (err) {
768 error.RelocFailure,
768769 error.RelaxFailure,
769770 error.InvalidInstruction,
770771 error.CannotEncode,
......@@ -880,10 +881,83 @@ fn applyDynamicReloc(value: i64, elf_file: *Elf, writer: anytype) !void {
880881pub fn resolveRelocsNonAlloc(self: Atom, elf_file: *Elf, code: []u8, undefs: anytype) !void {
881882 relocs_log.debug("0x{x}: {s}", .{ self.address(elf_file), self.name(elf_file) });
882883
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 }
886958 }
959
960 if (has_reloc_errors) return error.RelocFailure;
887961}
888962
889963pub fn format(
......@@ -1210,98 +1284,48 @@ const x86_64 = struct {
12101284 Elf.R_ZIG_GOT32 => try cwriter.writeInt(u32, @as(u32, @intCast(ZIG_GOT + A)), .little),
12111285 Elf.R_ZIG_GOTPCREL => try cwriter.writeInt(i32, @as(i32, @intCast(ZIG_GOT + A - P)), .little),
12121286
1213 else => {},
1287 else => try atom.reportUnhandledRelocError(rel, elf_file),
12141288 },
12151289 }
12161290 }
12171291
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());
12211305 const cwriter = stream.writer();
12221306
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;
12491308
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),
13051329 }
13061330 }
13071331