| ... | ... | @@ -31,6 +31,13 @@ long_names_table: LongNamesTable, |
| 31 | 31 | import_table: ImportTable, |
| 32 | 32 | export_table: ExportTable, |
| 33 | 33 | symbol_table: SymbolTable, |
| 34 | input_archives: std.ArrayList(InputArchive), |
| 35 | input_archive_members: std.ArrayList(InputArchive.Member), |
| 36 | input_archive_symbols: std.ArrayList(InputArchive.Member.Symbol), |
| 37 | input_archive_symbol_indices: std.AutoArrayHashMapUnmanaged(String, struct { |
| 38 | first: InputArchive.Member.Symbol.Index, |
| 39 | last: InputArchive.Member.Symbol.Index, |
| 40 | }), |
| 34 | 41 | inputs: std.ArrayList(Input), |
| 35 | 42 | input_resolved: std.ArrayList(Symbol.Index), |
| 36 | 43 | input_sections: std.ArrayList(struct { |
| ... | ... | @@ -47,7 +54,6 @@ strings: std.HashMapUnmanaged( |
| 47 | 54 | ), |
| 48 | 55 | string_bytes: std.ArrayList(u8), |
| 49 | 56 | section_table: std.AutoArrayHashMapUnmanaged(String, Section), |
| 50 | | tls_si: Symbol.Index, |
| 51 | 57 | pseudo_section_table: std.array_hash_map.Auto(String, Symbol.Index), |
| 52 | 58 | object_section_table: std.array_hash_map.Auto(String, Symbol.Index), |
| 53 | 59 | symbols: std.ArrayList(Symbol), |
| ... | ... | @@ -387,6 +393,36 @@ pub const Node = union(enum) { |
| 387 | 393 | } |
| 388 | 394 | }; |
| 389 | 395 | |
| 396 | pub const InputArchive = struct { |
| 397 | path: std.Build.Cache.Path, |
| 398 | |
| 399 | const Index = enum(u32) { |
| 400 | _, |
| 401 | }; |
| 402 | |
| 403 | pub const Member = struct { |
| 404 | iai: InputArchive.Index, |
| 405 | name: String, |
| 406 | // This range includes the member header |
| 407 | file_location: MappedFile.Node.FileLocation, |
| 408 | is_import: bool, |
| 409 | // TODO: Field indicating we loaded it already (ii) |
| 410 | const Index = enum(u32) { |
| 411 | _, |
| 412 | }; |
| 413 | |
| 414 | pub const Symbol = struct { |
| 415 | iami: InputArchive.Member.Index, |
| 416 | // Set to its own index to indicate its the last in the list |
| 417 | next: InputArchive.Member.Symbol.Index, |
| 418 | |
| 419 | const Index = enum(u32) { |
| 420 | _, |
| 421 | }; |
| 422 | }; |
| 423 | }; |
| 424 | }; |
| 425 | |
| 390 | 426 | pub const Input = struct { |
| 391 | 427 | path: std.Build.Cache.Path, |
| 392 | 428 | archive_name: ?[]const u8, |
| ... | ... | @@ -771,6 +807,7 @@ pub const Symbol = struct { |
| 771 | 807 | /// Relocations targeting this symbol |
| 772 | 808 | target_relocs: Reloc.Index, |
| 773 | 809 | section_number: SectionNumber, |
| 810 | /// Only used when outputting objects |
| 774 | 811 | sti: SymbolTable.Index, |
| 775 | 812 | gmi: Node.GlobalMapIndex, |
| 776 | 813 | unused0: u16 = 0, |
| ... | ... | @@ -1295,6 +1332,10 @@ fn create( |
| 1295 | 1332 | .pending = .empty, |
| 1296 | 1333 | .pending_shrink = false, |
| 1297 | 1334 | }, |
| 1335 | .input_archives = .empty, |
| 1336 | .input_archive_members = .empty, |
| 1337 | .input_archive_symbols = .empty, |
| 1338 | .input_archive_symbol_indices = .empty, |
| 1298 | 1339 | .inputs = .empty, |
| 1299 | 1340 | .input_resolved = .empty, |
| 1300 | 1341 | .input_sections = .empty, |
| ... | ... | @@ -1302,7 +1343,6 @@ fn create( |
| 1302 | 1343 | .strings = .empty, |
| 1303 | 1344 | .string_bytes = .empty, |
| 1304 | 1345 | .section_table = .empty, |
| 1305 | | .tls_si = .null, |
| 1306 | 1346 | .pseudo_section_table = .empty, |
| 1307 | 1347 | .object_section_table = .empty, |
| 1308 | 1348 | .symbols = .empty, |
| ... | ... | @@ -1356,6 +1396,10 @@ pub fn deinit(coff: *Coff) void { |
| 1356 | 1396 | coff.export_table.entries.deinit(gpa); |
| 1357 | 1397 | coff.symbol_table.strings.deinit(gpa); |
| 1358 | 1398 | coff.symbol_table.pending.deinit(gpa); |
| 1399 | coff.input_archives.deinit(gpa); |
| 1400 | coff.input_archive_members.deinit(gpa); |
| 1401 | coff.input_archive_symbols.deinit(gpa); |
| 1402 | coff.input_archive_symbol_indices.deinit(gpa); |
| 1359 | 1403 | coff.inputs.deinit(gpa); |
| 1360 | 1404 | coff.input_resolved.deinit(gpa); |
| 1361 | 1405 | coff.input_sections.deinit(gpa); |
| ... | ... | @@ -1841,7 +1885,7 @@ fn initHeaders( |
| 1841 | 1885 | |
| 1842 | 1886 | if (comp.config.any_non_single_threaded) { |
| 1843 | 1887 | if (!is_image) |
| 1844 | | coff.tls_si = try coff.addSection(.@".tls$", .{ |
| 1888 | _ = try coff.addSection(.@".tls$", .{ |
| 1845 | 1889 | .CNT_INITIALIZED_DATA = true, |
| 1846 | 1890 | .MEM_READ = true, |
| 1847 | 1891 | .MEM_WRITE = true, |
| ... | ... | @@ -3655,13 +3699,29 @@ fn loadObject( |
| 3655 | 3699 | } |
| 3656 | 3700 | } |
| 3657 | 3701 | |
| 3658 | | fn parseArchiveHeader( |
| 3659 | | header: *const std.coff.ArchiveMemberHeader, |
| 3660 | | opt_longnames: ?[]const u8, |
| 3661 | | ) !struct { |
| 3702 | const ArchiveMemberHeader = struct { |
| 3662 | 3703 | name: []const u8, |
| 3663 | 3704 | size: u34, |
| 3664 | | } { |
| 3705 | }; |
| 3706 | |
| 3707 | fn parseArchiveMemberHeader( |
| 3708 | diags: *link.Diags, |
| 3709 | path: std.Build.Cache.Path, |
| 3710 | header: *const std.coff.ArchiveMemberHeader, |
| 3711 | opt_longnames: ?[]const u8, |
| 3712 | ) !ArchiveMemberHeader { |
| 3713 | return parseArchiveMemberHeaderInner(header, opt_longnames) catch |err| switch (err) { |
| 3714 | error.BadName => return diags.failParse(path, "malformed member header name: '{s}'", .{&header.name}), |
| 3715 | error.BadSize => return diags.failParse(path, "malformed member header size: '{s}'", .{&header.size}), |
| 3716 | error.BadEndOfHeader => return diags.failParse(path, "bad member header end of header", .{}), |
| 3717 | error.NoLongNames => return diags.failParse(path, "long name used without longnames member", .{}), |
| 3718 | }; |
| 3719 | } |
| 3720 | |
| 3721 | fn parseArchiveMemberHeaderInner( |
| 3722 | header: *const std.coff.ArchiveMemberHeader, |
| 3723 | opt_longnames: ?[]const u8, |
| 3724 | ) !ArchiveMemberHeader { |
| 3665 | 3725 | const trim = std.mem.trimEnd(u8, &header.name, &.{' '}); |
| 3666 | 3726 | |
| 3667 | 3727 | if (trim.len == 0) return error.BadName; |
| ... | ... | @@ -3711,23 +3771,58 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo |
| 3711 | 3771 | var opt_longnames: ?[]const u8 = null; |
| 3712 | 3772 | defer if (opt_longnames) |l| gpa.free(l); |
| 3713 | 3773 | |
| 3774 | var members: std.ArrayList(struct { |
| 3775 | offset: u32, |
| 3776 | iami: ?InputArchive.Member.Index, |
| 3777 | }) = .empty; |
| 3778 | var symbol_member_indices: std.ArrayList(u32) = .empty; |
| 3779 | |
| 3780 | const iai: InputArchive.Index = @enumFromInt(coff.input_archives.items.len); |
| 3781 | (try coff.input_archives.addOne(gpa)).* = .{ |
| 3782 | .path = path, |
| 3783 | }; |
| 3784 | |
| 3785 | const first_iami = coff.input_archive_members.items.len; |
| 3786 | const first_iamsi = coff.input_archive_symbols.items.len; |
| 3787 | const first_symbol_indices_index = coff.input_archive_symbol_indices.count(); |
| 3788 | |
| 3789 | errdefer { |
| 3790 | for (coff.input_archive_symbol_indices.values()) |*v| { |
| 3791 | if (@intFromEnum(v.last) < first_iamsi) continue; |
| 3792 | if (@intFromEnum(v.first) >= first_iamsi) continue; |
| 3793 | |
| 3794 | var iter = v.first; |
| 3795 | v.last = while (iter != v.last) { |
| 3796 | const sym = &coff.input_archive_symbols.items[@intFromEnum(iter)]; |
| 3797 | if (@intFromEnum(sym.next) >= first_iamsi) { |
| 3798 | sym.next = iter; |
| 3799 | break iter; |
| 3800 | } |
| 3801 | |
| 3802 | iter = sym.next; |
| 3803 | } else unreachable; |
| 3804 | } |
| 3805 | |
| 3806 | // New entries in this map will only have pointed to iamsi we also just added |
| 3807 | coff.input_archive_symbol_indices.shrinkRetainingCapacity(first_symbol_indices_index); |
| 3808 | coff.input_archive_symbols.shrinkRetainingCapacity(first_iamsi); |
| 3809 | coff.input_archive_members.shrinkRetainingCapacity(first_iami); |
| 3810 | _ = coff.input_archives.pop(); |
| 3811 | } |
| 3812 | |
| 3714 | 3813 | var pos = fr.logicalPos(); |
| 3715 | 3814 | const size = try fr.getSize(); |
| 3716 | 3815 | while (pos < size) : (pos = fr.logicalPos()) { |
| 3717 | 3816 | const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian); |
| 3718 | | const res = parseArchiveHeader(&header, opt_longnames) catch |err| switch (err) { |
| 3719 | | error.BadName => return diags.failParse(path, "malformed member header name: '{s}'", .{&header.name}), |
| 3720 | | error.BadSize => return diags.failParse(path, "malformed member header size: '{s}'", .{&header.size}), |
| 3721 | | error.BadEndOfHeader => return diags.failParse(path, "bad member header end of header", .{}), |
| 3722 | | error.NoLongNames => return diags.failParse(path, "long name used without longnames member", .{}), |
| 3723 | | }; |
| 3817 | const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames); |
| 3724 | 3818 | |
| 3725 | | if (pos + res.size > size) |
| 3819 | const member_end = fr.logicalPos() + res.size; |
| 3820 | if (member_end > size) |
| 3726 | 3821 | return diags.failParse(path, "out-of-bounds length 0x{x} in member '{s}'", .{ res.size, res.name }); |
| 3727 | 3822 | |
| 3728 | 3823 | log.debug("loadArchiveMember({s})", .{res.name}); |
| 3729 | 3824 | |
| 3730 | | if (opt_expected_kind) |expected_kind| expected: switch (expected_kind) { |
| 3825 | if (opt_expected_kind) |expected_kind| switch (expected_kind) { |
| 3731 | 3826 | .first_linker => { |
| 3732 | 3827 | if (!std.mem.eql(u8, res.name, "/")) |
| 3733 | 3828 | return diags.failParse(path, "expected first linker member, found '{s}'", .{res.name}); |
| ... | ... | @@ -3740,50 +3835,142 @@ fn loadArchive(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !vo |
| 3740 | 3835 | if (!std.mem.eql(u8, res.name, "/")) |
| 3741 | 3836 | return diags.failParse(path, "expected second linker member, found '{s}'", .{res.name}); |
| 3742 | 3837 | |
| 3743 | | // TODO: Parse this! |
| 3744 | | // TODO: Build index of symbols -> members from 2nd linker member |
| 3745 | | // TODO: We don't actually have to load an object unless we need a symbol from it (when linking images) |
| 3746 | | // TODO: Lazily call loadObject whenever a symbol is need from one of the members. |
| 3747 | | // Could do that in flushGlobal if we haven't gotten an .ni for the symbol yet (and no lib_name)? |
| 3748 | | try r.discardAll(res.size); |
| 3838 | const num_members = try r.takeInt(u32, target_endian); |
| 3839 | pos = fr.logicalPos(); |
| 3840 | if (pos + num_members * 4 > member_end) |
| 3841 | return diags.failParse(path, "invalid member count 0x{x} in second linker member", .{num_members}); |
| 3842 | |
| 3843 | try members.ensureTotalCapacity(gpa, num_members); |
| 3844 | for (0..num_members) |_| |
| 3845 | members.addOneAssumeCapacity().* = .{ |
| 3846 | .offset = try r.takeInt(u32, target_endian), |
| 3847 | .iami = null, |
| 3848 | }; |
| 3849 | |
| 3850 | const num_symbols = try r.takeInt(u32, target_endian); |
| 3851 | pos = fr.logicalPos(); |
| 3852 | if (pos + num_symbols * 2 > member_end) |
| 3853 | return diags.failParse(path, "invalid symbol count 0x{x} in second linker member", .{num_symbols}); |
| 3854 | |
| 3855 | try symbol_member_indices.ensureTotalCapacity(gpa, num_symbols); |
| 3856 | for (0..num_symbols) |_| |
| 3857 | symbol_member_indices.addOneAssumeCapacity().* = (try r.takeInt(u16, target_endian)) - 1; |
| 3858 | |
| 3859 | pos = fr.logicalPos(); |
| 3860 | try coff.ensureManyUnusedStringCapacity(num_symbols, member_end - pos); |
| 3861 | try coff.input_archive_members.ensureUnusedCapacity(gpa, num_members); |
| 3862 | try coff.input_archive_symbols.ensureUnusedCapacity(gpa, num_symbols); |
| 3863 | try coff.input_archive_symbol_indices.ensureUnusedCapacity(gpa, num_symbols); |
| 3864 | |
| 3865 | var symbol_i: u32 = 0; |
| 3866 | while (pos < member_end and symbol_i < num_symbols) : ({ |
| 3867 | pos = fr.logicalPos(); |
| 3868 | symbol_i += 1; |
| 3869 | }) { |
| 3870 | const name = if (r.takeDelimiter(0) catch |err| switch (err) { |
| 3871 | error.StreamTooLong => null, |
| 3872 | else => |e| return e, |
| 3873 | }) |n| n else return diags.failParse(path, "unterminated string found in second linker member", .{}); |
| 3874 | |
| 3875 | const string = coff.getOrPutStringAssumeCapacity(name); |
| 3876 | const iamsi: InputArchive.Member.Symbol.Index = @enumFromInt(coff.input_archive_symbols.items.len); |
| 3877 | const symbol_gop = coff.input_archive_symbol_indices.getOrPutAssumeCapacity(string); |
| 3878 | if (!symbol_gop.found_existing) { |
| 3879 | symbol_gop.value_ptr.* = .{ |
| 3880 | .first = iamsi, |
| 3881 | .last = iamsi, |
| 3882 | }; |
| 3883 | } else { |
| 3884 | coff.input_archive_symbols.items[@intFromEnum(symbol_gop.value_ptr.last)].next = iamsi; |
| 3885 | symbol_gop.value_ptr.last = iamsi; |
| 3886 | } |
| 3887 | |
| 3888 | const iami = members.items[symbol_member_indices.items[symbol_i]].iami orelse iami: { |
| 3889 | const iami: InputArchive.Member.Index = @enumFromInt(coff.input_archive_members.items.len); |
| 3890 | const member_offset = members.items[symbol_member_indices.items[symbol_i]].offset; |
| 3891 | coff.input_archive_members.addOneAssumeCapacity().* = .{ |
| 3892 | .iai = iai, |
| 3893 | .name = undefined, |
| 3894 | .is_import = undefined, |
| 3895 | .file_location = .{ |
| 3896 | .offset = member_offset, |
| 3897 | .size = undefined, |
| 3898 | }, |
| 3899 | }; |
| 3900 | |
| 3901 | members.items[symbol_member_indices.items[symbol_i]].iami = iami; |
| 3902 | break :iami iami; |
| 3903 | }; |
| 3904 | |
| 3905 | log.debug("loadArchiveMemberSymbol({s}) = ({d}, {d}, {d})", .{ name, iai, iami, iamsi }); |
| 3906 | |
| 3907 | coff.input_archive_symbols.addOneAssumeCapacity().* = .{ |
| 3908 | .iami = iami, |
| 3909 | .next = iamsi, |
| 3910 | }; |
| 3911 | } |
| 3912 | |
| 3913 | if (symbol_i != num_symbols) |
| 3914 | return diags.failParse( |
| 3915 | path, |
| 3916 | " expected {d} entries in second linker member string table, but found {d}", |
| 3917 | .{ num_symbols, symbol_i }, |
| 3918 | ); |
| 3749 | 3919 | |
| 3920 | try fr.seekTo(member_end); |
| 3750 | 3921 | opt_expected_kind = .longnames; |
| 3751 | 3922 | continue; |
| 3752 | 3923 | }, |
| 3753 | 3924 | .longnames => { |
| 3754 | | defer opt_expected_kind = null; |
| 3755 | | |
| 3756 | 3925 | // This member is optional |
| 3757 | | if (!std.mem.eql(u8, res.name, "//")) break :expected; |
| 3758 | | opt_longnames = try r.readAlloc(gpa, res.size); |
| 3759 | | continue; |
| 3926 | if (std.mem.eql(u8, res.name, "//")) |
| 3927 | opt_longnames = try r.readAlloc(gpa, res.size); |
| 3928 | |
| 3929 | opt_expected_kind = null; |
| 3930 | break; |
| 3760 | 3931 | }, |
| 3761 | 3932 | else => unreachable, |
| 3762 | 3933 | }; |
| 3934 | } |
| 3935 | |
| 3936 | if (opt_expected_kind) |expected_kind| switch (expected_kind) { |
| 3937 | .first_linker => return diags.failParse(path, "missing first linker member", .{}), |
| 3938 | .second_linker => return diags.failParse(path, "missing second linker member", .{}), |
| 3939 | else => {}, |
| 3940 | }; |
| 3941 | |
| 3942 | // Validate / read names and sizes of all the referenced members |
| 3943 | for (coff.input_archive_members.items[first_iami..]) |*member| { |
| 3944 | try fr.seekTo(member.file_location.offset); |
| 3945 | |
| 3946 | const header = try r.takeStruct(std.coff.ArchiveMemberHeader, target_endian); |
| 3947 | const res = try parseArchiveMemberHeader(diags, path, &header, opt_longnames); |
| 3948 | |
| 3949 | try coff.ensureUnusedStringCapacity(res.name.len); |
| 3950 | member.name = coff.getOrPutStringAssumeCapacity(res.name); |
| 3763 | 3951 | |
| 3764 | 3952 | const member_sig = try r.peek(4); |
| 3765 | 3953 | const machine = std.mem.readInt(u16, member_sig[0..2], target_endian); |
| 3766 | 3954 | const sig = std.mem.readInt(u16, member_sig[2..4], target_endian); |
| 3767 | | if (machine == @intFromEnum(std.coff.IMAGE.FILE.MACHINE.UNKNOWN) and sig == 0xffff) { |
| 3768 | | const import_header = try r.peekStruct(std.coff.ImportHeader, target_endian); |
| 3955 | member.is_import = machine == @intFromEnum(std.coff.IMAGE.FILE.MACHINE.UNKNOWN) and sig == 0xffff; |
| 3956 | member.file_location.size = res.size; |
| 3769 | 3957 | |
| 3770 | | // TODO: Validate import table header fields |
| 3771 | | _ = import_header; |
| 3772 | | } else { |
| 3773 | | const coff_header = try r.peekStruct(std.coff.Header, target_endian); |
| 3958 | log.debug("verifyArchiveMember({s}) = 0x{x}+{x}", .{ |
| 3959 | res.name, |
| 3960 | member.file_location.offset, |
| 3961 | member.file_location.size, |
| 3962 | }); |
| 3774 | 3963 | |
| 3775 | | // TODO: Validate COFF header fields |
| 3776 | | _ = coff_header; |
| 3964 | if (member.is_import) { |
| 3965 | const import_header = try r.peekStruct(std.coff.ImportHeader, target_endian); |
| 3966 | // TODO: Validate import table header fields |
| 3967 | // TODO: Use this result in flushGlobal |
| 3968 | return diags.failParse(path, "TODO implement parsing import headers: {t} {t}", .{ |
| 3969 | import_header.types.type, |
| 3970 | import_header.types.name_type, |
| 3971 | }); |
| 3777 | 3972 | } |
| 3778 | | |
| 3779 | | try fr.seekTo(fr.logicalPos() + res.size); |
| 3780 | 3973 | } |
| 3781 | | |
| 3782 | | if (opt_expected_kind) |expected_kind| switch (expected_kind) { |
| 3783 | | .first_linker => return diags.failParse(path, "missing first linker member", .{}), |
| 3784 | | .second_linker => return diags.failParse(path, "missing second linker member", .{}), |
| 3785 | | else => {}, |
| 3786 | | }; |
| 3787 | 3974 | } |
| 3788 | 3975 | |
| 3789 | 3976 | fn loadRes(coff: *Coff, path: std.Build.Cache.Path, fr: *Io.File.Reader) !void { |
| ... | ... | @@ -4723,6 +4910,15 @@ fn flushGlobal(coff: *Coff, gmi: Node.GlobalMapIndex) !void { |
| 4723 | 4910 | coff.nodes.appendAssumeCapacity(.{ .global = gmi }); |
| 4724 | 4911 | sym.rva = coff.computeNodeRva(sym.ni); |
| 4725 | 4912 | si.applyLocationRelocs(coff); |
| 4913 | } else { |
| 4914 | |
| 4915 | // TODO: Check if not defined, and if in an input member |
| 4916 | // TODO: If so, queue a loadObject for that member |
| 4917 | // TODO: Return a value indicating to retry this flushGlobal |
| 4918 | // TODO: The loadObject idle task should be before the flushGlobal idle task |
| 4919 | // |
| 4920 | // TODO: Check if we can get flushGlobal before prelink, that would cause a problem |
| 4921 | |
| 4726 | 4922 | } |
| 4727 | 4923 | } |
| 4728 | 4924 | |