| ... | ... | @@ -8,12 +8,13 @@ shstrtab: StringTable, |
| 8 | 8 | strtab: StringTable, |
| 9 | 9 | inputs: std.ArrayList(struct { |
| 10 | 10 | path: std.Build.Cache.Path, |
| 11 | member: ?[]const u8, |
| 11 | 12 | si: Symbol.Index, |
| 12 | 13 | }), |
| 13 | 14 | input_sections: std.ArrayList(struct { |
| 14 | 15 | ii: Node.InputIndex, |
| 15 | | si: Symbol.Index, |
| 16 | 16 | file_location: MappedFile.Node.FileLocation, |
| 17 | si: Symbol.Index, |
| 17 | 18 | }), |
| 18 | 19 | input_section_pending_index: u32, |
| 19 | 20 | globals: std.AutoArrayHashMapUnmanaged(u32, Symbol.Index), |
| ... | ... | @@ -53,6 +54,10 @@ pub const Node = union(enum) { |
| 53 | 54 | return elf.inputs.items[@intFromEnum(ii)].path; |
| 54 | 55 | } |
| 55 | 56 | |
| 57 | pub fn member(ii: InputIndex, elf: *const Elf) ?[]const u8 { |
| 58 | return elf.inputs.items[@intFromEnum(ii)].member; |
| 59 | } |
| 60 | |
| 56 | 61 | pub fn symbol(ii: InputIndex, elf: *const Elf) Symbol.Index { |
| 57 | 62 | return elf.inputs.items[@intFromEnum(ii)].si; |
| 58 | 63 | } |
| ... | ... | @@ -73,13 +78,13 @@ pub const Node = union(enum) { |
| 73 | 78 | return elf.input_sections.items[@intFromEnum(isi)].ii; |
| 74 | 79 | } |
| 75 | 80 | |
| 76 | | pub fn symbol(isi: InputSectionIndex, elf: *const Elf) Symbol.Index { |
| 77 | | return elf.input_sections.items[@intFromEnum(isi)].si; |
| 78 | | } |
| 79 | | |
| 80 | 81 | pub fn fileLocation(isi: InputSectionIndex, elf: *const Elf) MappedFile.Node.FileLocation { |
| 81 | 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 | }; |
| 84 | 89 | |
| 85 | 90 | pub const NavMapIndex = enum(u32) { |
| ... | ... | @@ -571,6 +576,7 @@ pub fn deinit(elf: *Elf) void { |
| 571 | 576 | elf.symtab.deinit(gpa); |
| 572 | 577 | elf.shstrtab.map.deinit(gpa); |
| 573 | 578 | elf.strtab.map.deinit(gpa); |
| 579 | for (elf.inputs.items) |input| if (input.member) |m| gpa.free(m); |
| 574 | 580 | elf.inputs.deinit(gpa); |
| 575 | 581 | elf.input_sections.deinit(gpa); |
| 576 | 582 | elf.globals.deinit(gpa); |
| ... | ... | @@ -1255,256 +1261,370 @@ pub fn lazySymbol(elf: *Elf, lazy: link.File.LazySymbol) !Symbol.Index { |
| 1255 | 1261 | return lazy_gop.value_ptr.*; |
| 1256 | 1262 | } |
| 1257 | 1263 | |
| 1258 | | pub fn loadInput(elf: *Elf, input: link.Input) !void { |
| 1264 | pub 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 | } |
| 1287 | const 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 | }; |
| 1322 | fn loadArchive(elf: *Elf, path: std.Build.Cache.Path, fsr: *FileSliceReader) !void { |
| 1259 | 1323 | const comp = elf.base.comp; |
| 1260 | 1324 | const gpa = comp.gpa; |
| 1261 | 1325 | const diags = &comp.link_diags; |
| 1262 | | switch (input) { |
| 1263 | | .object => |object| { |
| 1264 | | const ii: Node.InputIndex = @enumFromInt(elf.inputs.items.len); |
| 1265 | | log.debug("loadInput(.{{ .object = {f} }}) = {d}", .{ object.path, ii }); |
| 1266 | | { |
| 1267 | | try elf.symtab.ensureUnusedCapacity(gpa, 1); |
| 1268 | | try elf.inputs.ensureUnusedCapacity(gpa, 1); |
| 1269 | | const si = try elf.initSymbolAssumeCapacity(.{ |
| 1270 | | .name = std.fs.path.stem(object.path.sub_path), |
| 1271 | | .type = .FILE, |
| 1272 | | .shndx = std.elf.SHN_ABS, |
| 1273 | | }); |
| 1274 | | elf.inputs.addOneAssumeCapacity().* = .{ |
| 1275 | | .path = object.path, |
| 1276 | | .si = si, |
| 1326 | const r = &fsr.file.interface; |
| 1327 | |
| 1328 | log.debug("loadArchive({f})", .{path.fmtEscapeString()}); |
| 1329 | if (!std.mem.eql(u8, try r.take(std.elf.ARMAG.len), std.elf.ARMAG)) |
| 1330 | return diags.failParse(path, "bad magic", .{}); |
| 1331 | var strtab: std.Io.Writer.Allocating = .init(gpa); |
| 1332 | defer strtab.deinit(); |
| 1333 | while (r.takeStruct(std.elf.ar_hdr, native_endian)) |header| { |
| 1334 | if (!std.mem.eql(u8, &header.ar_fmag, std.elf.ARFMAG)) |
| 1335 | return diags.failParse(path, "bad file magic", .{}); |
| 1336 | const size = header.size() catch |
| 1337 | return diags.failParse(path, "bad member size", .{}); |
| 1338 | if (std.mem.eql(u8, &header.ar_name, std.elf.STRNAME)) { |
| 1339 | strtab.clearRetainingCapacity(); |
| 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 | } |
| 1376 | fn fmtMemberString(member: ?[]const u8) std.fmt.Alt(?[]const u8, memberStringEscape) { |
| 1377 | return .{ .data = member }; |
| 1378 | } |
| 1379 | fn 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 | } |
| 1382 | fn 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; |
| 1280 | | var fr = object.file.reader(comp.io, &buf); |
| 1281 | | const r = &fr.interface; |
| 1282 | | const ident = try r.peek(std.elf.EI.NIDENT); |
| 1283 | | if (!std.mem.eql(u8, elf.mf.contents[0..std.elf.EI.NIDENT], ident)) |
| 1284 | | return diags.failParse(object.path, "wrong ident", .{}); |
| 1285 | | const target_endian = elf.targetEndian(); |
| 1286 | | switch (elf.identClass()) { |
| 1287 | | .NONE, _ => unreachable, |
| 1288 | | inline else => |class| { |
| 1289 | | const ElfN = class.ElfN(); |
| 1290 | | const ehdr = try r.peekStruct(ElfN.Ehdr, target_endian); |
| 1291 | | if (ehdr.type != .REL) |
| 1292 | | return diags.failParse(object.path, "unsupported object type", .{}); |
| 1293 | | if (ehdr.machine != elf.ehdrField(.machine)) |
| 1294 | | return diags.failParse(object.path, "wrong machine", .{}); |
| 1295 | | if (ehdr.shoff == 0 or ehdr.shnum <= 1) return; |
| 1296 | | if (ehdr.shentsize < @sizeOf(ElfN.Shdr)) |
| 1297 | | return diags.failParse(object.path, "unsupported shentsize", .{}); |
| 1298 | | const sections = try gpa.alloc(struct { |
| 1299 | | shdr: ElfN.Shdr, |
| 1300 | | si: Symbol.Index, |
| 1301 | | }, ehdr.shnum); |
| 1302 | | defer gpa.free(sections); |
| 1303 | | try fr.seekTo(ehdr.shoff); |
| 1304 | | for (sections) |*section| { |
| 1305 | | section.* = .{ |
| 1306 | | .shdr = try r.peekStruct(ElfN.Shdr, target_endian), |
| 1307 | | .si = .null, |
| 1308 | | }; |
| 1309 | | try fr.seekBy(ehdr.shentsize); |
| 1310 | | } |
| 1311 | | const shstrtab = shstrtab: { |
| 1312 | | if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum) |
| 1313 | | return diags.failParse(object.path, "missing section names", .{}); |
| 1314 | | const shdr = &sections[ehdr.shstrndx].shdr; |
| 1315 | | if (shdr.type != std.elf.SHT_STRTAB) |
| 1316 | | return diags.failParse(object.path, "invalid shstrtab type", .{}); |
| 1317 | | const shstrtab = try gpa.alloc(u8, @intCast(shdr.size)); |
| 1318 | | errdefer gpa.free(shstrtab); |
| 1319 | | try fr.seekTo(shdr.offset); |
| 1320 | | try r.readSliceAll(shstrtab); |
| 1321 | | break :shstrtab shstrtab; |
| 1322 | | }; |
| 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); |
| 1431 | const shstrtab = shstrtab: { |
| 1432 | if (ehdr.shstrndx == std.elf.SHN_UNDEF or ehdr.shstrndx >= ehdr.shnum) |
| 1433 | return diags.failParse(path, "missing section names", .{}); |
| 1434 | const shdr = &sections[ehdr.shstrndx].shdr; |
| 1435 | if (shdr.type != std.elf.SHT_STRTAB) |
| 1436 | return diags.failParse(path, "invalid shstrtab type", .{}); |
| 1437 | const shstrtab = try gpa.alloc(u8, @intCast(shdr.size)); |
| 1438 | errdefer gpa.free(shstrtab); |
| 1439 | try fsr.seekTo(shdr.offset); |
| 1440 | try r.readSliceAll(shstrtab); |
| 1441 | break :shstrtab shstrtab; |
| 1442 | }; |
| 1443 | defer gpa.free(shstrtab); |
| 1444 | try elf.nodes.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 1445 | try elf.symtab.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 1446 | try elf.input_sections.ensureUnusedCapacity(gpa, ehdr.shnum - 1); |
| 1447 | for (sections[1..]) |*section| switch (section.shdr.type) { |
| 1448 | else => {}, |
| 1449 | std.elf.SHT_PROGBITS, std.elf.SHT_NOBITS => { |
| 1450 | if (section.shdr.name >= shstrtab.len) continue; |
| 1451 | const name = std.mem.sliceTo(shstrtab[section.shdr.name..], 0); |
| 1452 | const parent_si = namedSection(name) orelse continue; |
| 1453 | const ni = try elf.mf.addLastChildNode(gpa, parent_si.node(elf), .{ |
| 1454 | .size = section.shdr.size, |
| 1455 | .alignment = .fromByteUnits(std.math.ceilPowerOfTwoAssert( |
| 1456 | usize, |
| 1457 | @intCast(@max(section.shdr.addralign, 1)), |
| 1458 | )), |
| 1459 | .moved = true, |
| 1460 | }); |
| 1461 | elf.nodes.appendAssumeCapacity(.{ |
| 1462 | .input_section = @enumFromInt(elf.input_sections.items.len), |
| 1463 | }); |
| 1464 | section.si = try elf.initSymbolAssumeCapacity(.{ |
| 1465 | .type = .SECTION, |
| 1466 | .shndx = elf.targetLoad(&@field(elf.symPtr(parent_si), @tagName(class)).shndx), |
| 1467 | }); |
| 1468 | section.si.get(elf).ni = ni; |
| 1469 | elf.input_sections.addOneAssumeCapacity().* = .{ |
| 1470 | .ii = ii, |
| 1471 | .si = section.si, |
| 1472 | .file_location = .{ |
| 1473 | .offset = fsr.file_location.offset + section.shdr.offset, |
| 1474 | .size = section.shdr.size, |
| 1358 | 1475 | }, |
| 1359 | 1476 | }; |
| 1360 | | var symmap: std.ArrayList(Symbol.Index) = .empty; |
| 1361 | | defer symmap.deinit(gpa); |
| 1362 | | for (sections[1..], 1..) |*symtab, symtab_shndx| switch (symtab.shdr.type) { |
| 1477 | elf.synth_prog_node.increaseEstimatedTotalItems(1); |
| 1478 | }, |
| 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 | 1574 | else => {}, |
| 1364 | | std.elf.SHT_SYMTAB => { |
| 1365 | | if (symtab.shdr.entsize < @sizeOf(ElfN.Sym)) |
| 1366 | | return diags.failParse(object.path, "unsupported symtab entsize", .{}); |
| 1367 | | const strtab = strtab: { |
| 1368 | | if (symtab.shdr.link == std.elf.SHN_UNDEF or symtab.shdr.link >= ehdr.shnum) |
| 1369 | | return diags.failParse(object.path, "missing symbol names", .{}); |
| 1370 | | const shdr = &sections[symtab.shdr.link].shdr; |
| 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; |
| 1575 | inline std.elf.SHT_REL, std.elf.SHT_RELA => |sht| { |
| 1576 | if (rels.shdr.link != symtab_shndx or rels.shdr.info == std.elf.SHN_UNDEF or |
| 1577 | rels.shdr.info >= ehdr.shnum) continue; |
| 1578 | const Rel = switch (sht) { |
| 1579 | else => comptime unreachable, |
| 1580 | std.elf.SHT_REL => ElfN.Rel, |
| 1581 | std.elf.SHT_RELA => ElfN.Rela, |
| 1378 | 1582 | }; |
| 1379 | | defer gpa.free(strtab); |
| 1380 | | const symnum = try std.math.divExact( |
| 1583 | if (rels.shdr.entsize < @sizeOf(Rel)) |
| 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 | 1588 | u32, |
| 1382 | | @intCast(symtab.shdr.size), |
| 1383 | | @intCast(symtab.shdr.entsize), |
| 1589 | @intCast(rels.shdr.size), |
| 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(); |
| 1386 | | try symmap.resize(gpa, symnum); |
| 1387 | | try elf.symtab.ensureUnusedCapacity(gpa, symnum); |
| 1388 | | try elf.globals.ensureUnusedCapacity(gpa, symnum); |
| 1389 | | try fr.seekTo(symtab.shdr.offset + symtab.shdr.entsize); |
| 1390 | | symmap.items[0] = .null; |
| 1391 | | for (symmap.items[1..]) |*si| { |
| 1392 | | si.* = .null; |
| 1393 | | const input_sym = try r.peekStruct(ElfN.Sym, target_endian); |
| 1394 | | try fr.seekBy(@intCast(symtab.shdr.entsize)); |
| 1395 | | if (input_sym.name >= strtab.len or |
| 1396 | | input_sym.shndx == std.elf.SHN_UNDEF or |
| 1397 | | input_sym.shndx >= ehdr.shnum) continue; |
| 1398 | | switch (input_sym.info.type) { |
| 1399 | | else => continue, |
| 1400 | | .SECTION => { |
| 1401 | | const section = &sections[input_sym.shndx]; |
| 1402 | | if (input_sym.value == section.shdr.addr) si.* = section.si; |
| 1403 | | continue; |
| 1404 | | }, |
| 1405 | | .OBJECT, .FUNC => {}, |
| 1406 | | } |
| 1407 | | const name = std.mem.sliceTo(strtab[input_sym.name..], 0); |
| 1408 | | const parent_si = sections[input_sym.shndx].si; |
| 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.*; |
| 1596 | try elf.relocs.ensureUnusedCapacity(gpa, relnum); |
| 1597 | try fsr.seekTo(rels.shdr.offset); |
| 1598 | for (0..relnum) |_| { |
| 1599 | const rel = try r.peekStruct(Rel, target_endian); |
| 1600 | try r.discardAll64(rels.shdr.entsize); |
| 1601 | if (rel.info.sym >= symnum) continue; |
| 1602 | const target_si = symmap.items[rel.info.sym]; |
| 1603 | if (target_si == .null) continue; |
| 1604 | elf.addRelocAssumeCapacity( |
| 1605 | loc_sec.si, |
| 1606 | rel.offset - loc_sec.shdr.addr, |
| 1607 | target_si, |
| 1608 | rel.addend, |
| 1609 | switch (elf.ehdrField(.machine)) { |
| 1610 | else => unreachable, |
| 1611 | inline .AARCH64, |
| 1612 | .PPC64, |
| 1613 | .RISCV, |
| 1614 | .X86_64, |
| 1615 | => |machine| @unionInit( |
| 1616 | Reloc.Type, |
| 1617 | @tagName(machine), |
| 1618 | @enumFromInt(rel.info.type), |
| 1619 | ), |
| 1441 | 1620 | }, |
| 1442 | | .WEAK => { |
| 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 | | } |
| 1621 | ); |
| 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 | } |
| 1510 | 1630 | |
| ... | ... | @@ -1530,6 +1650,7 @@ fn prelinkInner(elf: *Elf) !void { |
| 1530 | 1650 | }); |
| 1531 | 1651 | elf.inputs.addOneAssumeCapacity().* = .{ |
| 1532 | 1652 | .path = elf.base.emit, |
| 1653 | .member = null, |
| 1533 | 1654 | .si = si, |
| 1534 | 1655 | }; |
| 1535 | 1656 | } |
| ... | ... | @@ -1667,10 +1788,20 @@ pub fn addReloc( |
| 1667 | 1788 | addend: i64, |
| 1668 | 1789 | @"type": Reloc.Type, |
| 1669 | 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 | } |
| 1794 | pub 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 | 1802 | const target = target_si.get(elf); |
| 1672 | 1803 | const ri: Reloc.Index = @enumFromInt(elf.relocs.items.len); |
| 1673 | | (try elf.relocs.addOne(gpa)).* = .{ |
| 1804 | elf.relocs.addOneAssumeCapacity().* = .{ |
| 1674 | 1805 | .type = @"type", |
| 1675 | 1806 | .prev = .none, |
| 1676 | 1807 | .next = target.target_relocs, |
| ... | ... | @@ -1961,11 +2092,20 @@ pub fn idle(elf: *Elf, tid: Zcu.PerThread.Id) !bool { |
| 1961 | 2092 | const sub_prog_node = elf.idleProgNode(tid, elf.input_prog_node, elf.getNode(isi.symbol(elf).node(elf))); |
| 1962 | 2093 | defer sub_prog_node.end(); |
| 1963 | 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}", .{ |
| 1965 | | elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), |
| 1966 | | isi.input(elf).path(elf).fmtEscapeString(), |
| 1967 | | e, |
| 1968 | | }), |
| 2095 | else => |e| { |
| 2096 | const ii = isi.input(elf); |
| 2097 | return comp.link_diags.fail( |
| 2098 | "linker failed to read input section '{s}' from \"{f}{f}\": {t}", |
| 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 | 2110 | break :task; |
| 1971 | 2111 | } |
| ... | ... | @@ -1998,10 +2138,14 @@ fn idleProgNode( |
| 1998 | 2138 | return prog_node.start(name: switch (node) { |
| 1999 | 2139 | else => |tag| @tagName(tag), |
| 2000 | 2140 | .section => |si| elf.sectionName(si), |
| 2001 | | .input_section => |isi| std.fmt.bufPrint(&name, "{f} {s}", .{ |
| 2002 | | isi.input(elf).path(elf), |
| 2003 | | elf.sectionName(elf.getNode(isi.symbol(elf).node(elf).parent(&elf.mf)).section), |
| 2004 | | }) catch &name, |
| 2141 | .input_section => |isi| { |
| 2142 | const ii = isi.input(elf); |
| 2143 | break :name std.fmt.bufPrint(&name, "{f}{f} {s}", .{ |
| 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 | 2149 | .nav => |nmi| { |
| 2006 | 2150 | const ip = &elf.base.comp.zcu.?.intern_pool; |
| 2007 | 2151 | break :name ip.getNav(nmi.navIndex(elf)).fqn.toSlice(ip); |
| ... | ... | @@ -2128,16 +2272,17 @@ fn flushInputSection(elf: *Elf, isi: Node.InputSectionIndex) !void { |
| 2128 | 2272 | if (file_loc.size == 0) return; |
| 2129 | 2273 | const comp = elf.base.comp; |
| 2130 | 2274 | const gpa = comp.gpa; |
| 2131 | | const io = comp.io; |
| 2132 | | const path = isi.input(elf).path(elf); |
| 2133 | | const file = try path.root_dir.handle.adaptToNewApi().openFile(io, path.sub_path, .{}); |
| 2134 | | defer file.close(io); |
| 2135 | | var fr = file.reader(io, &.{}); |
| 2275 | const ii = isi.input(elf); |
| 2276 | const path = ii.path(elf); |
| 2277 | const file = try path.root_dir.handle.adaptToNewApi().openFile(comp.io, path.sub_path, .{}); |
| 2278 | defer file.close(comp.io); |
| 2279 | var fr = file.reader(comp.io, &.{}); |
| 2136 | 2280 | try fr.seekTo(file_loc.offset); |
| 2137 | 2281 | var nw: MappedFile.Node.Writer = undefined; |
| 2138 | 2282 | isi.symbol(elf).node(elf).writer(&elf.mf, gpa, &nw); |
| 2139 | 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 | } |
| 2142 | 2287 | |
| 2143 | 2288 | fn flushMoved(elf: *Elf, ni: MappedFile.Node.Index) !void { |
| ... | ... | @@ -2396,10 +2541,14 @@ pub fn printNode( |
| 2396 | 2541 | switch (node) { |
| 2397 | 2542 | else => {}, |
| 2398 | 2543 | .section => |si| try w.print("({s})", .{elf.sectionName(si)}), |
| 2399 | | .input_section => |isi| try w.print("({f}, {s})", .{ |
| 2400 | | isi.input(elf).path(elf), |
| 2401 | | elf.sectionName(elf.getNode(isi.node(elf).parent(&elf.mf)).section), |
| 2402 | | }), |
| 2544 | .input_section => |isi| { |
| 2545 | const ii = isi.input(elf); |
| 2546 | try w.print("({f}{f}, {s})", .{ |
| 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 | 2552 | .nav => |nmi| { |
| 2404 | 2553 | const zcu = elf.base.comp.zcu.?; |
| 2405 | 2554 | const ip = &zcu.intern_pool; |