authorgravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-10-14 22:24:26-04:00
committergravatar for jacobly@ziglang.orgJacob Young <jacobly@ziglang.org> 2025-10-29 18:07:12-04:00
logc4478e078b88c836b53d0e42065b0f68e62c0add
tree28746d9cbc9c403410985d4d6f03343614b9469e
parent7542c3260f93e92fd8da7e5329bc80fd601964e9

Elf2: load archives


4 files changed, 420 insertions(+), 270 deletions(-)

lib/std/Io/File.zig+4-9
...@@ -434,8 +434,7 @@ pub const Reader = struct {...@@ -434,8 +434,7 @@ pub const Reader = struct {
434 return err;434 return err;
435 };435 };
436 }436 }
437 r.interface.seek = 0;437 r.interface.tossBuffered();
438 r.interface.end = 0;
439 },438 },
440 .failure => return r.seek_err.?,439 .failure => return r.seek_err.?,
441 }440 }
...@@ -467,15 +466,11 @@ pub const Reader = struct {...@@ -467,15 +466,11 @@ pub const Reader = struct {
467 }466 }
468467
469 fn setLogicalPos(r: *Reader, offset: u64) void {468 fn setLogicalPos(r: *Reader, offset: u64) void {
470 const logical_pos = logicalPos(r);469 const logical_pos = r.logicalPos();
471 if (offset < logical_pos or offset >= r.pos) {470 if (offset < logical_pos or offset >= r.pos) {
472 r.interface.seek = 0;471 r.interface.tossBuffered();
473 r.interface.end = 0;
474 r.pos = offset;472 r.pos = offset;
475 } else {473 } else r.interface.toss(@intCast(offset - logical_pos));
476 const logical_delta: usize = @intCast(offset - logical_pos);
477 r.interface.seek += logical_delta;
478 }
479 }474 }
480475
481 /// Number of slices to store on the stack, when trying to send as many byte476 /// Number of slices to store on the stack, when trying to send as many byte
src/Compilation.zig-1
...@@ -1989,7 +1989,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,...@@ -1989,7 +1989,6 @@ pub fn create(gpa: Allocator, arena: Allocator, io: Io, diag: *CreateDiagnostic,
1989 break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu;1989 break :s if (is_exe_or_dyn_lib and build_options.have_llvm) .dyn_lib else .zcu;
1990 },1990 },
1991 }1991 }
1992 if (options.config.use_new_linker) break :s .obj;
1993 }1992 }
1994 if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm1993 if (need_llvm and !build_options.have_llvm) break :s .none; // impossible to build without llvm
1995 if (is_exe_or_dyn_lib) break :s .lib;1994 if (is_exe_or_dyn_lib) break :s .lib;
src/link/Elf2.zig+408-259
...@@ -8,12 +8,13 @@ shstrtab: StringTable,...@@ -8,12 +8,13 @@ shstrtab: StringTable,
8strtab: StringTable,8strtab: StringTable,
9inputs: std.ArrayList(struct {9inputs: std.ArrayList(struct {
10 path: std.Build.Cache.Path,10 path: std.Build.Cache.Path,
11 member: ?[]const u8,
11 si: Symbol.Index,12 si: Symbol.Index,
12}),13}),
13input_sections: std.ArrayList(struct {14input_sections: std.ArrayList(struct {
14 ii: Node.InputIndex,15 ii: Node.InputIndex,
15 si: Symbol.Index,
16 file_location: MappedFile.Node.FileLocation,16 file_location: MappedFile.Node.FileLocation,
17 si: Symbol.Index,
17}),18}),
18input_section_pending_index: u32,19input_section_pending_index: u32,
19globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index),20globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index),
...@@ -53,6 +54,10 @@ pub const Node = union(enum) {...@@ -53,6 +54,10 @@ pub const Node = union(enum) {
53 return elf.inputs.items[@intFromEnum(ii)].path;54 return elf.inputs.items[@intFromEnum(ii)].path;
54 }55 }
5556
57 pub fn member(ii: InputIndex, elf: *const Elf) ?[]const u8 {
58 return elf.inputs.items[@intFromEnum(ii)].member;
59 }
60
56 pub fn symbol(ii: InputIndex, elf: *const Elf) Symbol.Index {61 pub fn symbol(ii: InputIndex, elf: *const Elf) Symbol.Index {
57 return elf.inputs.items[@intFromEnum(ii)].si;62 return elf.inputs.items[@intFromEnum(ii)].si;
58 }63 }
...@@ -73,13 +78,13 @@ pub const Node = union(enum) {...@@ -73,13 +78,13 @@ pub const Node = union(enum) {
73 return elf.input_sections.items[@intFromEnum(isi)].ii;78 return elf.input_sections.items[@intFromEnum(isi)].ii;
74 }79 }
7580
76 pub fn symbol(isi: InputSectionIndex, elf: *const Elf) Symbol.Index {
77 return elf.input_sections.items[@intFromEnum(isi)].si;
78 }
79
80 pub fn fileLocation(isi: InputSectionIndex, elf: *const Elf) MappedFile.Node.FileLocation {81 pub fn fileLocation(isi: InputSectionIndex, elf: *const Elf) MappedFile.Node.FileLocation {
81 return elf.input_sections.items[@intFromEnum(isi)].file_location;82 return elf.input_sections.items[@intFromEnum(isi)].file_location;
82 }83 }
84
85 pub fn symbol(isi: InputSectionIndex, elf: *const Elf) Symbol.Index {
86 return elf.input_sections.items[@intFromEnum(isi)].si;
87 }
83 };88 };
8489
85 pub const NavMapIndex = enum(u32) {90 pub const NavMapIndex = enum(u32) {
...@@ -571,6 +576,7 @@ pub fn deinit(elf: *Elf) void {...@@ -571,6 +576,7 @@ pub fn deinit(elf: *Elf) void {
571 elf.symtab.deinit(gpa);576 elf.symtab.deinit(gpa);
572 elf.shstrtab.map.deinit(gpa);577 elf.shstrtab.map.deinit(gpa);
573 elf.strtab.map.deinit(gpa);578 elf.strtab.map.deinit(gpa);
579 for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m);
574 elf.inputs.deinit(gpa);580 elf.inputs.deinit(gpa);
575 elf.input_sections.deinit(gpa);581 elf.input_sections.deinit(gpa);
576 elf.globals.deinit(gpa);582 elf.globals.deinit(gpa);
...@@ -1255,256 +1261,370 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !Symbol.Index {...@@ -1255,256 +1261,370 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !Symbol.Index {
1255 return lazy_gop.value_ptr.*;1261 return lazy_gop.value_ptr.*;
1256}1262}
12571263
1258pub fn loadInput(elf: *Elf, input: link.Input) !void {1264pub fn loadInput(elf: *Elf, input: link.Input) (std.fs.File.Reader.SizeError ||
1265 std.Io.File.Reader.Error || MappedFile.Error || error{ EndOfStream, LinkFailure })!void {
1266 const io = elf.base.comp.io;
1267 var buf: [4096]u8 = undefined;
1268 switch (input) {
1269 else => {},
1270 .object => |object| {
1271 var fsr: FileSliceReader = .init(object.file.reader(io, &.{}));
1272 fsr.reset(try fsr.file.getSize(), &buf);
1273 elf.loadObject(object.path, null, &fsr) catch |err| switch (err) {
1274 error.ReadFailed => return fsr.file.err.?,
1275 else => |e| return e,
1276 };
1277 },
1278 .archive => |archive| {
1279 var fsr: FileSliceReader = .init(archive.file.reader(io, &buf));
1280 elf.loadArchive(archive.path, &fsr) catch |err| switch (err) {
1281 error.ReadFailed => return fsr.file.err.?,
1282 else => |e| return e,
1283 };
1284 },
1285 }
1286}
1287const FileSliceReader = struct {
1288 file: std.Io.File.Reader,
1289 file_location: MappedFile.Node.FileLocation,
1290 reader: std.Io.Reader.Limited,
1291
1292 pub fn init(file: std.Io.File.Reader) FileSliceReader {
1293 return .{ .file = file, .file_location = undefined, .reader = undefined };
1294 }
1295
1296 pub fn reset(fsr: *FileSliceReader, size: u64, buffer: []u8) void {
1297 fsr.file_location = .{
1298 .offset = fsr.file.logicalPos(),
1299 .size = size,
1300 };
1301 fsr.reader = .init(&fsr.file.interface, .limited(@intCast(size)), buffer);
1302 }
1303
1304 pub fn pos(fsr: *const FileSliceReader) u64 {
1305 return fsr.file.logicalPos() - fsr.file_location.offset;
1306 }
1307
1308 pub fn logicalPos(fsr: *const FileSliceReader) u64 {
1309 return fsr.pos() - fsr.reader.interface.bufferedLen();
1310 }
1311
1312 pub fn seekTo(fsr: *FileSliceReader, offset: u64) std.Io.File.Reader.SeekError!void {
1313 if (offset > fsr.file_location.size) return error.EndOfStream;
1314 const logical_pos = fsr.logicalPos();
1315 if (offset < logical_pos or offset >= fsr.pos()) {
1316 fsr.reader.interface.tossBuffered();
1317 try fsr.file.seekTo(fsr.file_location.offset + offset);
1318 fsr.reader.remaining = .limited(@intCast(fsr.file_location.size - offset));
1319 } else fsr.reader.interface.toss(@intCast(offset - logical_pos));
1320 }
1321};
1322fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fsr: *FileSliceReader) !void {
1259 const comp = elf.base.comp;1323 const comp = elf.base.comp;
1260 const gpa = comp.gpa;1324 const gpa = comp.gpa;
1261 const diags = &comp.link_diags;1325 const diags = &comp.link_diags;
1262 switch (input) {1326 const r = &fsr.file.interface;
1263 .object => |object| {1327
1264 const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len);1328 log.debug("loadArchive({f})", .{path.fmtEscapeString()});
1265 log.debug("loadInput(.{{ .object = {f} }}) = {d}", .{ object.path, ii });1329 if (!std.mem.eql(u8, try r.take(std.elf.ARMAG.len), std.elf.ARMAG))
1266 {1330 return diags.failParse(path, "bad magic", .{});
1267 try elf.symtab.ensureUnusedCapacity(gpa, 1);1331 var strtab: std.Io.Writer.Allocating = .init(gpa);
1268 try elf.inputs.ensureUnusedCapacity(gpa, 1);1332 defer strtab.deinit();
1269 const si = try elf.initSymbolAssumeCapacity(.{1333 while (r.takeStruct(std.elf.ar_hdr, native_endian)) |header| {
1270 .name = std.fs.path.stem(object.path.sub_path),1334 if (!std.mem.eql(u8, &header.ar_fmag, std.elf.ARFMAG))
1271 .type = .FILE,1335 return diags.failParse(path, "bad file magic", .{});
1272 .shndx = std.elf.SHN_ABS,1336 const size = header.size() catch
1273 });1337 return diags.failParse(path, "bad member size", .{});
1274 elf.inputs.addOneAssumeCapacity().* = .{1338 if (std.mem.eql(u8, &header.ar_name, std.elf.STRNAME)) {
1275 .path = object.path,1339 strtab.clearRetainingCapacity();
1276 .si = si,1340 try strtab.ensureTotalCapacityPrecise(size);
1341 r.streamExact(&strtab.writer, size) catch |err| switch (err) {
1342 error.WriteFailed => return error.OutOfMemory,
1343 else => |e| return e,
1344 };
1345 continue;
1346 }
1347 load_object: {
1348 const member = header.name() orelse member: {
1349 const strtab_offset = header.nameOffset() catch |err| switch (err) {
1350 error.Overflow => break :member error.Overflow,
1351 error.InvalidCharacter => break :load_object,
1352 } orelse break :load_object;
1353 const strtab_written = strtab.written();
1354 if (strtab_offset > strtab_written.len) break :member error.Overflow;
1355 const member = std.mem.sliceTo(strtab_written[strtab_offset..], '\n');
1356 break :member if (std.mem.endsWith(u8, member, "/"))
1357 member[0 .. member.len - "/".len]
1358 else
1359 member;
1360 } catch |err| switch (err) {
1361 error.Overflow => return diags.failParse(path, "bad member name offset", .{}),
1362 };
1363 if (!std.mem.endsWith(u8, member, ".o")) break :load_object;
1364 var buf: [4096]u8 = undefined;
1365 fsr.reset(size, &buf);
1366 try elf.loadObject(path, member, fsr);
1367 try fsr.seekTo(size);
1368 continue;
1369 }
1370 try r.discardAll(size);
1371 } else |err| switch (err) {
1372 error.EndOfStream => if (!fsr.file.atEnd()) return error.EndOfStream,
1373 else => |e| return e,
1374 }
1375}
1376fn fmtMemberString(member: ?[]const u8) std.fmt.Alt(?[]const u8, memberStringEscape) {
1377 return .{ .data = member };
1378}
1379fn memberStringEscape(member: ?[]const u8, w: *std.Io.Writer) std.Io.Writer.Error!void {
1380 try w.print("({f})", .{std.zig.fmtString(member orelse return)});
1381}
1382fn loadObject(elf: *Elf, path: std.Build.Cache.Path, member: ?[]const u8, fsr: *FileSliceReader) !void {
1383 const comp = elf.base.comp;
1384 const gpa = comp.gpa;
1385 const diags = &comp.link_diags;
1386 const r = &fsr.reader.interface;
1387
1388 const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len);
1389 log.debug("loadObject({f}{f})", .{ path.fmtEscapeString(), fmtMemberString(member) });
1390 const ident = try r.peek(std.elf.EI.NIDENT);
1391 if (!std.mem.eql(u8, ident, elf.mf.contents[0..std.elf.EI.NIDENT]))
1392 return diags.failParse(path, "bad ident", .{});
1393 try elf.symtab.ensureUnusedCapacity(gpa, 1);
1394 try elf.inputs.ensureUnusedCapacity(gpa, 1);
1395 elf.inputs.addOneAssumeCapacity().* = .{
1396 .path = path,
1397 .member = if (member) |m| try gpa.dupe(u8, m) else null,
1398 .si = try elf.initSymbolAssumeCapacity(.{
1399 .name = std.fs.path.stem(member orelse path.sub_path),
1400 .type = .FILE,
1401 .shndx = std.elf.SHN_ABS,
1402 }),
1403 };
1404 const target_endian = elf.targetEndian();
1405 switch (elf.identClass()) {
1406 .NONE, _ => unreachable,
1407 inline else => |class| {
1408 const ElfN = class.ElfN();
1409 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);
1410 if (ehdr.type != .REL) return diags.failParse(path, "unsupported object type", .{});
1411 if (ehdr.machine != elf.ehdrField(.machine))
1412 return diags.failParse(path, "bad machine", .{});
1413 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;
1414 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))
1415 return diags.failParse(path, "unsupported shentsize", .{});
1416 const sections = try gpa.alloc(struct { shdr: ElfN.Shdr, si: Symbol.Index }, ehdr.shnum);
1417 defer gpa.free(sections);
1418 try fsr.seekTo(ehdr.shoff);
1419 for (sections) |*section| {
1420 section.* = .{
1421 .shdr = try r.peekStruct(ElfN.Shdr, target_endian),
1422 .si = .null,
1277 };1423 };
1424 try r.discardAll(ehdr.shentsize);
1425 switch (section.shdr.type) {
1426 std.elf.SHT_NULL, std.elf.SHT_NOBITS => {},
1427 else => if (section.shdr.offset + section.shdr.size > fsr.file_location.size)
1428 return diags.failParse(path, "bad section offset/size", .{}),
1429 }
1278 }1430 }
1279 var buf: [4096]u8 = undefined;1431 const shstrtab = shstrtab: {
1280 var fr = object.file.reader(comp.io, &buf);1432 if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum)
1281 const r = &fr.interface;1433 return diags.failParse(path, "missing section names", .{});
1282 const ident = try r.peek(std.elf.EI.NIDENT);1434 const shdr = &sections[ehdr.shstrndx].shdr;
1283 if (!std.mem.eql(u8, elf.mf.contents[0..std.elf.EI.NIDENT], ident))1435 if (shdr.type != std.elf.SHT_STRTAB)
1284 return diags.failParse(object.path, "wrong ident", .{});1436 return diags.failParse(path, "invalid shstrtab type", .{});
1285 const target_endian = elf.targetEndian();1437 const shstrtab = try gpa.alloc(u8, @intCast(shdr.size));
1286 switch (elf.identClass()) {1438 errdefer gpa.free(shstrtab);
1287 .NONE, _ => unreachable,1439 try fsr.seekTo(shdr.offset);
1288 inline else => |class| {1440 try r.readSliceAll(shstrtab);
1289 const ElfN = class.ElfN();1441 break :shstrtab shstrtab;
1290 const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian);1442 };
1291 if (ehdr.type != .REL)1443 defer gpa.free(shstrtab);
1292 return diags.failParse(object.path, "unsupported object type", .{});1444 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
1293 if (ehdr.machine != elf.ehdrField(.machine))1445 try elf.symtab.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
1294 return diags.failParse(object.path, "wrong machine", .{});1446 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
1295 if (ehdr.shoff == 0 or ehdr.shnum <= 1) return;1447 for (sections[1..]) |*section| switch (section.shdr.type) {
1296 if (ehdr.shentsize < @sizeOf(ElfN.Shdr))1448 else => {},
1297 return diags.failParse(object.path, "unsupported shentsize", .{});1449 std.elf.SHT_PROGBITS, std.elf.SHT_NOBITS => {
1298 const sections = try gpa.alloc(struct {1450 if (section.shdr.name >= shstrtab.len) continue;
1299 shdr: ElfN.Shdr,1451 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
1300 si: Symbol.Index,1452 const parent_si = namedSection(name) orelse continue;
1301 }, ehdr.shnum);1453 const ni = try elf.mf.addLastChildNode(gpa, parent_si.node(elf), .{
1302 defer gpa.free(sections);1454 .size = section.shdr.size,
1303 try fr.seekTo(ehdr.shoff);1455 .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
1304 for (sections) |*section| {1456 usize,
1305 section.* = .{1457 @intCast(@max(section.shdr.addralign, 1)),
1306 .shdr = try r.peekStruct(ElfN.Shdr, target_endian),1458 )),
1307 .si = .null,1459 .moved = true,
1308 };1460 });
1309 try fr.seekBy(ehdr.shentsize);1461 elf.nodes.appendAssumeCapacity(.{
1310 }1462 .input_section = @enumFromInt(elf.input_sections.items.len),
1311 const shstrtab = shstrtab: {1463 });
1312 if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum)1464 section.si = try elf.initSymbolAssumeCapacity(.{
1313 return diags.failParse(object.path, "missing section names", .{});1465 .type = .SECTION,
1314 const shdr = &sections[ehdr.shstrndx].shdr;1466 .shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx),
1315 if (shdr.type != std.elf.SHT_STRTAB)1467 });
1316 return diags.failParse(object.path, "invalid shstrtab type", .{});1468 section.si.get(elf).ni = ni;
1317 const shstrtab = try gpa.alloc(u8, @intCast(shdr.size));1469 elf.input_sections.addOneAssumeCapacity().* = .{
1318 errdefer gpa.free(shstrtab);1470 .ii = ii,
1319 try fr.seekTo(shdr.offset);1471 .si = section.si,
1320 try r.readSliceAll(shstrtab);1472 .file_location = .{
1321 break :shstrtab shstrtab;1473 .offset = fsr.file_location.offset + section.shdr.offset,
1322 };1474 .size = section.shdr.size,
1323 defer gpa.free(shstrtab);
1324 try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
1325 try elf.symtab.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
1326 try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1);
1327 for (sections[1..]) |*section| switch (section.shdr.type) {
1328 else => {},
1329 std.elf.SHT_PROGBITS, std.elf.SHT_NOBITS => {
1330 if (section.shdr.name >= shstrtab.len) continue;
1331 const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0);
1332 const parent_si = namedSection(name) orelse continue;
1333 const ni = try elf.mf.addLastChildNode(gpa, parent_si.node(elf), .{
1334 .size = section.shdr.size,
1335 .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert(
1336 usize,
1337 @intCast(@max(section.shdr.addralign, 1)),
1338 )),
1339 .moved = true,
1340 });
1341 elf.nodes.appendAssumeCapacity(.{
1342 .input_section = @enumFromInt(elf.input_sections.items.len),
1343 });
1344 section.si = try elf.initSymbolAssumeCapacity(.{
1345 .type = .SECTION,
1346 .shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx),
1347 });
1348 section.si.get(elf).ni = ni;
1349 elf.input_sections.addOneAssumeCapacity().* = .{
1350 .ii = ii,
1351 .si = section.si,
1352 .file_location = .{
1353 .offset = section.shdr.offset,
1354 .size = section.shdr.size,
1355 },
1356 };
1357 elf.synth_prog_node.increaseEstimatedTotalItems(1);
1358 },1475 },
1359 };1476 };
1360 var symmap: std.ArrayList(Symbol.Index) = .empty;1477 elf.synth_prog_node.increaseEstimatedTotalItems(1);
1361 defer symmap.deinit(gpa);1478 },
1362 for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {1479 };
1480 var symmap: std.ArrayList(Symbol.Index) = .empty;
1481 defer symmap.deinit(gpa);
1482 for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) {
1483 else => {},
1484 std.elf.SHT_SYMTAB => {
1485 if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))
1486 return diags.failParse(path, "unsupported symtab entsize", .{});
1487 const strtab = strtab: {
1488 if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)
1489 return diags.failParse(path, "missing symbol names", .{});
1490 const shdr = &sections[symtab.shdr.link].shdr;
1491 if (shdr.type != std.elf.SHT_STRTAB)
1492 return diags.failParse(path, "invalid strtab type", .{});
1493 const strtab = try gpa.alloc(u8, @intCast(shdr.size));
1494 errdefer gpa.free(strtab);
1495 try fsr.seekTo(shdr.offset);
1496 try r.readSliceAll(strtab);
1497 break :strtab strtab;
1498 };
1499 defer gpa.free(strtab);
1500 const symnum = std.math.divExact(
1501 u32,
1502 @intCast(symtab.shdr.size),
1503 @intCast(symtab.shdr.entsize),
1504 ) catch return diags.failParse(
1505 path,
1506 "symtab section size (0x{x}) is not a multiple of entsize (0x{x})",
1507 .{ symtab.shdr.size, symtab.shdr.entsize },
1508 );
1509 symmap.clearRetainingCapacity();
1510 try symmap.resize(gpa, symnum);
1511 try elf.symtab.ensureUnusedCapacity(gpa, symnum);
1512 try elf.globals.ensureUnusedCapacity(gpa, symnum);
1513 try fsr.seekTo(symtab.shdr.offset + symtab.shdr.entsize);
1514 symmap.items[0] = .null;
1515 for (symmap.items[1..]) |*si| {
1516 si.* = .null;
1517 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);
1518 try r.discardAll64(symtab.shdr.entsize);
1519 if (input_sym.name >= strtab.len or input_sym.shndx == std.elf.SHN_UNDEF or
1520 input_sym.shndx >= ehdr.shnum) continue;
1521 switch (input_sym.info.type) {
1522 else => continue,
1523 .SECTION => {
1524 const section = &sections[input_sym.shndx];
1525 if (input_sym.value == section.shdr.addr) si.* = section.si;
1526 continue;
1527 },
1528 .OBJECT, .FUNC => {},
1529 }
1530 const name = std.mem.sliceTo(strtab[input_sym.name..], 0);
1531 const parent_si = sections[input_sym.shndx].si;
1532 si.* = try elf.initSymbolAssumeCapacity(.{
1533 .name = name,
1534 .value = input_sym.value,
1535 .size = input_sym.size,
1536 .type = input_sym.info.type,
1537 .bind = input_sym.info.bind,
1538 .visibility = input_sym.other.visibility,
1539 .shndx = elf.targetLoad(switch (elf.symPtr(parent_si)) {
1540 inline else => |parent_sym| &parent_sym.shndx,
1541 }),
1542 });
1543 si.get(elf).ni = parent_si.get(elf).ni;
1544 switch (input_sym.info.bind) {
1545 else => {},
1546 .GLOBAL => {
1547 const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
1548 &@field(elf.symPtr(si.*), @tagName(class)).name,
1549 ));
1550 if (gop.found_existing) switch (elf.targetLoad(
1551 switch (elf.symPtr(gop.value_ptr.*)) {
1552 inline else => |sym| &sym.info,
1553 },
1554 ).bind) {
1555 else => unreachable,
1556 .GLOBAL => return diags.failParse(
1557 path,
1558 "multiple definitions of '{s}'",
1559 .{name},
1560 ),
1561 .WEAK => {},
1562 };
1563 gop.value_ptr.* = si.*;
1564 },
1565 .WEAK => {
1566 const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
1567 &@field(elf.symPtr(si.*), @tagName(class)).name,
1568 ));
1569 if (!gop.found_existing) gop.value_ptr.* = si.*;
1570 },
1571 }
1572 }
1573 for (sections[1..]) |*rels| switch (rels.shdr.type) {
1363 else => {},1574 else => {},
1364 std.elf.SHT_SYMTAB => {1575 inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| {
1365 if (symtab.shdr.entsize < @sizeOf(ElfN.Sym))1576 if (rels.shdr.link != symtab_shndx or rels.shdr.info == std.elf.SHN_UNDEF or
1366 return diags.failParse(object.path, "unsupported symtab entsize", .{});1577 rels.shdr.info >= ehdr.shnum) continue;
1367 const strtab = strtab: {1578 const Rel = switch (sht) {
1368 if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum)1579 else => comptime unreachable,
1369 return diags.failParse(object.path, "missing symbol names", .{});1580 std.elf.SHT_REL => ElfN.Rel,
1370 const shdr = &sections[symtab.shdr.link].shdr;1581 std.elf.SHT_RELA => ElfN.Rela,
1371 if (shdr.type != std.elf.SHT_STRTAB)
1372 return diags.failParse(object.path, "invalid strtab type", .{});
1373 const strtab = try gpa.alloc(u8, @intCast(shdr.size));
1374 errdefer gpa.free(strtab);
1375 try fr.seekTo(shdr.offset);
1376 try r.readSliceAll(strtab);
1377 break :strtab strtab;
1378 };1582 };
1379 defer gpa.free(strtab);1583 if (rels.shdr.entsize < @sizeOf(Rel))
1380 const symnum = try std.math.divExact(1584 return diags.failParse(path, "unsupported rel entsize", .{});
1585 const loc_sec = &sections[rels.shdr.info];
1586 if (loc_sec.si == .null) continue;
1587 const relnum = std.math.divExact(
1381 u32,1588 u32,
1382 @intCast(symtab.shdr.size),1589 @intCast(rels.shdr.size),
1383 @intCast(symtab.shdr.entsize),1590 @intCast(rels.shdr.entsize),
1591 ) catch return diags.failParse(
1592 path,
1593 "relocation section size (0x{x}) is not a multiple of entsize (0x{x})",
1594 .{ rels.shdr.size, rels.shdr.entsize },
1384 );1595 );
1385 symmap.clearRetainingCapacity();1596 try elf.relocs.ensureUnusedCapacity(gpa, relnum);
1386 try symmap.resize(gpa, symnum);1597 try fsr.seekTo(rels.shdr.offset);
1387 try elf.symtab.ensureUnusedCapacity(gpa, symnum);1598 for (0..relnum) |_| {
1388 try elf.globals.ensureUnusedCapacity(gpa, symnum);1599 const rel = try r.peekStruct(Rel, target_endian);
1389 try fr.seekTo(symtab.shdr.offset + symtab.shdr.entsize);1600 try r.discardAll64(rels.shdr.entsize);
1390 symmap.items[0] = .null;1601 if (rel.info.sym >= symnum) continue;
1391 for (symmap.items[1..]) |*si| {1602 const target_si = symmap.items[rel.info.sym];
1392 si.* = .null;1603 if (target_si == .null) continue;
1393 const input_sym = try r.peekStruct(ElfN.Sym, target_endian);1604 elf.addRelocAssumeCapacity(
1394 try fr.seekBy(@intCast(symtab.shdr.entsize));1605 loc_sec.si,
1395 if (input_sym.name >= strtab.len or1606 rel.offset - loc_sec.shdr.addr,
1396 input_sym.shndx == std.elf.SHN_UNDEF or1607 target_si,
1397 input_sym.shndx >= ehdr.shnum) continue;1608 rel.addend,
1398 switch (input_sym.info.type) {1609 switch (elf.ehdrField(.machine)) {
1399 else => continue,1610 else => unreachable,
1400 .SECTION => {1611 inline .AARCH64,
1401 const section = &sections[input_sym.shndx];1612 .PPC64,
1402 if (input_sym.value == section.shdr.addr) si.* = section.si;1613 .RISCV,
1403 continue;1614 .X86_64,
1404 },1615 => |machine| @unionInit(
1405 .OBJECT, .FUNC => {},1616 Reloc.Type,
1406 }1617 @tagName(machine),
1407 const name = std.mem.sliceTo(strtab[input_sym.name..], 0);1618 @enumFromInt(rel.info.type),
1408 const parent_si = sections[input_sym.shndx].si;1619 ),
1409 si.* = try elf.initSymbolAssumeCapacity(.{
1410 .name = name,
1411 .value = input_sym.value,
1412 .size = input_sym.size,
1413 .type = input_sym.info.type,
1414 .bind = input_sym.info.bind,
1415 .visibility = input_sym.other.visibility,
1416 .shndx = elf.targetLoad(switch (elf.symPtr(parent_si)) {
1417 inline else => |parent_sym| &parent_sym.shndx,
1418 }),
1419 });
1420 si.get(elf).ni = parent_si.get(elf).ni;
1421 switch (input_sym.info.bind) {
1422 else => {},
1423 .GLOBAL => {
1424 const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
1425 &@field(elf.symPtr(si.*), @tagName(class)).name,
1426 ));
1427 if (gop.found_existing) switch (elf.targetLoad(
1428 switch (elf.symPtr(gop.value_ptr.*)) {
1429 inline else => |sym| &sym.info,
1430 },
1431 ).bind) {
1432 else => unreachable,
1433 .GLOBAL => return diags.failParse(
1434 object.path,
1435 "multiple definitions of '{s}'",
1436 .{name},
1437 ),
1438 .WEAK => {},
1439 };
1440 gop.value_ptr.* = si.*;
1441 },1620 },
1442 .WEAK => {1621 );
1443 const gop = elf.globals.getOrPutAssumeCapacity(elf.targetLoad(
1444 &@field(elf.symPtr(si.*), @tagName(class)).name,
1445 ));
1446 if (!gop.found_existing) gop.value_ptr.* = si.*;
1447 },
1448 }
1449 }1622 }
1450 for (sections[1..]) |*rels| switch (rels.shdr.type) {
1451 else => {},
1452 inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| {
1453 if (rels.shdr.link != symtab_shndx or
1454 rels.shdr.info == std.elf.SHN_UNDEF or
1455 rels.shdr.info >= ehdr.shnum) continue;
1456 const Rel = switch (sht) {
1457 else => comptime unreachable,
1458 std.elf.SHT_REL => ElfN.Rel,
1459 std.elf.SHT_RELA => ElfN.Rela,
1460 };
1461 if (rels.shdr.entsize < @sizeOf(Rel)) return diags.failParse(
1462 object.path,
1463 "unsupported rel entsize",
1464 .{},
1465 );
1466 const loc_sec = &sections[rels.shdr.info];
1467 if (loc_sec.si == .null) continue;
1468 const relnum = try std.math.divExact(
1469 u32,
1470 @intCast(rels.shdr.size),
1471 @intCast(rels.shdr.entsize),
1472 );
1473 try elf.relocs.ensureUnusedCapacity(gpa, relnum);
1474 try fr.seekTo(rels.shdr.offset);
1475 for (0..relnum) |_| {
1476 const rel = try r.peekStruct(Rel, target_endian);
1477 try fr.seekBy(@intCast(rels.shdr.entsize));
1478 if (rel.info.sym >= symnum) continue;
1479 const target_si = symmap.items[rel.info.sym];
1480 if (target_si == .null) continue;
1481 elf.addReloc(
1482 loc_sec.si,
1483 rel.offset - loc_sec.shdr.addr,
1484 target_si,
1485 rel.addend,
1486 switch (elf.ehdrField(.machine)) {
1487 else => unreachable,
1488 inline .AARCH64,
1489 .PPC64,
1490 .RISCV,
1491 .X86_64,
1492 => |machine| @unionInit(
1493 Reloc.Type,
1494 @tagName(machine),
1495 @enumFromInt(rel.info.type),
1496 ),
1497 },
1498 ) catch unreachable;
1499 }
1500 },
1501 };
1502 },1623 },
1503 };1624 };
1504 },1625 },
1505 }1626 };
1506 },1627 },
1507 else => {},
1508 }1628 }
1509}1629}
15101630
...@@ -1530,6 +1650,7 @@ fn prelinkInner(elf: *Elf) !void {...@@ -1530,6 +1650,7 @@ fn prelinkInner(elf: *Elf) !void {
1530 });1650 });
1531 elf.inputs.addOneAssumeCapacity().* = .{1651 elf.inputs.addOneAssumeCapacity().* = .{
1532 .path = elf.base.emit,1652 .path = elf.base.emit,
1653 .member = null,
1533 .si = si,1654 .si = si,
1534 };1655 };
1535}1656}
...@@ -1667,10 +1788,20 @@ pub fn addReloc(...@@ -1667,10 +1788,20 @@ pub fn addReloc(
1667 addend: i64,1788 addend: i64,
1668 @"type": Reloc.Type,1789 @"type": Reloc.Type,
1669) !void {1790) !void {
1670 const gpa = elf.base.comp.gpa;1791 try elf.relocs.ensureUnusedCapacity(elf.base.comp.gpa, 1);
1792 elf.addRelocAssumeCapacity(loc_si, offset, target_si, addend, @"type");
1793}
1794pub fn addRelocAssumeCapacity(
1795 elf: *Elf,
1796 loc_si: Symbol.Index,
1797 offset: u64,
1798 target_si: Symbol.Index,
1799 addend: i64,
1800 @"type": Reloc.Type,
1801) void {
1671 const target = target_si.get(elf);1802 const target = target_si.get(elf);
1672 const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);1803 const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len);
1673 (try elf.relocs.addOne(gpa)).* = .{1804 elf.relocs.addOneAssumeCapacity().* = .{
1674 .type = @"type",1805 .type = @"type",
1675 .prev = .none,1806 .prev = .none,
1676 .next = target.target_relocs,1807 .next = target.target_relocs,
...@@ -1961,11 +2092,20 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {...@@ -1961,11 +2092,20 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool {
1961 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.symbol(elf).node(elf)));2092 const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.symbol(elf).node(elf)));
1962 defer sub_prog_node.end();2093 defer sub_prog_node.end();
1963 elf.flushInputSection(isi) catch |err| switch (err) {2094 elf.flushInputSection(isi) catch |err| switch (err) {
1964 else => |e| return comp.link_diags.fail("linker failed to read input section '{s}' from \"{f}\": {t}", .{2095 else => |e| {
1965 elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),2096 const ii = isi.input(elf);
1966 isi.input(elf).path(elf).fmtEscapeString(),2097 return comp.link_diags.fail(
1967 e,2098 "linker failed to read input section '{s}' from \"{f}{f}\": {t}",
1968 }),2099 .{
2100 elf.sectionName(
2101 elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section,
2102 ),
2103 ii.path(elf).fmtEscapeString(),
2104 fmtMemberString(ii.member(elf)),
2105 e,
2106 },
2107 );
2108 },
1969 };2109 };
1970 break :task;2110 break :task;
1971 }2111 }
...@@ -1998,10 +2138,14 @@ fn idleProgNode(...@@ -1998,10 +2138,14 @@ fn idleProgNode(
1998 return prog_node.start(name: switch (node) {2138 return prog_node.start(name: switch (node) {
1999 else => |tag| @tagName(tag),2139 else => |tag| @tagName(tag),
2000 .section => |si| elf.sectionName(si),2140 .section => |si| elf.sectionName(si),
2001 .input_section => |isi| std.fmt.bufPrint(&name, "{f} {s}", .{2141 .input_section => |isi| {
2002 isi.input(elf).path(elf),2142 const ii = isi.input(elf);
2003 elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),2143 break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{
2004 }) catch &name,2144 ii.path(elf).fmtEscapeString(),
2145 fmtMemberString(ii.member(elf)),
2146 elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),
2147 }) catch &name;
2148 },
2005 .nav => |nmi| {2149 .nav => |nmi| {
2006 const ip = &elf.base.comp.zcu.?.intern_pool;2150 const ip = &elf.base.comp.zcu.?.intern_pool;
2007 break :name ip.getNav(nmi.navIndex(elf)).fqn.toSlice(ip);2151 break :name ip.getNav(nmi.navIndex(elf)).fqn.toSlice(ip);
...@@ -2128,16 +2272,17 @@ fn flushInputSection(elf: *Elf, isi: Node.InputSectionIndex) !void {...@@ -2128,16 +2272,17 @@ fn flushInputSection(elf: *Elf, isi: Node.InputSectionIndex) !void {
2128 if (file_loc.size == 0) return;2272 if (file_loc.size == 0) return;
2129 const comp = elf.base.comp;2273 const comp = elf.base.comp;
2130 const gpa = comp.gpa;2274 const gpa = comp.gpa;
2131 const io = comp.io;2275 const ii = isi.input(elf);
2132 const path = isi.input(elf).path(elf);2276 const path = ii.path(elf);
2133 const file = try path.root_dir.handle.adaptToNewApi().openFile(io, path.sub_path, .{});2277 const file = try path.root_dir.handle.adaptToNewApi().openFile(comp.io, path.sub_path, .{});
2134 defer file.close(io);2278 defer file.close(comp.io);
2135 var fr = file.reader(io, &.{});2279 var fr = file.reader(comp.io, &.{});
2136 try fr.seekTo(file_loc.offset);2280 try fr.seekTo(file_loc.offset);
2137 var nw: MappedFile.Node.Writer = undefined;2281 var nw: MappedFile.Node.Writer = undefined;
2138 isi.symbol(elf).node(elf).writer(&elf.mf, gpa, &nw);2282 isi.symbol(elf).node(elf).writer(&elf.mf, gpa, &nw);
2139 defer nw.deinit();2283 defer nw.deinit();
2140 if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size) return error.EndOfStream;2284 if (try nw.interface.sendFileAll(&fr, .limited(@intCast(file_loc.size))) != file_loc.size)
2285 return error.EndOfStream;
2141}2286}
21422287
2143fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {2288fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void {
...@@ -2396,10 +2541,14 @@ pub fn printNode(...@@ -2396,10 +2541,14 @@ pub fn printNode(
2396 switch (node) {2541 switch (node) {
2397 else => {},2542 else => {},
2398 .section => |si| try w.print("({s})", .{elf.sectionName(si)}),2543 .section => |si| try w.print("({s})", .{elf.sectionName(si)}),
2399 .input_section => |isi| try w.print("({f}, {s})", .{2544 .input_section => |isi| {
2400 isi.input(elf).path(elf),2545 const ii = isi.input(elf);
2401 elf.sectionName(elf.getNode(isi.node(elf).parent(&elf.mf)).section),2546 try w.print("({f}{f}, {s})", .{
2402 }),2547 ii.path(elf).fmtEscapeString(),
2548 fmtMemberString(ii.member(elf)),
2549 elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section),
2550 });
2551 },
2403 .nav => |nmi| {2552 .nav => |nmi| {
2404 const zcu = elf.base.comp.zcu.?;2553 const zcu = elf.base.comp.zcu.?;
2405 const ip = &zcu.intern_pool;2554 const ip = &zcu.intern_pool;
src/link/MappedFile.zig+8-1
...@@ -144,7 +144,14 @@ pub const Node = extern struct {...@@ -144,7 +144,14 @@ pub const Node = extern struct {
144 }144 }
145 };145 };
146146
147 pub const FileLocation = struct { offset: u64, size: u64 };147 pub const FileLocation = struct {
148 offset: u64,
149 size: u64,
150
151 pub fn end(fl: FileLocation) u64 {
152 return fl.offset + fl.size;
153 }
154 };
148155
149 pub const Index = enum(u32) {156 pub const Index = enum(u32) {
150 none,157 none,