| author | |
| committer | |
| log | bf0387b6bb9c65c30f14e699a2f2cfbfea27184e |
| tree | c0d68c1f225ed91cc900475958d1c125ae3239fb |
| parent | 234693bcbba6f55ff6e975ddbedf0fad4dfaa8f1 |
| parent | 261db02018c71e8977d2bf2a78d495cc31abd1bc |
| signature |
elf: implement archiving input object files10 files changed, 888 insertions(+), 496 deletions(-)
lib/std/Build/Step/CheckObject.zig+497-318| ... | @@ -405,6 +405,17 @@ pub fn checkInDynamicSection(self: *CheckObject) void { | ... | @@ -405,6 +405,17 @@ pub fn checkInDynamicSection(self: *CheckObject) void { |
| 405 | self.checkExact(label); | 405 | self.checkExact(label); |
| 406 | } | 406 | } |
| 407 | 407 | ||
| 408 | /// Creates a new check checking specifically symbol table parsed and dumped from the archive | ||
| 409 | /// file. | ||
| 410 | pub fn checkInArchiveSymtab(self: *CheckObject) void { | ||
| 411 | const label = switch (self.obj_format) { | ||
| 412 | .elf => ElfDumper.archive_symtab_label, | ||
| 413 | else => @panic("TODO other file formats"), | ||
| 414 | }; | ||
| 415 | self.checkStart(); | ||
| 416 | self.checkExact(label); | ||
| 417 | } | ||
| 418 | |||
| 408 | /// Creates a new standalone, singular check which allows running simple binary operations | 419 | /// Creates a new standalone, singular check which allows running simple binary operations |
| 409 | /// on the extracted variables. It will then compare the reduced program with the value of | 420 | /// on the extracted variables. It will then compare the reduced program with the value of |
| 410 | /// the expected variable. | 421 | /// the expected variable. |
| ... | @@ -884,35 +895,177 @@ const ElfDumper = struct { | ... | @@ -884,35 +895,177 @@ const ElfDumper = struct { |
| 884 | const symtab_label = "symbol table"; | 895 | const symtab_label = "symbol table"; |
| 885 | const dynamic_symtab_label = "dynamic symbol table"; | 896 | const dynamic_symtab_label = "dynamic symbol table"; |
| 886 | const dynamic_section_label = "dynamic section"; | 897 | const dynamic_section_label = "dynamic section"; |
| 898 | const archive_symtab_label = "archive symbol table"; | ||
| 887 | 899 | ||
| 888 | const Symtab = struct { | 900 | fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 { |
| 889 | symbols: []align(1) const elf.Elf64_Sym, | 901 | const gpa = step.owner.allocator; |
| 890 | strings: []const u8, | 902 | return parseAndDumpArchive(gpa, bytes) catch |err| switch (err) { |
| 903 | error.InvalidArchiveMagicNumber => try parseAndDumpObject(gpa, bytes), | ||
| 904 | else => |e| return e, | ||
| 905 | }; | ||
| 906 | } | ||
| 891 | 907 | ||
| 892 | fn get(st: Symtab, index: usize) ?elf.Elf64_Sym { | 908 | fn parseAndDumpArchive(gpa: Allocator, bytes: []const u8) ![]const u8 { |
| 893 | if (index >= st.symbols.len) return null; | 909 | var stream = std.io.fixedBufferStream(bytes); |
| 894 | return st.symbols[index]; | 910 | const reader = stream.reader(); |
| 911 | |||
| 912 | const magic = try reader.readBytesNoEof(elf.ARMAG.len); | ||
| 913 | if (!mem.eql(u8, &magic, elf.ARMAG)) { | ||
| 914 | return error.InvalidArchiveMagicNumber; | ||
| 895 | } | 915 | } |
| 896 | 916 | ||
| 897 | fn getName(st: Symtab, index: usize) ?[]const u8 { | 917 | var ctx = ArchiveContext{ |
| 898 | const sym = st.get(index) orelse return null; | 918 | .gpa = gpa, |
| 899 | return getString(st.strings, sym.st_name); | 919 | .data = bytes, |
| 920 | .strtab = &[0]u8{}, | ||
| 921 | }; | ||
| 922 | defer { | ||
| 923 | for (ctx.objects.items) |*object| { | ||
| 924 | gpa.free(object.name); | ||
| 925 | } | ||
| 926 | ctx.objects.deinit(gpa); | ||
| 900 | } | 927 | } |
| 901 | }; | ||
| 902 | 928 | ||
| 903 | const Context = struct { | 929 | while (true) { |
| 930 | if (stream.pos >= ctx.data.len) break; | ||
| 931 | if (!mem.isAligned(stream.pos, 2)) stream.pos += 1; | ||
| 932 | |||
| 933 | const hdr = try reader.readStruct(elf.ar_hdr); | ||
| 934 | |||
| 935 | if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) return error.InvalidArchiveHeaderMagicNumber; | ||
| 936 | |||
| 937 | const size = try hdr.size(); | ||
| 938 | defer { | ||
| 939 | _ = stream.seekBy(size) catch {}; | ||
| 940 | } | ||
| 941 | |||
| 942 | if (hdr.isSymtab()) { | ||
| 943 | try ctx.parseSymtab(ctx.data[stream.pos..][0..size], .p32); | ||
| 944 | continue; | ||
| 945 | } | ||
| 946 | if (hdr.isSymtab64()) { | ||
| 947 | try ctx.parseSymtab(ctx.data[stream.pos..][0..size], .p64); | ||
| 948 | continue; | ||
| 949 | } | ||
| 950 | if (hdr.isStrtab()) { | ||
| 951 | ctx.strtab = ctx.data[stream.pos..][0..size]; | ||
| 952 | continue; | ||
| 953 | } | ||
| 954 | if (hdr.isSymdef() or hdr.isSymdefSorted()) continue; | ||
| 955 | |||
| 956 | const name = if (hdr.name()) |name| | ||
| 957 | try gpa.dupe(u8, name) | ||
| 958 | else if (try hdr.nameOffset()) |off| | ||
| 959 | try gpa.dupe(u8, ctx.getString(off)) | ||
| 960 | else | ||
| 961 | unreachable; | ||
| 962 | |||
| 963 | try ctx.objects.append(gpa, .{ .name = name, .off = stream.pos, .len = size }); | ||
| 964 | } | ||
| 965 | |||
| 966 | var output = std.ArrayList(u8).init(gpa); | ||
| 967 | const writer = output.writer(); | ||
| 968 | |||
| 969 | try ctx.dumpSymtab(writer); | ||
| 970 | try ctx.dumpObjects(writer); | ||
| 971 | |||
| 972 | return output.toOwnedSlice(); | ||
| 973 | } | ||
| 974 | |||
| 975 | const ArchiveContext = struct { | ||
| 904 | gpa: Allocator, | 976 | gpa: Allocator, |
| 905 | data: []const u8, | 977 | data: []const u8, |
| 906 | hdr: elf.Elf64_Ehdr, | 978 | symtab: std.ArrayListUnmanaged(ArSymtabEntry) = .{}, |
| 907 | shdrs: []align(1) const elf.Elf64_Shdr, | 979 | strtab: []const u8, |
| 908 | phdrs: []align(1) const elf.Elf64_Phdr, | 980 | objects: std.ArrayListUnmanaged(struct { name: []const u8, off: usize, len: usize }) = .{}, |
| 909 | shstrtab: []const u8, | 981 | |
| 910 | symtab: ?Symtab = null, | 982 | fn parseSymtab(ctx: *ArchiveContext, raw: []const u8, ptr_width: enum { p32, p64 }) !void { |
| 911 | dysymtab: ?Symtab = null, | 983 | var stream = std.io.fixedBufferStream(raw); |
| 984 | const reader = stream.reader(); | ||
| 985 | const num = switch (ptr_width) { | ||
| 986 | .p32 => try reader.readInt(u32, .big), | ||
| 987 | .p64 => try reader.readInt(u64, .big), | ||
| 988 | }; | ||
| 989 | const ptr_size: usize = switch (ptr_width) { | ||
| 990 | .p32 => @sizeOf(u32), | ||
| 991 | .p64 => @sizeOf(u64), | ||
| 992 | }; | ||
| 993 | const strtab_off = (num + 1) * ptr_size; | ||
| 994 | const strtab_len = raw.len - strtab_off; | ||
| 995 | const strtab = raw[strtab_off..][0..strtab_len]; | ||
| 996 | |||
| 997 | try ctx.symtab.ensureTotalCapacityPrecise(ctx.gpa, num); | ||
| 998 | |||
| 999 | var stroff: usize = 0; | ||
| 1000 | for (0..num) |_| { | ||
| 1001 | const off = switch (ptr_width) { | ||
| 1002 | .p32 => try reader.readInt(u32, .big), | ||
| 1003 | .p64 => try reader.readInt(u64, .big), | ||
| 1004 | }; | ||
| 1005 | const name = mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + stroff)), 0); | ||
| 1006 | stroff += name.len + 1; | ||
| 1007 | ctx.symtab.appendAssumeCapacity(.{ .off = off, .name = name }); | ||
| 1008 | } | ||
| 1009 | } | ||
| 1010 | |||
| 1011 | fn dumpSymtab(ctx: ArchiveContext, writer: anytype) !void { | ||
| 1012 | if (ctx.symtab.items.len == 0) return; | ||
| 1013 | |||
| 1014 | var files = std.AutoHashMap(usize, []const u8).init(ctx.gpa); | ||
| 1015 | defer files.deinit(); | ||
| 1016 | try files.ensureUnusedCapacity(@intCast(ctx.objects.items.len)); | ||
| 1017 | |||
| 1018 | for (ctx.objects.items) |object| { | ||
| 1019 | files.putAssumeCapacityNoClobber(object.off - @sizeOf(elf.ar_hdr), object.name); | ||
| 1020 | } | ||
| 1021 | |||
| 1022 | var symbols = std.AutoArrayHashMap(usize, std.ArrayList([]const u8)).init(ctx.gpa); | ||
| 1023 | defer { | ||
| 1024 | for (symbols.values()) |*value| { | ||
| 1025 | value.deinit(); | ||
| 1026 | } | ||
| 1027 | symbols.deinit(); | ||
| 1028 | } | ||
| 1029 | |||
| 1030 | for (ctx.symtab.items) |entry| { | ||
| 1031 | const gop = try symbols.getOrPut(@intCast(entry.off)); | ||
| 1032 | if (!gop.found_existing) { | ||
| 1033 | gop.value_ptr.* = std.ArrayList([]const u8).init(ctx.gpa); | ||
| 1034 | } | ||
| 1035 | try gop.value_ptr.append(entry.name); | ||
| 1036 | } | ||
| 1037 | |||
| 1038 | try writer.print("{s}\n", .{archive_symtab_label}); | ||
| 1039 | for (symbols.keys(), symbols.values()) |off, values| { | ||
| 1040 | try writer.print("in object {s}\n", .{files.get(off).?}); | ||
| 1041 | for (values.items) |value| { | ||
| 1042 | try writer.print("{s}\n", .{value}); | ||
| 1043 | } | ||
| 1044 | } | ||
| 1045 | } | ||
| 1046 | |||
| 1047 | fn dumpObjects(ctx: ArchiveContext, writer: anytype) !void { | ||
| 1048 | for (ctx.objects.items) |object| { | ||
| 1049 | try writer.print("object {s}\n", .{object.name}); | ||
| 1050 | const output = try parseAndDumpObject(ctx.gpa, ctx.data[object.off..][0..object.len]); | ||
| 1051 | defer ctx.gpa.free(output); | ||
| 1052 | try writer.print("{s}\n", .{output}); | ||
| 1053 | } | ||
| 1054 | } | ||
| 1055 | |||
| 1056 | fn getString(ctx: ArchiveContext, off: u32) []const u8 { | ||
| 1057 | assert(off < ctx.strtab.len); | ||
| 1058 | const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(ctx.strtab.ptr + off)), 0); | ||
| 1059 | return name[0 .. name.len - 1]; | ||
| 1060 | } | ||
| 1061 | |||
| 1062 | const ArSymtabEntry = struct { | ||
| 1063 | name: [:0]const u8, | ||
| 1064 | off: u64, | ||
| 1065 | }; | ||
| 912 | }; | 1066 | }; |
| 913 | 1067 | ||
| 914 | fn parseAndDump(step: *Step, bytes: []const u8) ![]const u8 { | 1068 | fn parseAndDumpObject(gpa: Allocator, bytes: []const u8) ![]const u8 { |
| 915 | const gpa = step.owner.allocator; | ||
| 916 | var stream = std.io.fixedBufferStream(bytes); | 1069 | var stream = std.io.fixedBufferStream(bytes); |
| 917 | const reader = stream.reader(); | 1070 | const reader = stream.reader(); |
| 918 | 1071 | ||
| ... | @@ -924,7 +1077,7 @@ const ElfDumper = struct { | ... | @@ -924,7 +1077,7 @@ const ElfDumper = struct { |
| 924 | const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(bytes.ptr + hdr.e_shoff))[0..hdr.e_shnum]; | 1077 | const shdrs = @as([*]align(1) const elf.Elf64_Shdr, @ptrCast(bytes.ptr + hdr.e_shoff))[0..hdr.e_shnum]; |
| 925 | const phdrs = @as([*]align(1) const elf.Elf64_Phdr, @ptrCast(bytes.ptr + hdr.e_phoff))[0..hdr.e_phnum]; | 1078 | const phdrs = @as([*]align(1) const elf.Elf64_Phdr, @ptrCast(bytes.ptr + hdr.e_phoff))[0..hdr.e_phnum]; |
| 926 | 1079 | ||
| 927 | var ctx = Context{ | 1080 | var ctx = ObjectContext{ |
| 928 | .gpa = gpa, | 1081 | .gpa = gpa, |
| 929 | .data = bytes, | 1082 | .data = bytes, |
| 930 | .hdr = hdr, | 1083 | .hdr = hdr, |
| ... | @@ -932,14 +1085,14 @@ const ElfDumper = struct { | ... | @@ -932,14 +1085,14 @@ const ElfDumper = struct { |
| 932 | .phdrs = phdrs, | 1085 | .phdrs = phdrs, |
| 933 | .shstrtab = undefined, | 1086 | .shstrtab = undefined, |
| 934 | }; | 1087 | }; |
| 935 | ctx.shstrtab = getSectionContents(ctx, ctx.hdr.e_shstrndx); | 1088 | ctx.shstrtab = ctx.getSectionContents(ctx.hdr.e_shstrndx); |
| 936 | 1089 | ||
| 937 | for (ctx.shdrs, 0..) |shdr, i| switch (shdr.sh_type) { | 1090 | for (ctx.shdrs, 0..) |shdr, i| switch (shdr.sh_type) { |
| 938 | elf.SHT_SYMTAB, elf.SHT_DYNSYM => { | 1091 | elf.SHT_SYMTAB, elf.SHT_DYNSYM => { |
| 939 | const raw = getSectionContents(ctx, i); | 1092 | const raw = ctx.getSectionContents(i); |
| 940 | const nsyms = @divExact(raw.len, @sizeOf(elf.Elf64_Sym)); | 1093 | const nsyms = @divExact(raw.len, @sizeOf(elf.Elf64_Sym)); |
| 941 | const symbols = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw.ptr))[0..nsyms]; | 1094 | const symbols = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw.ptr))[0..nsyms]; |
| 942 | const strings = getSectionContents(ctx, shdr.sh_link); | 1095 | const strings = ctx.getSectionContents(shdr.sh_link); |
| 943 | 1096 | ||
| 944 | switch (shdr.sh_type) { | 1097 | switch (shdr.sh_type) { |
| 945 | elf.SHT_SYMTAB => { | 1098 | elf.SHT_SYMTAB => { |
| ... | @@ -964,199 +1117,346 @@ const ElfDumper = struct { | ... | @@ -964,199 +1117,346 @@ const ElfDumper = struct { |
| 964 | var output = std.ArrayList(u8).init(gpa); | 1117 | var output = std.ArrayList(u8).init(gpa); |
| 965 | const writer = output.writer(); | 1118 | const writer = output.writer(); |
| 966 | 1119 | ||
| 967 | try dumpHeader(ctx, writer); | 1120 | try ctx.dumpHeader(writer); |
| 968 | try dumpShdrs(ctx, writer); | 1121 | try ctx.dumpShdrs(writer); |
| 969 | try dumpPhdrs(ctx, writer); | 1122 | try ctx.dumpPhdrs(writer); |
| 970 | try dumpDynamicSection(ctx, writer); | 1123 | try ctx.dumpDynamicSection(writer); |
| 971 | try dumpSymtab(ctx, .symtab, writer); | 1124 | try ctx.dumpSymtab(.symtab, writer); |
| 972 | try dumpSymtab(ctx, .dysymtab, writer); | 1125 | try ctx.dumpSymtab(.dysymtab, writer); |
| 973 | 1126 | ||
| 974 | return output.toOwnedSlice(); | 1127 | return output.toOwnedSlice(); |
| 975 | } | 1128 | } |
| 976 | 1129 | ||
| 977 | inline fn getSectionName(ctx: Context, shndx: usize) []const u8 { | 1130 | const ObjectContext = struct { |
| 978 | const shdr = ctx.shdrs[shndx]; | 1131 | gpa: Allocator, |
| 979 | return getString(ctx.shstrtab, shdr.sh_name); | 1132 | data: []const u8, |
| 980 | } | 1133 | hdr: elf.Elf64_Ehdr, |
| 1134 | shdrs: []align(1) const elf.Elf64_Shdr, | ||
| 1135 | phdrs: []align(1) const elf.Elf64_Phdr, | ||
| 1136 | shstrtab: []const u8, | ||
| 1137 | symtab: ?Symtab = null, | ||
| 1138 | dysymtab: ?Symtab = null, | ||
| 981 | 1139 | ||
| 982 | fn getSectionContents(ctx: Context, shndx: usize) []const u8 { | 1140 | fn dumpHeader(ctx: ObjectContext, writer: anytype) !void { |
| 983 | const shdr = ctx.shdrs[shndx]; | 1141 | try writer.writeAll("header\n"); |
| 984 | assert(shdr.sh_offset < ctx.data.len); | 1142 | try writer.print("type {s}\n", .{@tagName(ctx.hdr.e_type)}); |
| 985 | assert(shdr.sh_offset + shdr.sh_size <= ctx.data.len); | 1143 | try writer.print("entry {x}\n", .{ctx.hdr.e_entry}); |
| 986 | return ctx.data[shdr.sh_offset..][0..shdr.sh_size]; | 1144 | } |
| 987 | } | ||
| 988 | 1145 | ||
| 989 | fn getSectionByName(ctx: Context, name: []const u8) ?usize { | 1146 | fn dumpPhdrs(ctx: ObjectContext, writer: anytype) !void { |
| 990 | for (0..ctx.shdrs.len) |shndx| { | 1147 | if (ctx.phdrs.len == 0) return; |
| 991 | if (mem.eql(u8, getSectionName(ctx, shndx), name)) return shndx; | 1148 | |
| 992 | } else return null; | 1149 | try writer.writeAll("program headers\n"); |
| 993 | } | 1150 | |
| 1151 | for (ctx.phdrs, 0..) |phdr, phndx| { | ||
| 1152 | try writer.print("phdr {d}\n", .{phndx}); | ||
| 1153 | try writer.print("type {s}\n", .{fmtPhType(phdr.p_type)}); | ||
| 1154 | try writer.print("vaddr {x}\n", .{phdr.p_vaddr}); | ||
| 1155 | try writer.print("paddr {x}\n", .{phdr.p_paddr}); | ||
| 1156 | try writer.print("offset {x}\n", .{phdr.p_offset}); | ||
| 1157 | try writer.print("memsz {x}\n", .{phdr.p_memsz}); | ||
| 1158 | try writer.print("filesz {x}\n", .{phdr.p_filesz}); | ||
| 1159 | try writer.print("align {x}\n", .{phdr.p_align}); | ||
| 1160 | |||
| 1161 | { | ||
| 1162 | const flags = phdr.p_flags; | ||
| 1163 | try writer.writeAll("flags"); | ||
| 1164 | if (flags > 0) try writer.writeByte(' '); | ||
| 1165 | if (flags & elf.PF_R != 0) { | ||
| 1166 | try writer.writeByte('R'); | ||
| 1167 | } | ||
| 1168 | if (flags & elf.PF_W != 0) { | ||
| 1169 | try writer.writeByte('W'); | ||
| 1170 | } | ||
| 1171 | if (flags & elf.PF_X != 0) { | ||
| 1172 | try writer.writeByte('E'); | ||
| 1173 | } | ||
| 1174 | if (flags & elf.PF_MASKOS != 0) { | ||
| 1175 | try writer.writeAll("OS"); | ||
| 1176 | } | ||
| 1177 | if (flags & elf.PF_MASKPROC != 0) { | ||
| 1178 | try writer.writeAll("PROC"); | ||
| 1179 | } | ||
| 1180 | try writer.writeByte('\n'); | ||
| 1181 | } | ||
| 1182 | } | ||
| 1183 | } | ||
| 994 | 1184 | ||
| 995 | fn getString(strtab: []const u8, off: u32) []const u8 { | 1185 | fn dumpShdrs(ctx: ObjectContext, writer: anytype) !void { |
| 996 | assert(off < strtab.len); | 1186 | if (ctx.shdrs.len == 0) return; |
| 997 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0); | ||
| 998 | } | ||
| 999 | 1187 | ||
| 1000 | fn dumpHeader(ctx: Context, writer: anytype) !void { | 1188 | try writer.writeAll("section headers\n"); |
| 1001 | try writer.writeAll("header\n"); | 1189 | |
| 1002 | try writer.print("type {s}\n", .{@tagName(ctx.hdr.e_type)}); | 1190 | for (ctx.shdrs, 0..) |shdr, shndx| { |
| 1003 | try writer.print("entry {x}\n", .{ctx.hdr.e_entry}); | 1191 | try writer.print("shdr {d}\n", .{shndx}); |
| 1004 | } | 1192 | try writer.print("name {s}\n", .{ctx.getSectionName(shndx)}); |
| 1193 | try writer.print("type {s}\n", .{fmtShType(shdr.sh_type)}); | ||
| 1194 | try writer.print("addr {x}\n", .{shdr.sh_addr}); | ||
| 1195 | try writer.print("offset {x}\n", .{shdr.sh_offset}); | ||
| 1196 | try writer.print("size {x}\n", .{shdr.sh_size}); | ||
| 1197 | try writer.print("addralign {x}\n", .{shdr.sh_addralign}); | ||
| 1198 | // TODO dump formatted sh_flags | ||
| 1199 | } | ||
| 1200 | } | ||
| 1201 | |||
| 1202 | fn dumpDynamicSection(ctx: ObjectContext, writer: anytype) !void { | ||
| 1203 | const shndx = ctx.getSectionByName(".dynamic") orelse return; | ||
| 1204 | const shdr = ctx.shdrs[shndx]; | ||
| 1205 | const strtab = ctx.getSectionContents(shdr.sh_link); | ||
| 1206 | const data = ctx.getSectionContents(shndx); | ||
| 1207 | const nentries = @divExact(data.len, @sizeOf(elf.Elf64_Dyn)); | ||
| 1208 | const entries = @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(data.ptr))[0..nentries]; | ||
| 1209 | |||
| 1210 | try writer.writeAll(ElfDumper.dynamic_section_label ++ "\n"); | ||
| 1211 | |||
| 1212 | for (entries) |entry| { | ||
| 1213 | const key = @as(u64, @bitCast(entry.d_tag)); | ||
| 1214 | const value = entry.d_val; | ||
| 1215 | |||
| 1216 | const key_str = switch (key) { | ||
| 1217 | elf.DT_NEEDED => "NEEDED", | ||
| 1218 | elf.DT_SONAME => "SONAME", | ||
| 1219 | elf.DT_INIT_ARRAY => "INIT_ARRAY", | ||
| 1220 | elf.DT_INIT_ARRAYSZ => "INIT_ARRAYSZ", | ||
| 1221 | elf.DT_FINI_ARRAY => "FINI_ARRAY", | ||
| 1222 | elf.DT_FINI_ARRAYSZ => "FINI_ARRAYSZ", | ||
| 1223 | elf.DT_HASH => "HASH", | ||
| 1224 | elf.DT_GNU_HASH => "GNU_HASH", | ||
| 1225 | elf.DT_STRTAB => "STRTAB", | ||
| 1226 | elf.DT_SYMTAB => "SYMTAB", | ||
| 1227 | elf.DT_STRSZ => "STRSZ", | ||
| 1228 | elf.DT_SYMENT => "SYMENT", | ||
| 1229 | elf.DT_PLTGOT => "PLTGOT", | ||
| 1230 | elf.DT_PLTRELSZ => "PLTRELSZ", | ||
| 1231 | elf.DT_PLTREL => "PLTREL", | ||
| 1232 | elf.DT_JMPREL => "JMPREL", | ||
| 1233 | elf.DT_RELA => "RELA", | ||
| 1234 | elf.DT_RELASZ => "RELASZ", | ||
| 1235 | elf.DT_RELAENT => "RELAENT", | ||
| 1236 | elf.DT_VERDEF => "VERDEF", | ||
| 1237 | elf.DT_VERDEFNUM => "VERDEFNUM", | ||
| 1238 | elf.DT_FLAGS => "FLAGS", | ||
| 1239 | elf.DT_FLAGS_1 => "FLAGS_1", | ||
| 1240 | elf.DT_VERNEED => "VERNEED", | ||
| 1241 | elf.DT_VERNEEDNUM => "VERNEEDNUM", | ||
| 1242 | elf.DT_VERSYM => "VERSYM", | ||
| 1243 | elf.DT_RELACOUNT => "RELACOUNT", | ||
| 1244 | elf.DT_RPATH => "RPATH", | ||
| 1245 | elf.DT_RUNPATH => "RUNPATH", | ||
| 1246 | elf.DT_INIT => "INIT", | ||
| 1247 | elf.DT_FINI => "FINI", | ||
| 1248 | elf.DT_NULL => "NULL", | ||
| 1249 | else => "UNKNOWN", | ||
| 1250 | }; | ||
| 1251 | try writer.print("{s}", .{key_str}); | ||
| 1252 | |||
| 1253 | switch (key) { | ||
| 1254 | elf.DT_NEEDED, | ||
| 1255 | elf.DT_SONAME, | ||
| 1256 | elf.DT_RPATH, | ||
| 1257 | elf.DT_RUNPATH, | ||
| 1258 | => { | ||
| 1259 | const name = getString(strtab, @intCast(value)); | ||
| 1260 | try writer.print(" {s}", .{name}); | ||
| 1261 | }, | ||
| 1005 | 1262 | ||
| 1006 | fn dumpShdrs(ctx: Context, writer: anytype) !void { | 1263 | elf.DT_INIT_ARRAY, |
| 1007 | if (ctx.shdrs.len == 0) return; | 1264 | elf.DT_FINI_ARRAY, |
| 1265 | elf.DT_HASH, | ||
| 1266 | elf.DT_GNU_HASH, | ||
| 1267 | elf.DT_STRTAB, | ||
| 1268 | elf.DT_SYMTAB, | ||
| 1269 | elf.DT_PLTGOT, | ||
| 1270 | elf.DT_JMPREL, | ||
| 1271 | elf.DT_RELA, | ||
| 1272 | elf.DT_VERDEF, | ||
| 1273 | elf.DT_VERNEED, | ||
| 1274 | elf.DT_VERSYM, | ||
| 1275 | elf.DT_INIT, | ||
| 1276 | elf.DT_FINI, | ||
| 1277 | elf.DT_NULL, | ||
| 1278 | => try writer.print(" {x}", .{value}), | ||
| 1279 | |||
| 1280 | elf.DT_INIT_ARRAYSZ, | ||
| 1281 | elf.DT_FINI_ARRAYSZ, | ||
| 1282 | elf.DT_STRSZ, | ||
| 1283 | elf.DT_SYMENT, | ||
| 1284 | elf.DT_PLTRELSZ, | ||
| 1285 | elf.DT_RELASZ, | ||
| 1286 | elf.DT_RELAENT, | ||
| 1287 | elf.DT_RELACOUNT, | ||
| 1288 | => try writer.print(" {d}", .{value}), | ||
| 1289 | |||
| 1290 | elf.DT_PLTREL => try writer.writeAll(switch (value) { | ||
| 1291 | elf.DT_REL => " REL", | ||
| 1292 | elf.DT_RELA => " RELA", | ||
| 1293 | else => " UNKNOWN", | ||
| 1294 | }), | ||
| 1295 | |||
| 1296 | elf.DT_FLAGS => if (value > 0) { | ||
| 1297 | if (value & elf.DF_ORIGIN != 0) try writer.writeAll(" ORIGIN"); | ||
| 1298 | if (value & elf.DF_SYMBOLIC != 0) try writer.writeAll(" SYMBOLIC"); | ||
| 1299 | if (value & elf.DF_TEXTREL != 0) try writer.writeAll(" TEXTREL"); | ||
| 1300 | if (value & elf.DF_BIND_NOW != 0) try writer.writeAll(" BIND_NOW"); | ||
| 1301 | if (value & elf.DF_STATIC_TLS != 0) try writer.writeAll(" STATIC_TLS"); | ||
| 1302 | }, | ||
| 1008 | 1303 | ||
| 1009 | try writer.writeAll("section headers\n"); | 1304 | elf.DT_FLAGS_1 => if (value > 0) { |
| 1305 | if (value & elf.DF_1_NOW != 0) try writer.writeAll(" NOW"); | ||
| 1306 | if (value & elf.DF_1_GLOBAL != 0) try writer.writeAll(" GLOBAL"); | ||
| 1307 | if (value & elf.DF_1_GROUP != 0) try writer.writeAll(" GROUP"); | ||
| 1308 | if (value & elf.DF_1_NODELETE != 0) try writer.writeAll(" NODELETE"); | ||
| 1309 | if (value & elf.DF_1_LOADFLTR != 0) try writer.writeAll(" LOADFLTR"); | ||
| 1310 | if (value & elf.DF_1_INITFIRST != 0) try writer.writeAll(" INITFIRST"); | ||
| 1311 | if (value & elf.DF_1_NOOPEN != 0) try writer.writeAll(" NOOPEN"); | ||
| 1312 | if (value & elf.DF_1_ORIGIN != 0) try writer.writeAll(" ORIGIN"); | ||
| 1313 | if (value & elf.DF_1_DIRECT != 0) try writer.writeAll(" DIRECT"); | ||
| 1314 | if (value & elf.DF_1_TRANS != 0) try writer.writeAll(" TRANS"); | ||
| 1315 | if (value & elf.DF_1_INTERPOSE != 0) try writer.writeAll(" INTERPOSE"); | ||
| 1316 | if (value & elf.DF_1_NODEFLIB != 0) try writer.writeAll(" NODEFLIB"); | ||
| 1317 | if (value & elf.DF_1_NODUMP != 0) try writer.writeAll(" NODUMP"); | ||
| 1318 | if (value & elf.DF_1_CONFALT != 0) try writer.writeAll(" CONFALT"); | ||
| 1319 | if (value & elf.DF_1_ENDFILTEE != 0) try writer.writeAll(" ENDFILTEE"); | ||
| 1320 | if (value & elf.DF_1_DISPRELDNE != 0) try writer.writeAll(" DISPRELDNE"); | ||
| 1321 | if (value & elf.DF_1_DISPRELPND != 0) try writer.writeAll(" DISPRELPND"); | ||
| 1322 | if (value & elf.DF_1_NODIRECT != 0) try writer.writeAll(" NODIRECT"); | ||
| 1323 | if (value & elf.DF_1_IGNMULDEF != 0) try writer.writeAll(" IGNMULDEF"); | ||
| 1324 | if (value & elf.DF_1_NOKSYMS != 0) try writer.writeAll(" NOKSYMS"); | ||
| 1325 | if (value & elf.DF_1_NOHDR != 0) try writer.writeAll(" NOHDR"); | ||
| 1326 | if (value & elf.DF_1_EDITED != 0) try writer.writeAll(" EDITED"); | ||
| 1327 | if (value & elf.DF_1_NORELOC != 0) try writer.writeAll(" NORELOC"); | ||
| 1328 | if (value & elf.DF_1_SYMINTPOSE != 0) try writer.writeAll(" SYMINTPOSE"); | ||
| 1329 | if (value & elf.DF_1_GLOBAUDIT != 0) try writer.writeAll(" GLOBAUDIT"); | ||
| 1330 | if (value & elf.DF_1_SINGLETON != 0) try writer.writeAll(" SINGLETON"); | ||
| 1331 | if (value & elf.DF_1_STUB != 0) try writer.writeAll(" STUB"); | ||
| 1332 | if (value & elf.DF_1_PIE != 0) try writer.writeAll(" PIE"); | ||
| 1333 | }, | ||
| 1010 | 1334 | ||
| 1011 | for (ctx.shdrs, 0..) |shdr, shndx| { | 1335 | else => try writer.print(" {x}", .{value}), |
| 1012 | try writer.print("shdr {d}\n", .{shndx}); | 1336 | } |
| 1013 | try writer.print("name {s}\n", .{getSectionName(ctx, shndx)}); | 1337 | try writer.writeByte('\n'); |
| 1014 | try writer.print("type {s}\n", .{fmtShType(shdr.sh_type)}); | 1338 | } |
| 1015 | try writer.print("addr {x}\n", .{shdr.sh_addr}); | ||
| 1016 | try writer.print("offset {x}\n", .{shdr.sh_offset}); | ||
| 1017 | try writer.print("size {x}\n", .{shdr.sh_size}); | ||
| 1018 | try writer.print("addralign {x}\n", .{shdr.sh_addralign}); | ||
| 1019 | // TODO dump formatted sh_flags | ||
| 1020 | } | 1339 | } |
| 1021 | } | ||
| 1022 | 1340 | ||
| 1023 | fn dumpDynamicSection(ctx: Context, writer: anytype) !void { | 1341 | fn dumpSymtab(ctx: ObjectContext, comptime @"type": enum { symtab, dysymtab }, writer: anytype) !void { |
| 1024 | const shndx = getSectionByName(ctx, ".dynamic") orelse return; | 1342 | const symtab = switch (@"type") { |
| 1025 | const shdr = ctx.shdrs[shndx]; | 1343 | .symtab => ctx.symtab, |
| 1026 | const strtab = getSectionContents(ctx, shdr.sh_link); | 1344 | .dysymtab => ctx.dysymtab, |
| 1027 | const data = getSectionContents(ctx, shndx); | 1345 | } orelse return; |
| 1028 | const nentries = @divExact(data.len, @sizeOf(elf.Elf64_Dyn)); | 1346 | |
| 1029 | const entries = @as([*]align(1) const elf.Elf64_Dyn, @ptrCast(data.ptr))[0..nentries]; | 1347 | try writer.writeAll(switch (@"type") { |
| 1030 | 1348 | .symtab => symtab_label, | |
| 1031 | try writer.writeAll(ElfDumper.dynamic_section_label ++ "\n"); | 1349 | .dysymtab => dynamic_symtab_label, |
| 1032 | 1350 | } ++ "\n"); | |
| 1033 | for (entries) |entry| { | 1351 | |
| 1034 | const key = @as(u64, @bitCast(entry.d_tag)); | 1352 | for (symtab.symbols, 0..) |sym, index| { |
| 1035 | const value = entry.d_val; | 1353 | try writer.print("{x} {x}", .{ sym.st_value, sym.st_size }); |
| 1036 | 1354 | ||
| 1037 | const key_str = switch (key) { | 1355 | { |
| 1038 | elf.DT_NEEDED => "NEEDED", | 1356 | if (elf.SHN_LORESERVE <= sym.st_shndx and sym.st_shndx < elf.SHN_HIRESERVE) { |
| 1039 | elf.DT_SONAME => "SONAME", | 1357 | if (elf.SHN_LOPROC <= sym.st_shndx and sym.st_shndx < elf.SHN_HIPROC) { |
| 1040 | elf.DT_INIT_ARRAY => "INIT_ARRAY", | 1358 | try writer.print(" LO+{d}", .{sym.st_shndx - elf.SHN_LOPROC}); |
| 1041 | elf.DT_INIT_ARRAYSZ => "INIT_ARRAYSZ", | 1359 | } else { |
| 1042 | elf.DT_FINI_ARRAY => "FINI_ARRAY", | 1360 | const sym_ndx = switch (sym.st_shndx) { |
| 1043 | elf.DT_FINI_ARRAYSZ => "FINI_ARRAYSZ", | 1361 | elf.SHN_ABS => "ABS", |
| 1044 | elf.DT_HASH => "HASH", | 1362 | elf.SHN_COMMON => "COM", |
| 1045 | elf.DT_GNU_HASH => "GNU_HASH", | 1363 | elf.SHN_LIVEPATCH => "LIV", |
| 1046 | elf.DT_STRTAB => "STRTAB", | 1364 | else => "UNK", |
| 1047 | elf.DT_SYMTAB => "SYMTAB", | 1365 | }; |
| 1048 | elf.DT_STRSZ => "STRSZ", | 1366 | try writer.print(" {s}", .{sym_ndx}); |
| 1049 | elf.DT_SYMENT => "SYMENT", | 1367 | } |
| 1050 | elf.DT_PLTGOT => "PLTGOT", | 1368 | } else if (sym.st_shndx == elf.SHN_UNDEF) { |
| 1051 | elf.DT_PLTRELSZ => "PLTRELSZ", | 1369 | try writer.writeAll(" UND"); |
| 1052 | elf.DT_PLTREL => "PLTREL", | 1370 | } else { |
| 1053 | elf.DT_JMPREL => "JMPREL", | 1371 | try writer.print(" {x}", .{sym.st_shndx}); |
| 1054 | elf.DT_RELA => "RELA", | 1372 | } |
| 1055 | elf.DT_RELASZ => "RELASZ", | 1373 | } |
| 1056 | elf.DT_RELAENT => "RELAENT", | ||
| 1057 | elf.DT_VERDEF => "VERDEF", | ||
| 1058 | elf.DT_VERDEFNUM => "VERDEFNUM", | ||
| 1059 | elf.DT_FLAGS => "FLAGS", | ||
| 1060 | elf.DT_FLAGS_1 => "FLAGS_1", | ||
| 1061 | elf.DT_VERNEED => "VERNEED", | ||
| 1062 | elf.DT_VERNEEDNUM => "VERNEEDNUM", | ||
| 1063 | elf.DT_VERSYM => "VERSYM", | ||
| 1064 | elf.DT_RELACOUNT => "RELACOUNT", | ||
| 1065 | elf.DT_RPATH => "RPATH", | ||
| 1066 | elf.DT_RUNPATH => "RUNPATH", | ||
| 1067 | elf.DT_INIT => "INIT", | ||
| 1068 | elf.DT_FINI => "FINI", | ||
| 1069 | elf.DT_NULL => "NULL", | ||
| 1070 | else => "UNKNOWN", | ||
| 1071 | }; | ||
| 1072 | try writer.print("{s}", .{key_str}); | ||
| 1073 | 1374 | ||
| 1074 | switch (key) { | 1375 | blk: { |
| 1075 | elf.DT_NEEDED, | 1376 | const tt = sym.st_type(); |
| 1076 | elf.DT_SONAME, | 1377 | const sym_type = switch (tt) { |
| 1077 | elf.DT_RPATH, | 1378 | elf.STT_NOTYPE => "NOTYPE", |
| 1078 | elf.DT_RUNPATH, | 1379 | elf.STT_OBJECT => "OBJECT", |
| 1079 | => { | 1380 | elf.STT_FUNC => "FUNC", |
| 1080 | const name = getString(strtab, @intCast(value)); | 1381 | elf.STT_SECTION => "SECTION", |
| 1081 | try writer.print(" {s}", .{name}); | 1382 | elf.STT_FILE => "FILE", |
| 1082 | }, | 1383 | elf.STT_COMMON => "COMMON", |
| 1384 | elf.STT_TLS => "TLS", | ||
| 1385 | elf.STT_NUM => "NUM", | ||
| 1386 | elf.STT_GNU_IFUNC => "IFUNC", | ||
| 1387 | else => if (elf.STT_LOPROC <= tt and tt < elf.STT_HIPROC) { | ||
| 1388 | break :blk try writer.print(" LOPROC+{d}", .{tt - elf.STT_LOPROC}); | ||
| 1389 | } else if (elf.STT_LOOS <= tt and tt < elf.STT_HIOS) { | ||
| 1390 | break :blk try writer.print(" LOOS+{d}", .{tt - elf.STT_LOOS}); | ||
| 1391 | } else "UNK", | ||
| 1392 | }; | ||
| 1393 | try writer.print(" {s}", .{sym_type}); | ||
| 1394 | } | ||
| 1083 | 1395 | ||
| 1084 | elf.DT_INIT_ARRAY, | 1396 | blk: { |
| 1085 | elf.DT_FINI_ARRAY, | 1397 | const bind = sym.st_bind(); |
| 1086 | elf.DT_HASH, | 1398 | const sym_bind = switch (bind) { |
| 1087 | elf.DT_GNU_HASH, | 1399 | elf.STB_LOCAL => "LOCAL", |
| 1088 | elf.DT_STRTAB, | 1400 | elf.STB_GLOBAL => "GLOBAL", |
| 1089 | elf.DT_SYMTAB, | 1401 | elf.STB_WEAK => "WEAK", |
| 1090 | elf.DT_PLTGOT, | 1402 | elf.STB_NUM => "NUM", |
| 1091 | elf.DT_JMPREL, | 1403 | else => if (elf.STB_LOPROC <= bind and bind < elf.STB_HIPROC) { |
| 1092 | elf.DT_RELA, | 1404 | break :blk try writer.print(" LOPROC+{d}", .{bind - elf.STB_LOPROC}); |
| 1093 | elf.DT_VERDEF, | 1405 | } else if (elf.STB_LOOS <= bind and bind < elf.STB_HIOS) { |
| 1094 | elf.DT_VERNEED, | 1406 | break :blk try writer.print(" LOOS+{d}", .{bind - elf.STB_LOOS}); |
| 1095 | elf.DT_VERSYM, | 1407 | } else "UNKNOWN", |
| 1096 | elf.DT_INIT, | 1408 | }; |
| 1097 | elf.DT_FINI, | 1409 | try writer.print(" {s}", .{sym_bind}); |
| 1098 | elf.DT_NULL, | 1410 | } |
| 1099 | => try writer.print(" {x}", .{value}), | ||
| 1100 | |||
| 1101 | elf.DT_INIT_ARRAYSZ, | ||
| 1102 | elf.DT_FINI_ARRAYSZ, | ||
| 1103 | elf.DT_STRSZ, | ||
| 1104 | elf.DT_SYMENT, | ||
| 1105 | elf.DT_PLTRELSZ, | ||
| 1106 | elf.DT_RELASZ, | ||
| 1107 | elf.DT_RELAENT, | ||
| 1108 | elf.DT_RELACOUNT, | ||
| 1109 | => try writer.print(" {d}", .{value}), | ||
| 1110 | |||
| 1111 | elf.DT_PLTREL => try writer.writeAll(switch (value) { | ||
| 1112 | elf.DT_REL => " REL", | ||
| 1113 | elf.DT_RELA => " RELA", | ||
| 1114 | else => " UNKNOWN", | ||
| 1115 | }), | ||
| 1116 | |||
| 1117 | elf.DT_FLAGS => if (value > 0) { | ||
| 1118 | if (value & elf.DF_ORIGIN != 0) try writer.writeAll(" ORIGIN"); | ||
| 1119 | if (value & elf.DF_SYMBOLIC != 0) try writer.writeAll(" SYMBOLIC"); | ||
| 1120 | if (value & elf.DF_TEXTREL != 0) try writer.writeAll(" TEXTREL"); | ||
| 1121 | if (value & elf.DF_BIND_NOW != 0) try writer.writeAll(" BIND_NOW"); | ||
| 1122 | if (value & elf.DF_STATIC_TLS != 0) try writer.writeAll(" STATIC_TLS"); | ||
| 1123 | }, | ||
| 1124 | 1411 | ||
| 1125 | elf.DT_FLAGS_1 => if (value > 0) { | 1412 | const sym_vis = @as(elf.STV, @enumFromInt(sym.st_other)); |
| 1126 | if (value & elf.DF_1_NOW != 0) try writer.writeAll(" NOW"); | 1413 | try writer.print(" {s}", .{@tagName(sym_vis)}); |
| 1127 | if (value & elf.DF_1_GLOBAL != 0) try writer.writeAll(" GLOBAL"); | ||
| 1128 | if (value & elf.DF_1_GROUP != 0) try writer.writeAll(" GROUP"); | ||
| 1129 | if (value & elf.DF_1_NODELETE != 0) try writer.writeAll(" NODELETE"); | ||
| 1130 | if (value & elf.DF_1_LOADFLTR != 0) try writer.writeAll(" LOADFLTR"); | ||
| 1131 | if (value & elf.DF_1_INITFIRST != 0) try writer.writeAll(" INITFIRST"); | ||
| 1132 | if (value & elf.DF_1_NOOPEN != 0) try writer.writeAll(" NOOPEN"); | ||
| 1133 | if (value & elf.DF_1_ORIGIN != 0) try writer.writeAll(" ORIGIN"); | ||
| 1134 | if (value & elf.DF_1_DIRECT != 0) try writer.writeAll(" DIRECT"); | ||
| 1135 | if (value & elf.DF_1_TRANS != 0) try writer.writeAll(" TRANS"); | ||
| 1136 | if (value & elf.DF_1_INTERPOSE != 0) try writer.writeAll(" INTERPOSE"); | ||
| 1137 | if (value & elf.DF_1_NODEFLIB != 0) try writer.writeAll(" NODEFLIB"); | ||
| 1138 | if (value & elf.DF_1_NODUMP != 0) try writer.writeAll(" NODUMP"); | ||
| 1139 | if (value & elf.DF_1_CONFALT != 0) try writer.writeAll(" CONFALT"); | ||
| 1140 | if (value & elf.DF_1_ENDFILTEE != 0) try writer.writeAll(" ENDFILTEE"); | ||
| 1141 | if (value & elf.DF_1_DISPRELDNE != 0) try writer.writeAll(" DISPRELDNE"); | ||
| 1142 | if (value & elf.DF_1_DISPRELPND != 0) try writer.writeAll(" DISPRELPND"); | ||
| 1143 | if (value & elf.DF_1_NODIRECT != 0) try writer.writeAll(" NODIRECT"); | ||
| 1144 | if (value & elf.DF_1_IGNMULDEF != 0) try writer.writeAll(" IGNMULDEF"); | ||
| 1145 | if (value & elf.DF_1_NOKSYMS != 0) try writer.writeAll(" NOKSYMS"); | ||
| 1146 | if (value & elf.DF_1_NOHDR != 0) try writer.writeAll(" NOHDR"); | ||
| 1147 | if (value & elf.DF_1_EDITED != 0) try writer.writeAll(" EDITED"); | ||
| 1148 | if (value & elf.DF_1_NORELOC != 0) try writer.writeAll(" NORELOC"); | ||
| 1149 | if (value & elf.DF_1_SYMINTPOSE != 0) try writer.writeAll(" SYMINTPOSE"); | ||
| 1150 | if (value & elf.DF_1_GLOBAUDIT != 0) try writer.writeAll(" GLOBAUDIT"); | ||
| 1151 | if (value & elf.DF_1_SINGLETON != 0) try writer.writeAll(" SINGLETON"); | ||
| 1152 | if (value & elf.DF_1_STUB != 0) try writer.writeAll(" STUB"); | ||
| 1153 | if (value & elf.DF_1_PIE != 0) try writer.writeAll(" PIE"); | ||
| 1154 | }, | ||
| 1155 | 1414 | ||
| 1156 | else => try writer.print(" {x}", .{value}), | 1415 | const sym_name = switch (sym.st_type()) { |
| 1416 | elf.STT_SECTION => ctx.getSectionName(sym.st_shndx), | ||
| 1417 | else => symtab.getName(index).?, | ||
| 1418 | }; | ||
| 1419 | try writer.print(" {s}\n", .{sym_name}); | ||
| 1157 | } | 1420 | } |
| 1158 | try writer.writeByte('\n'); | ||
| 1159 | } | 1421 | } |
| 1422 | |||
| 1423 | inline fn getSectionName(ctx: ObjectContext, shndx: usize) []const u8 { | ||
| 1424 | const shdr = ctx.shdrs[shndx]; | ||
| 1425 | return getString(ctx.shstrtab, shdr.sh_name); | ||
| 1426 | } | ||
| 1427 | |||
| 1428 | fn getSectionContents(ctx: ObjectContext, shndx: usize) []const u8 { | ||
| 1429 | const shdr = ctx.shdrs[shndx]; | ||
| 1430 | assert(shdr.sh_offset < ctx.data.len); | ||
| 1431 | assert(shdr.sh_offset + shdr.sh_size <= ctx.data.len); | ||
| 1432 | return ctx.data[shdr.sh_offset..][0..shdr.sh_size]; | ||
| 1433 | } | ||
| 1434 | |||
| 1435 | fn getSectionByName(ctx: ObjectContext, name: []const u8) ?usize { | ||
| 1436 | for (0..ctx.shdrs.len) |shndx| { | ||
| 1437 | if (mem.eql(u8, ctx.getSectionName(shndx), name)) return shndx; | ||
| 1438 | } else return null; | ||
| 1439 | } | ||
| 1440 | }; | ||
| 1441 | |||
| 1442 | const Symtab = struct { | ||
| 1443 | symbols: []align(1) const elf.Elf64_Sym, | ||
| 1444 | strings: []const u8, | ||
| 1445 | |||
| 1446 | fn get(st: Symtab, index: usize) ?elf.Elf64_Sym { | ||
| 1447 | if (index >= st.symbols.len) return null; | ||
| 1448 | return st.symbols[index]; | ||
| 1449 | } | ||
| 1450 | |||
| 1451 | fn getName(st: Symtab, index: usize) ?[]const u8 { | ||
| 1452 | const sym = st.get(index) orelse return null; | ||
| 1453 | return getString(st.strings, sym.st_name); | ||
| 1454 | } | ||
| 1455 | }; | ||
| 1456 | |||
| 1457 | fn getString(strtab: []const u8, off: u32) []const u8 { | ||
| 1458 | assert(off < strtab.len); | ||
| 1459 | return mem.sliceTo(@as([*:0]const u8, @ptrCast(strtab.ptr + off)), 0); | ||
| 1160 | } | 1460 | } |
| 1161 | 1461 | ||
| 1162 | fn fmtShType(sh_type: u32) std.fmt.Formatter(formatShType) { | 1462 | fn fmtShType(sh_type: u32) std.fmt.Formatter(formatShType) { |
| ... | @@ -1206,45 +1506,6 @@ const ElfDumper = struct { | ... | @@ -1206,45 +1506,6 @@ const ElfDumper = struct { |
| 1206 | try writer.writeAll(name); | 1506 | try writer.writeAll(name); |
| 1207 | } | 1507 | } |
| 1208 | 1508 | ||
| 1209 | fn dumpPhdrs(ctx: Context, writer: anytype) !void { | ||
| 1210 | if (ctx.phdrs.len == 0) return; | ||
| 1211 | |||
| 1212 | try writer.writeAll("program headers\n"); | ||
| 1213 | |||
| 1214 | for (ctx.phdrs, 0..) |phdr, phndx| { | ||
| 1215 | try writer.print("phdr {d}\n", .{phndx}); | ||
| 1216 | try writer.print("type {s}\n", .{fmtPhType(phdr.p_type)}); | ||
| 1217 | try writer.print("vaddr {x}\n", .{phdr.p_vaddr}); | ||
| 1218 | try writer.print("paddr {x}\n", .{phdr.p_paddr}); | ||
| 1219 | try writer.print("offset {x}\n", .{phdr.p_offset}); | ||
| 1220 | try writer.print("memsz {x}\n", .{phdr.p_memsz}); | ||
| 1221 | try writer.print("filesz {x}\n", .{phdr.p_filesz}); | ||
| 1222 | try writer.print("align {x}\n", .{phdr.p_align}); | ||
| 1223 | |||
| 1224 | { | ||
| 1225 | const flags = phdr.p_flags; | ||
| 1226 | try writer.writeAll("flags"); | ||
| 1227 | if (flags > 0) try writer.writeByte(' '); | ||
| 1228 | if (flags & elf.PF_R != 0) { | ||
| 1229 | try writer.writeByte('R'); | ||
| 1230 | } | ||
| 1231 | if (flags & elf.PF_W != 0) { | ||
| 1232 | try writer.writeByte('W'); | ||
| 1233 | } | ||
| 1234 | if (flags & elf.PF_X != 0) { | ||
| 1235 | try writer.writeByte('E'); | ||
| 1236 | } | ||
| 1237 | if (flags & elf.PF_MASKOS != 0) { | ||
| 1238 | try writer.writeAll("OS"); | ||
| 1239 | } | ||
| 1240 | if (flags & elf.PF_MASKPROC != 0) { | ||
| 1241 | try writer.writeAll("PROC"); | ||
| 1242 | } | ||
| 1243 | try writer.writeByte('\n'); | ||
| 1244 | } | ||
| 1245 | } | ||
| 1246 | } | ||
| 1247 | |||
| 1248 | fn fmtPhType(ph_type: u32) std.fmt.Formatter(formatPhType) { | 1509 | fn fmtPhType(ph_type: u32) std.fmt.Formatter(formatPhType) { |
| 1249 | return .{ .data = ph_type }; | 1510 | return .{ .data = ph_type }; |
| 1250 | } | 1511 | } |
| ... | @@ -1278,88 +1539,6 @@ const ElfDumper = struct { | ... | @@ -1278,88 +1539,6 @@ const ElfDumper = struct { |
| 1278 | }; | 1539 | }; |
| 1279 | try writer.writeAll(p_type); | 1540 | try writer.writeAll(p_type); |
| 1280 | } | 1541 | } |
| 1281 | |||
| 1282 | fn dumpSymtab(ctx: Context, comptime @"type": enum { symtab, dysymtab }, writer: anytype) !void { | ||
| 1283 | const symtab = switch (@"type") { | ||
| 1284 | .symtab => ctx.symtab, | ||
| 1285 | .dysymtab => ctx.dysymtab, | ||
| 1286 | } orelse return; | ||
| 1287 | |||
| 1288 | try writer.writeAll(switch (@"type") { | ||
| 1289 | .symtab => symtab_label, | ||
| 1290 | .dysymtab => dynamic_symtab_label, | ||
| 1291 | } ++ "\n"); | ||
| 1292 | |||
| 1293 | for (symtab.symbols, 0..) |sym, index| { | ||
| 1294 | try writer.print("{x} {x}", .{ sym.st_value, sym.st_size }); | ||
| 1295 | |||
| 1296 | { | ||
| 1297 | if (elf.SHN_LORESERVE <= sym.st_shndx and sym.st_shndx < elf.SHN_HIRESERVE) { | ||
| 1298 | if (elf.SHN_LOPROC <= sym.st_shndx and sym.st_shndx < elf.SHN_HIPROC) { | ||
| 1299 | try writer.print(" LO+{d}", .{sym.st_shndx - elf.SHN_LOPROC}); | ||
| 1300 | } else { | ||
| 1301 | const sym_ndx = &switch (sym.st_shndx) { | ||
| 1302 | elf.SHN_ABS => "ABS", | ||
| 1303 | elf.SHN_COMMON => "COM", | ||
| 1304 | elf.SHN_LIVEPATCH => "LIV", | ||
| 1305 | else => "UNK", | ||
| 1306 | }; | ||
| 1307 | try writer.print(" {s}", .{sym_ndx}); | ||
| 1308 | } | ||
| 1309 | } else if (sym.st_shndx == elf.SHN_UNDEF) { | ||
| 1310 | try writer.writeAll(" UND"); | ||
| 1311 | } else { | ||
| 1312 | try writer.print(" {x}", .{sym.st_shndx}); | ||
| 1313 | } | ||
| 1314 | } | ||
| 1315 | |||
| 1316 | blk: { | ||
| 1317 | const tt = sym.st_type(); | ||
| 1318 | const sym_type = switch (tt) { | ||
| 1319 | elf.STT_NOTYPE => "NOTYPE", | ||
| 1320 | elf.STT_OBJECT => "OBJECT", | ||
| 1321 | elf.STT_FUNC => "FUNC", | ||
| 1322 | elf.STT_SECTION => "SECTION", | ||
| 1323 | elf.STT_FILE => "FILE", | ||
| 1324 | elf.STT_COMMON => "COMMON", | ||
| 1325 | elf.STT_TLS => "TLS", | ||
| 1326 | elf.STT_NUM => "NUM", | ||
| 1327 | elf.STT_GNU_IFUNC => "IFUNC", | ||
| 1328 | else => if (elf.STT_LOPROC <= tt and tt < elf.STT_HIPROC) { | ||
| 1329 | break :blk try writer.print(" LOPROC+{d}", .{tt - elf.STT_LOPROC}); | ||
| 1330 | } else if (elf.STT_LOOS <= tt and tt < elf.STT_HIOS) { | ||
| 1331 | break :blk try writer.print(" LOOS+{d}", .{tt - elf.STT_LOOS}); | ||
| 1332 | } else "UNK", | ||
| 1333 | }; | ||
| 1334 | try writer.print(" {s}", .{sym_type}); | ||
| 1335 | } | ||
| 1336 | |||
| 1337 | blk: { | ||
| 1338 | const bind = sym.st_bind(); | ||
| 1339 | const sym_bind = switch (bind) { | ||
| 1340 | elf.STB_LOCAL => "LOCAL", | ||
| 1341 | elf.STB_GLOBAL => "GLOBAL", | ||
| 1342 | elf.STB_WEAK => "WEAK", | ||
| 1343 | elf.STB_NUM => "NUM", | ||
| 1344 | else => if (elf.STB_LOPROC <= bind and bind < elf.STB_HIPROC) { | ||
| 1345 | break :blk try writer.print(" LOPROC+{d}", .{bind - elf.STB_LOPROC}); | ||
| 1346 | } else if (elf.STB_LOOS <= bind and bind < elf.STB_HIOS) { | ||
| 1347 | break :blk try writer.print(" LOOS+{d}", .{bind - elf.STB_LOOS}); | ||
| 1348 | } else "UNKNOWN", | ||
| 1349 | }; | ||
| 1350 | try writer.print(" {s}", .{sym_bind}); | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | const sym_vis = @as(elf.STV, @enumFromInt(sym.st_other)); | ||
| 1354 | try writer.print(" {s}", .{@tagName(sym_vis)}); | ||
| 1355 | |||
| 1356 | const sym_name = switch (sym.st_type()) { | ||
| 1357 | elf.STT_SECTION => getSectionName(ctx, sym.st_shndx), | ||
| 1358 | else => symtab.getName(index).?, | ||
| 1359 | }; | ||
| 1360 | try writer.print(" {s}\n", .{sym_name}); | ||
| 1361 | } | ||
| 1362 | } | ||
| 1363 | }; | 1542 | }; |
| 1364 | 1543 | ||
| 1365 | const WasmDumper = struct { | 1544 | const WasmDumper = struct { |
lib/std/elf.zig+89| ... | @@ -1896,3 +1896,92 @@ pub const STV = enum(u2) { | ... | @@ -1896,3 +1896,92 @@ pub const STV = enum(u2) { |
| 1896 | HIDDEN = 2, | 1896 | HIDDEN = 2, |
| 1897 | PROTECTED = 3, | 1897 | PROTECTED = 3, |
| 1898 | }; | 1898 | }; |
| 1899 | |||
| 1900 | pub const ar_hdr = extern struct { | ||
| 1901 | /// Member file name, sometimes / terminated. | ||
| 1902 | ar_name: [16]u8, | ||
| 1903 | |||
| 1904 | /// File date, decimal seconds since Epoch. | ||
| 1905 | ar_date: [12]u8, | ||
| 1906 | |||
| 1907 | /// User ID, in ASCII format. | ||
| 1908 | ar_uid: [6]u8, | ||
| 1909 | |||
| 1910 | /// Group ID, in ASCII format. | ||
| 1911 | ar_gid: [6]u8, | ||
| 1912 | |||
| 1913 | /// File mode, in ASCII octal. | ||
| 1914 | ar_mode: [8]u8, | ||
| 1915 | |||
| 1916 | /// File size, in ASCII decimal. | ||
| 1917 | ar_size: [10]u8, | ||
| 1918 | |||
| 1919 | /// Always contains ARFMAG. | ||
| 1920 | ar_fmag: [2]u8, | ||
| 1921 | |||
| 1922 | pub fn date(self: ar_hdr) std.fmt.ParseIntError!u64 { | ||
| 1923 | const value = mem.trimRight(u8, &self.ar_date, &[_]u8{0x20}); | ||
| 1924 | return std.fmt.parseInt(u64, value, 10); | ||
| 1925 | } | ||
| 1926 | |||
| 1927 | pub fn size(self: ar_hdr) std.fmt.ParseIntError!u32 { | ||
| 1928 | const value = mem.trimRight(u8, &self.ar_size, &[_]u8{0x20}); | ||
| 1929 | return std.fmt.parseInt(u32, value, 10); | ||
| 1930 | } | ||
| 1931 | |||
| 1932 | pub fn isStrtab(self: ar_hdr) bool { | ||
| 1933 | return mem.eql(u8, &self.ar_name, STRNAME); | ||
| 1934 | } | ||
| 1935 | |||
| 1936 | pub fn isSymtab(self: ar_hdr) bool { | ||
| 1937 | return mem.eql(u8, &self.ar_name, SYMNAME); | ||
| 1938 | } | ||
| 1939 | |||
| 1940 | pub fn isSymtab64(self: ar_hdr) bool { | ||
| 1941 | return mem.eql(u8, &self.ar_name, SYM64NAME); | ||
| 1942 | } | ||
| 1943 | |||
| 1944 | pub fn isSymdef(self: ar_hdr) bool { | ||
| 1945 | return mem.eql(u8, &self.ar_name, SYMDEFNAME); | ||
| 1946 | } | ||
| 1947 | |||
| 1948 | pub fn isSymdefSorted(self: ar_hdr) bool { | ||
| 1949 | return mem.eql(u8, &self.ar_name, SYMDEFSORTEDNAME); | ||
| 1950 | } | ||
| 1951 | |||
| 1952 | pub fn name(self: *const ar_hdr) ?[]const u8 { | ||
| 1953 | const value = &self.ar_name; | ||
| 1954 | if (value[0] == '/') return null; | ||
| 1955 | const sentinel = mem.indexOfScalar(u8, value, '/') orelse value.len; | ||
| 1956 | return value[0..sentinel]; | ||
| 1957 | } | ||
| 1958 | |||
| 1959 | pub fn nameOffset(self: ar_hdr) std.fmt.ParseIntError!?u32 { | ||
| 1960 | const value = &self.ar_name; | ||
| 1961 | if (value[0] != '/') return null; | ||
| 1962 | const trimmed = mem.trimRight(u8, value, &[_]u8{0x20}); | ||
| 1963 | return try std.fmt.parseInt(u32, trimmed[1..], 10); | ||
| 1964 | } | ||
| 1965 | }; | ||
| 1966 | |||
| 1967 | fn genSpecialMemberName(comptime name: []const u8) *const [16]u8 { | ||
| 1968 | assert(name.len <= 16); | ||
| 1969 | const padding = 16 - name.len; | ||
| 1970 | return name ++ &[_]u8{0x20} ** padding; | ||
| 1971 | } | ||
| 1972 | |||
| 1973 | // Archive files start with the ARMAG identifying string. Then follows a | ||
| 1974 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' | ||
| 1975 | // member indicates, for each member file. | ||
| 1976 | /// String that begins an archive file. | ||
| 1977 | pub const ARMAG = "!<arch>\n"; | ||
| 1978 | /// String in ar_fmag at the end of each header. | ||
| 1979 | pub const ARFMAG = "`\n"; | ||
| 1980 | /// 32-bit symtab identifier | ||
| 1981 | pub const SYMNAME = genSpecialMemberName("/"); | ||
| 1982 | /// Strtab identifier | ||
| 1983 | pub const STRNAME = genSpecialMemberName("//"); | ||
| 1984 | /// 64-bit symtab identifier | ||
| 1985 | pub const SYM64NAME = genSpecialMemberName("/SYM64/"); | ||
| 1986 | pub const SYMDEFNAME = genSpecialMemberName("__.SYMDEF"); | ||
| 1987 | pub const SYMDEFSORTEDNAME = genSpecialMemberName("__.SYMDEF SORTED"); |
src/Compilation.zig+8-2| ... | @@ -2304,10 +2304,16 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void | ... | @@ -2304,10 +2304,16 @@ pub fn update(comp: *Compilation, main_progress_node: *std.Progress.Node) !void |
| 2304 | defer comp.gpa.free(o_sub_path); | 2304 | defer comp.gpa.free(o_sub_path); |
| 2305 | 2305 | ||
| 2306 | // Work around windows `AccessDenied` if any files within this directory are open | 2306 | // Work around windows `AccessDenied` if any files within this directory are open |
| 2307 | // by doing the makeExecutable/makeWritable dance. | 2307 | // by closing and reopening the file handles. |
| 2308 | const need_writable_dance = builtin.os.tag == .windows and comp.bin_file.file != null; | 2308 | const need_writable_dance = builtin.os.tag == .windows and comp.bin_file.file != null; |
| 2309 | if (need_writable_dance) { | 2309 | if (need_writable_dance) { |
| 2310 | try comp.bin_file.makeExecutable(); | 2310 | // We cannot just call `makeExecutable` as it makes a false assumption that we have a |
| 2311 | // file handle open only when linking an executable file. This used to be true when | ||
| 2312 | // our linkers were incapable of emitting relocatables and static archive. Now that | ||
| 2313 | // they are capable, we need to unconditionally close the file handle and re-open it | ||
| 2314 | // in the follow up call to `makeWritable`. | ||
| 2315 | comp.bin_file.file.?.close(); | ||
| 2316 | comp.bin_file.file = null; | ||
| 2311 | } | 2317 | } |
| 2312 | 2318 | ||
| 2313 | try comp.bin_file.renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path); | 2319 | try comp.bin_file.renameTmpIntoCache(comp.local_cache_directory, tmp_dir_sub_path, o_sub_path); |
src/link/Elf.zig+116-42| ... | @@ -288,7 +288,9 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option | ... | @@ -288,7 +288,9 @@ pub fn openPath(allocator: Allocator, sub_path: []const u8, options: link.Option |
| 288 | const index = @as(File.Index, @intCast(try self.files.addOne(allocator))); | 288 | const index = @as(File.Index, @intCast(try self.files.addOne(allocator))); |
| 289 | self.files.set(index, .{ .zig_object = .{ | 289 | self.files.set(index, .{ .zig_object = .{ |
| 290 | .index = index, | 290 | .index = index, |
| 291 | .path = options.module.?.main_mod.root_src_path, | 291 | .path = try std.fmt.allocPrint(self.base.allocator, "{s}.o", .{std.fs.path.stem( |
| 292 | options.module.?.main_mod.root_src_path, | ||
| 293 | )}), | ||
| 292 | } }); | 294 | } }); |
| 293 | self.zig_object_index = index; | 295 | self.zig_object_index = index; |
| 294 | try self.zigObjectPtr().?.init(self); | 296 | try self.zigObjectPtr().?.init(self); |
| ... | @@ -940,14 +942,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -940,14 +942,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 940 | } else null; | 942 | } else null; |
| 941 | const gc_sections = self.base.options.gc_sections orelse false; | 943 | const gc_sections = self.base.options.gc_sections orelse false; |
| 942 | 944 | ||
| 943 | if (self.isRelocatable() and self.zig_object_index == null) { | 945 | if (self.isObject() and self.zig_object_index == null) { |
| 944 | if (self.isStaticLib()) { | ||
| 945 | var err = try self.addErrorWithNotes(0); | ||
| 946 | try err.addMsg(self, "fatal linker error: emitting static libs unimplemented", .{}); | ||
| 947 | return; | ||
| 948 | } | ||
| 949 | // TODO this will become -r route I guess. For now, just copy the object file. | 946 | // TODO this will become -r route I guess. For now, just copy the object file. |
| 950 | assert(self.base.file == null); // TODO uncomment once we implement -r | ||
| 951 | const the_object_path = blk: { | 947 | const the_object_path = blk: { |
| 952 | if (self.base.options.objects.len != 0) { | 948 | if (self.base.options.objects.len != 0) { |
| 953 | break :blk self.base.options.objects[0].path; | 949 | break :blk self.base.options.objects[0].path; |
| ... | @@ -1287,8 +1283,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1287,8 +1283,6 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1287 | try positionals.append(.{ .path = ssp.full_object_path }); | 1283 | try positionals.append(.{ .path = ssp.full_object_path }); |
| 1288 | } | 1284 | } |
| 1289 | 1285 | ||
| 1290 | if (self.isStaticLib()) return self.flushStaticLib(comp, positionals.items); | ||
| 1291 | |||
| 1292 | for (positionals.items) |obj| { | 1286 | for (positionals.items) |obj| { |
| 1293 | var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; | 1287 | var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined }; |
| 1294 | self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| | 1288 | self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err| |
| ... | @@ -1394,6 +1388,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1394,6 +1388,16 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1394 | try self.handleAndReportParseError(obj.path, err, &parse_ctx); | 1388 | try self.handleAndReportParseError(obj.path, err, &parse_ctx); |
| 1395 | } | 1389 | } |
| 1396 | 1390 | ||
| 1391 | if (self.isStaticLib()) return self.flushStaticLib(comp); | ||
| 1392 | |||
| 1393 | // Init all objects | ||
| 1394 | for (self.objects.items) |index| { | ||
| 1395 | try self.file(index).?.object.init(self); | ||
| 1396 | } | ||
| 1397 | for (self.shared_objects.items) |index| { | ||
| 1398 | try self.file(index).?.shared_object.init(self); | ||
| 1399 | } | ||
| 1400 | |||
| 1397 | // Dedup shared objects | 1401 | // Dedup shared objects |
| 1398 | { | 1402 | { |
| 1399 | var seen_dsos = std.StringHashMap(void).init(gpa); | 1403 | var seen_dsos = std.StringHashMap(void).init(gpa); |
| ... | @@ -1523,18 +1527,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node | ... | @@ -1523,18 +1527,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node |
| 1523 | } | 1527 | } |
| 1524 | } | 1528 | } |
| 1525 | 1529 | ||
| 1526 | pub fn flushStaticLib( | 1530 | pub fn flushStaticLib(self: *Elf, comp: *Compilation) link.File.FlushError!void { |
| 1527 | self: *Elf, | ||
| 1528 | comp: *Compilation, | ||
| 1529 | positionals: []const Compilation.LinkObject, | ||
| 1530 | ) link.File.FlushError!void { | ||
| 1531 | _ = comp; | 1531 | _ = comp; |
| 1532 | if (positionals.len > 0) { | ||
| 1533 | var err = try self.addErrorWithNotes(1); | ||
| 1534 | try err.addMsg(self, "fatal linker error: too many input positionals", .{}); | ||
| 1535 | try err.addNote(self, "TODO implement linking objects into an static library", .{}); | ||
| 1536 | return; | ||
| 1537 | } | ||
| 1538 | const gpa = self.base.allocator; | 1532 | const gpa = self.base.allocator; |
| 1539 | 1533 | ||
| 1540 | // First, we flush relocatable object file generated with our backends. | 1534 | // First, we flush relocatable object file generated with our backends. |
| ... | @@ -1546,27 +1540,33 @@ pub fn flushStaticLib( | ... | @@ -1546,27 +1540,33 @@ pub fn flushStaticLib( |
| 1546 | try self.initShStrtab(); | 1540 | try self.initShStrtab(); |
| 1547 | try self.sortShdrs(); | 1541 | try self.sortShdrs(); |
| 1548 | zig_object.updateRelaSectionSizes(self); | 1542 | zig_object.updateRelaSectionSizes(self); |
| 1549 | try self.updateSymtabSize(); | 1543 | self.updateSymtabSizeObject(zig_object); |
| 1550 | self.updateShStrtabSize(); | 1544 | self.updateShStrtabSize(); |
| 1551 | 1545 | ||
| 1552 | try self.allocateNonAllocSections(); | 1546 | try self.allocateNonAllocSections(); |
| 1553 | 1547 | ||
| 1554 | try self.writeShdrTable(); | 1548 | try self.writeShdrTable(); |
| 1555 | try zig_object.writeRelaSections(self); | 1549 | try zig_object.writeRelaSections(self); |
| 1556 | try self.writeSymtab(); | 1550 | try self.writeSymtabObject(zig_object); |
| 1557 | try self.writeShStrtab(); | 1551 | try self.writeShStrtab(); |
| 1558 | try self.writeElfHeader(); | 1552 | try self.writeElfHeader(); |
| 1559 | } | 1553 | } |
| 1560 | 1554 | ||
| 1561 | // TODO parse positionals that we want to make part of the archive | 1555 | var files = std.ArrayList(File.Index).init(gpa); |
| 1562 | 1556 | defer files.deinit(); | |
| 1563 | // TODO update ar symtab from parsed positionals | 1557 | try files.ensureTotalCapacityPrecise(self.objects.items.len + 1); |
| 1558 | // Note to self: we currently must have ZigObject written out first as we write the object | ||
| 1559 | // file into the same file descriptor and then re-read its contents. | ||
| 1560 | // TODO implement writing ZigObject to a buffer instead of file. | ||
| 1561 | if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index); | ||
| 1562 | for (self.objects.items) |index| files.appendAssumeCapacity(index); | ||
| 1564 | 1563 | ||
| 1564 | // Update ar symtab from parsed objects | ||
| 1565 | var ar_symtab: Archive.ArSymtab = .{}; | 1565 | var ar_symtab: Archive.ArSymtab = .{}; |
| 1566 | defer ar_symtab.deinit(gpa); | 1566 | defer ar_symtab.deinit(gpa); |
| 1567 | 1567 | ||
| 1568 | if (self.zigObjectPtr()) |zig_object| { | 1568 | for (files.items) |index| { |
| 1569 | try zig_object.updateArSymtab(&ar_symtab, self); | 1569 | try self.file(index).?.updateArSymtab(&ar_symtab, self); |
| 1570 | } | 1570 | } |
| 1571 | 1571 | ||
| 1572 | ar_symtab.sort(); | 1572 | ar_symtab.sort(); |
| ... | @@ -1575,25 +1575,32 @@ pub fn flushStaticLib( | ... | @@ -1575,25 +1575,32 @@ pub fn flushStaticLib( |
| 1575 | var ar_strtab: Archive.ArStrtab = .{}; | 1575 | var ar_strtab: Archive.ArStrtab = .{}; |
| 1576 | defer ar_strtab.deinit(gpa); | 1576 | defer ar_strtab.deinit(gpa); |
| 1577 | 1577 | ||
| 1578 | if (self.zigObjectPtr()) |zig_object| { | 1578 | for (files.items) |index| { |
| 1579 | try zig_object.updateArStrtab(gpa, &ar_strtab); | 1579 | const file_ptr = self.file(index).?; |
| 1580 | zig_object.updateArSize(self); | 1580 | try file_ptr.updateArStrtab(gpa, &ar_strtab); |
| 1581 | file_ptr.updateArSize(self); | ||
| 1581 | } | 1582 | } |
| 1582 | 1583 | ||
| 1583 | // Update file offsets of contributing objects. | 1584 | // Update file offsets of contributing objects. |
| 1584 | const total_size: usize = blk: { | 1585 | const total_size: usize = blk: { |
| 1585 | var pos: usize = Archive.SARMAG; | 1586 | var pos: usize = elf.ARMAG.len; |
| 1586 | pos += @sizeOf(Archive.ar_hdr) + ar_symtab.size(.p64); | 1587 | pos += @sizeOf(elf.ar_hdr) + ar_symtab.size(.p64); |
| 1587 | 1588 | ||
| 1588 | if (ar_strtab.size() > 0) { | 1589 | if (ar_strtab.size() > 0) { |
| 1589 | pos = mem.alignForward(usize, pos, 2); | 1590 | pos = mem.alignForward(usize, pos, 2); |
| 1590 | pos += @sizeOf(Archive.ar_hdr) + ar_strtab.size(); | 1591 | pos += @sizeOf(elf.ar_hdr) + ar_strtab.size(); |
| 1591 | } | 1592 | } |
| 1592 | 1593 | ||
| 1593 | if (self.zigObjectPtr()) |zig_object| { | 1594 | for (files.items) |index| { |
| 1595 | const file_ptr = self.file(index).?; | ||
| 1596 | const state = switch (file_ptr) { | ||
| 1597 | .zig_object => |x| &x.output_ar_state, | ||
| 1598 | .object => |x| &x.output_ar_state, | ||
| 1599 | else => unreachable, | ||
| 1600 | }; | ||
| 1594 | pos = mem.alignForward(usize, pos, 2); | 1601 | pos = mem.alignForward(usize, pos, 2); |
| 1595 | zig_object.output_ar_state.file_off = pos; | 1602 | state.file_off = pos; |
| 1596 | pos += @sizeOf(Archive.ar_hdr) + (math.cast(usize, zig_object.output_ar_state.size) orelse return error.Overflow); | 1603 | pos += @sizeOf(elf.ar_hdr) + (math.cast(usize, state.size) orelse return error.Overflow); |
| 1597 | } | 1604 | } |
| 1598 | 1605 | ||
| 1599 | break :blk pos; | 1606 | break :blk pos; |
| ... | @@ -1609,7 +1616,7 @@ pub fn flushStaticLib( | ... | @@ -1609,7 +1616,7 @@ pub fn flushStaticLib( |
| 1609 | try buffer.ensureTotalCapacityPrecise(total_size); | 1616 | try buffer.ensureTotalCapacityPrecise(total_size); |
| 1610 | 1617 | ||
| 1611 | // Write magic | 1618 | // Write magic |
| 1612 | try buffer.writer().writeAll(Archive.ARMAG); | 1619 | try buffer.writer().writeAll(elf.ARMAG); |
| 1613 | 1620 | ||
| 1614 | // Write symtab | 1621 | // Write symtab |
| 1615 | try ar_symtab.write(.p64, self, buffer.writer()); | 1622 | try ar_symtab.write(.p64, self, buffer.writer()); |
| ... | @@ -1621,9 +1628,9 @@ pub fn flushStaticLib( | ... | @@ -1621,9 +1628,9 @@ pub fn flushStaticLib( |
| 1621 | } | 1628 | } |
| 1622 | 1629 | ||
| 1623 | // Write object files | 1630 | // Write object files |
| 1624 | if (self.zigObjectPtr()) |zig_object| { | 1631 | for (files.items) |index| { |
| 1625 | if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); | 1632 | if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0); |
| 1626 | try zig_object.writeAr(self, buffer.writer()); | 1633 | try self.file(index).?.writeAr(self, buffer.writer()); |
| 1627 | } | 1634 | } |
| 1628 | 1635 | ||
| 1629 | assert(buffer.items.len == total_size); | 1636 | assert(buffer.items.len == total_size); |
| ... | @@ -4054,7 +4061,7 @@ fn updateSectionSizes(self: *Elf) !void { | ... | @@ -4054,7 +4061,7 @@ fn updateSectionSizes(self: *Elf) !void { |
| 4054 | self.shdrs.items[index].sh_size = self.verneed.size(); | 4061 | self.shdrs.items[index].sh_size = self.verneed.size(); |
| 4055 | } | 4062 | } |
| 4056 | 4063 | ||
| 4057 | try self.updateSymtabSize(); | 4064 | self.updateSymtabSize(); |
| 4058 | self.updateShStrtabSize(); | 4065 | self.updateShStrtabSize(); |
| 4059 | } | 4066 | } |
| 4060 | 4067 | ||
| ... | @@ -4477,7 +4484,7 @@ fn writeAtoms(self: *Elf) !void { | ... | @@ -4477,7 +4484,7 @@ fn writeAtoms(self: *Elf) !void { |
| 4477 | try self.reportUndefined(&undefs); | 4484 | try self.reportUndefined(&undefs); |
| 4478 | } | 4485 | } |
| 4479 | 4486 | ||
| 4480 | fn updateSymtabSize(self: *Elf) !void { | 4487 | fn updateSymtabSize(self: *Elf) void { |
| 4481 | var sizes = SymtabSize{}; | 4488 | var sizes = SymtabSize{}; |
| 4482 | 4489 | ||
| 4483 | if (self.zigObjectPtr()) |zig_object| { | 4490 | if (self.zigObjectPtr()) |zig_object| { |
| ... | @@ -4538,6 +4545,25 @@ fn updateSymtabSize(self: *Elf) !void { | ... | @@ -4538,6 +4545,25 @@ fn updateSymtabSize(self: *Elf) !void { |
| 4538 | strtab.sh_size = sizes.strsize + 1; | 4545 | strtab.sh_size = sizes.strsize + 1; |
| 4539 | } | 4546 | } |
| 4540 | 4547 | ||
| 4548 | fn updateSymtabSizeObject(self: *Elf, zig_object: *ZigObject) void { | ||
| 4549 | zig_object.asFile().updateSymtabSize(self); | ||
| 4550 | const sizes = zig_object.output_symtab_size; | ||
| 4551 | |||
| 4552 | const symtab_shdr = &self.shdrs.items[self.symtab_section_index.?]; | ||
| 4553 | symtab_shdr.sh_info = sizes.nlocals + 1; | ||
| 4554 | symtab_shdr.sh_link = self.strtab_section_index.?; | ||
| 4555 | |||
| 4556 | const sym_size: u64 = switch (self.ptr_width) { | ||
| 4557 | .p32 => @sizeOf(elf.Elf32_Sym), | ||
| 4558 | .p64 => @sizeOf(elf.Elf64_Sym), | ||
| 4559 | }; | ||
| 4560 | const needed_size = (sizes.nlocals + sizes.nglobals + 1) * sym_size; | ||
| 4561 | symtab_shdr.sh_size = needed_size; | ||
| 4562 | |||
| 4563 | const strtab = &self.shdrs.items[self.strtab_section_index.?]; | ||
| 4564 | strtab.sh_size = sizes.strsize + 1; | ||
| 4565 | } | ||
| 4566 | |||
| 4541 | fn writeSyntheticSections(self: *Elf) !void { | 4567 | fn writeSyntheticSections(self: *Elf) !void { |
| 4542 | const gpa = self.base.allocator; | 4568 | const gpa = self.base.allocator; |
| 4543 | 4569 | ||
| ... | @@ -4782,6 +4808,54 @@ fn writeSymtab(self: *Elf) !void { | ... | @@ -4782,6 +4808,54 @@ fn writeSymtab(self: *Elf) !void { |
| 4782 | try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); | 4808 | try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); |
| 4783 | } | 4809 | } |
| 4784 | 4810 | ||
| 4811 | fn writeSymtabObject(self: *Elf, zig_object: *ZigObject) !void { | ||
| 4812 | const gpa = self.base.allocator; | ||
| 4813 | const symtab_shdr = self.shdrs.items[self.symtab_section_index.?]; | ||
| 4814 | const strtab_shdr = self.shdrs.items[self.strtab_section_index.?]; | ||
| 4815 | const sym_size: u64 = switch (self.ptr_width) { | ||
| 4816 | .p32 => @sizeOf(elf.Elf32_Sym), | ||
| 4817 | .p64 => @sizeOf(elf.Elf64_Sym), | ||
| 4818 | }; | ||
| 4819 | const nsyms = math.cast(usize, @divExact(symtab_shdr.sh_size, sym_size)) orelse return error.Overflow; | ||
| 4820 | |||
| 4821 | log.debug("writing {d} symbols at 0x{x}", .{ nsyms, symtab_shdr.sh_offset }); | ||
| 4822 | |||
| 4823 | try self.symtab.resize(gpa, nsyms); | ||
| 4824 | const needed_strtab_size = math.cast(usize, strtab_shdr.sh_size - 1) orelse return error.Overflow; | ||
| 4825 | try self.strtab.ensureUnusedCapacity(gpa, needed_strtab_size); | ||
| 4826 | |||
| 4827 | zig_object.asFile().writeSymtab(self, .{ .ilocal = 1, .iglobal = symtab_shdr.sh_info }); | ||
| 4828 | |||
| 4829 | const foreign_endian = self.base.options.target.cpu.arch.endian() != builtin.cpu.arch.endian(); | ||
| 4830 | switch (self.ptr_width) { | ||
| 4831 | .p32 => { | ||
| 4832 | const buf = try gpa.alloc(elf.Elf32_Sym, self.symtab.items.len); | ||
| 4833 | defer gpa.free(buf); | ||
| 4834 | |||
| 4835 | for (buf, self.symtab.items) |*out, sym| { | ||
| 4836 | out.* = .{ | ||
| 4837 | .st_name = sym.st_name, | ||
| 4838 | .st_info = sym.st_info, | ||
| 4839 | .st_other = sym.st_other, | ||
| 4840 | .st_shndx = sym.st_shndx, | ||
| 4841 | .st_value = @as(u32, @intCast(sym.st_value)), | ||
| 4842 | .st_size = @as(u32, @intCast(sym.st_size)), | ||
| 4843 | }; | ||
| 4844 | if (foreign_endian) mem.byteSwapAllFields(elf.Elf32_Sym, out); | ||
| 4845 | } | ||
| 4846 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(buf), symtab_shdr.sh_offset); | ||
| 4847 | }, | ||
| 4848 | .p64 => { | ||
| 4849 | if (foreign_endian) { | ||
| 4850 | for (self.symtab.items) |*sym| mem.byteSwapAllFields(elf.Elf64_Sym, sym); | ||
| 4851 | } | ||
| 4852 | try self.base.file.?.pwriteAll(mem.sliceAsBytes(self.symtab.items), symtab_shdr.sh_offset); | ||
| 4853 | }, | ||
| 4854 | } | ||
| 4855 | |||
| 4856 | try self.base.file.?.pwriteAll(self.strtab.items, strtab_shdr.sh_offset); | ||
| 4857 | } | ||
| 4858 | |||
| 4785 | /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. | 4859 | /// Always 4 or 8 depending on whether this is 32-bit ELF or 64-bit ELF. |
| 4786 | fn ptrWidthBytes(self: Elf) u8 { | 4860 | fn ptrWidthBytes(self: Elf) u8 { |
| 4787 | return switch (self.ptr_width) { | 4861 | return switch (self.ptr_width) { |
src/link/Elf/Archive.zig+27-94| ... | @@ -8,8 +8,8 @@ pub fn isArchive(path: []const u8) !bool { | ... | @@ -8,8 +8,8 @@ pub fn isArchive(path: []const u8) !bool { |
| 8 | const file = try std.fs.cwd().openFile(path, .{}); | 8 | const file = try std.fs.cwd().openFile(path, .{}); |
| 9 | defer file.close(); | 9 | defer file.close(); |
| 10 | const reader = file.reader(); | 10 | const reader = file.reader(); |
| 11 | const magic = reader.readBytesNoEof(SARMAG) catch return false; | 11 | const magic = reader.readBytesNoEof(elf.ARMAG.len) catch return false; |
| 12 | if (!mem.eql(u8, &magic, ARMAG)) return false; | 12 | if (!mem.eql(u8, &magic, elf.ARMAG)) return false; |
| 13 | return true; | 13 | return true; |
| 14 | } | 14 | } |
| 15 | 15 | ||
| ... | @@ -24,21 +24,19 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { | ... | @@ -24,21 +24,19 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { |
| 24 | 24 | ||
| 25 | var stream = std.io.fixedBufferStream(self.data); | 25 | var stream = std.io.fixedBufferStream(self.data); |
| 26 | const reader = stream.reader(); | 26 | const reader = stream.reader(); |
| 27 | _ = try reader.readBytesNoEof(SARMAG); | 27 | _ = try reader.readBytesNoEof(elf.ARMAG.len); |
| 28 | 28 | ||
| 29 | while (true) { | 29 | while (true) { |
| 30 | if (stream.pos >= self.data.len) break; | 30 | if (stream.pos >= self.data.len) break; |
| 31 | if (!mem.isAligned(stream.pos, 2)) stream.pos += 1; | ||
| 31 | 32 | ||
| 32 | if (stream.pos % 2 != 0) { | 33 | const hdr = try reader.readStruct(elf.ar_hdr); |
| 33 | stream.pos += 1; | ||
| 34 | } | ||
| 35 | const hdr = try reader.readStruct(ar_hdr); | ||
| 36 | 34 | ||
| 37 | if (!mem.eql(u8, &hdr.ar_fmag, ARFMAG)) { | 35 | if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) { |
| 38 | // TODO convert into an error | 36 | // TODO convert into an error |
| 39 | log.debug( | 37 | log.debug( |
| 40 | "{s}: invalid header delimiter: expected '{s}', found '{s}'", | 38 | "{s}: invalid header delimiter: expected '{s}', found '{s}'", |
| 41 | .{ self.path, std.fmt.fmtSliceEscapeLower(ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) }, | 39 | .{ self.path, std.fmt.fmtSliceEscapeLower(elf.ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) }, |
| 42 | ); | 40 | ); |
| 43 | return; | 41 | return; |
| 44 | } | 42 | } |
| ... | @@ -48,28 +46,23 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { | ... | @@ -48,28 +46,23 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { |
| 48 | _ = stream.seekBy(size) catch {}; | 46 | _ = stream.seekBy(size) catch {}; |
| 49 | } | 47 | } |
| 50 | 48 | ||
| 51 | if (hdr.isSymtab()) continue; | 49 | if (hdr.isSymtab() or hdr.isSymtab64()) continue; |
| 52 | if (hdr.isStrtab()) { | 50 | if (hdr.isStrtab()) { |
| 53 | self.strtab = self.data[stream.pos..][0..size]; | 51 | self.strtab = self.data[stream.pos..][0..size]; |
| 54 | continue; | 52 | continue; |
| 55 | } | 53 | } |
| 54 | if (hdr.isSymdef() or hdr.isSymdefSorted()) continue; | ||
| 56 | 55 | ||
| 57 | const name = ar_hdr.getValue(&hdr.ar_name); | 56 | const name = if (hdr.name()) |name| |
| 58 | 57 | try gpa.dupe(u8, name) | |
| 59 | if (mem.eql(u8, name, "__.SYMDEF") or mem.eql(u8, name, "__.SYMDEF SORTED")) continue; | 58 | else if (try hdr.nameOffset()) |off| |
| 60 | 59 | try gpa.dupe(u8, self.getString(off)) | |
| 61 | const object_name = blk: { | 60 | else |
| 62 | if (name[0] == '/') { | 61 | unreachable; |
| 63 | const off = try std.fmt.parseInt(u32, name[1..], 10); | ||
| 64 | const object_name = self.getString(off); | ||
| 65 | break :blk try gpa.dupe(u8, object_name[0 .. object_name.len - 1]); // To account for trailing '/' | ||
| 66 | } | ||
| 67 | break :blk try gpa.dupe(u8, name); | ||
| 68 | }; | ||
| 69 | 62 | ||
| 70 | const object = Object{ | 63 | const object = Object{ |
| 71 | .archive = try gpa.dupe(u8, self.path), | 64 | .archive = try gpa.dupe(u8, self.path), |
| 72 | .path = object_name, | 65 | .path = name, |
| 73 | .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]), | 66 | .data = try gpa.dupe(u8, self.data[stream.pos..][0..size]), |
| 74 | .index = undefined, | 67 | .index = undefined, |
| 75 | .alive = false, | 68 | .alive = false, |
| ... | @@ -83,7 +76,8 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { | ... | @@ -83,7 +76,8 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void { |
| 83 | 76 | ||
| 84 | fn getString(self: Archive, off: u32) []const u8 { | 77 | fn getString(self: Archive, off: u32) []const u8 { |
| 85 | assert(off < self.strtab.len); | 78 | assert(off < self.strtab.len); |
| 86 | return mem.sliceTo(@as([*:strtab_delimiter]const u8, @ptrCast(self.strtab.ptr + off)), 0); | 79 | const name = mem.sliceTo(@as([*:'\n']const u8, @ptrCast(self.strtab.ptr + off)), 0); |
| 80 | return name[0 .. name.len - 1]; | ||
| 87 | } | 81 | } |
| 88 | 82 | ||
| 89 | pub fn setArHdr(opts: struct { | 83 | pub fn setArHdr(opts: struct { |
| ... | @@ -94,8 +88,8 @@ pub fn setArHdr(opts: struct { | ... | @@ -94,8 +88,8 @@ pub fn setArHdr(opts: struct { |
| 94 | name_off: u32, | 88 | name_off: u32, |
| 95 | }, | 89 | }, |
| 96 | size: u32, | 90 | size: u32, |
| 97 | }) ar_hdr { | 91 | }) elf.ar_hdr { |
| 98 | var hdr: ar_hdr = .{ | 92 | var hdr: elf.ar_hdr = .{ |
| 99 | .ar_name = undefined, | 93 | .ar_name = undefined, |
| 100 | .ar_date = undefined, | 94 | .ar_date = undefined, |
| 101 | .ar_uid = undefined, | 95 | .ar_uid = undefined, |
| ... | @@ -105,15 +99,15 @@ pub fn setArHdr(opts: struct { | ... | @@ -105,15 +99,15 @@ pub fn setArHdr(opts: struct { |
| 105 | .ar_fmag = undefined, | 99 | .ar_fmag = undefined, |
| 106 | }; | 100 | }; |
| 107 | @memset(mem.asBytes(&hdr), 0x20); | 101 | @memset(mem.asBytes(&hdr), 0x20); |
| 108 | @memcpy(&hdr.ar_fmag, Archive.ARFMAG); | 102 | @memcpy(&hdr.ar_fmag, elf.ARFMAG); |
| 109 | 103 | ||
| 110 | { | 104 | { |
| 111 | var stream = std.io.fixedBufferStream(&hdr.ar_name); | 105 | var stream = std.io.fixedBufferStream(&hdr.ar_name); |
| 112 | const writer = stream.writer(); | 106 | const writer = stream.writer(); |
| 113 | switch (opts.name) { | 107 | switch (opts.name) { |
| 114 | .symtab => writer.print("{s}", .{Archive.SYM64NAME}) catch unreachable, | 108 | .symtab => writer.print("{s}", .{elf.SYM64NAME}) catch unreachable, |
| 115 | .strtab => writer.print("//", .{}) catch unreachable, | 109 | .strtab => writer.print("//", .{}) catch unreachable, |
| 116 | .name => |x| writer.print("{s}", .{x}) catch unreachable, | 110 | .name => |x| writer.print("{s}/", .{x}) catch unreachable, |
| 117 | .name_off => |x| writer.print("/{d}", .{x}) catch unreachable, | 111 | .name_off => |x| writer.print("/{d}", .{x}) catch unreachable, |
| 118 | } | 112 | } |
| 119 | } | 113 | } |
| ... | @@ -125,72 +119,8 @@ pub fn setArHdr(opts: struct { | ... | @@ -125,72 +119,8 @@ pub fn setArHdr(opts: struct { |
| 125 | return hdr; | 119 | return hdr; |
| 126 | } | 120 | } |
| 127 | 121 | ||
| 128 | // Archive files start with the ARMAG identifying string. Then follows a | ||
| 129 | // `struct ar_hdr', and as many bytes of member file data as its `ar_size' | ||
| 130 | // member indicates, for each member file. | ||
| 131 | /// String that begins an archive file. | ||
| 132 | pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n"; | ||
| 133 | /// Size of that string. | ||
| 134 | pub const SARMAG = 8; | ||
| 135 | |||
| 136 | /// String in ar_fmag at the end of each header. | ||
| 137 | const ARFMAG: *const [2:0]u8 = "`\n"; | ||
| 138 | |||
| 139 | /// Strtab identifier | ||
| 140 | const STRNAME: *const [2:0]u8 = "//"; | ||
| 141 | |||
| 142 | /// 32-bit symtab identifier | ||
| 143 | const SYMNAME: *const [1:0]u8 = "/"; | ||
| 144 | |||
| 145 | /// 64-bit symtab identifier | ||
| 146 | const SYM64NAME: *const [7:0]u8 = "/SYM64/"; | ||
| 147 | |||
| 148 | const strtab_delimiter = '\n'; | 122 | const strtab_delimiter = '\n'; |
| 149 | 123 | pub const max_member_name_len = 15; | |
| 150 | pub const ar_hdr = extern struct { | ||
| 151 | /// Member file name, sometimes / terminated. | ||
| 152 | ar_name: [16]u8, | ||
| 153 | |||
| 154 | /// File date, decimal seconds since Epoch. | ||
| 155 | ar_date: [12]u8, | ||
| 156 | |||
| 157 | /// User ID, in ASCII format. | ||
| 158 | ar_uid: [6]u8, | ||
| 159 | |||
| 160 | /// Group ID, in ASCII format. | ||
| 161 | ar_gid: [6]u8, | ||
| 162 | |||
| 163 | /// File mode, in ASCII octal. | ||
| 164 | ar_mode: [8]u8, | ||
| 165 | |||
| 166 | /// File size, in ASCII decimal. | ||
| 167 | ar_size: [10]u8, | ||
| 168 | |||
| 169 | /// Always contains ARFMAG. | ||
| 170 | ar_fmag: [2]u8, | ||
| 171 | |||
| 172 | fn date(self: ar_hdr) !u64 { | ||
| 173 | const value = getValue(&self.ar_date); | ||
| 174 | return std.fmt.parseInt(u64, value, 10); | ||
| 175 | } | ||
| 176 | |||
| 177 | fn size(self: ar_hdr) !u32 { | ||
| 178 | const value = getValue(&self.ar_size); | ||
| 179 | return std.fmt.parseInt(u32, value, 10); | ||
| 180 | } | ||
| 181 | |||
| 182 | fn getValue(raw: []const u8) []const u8 { | ||
| 183 | return mem.trimRight(u8, raw, &[_]u8{@as(u8, 0x20)}); | ||
| 184 | } | ||
| 185 | |||
| 186 | fn isStrtab(self: ar_hdr) bool { | ||
| 187 | return mem.eql(u8, getValue(&self.ar_name), STRNAME); | ||
| 188 | } | ||
| 189 | |||
| 190 | fn isSymtab(self: ar_hdr) bool { | ||
| 191 | return mem.eql(u8, getValue(&self.ar_name), SYMNAME) or mem.eql(u8, getValue(&self.ar_name), SYM64NAME); | ||
| 192 | } | ||
| 193 | }; | ||
| 194 | 124 | ||
| 195 | pub const ArSymtab = struct { | 125 | pub const ArSymtab = struct { |
| 196 | symtab: std.ArrayListUnmanaged(Entry) = .{}, | 126 | symtab: std.ArrayListUnmanaged(Entry) = .{}, |
| ... | @@ -230,6 +160,9 @@ pub const ArSymtab = struct { | ... | @@ -230,6 +160,9 @@ pub const ArSymtab = struct { |
| 230 | if (elf_file.zigObjectPtr()) |zig_object| { | 160 | if (elf_file.zigObjectPtr()) |zig_object| { |
| 231 | offsets.putAssumeCapacityNoClobber(zig_object.index, zig_object.output_ar_state.file_off); | 161 | offsets.putAssumeCapacityNoClobber(zig_object.index, zig_object.output_ar_state.file_off); |
| 232 | } | 162 | } |
| 163 | for (elf_file.objects.items) |index| { | ||
| 164 | offsets.putAssumeCapacityNoClobber(index, elf_file.file(index).?.object.output_ar_state.file_off); | ||
| 165 | } | ||
| 233 | 166 | ||
| 234 | // Number of symbols | 167 | // Number of symbols |
| 235 | try writer.writeInt(u64, @as(u64, @intCast(ar.symtab.items.len)), .big); | 168 | try writer.writeInt(u64, @as(u64, @intCast(ar.symtab.items.len)), .big); |
src/link/Elf/Object.zig+34| ... | @@ -20,6 +20,7 @@ alive: bool = true, | ... | @@ -20,6 +20,7 @@ alive: bool = true, |
| 20 | num_dynrelocs: u32 = 0, | 20 | num_dynrelocs: u32 = 0, |
| 21 | 21 | ||
| 22 | output_symtab_size: Elf.SymtabSize = .{}, | 22 | output_symtab_size: Elf.SymtabSize = .{}, |
| 23 | output_ar_state: Archive.ArState = .{}, | ||
| 23 | 24 | ||
| 24 | pub fn isObject(path: []const u8) !bool { | 25 | pub fn isObject(path: []const u8) !bool { |
| 25 | const file = try std.fs.cwd().openFile(path, .{}); | 26 | const file = try std.fs.cwd().openFile(path, .{}); |
| ... | @@ -96,7 +97,9 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { | ... | @@ -96,7 +97,9 @@ pub fn parse(self: *Object, elf_file: *Elf) !void { |
| 96 | sym.st_name + strtab_bias; | 97 | sym.st_name + strtab_bias; |
| 97 | } | 98 | } |
| 98 | } | 99 | } |
| 100 | } | ||
| 99 | 101 | ||
| 102 | pub fn init(self: *Object, elf_file: *Elf) !void { | ||
| 100 | try self.initAtoms(elf_file); | 103 | try self.initAtoms(elf_file); |
| 101 | try self.initSymtab(elf_file); | 104 | try self.initSymtab(elf_file); |
| 102 | 105 | ||
| ... | @@ -651,6 +654,36 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { | ... | @@ -651,6 +654,36 @@ pub fn allocateAtoms(self: Object, elf_file: *Elf) void { |
| 651 | } | 654 | } |
| 652 | } | 655 | } |
| 653 | 656 | ||
| 657 | pub fn updateArSymtab(self: Object, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { | ||
| 658 | const gpa = elf_file.base.allocator; | ||
| 659 | const start = self.first_global orelse self.symtab.items.len; | ||
| 660 | |||
| 661 | try ar_symtab.symtab.ensureUnusedCapacity(gpa, self.symtab.items.len - start); | ||
| 662 | |||
| 663 | for (self.symtab.items[start..]) |sym| { | ||
| 664 | if (sym.st_shndx == elf.SHN_UNDEF) continue; | ||
| 665 | const off = try ar_symtab.strtab.insert(gpa, self.getString(sym.st_name)); | ||
| 666 | ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); | ||
| 667 | } | ||
| 668 | } | ||
| 669 | |||
| 670 | pub fn updateArSize(self: *Object) void { | ||
| 671 | self.output_ar_state.size = self.data.len; | ||
| 672 | } | ||
| 673 | |||
| 674 | pub fn writeAr(self: Object, writer: anytype) !void { | ||
| 675 | const name = self.path; | ||
| 676 | const hdr = Archive.setArHdr(.{ | ||
| 677 | .name = if (name.len <= Archive.max_member_name_len) | ||
| 678 | .{ .name = name } | ||
| 679 | else | ||
| 680 | .{ .name_off = self.output_ar_state.name_off }, | ||
| 681 | .size = @intCast(self.data.len), | ||
| 682 | }); | ||
| 683 | try writer.writeAll(mem.asBytes(&hdr)); | ||
| 684 | try writer.writeAll(self.data); | ||
| 685 | } | ||
| 686 | |||
| 654 | pub fn locals(self: Object) []const Symbol.Index { | 687 | pub fn locals(self: Object) []const Symbol.Index { |
| 655 | const end = self.first_global orelse self.symbols.items.len; | 688 | const end = self.first_global orelse self.symbols.items.len; |
| 656 | return self.symbols.items[0..end]; | 689 | return self.symbols.items[0..end]; |
| ... | @@ -922,6 +955,7 @@ const math = std.math; | ... | @@ -922,6 +955,7 @@ const math = std.math; |
| 922 | const mem = std.mem; | 955 | const mem = std.mem; |
| 923 | 956 | ||
| 924 | const Allocator = mem.Allocator; | 957 | const Allocator = mem.Allocator; |
| 958 | const Archive = @import("Archive.zig"); | ||
| 925 | const Atom = @import("Atom.zig"); | 959 | const Atom = @import("Atom.zig"); |
| 926 | const Cie = eh_frame.Cie; | 960 | const Cie = eh_frame.Cie; |
| 927 | const Elf = @import("../Elf.zig"); | 961 | const Elf = @import("../Elf.zig"); |
src/link/Elf/SharedObject.zig+1-2| ... | @@ -72,7 +72,6 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void { | ... | @@ -72,7 +72,6 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void { |
| 72 | } | 72 | } |
| 73 | 73 | ||
| 74 | try self.parseVersions(elf_file); | 74 | try self.parseVersions(elf_file); |
| 75 | try self.initSymtab(elf_file); | ||
| 76 | } | 75 | } |
| 77 | 76 | ||
| 78 | fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { | 77 | fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { |
| ... | @@ -120,7 +119,7 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { | ... | @@ -120,7 +119,7 @@ fn parseVersions(self: *SharedObject, elf_file: *Elf) !void { |
| 120 | } | 119 | } |
| 121 | } | 120 | } |
| 122 | 121 | ||
| 123 | fn initSymtab(self: *SharedObject, elf_file: *Elf) !void { | 122 | pub fn init(self: *SharedObject, elf_file: *Elf) !void { |
| 124 | const gpa = elf_file.base.allocator; | 123 | const gpa = elf_file.base.allocator; |
| 125 | const symtab = self.getSymtabRaw(); | 124 | const symtab = self.getSymtabRaw(); |
| 126 | const strtab = self.getStrtabRaw(); | 125 | const strtab = self.getStrtabRaw(); |
src/link/Elf/ZigObject.zig+8-19| ... | @@ -3,7 +3,6 @@ | ... | @@ -3,7 +3,6 @@ |
| 3 | //! and any relocations that may have been emitted. | 3 | //! and any relocations that may have been emitted. |
| 4 | //! Think about this as fake in-memory Object file for the Zig module. | 4 | //! Think about this as fake in-memory Object file for the Zig module. |
| 5 | 5 | ||
| 6 | /// Path is owned by Module and lives as long as *Module. | ||
| 7 | path: []const u8, | 6 | path: []const u8, |
| 8 | index: File.Index, | 7 | index: File.Index, |
| 9 | 8 | ||
| ... | @@ -78,7 +77,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { | ... | @@ -78,7 +77,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { |
| 78 | try self.atoms.append(gpa, 0); // null input section | 77 | try self.atoms.append(gpa, 0); // null input section |
| 79 | try self.strtab.buffer.append(gpa, 0); | 78 | try self.strtab.buffer.append(gpa, 0); |
| 80 | 79 | ||
| 81 | const name_off = try self.strtab.insert(gpa, std.fs.path.stem(self.path)); | 80 | const name_off = try self.strtab.insert(gpa, self.path); |
| 82 | const symbol_index = try elf_file.addSymbol(); | 81 | const symbol_index = try elf_file.addSymbol(); |
| 83 | try self.local_symbols.append(gpa, symbol_index); | 82 | try self.local_symbols.append(gpa, symbol_index); |
| 84 | const symbol_ptr = elf_file.symbol(symbol_index); | 83 | const symbol_ptr = elf_file.symbol(symbol_index); |
| ... | @@ -98,6 +97,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { | ... | @@ -98,6 +97,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void { |
| 98 | } | 97 | } |
| 99 | 98 | ||
| 100 | pub fn deinit(self: *ZigObject, allocator: Allocator) void { | 99 | pub fn deinit(self: *ZigObject, allocator: Allocator) void { |
| 100 | allocator.free(self.path); | ||
| 101 | self.local_esyms.deinit(allocator); | 101 | self.local_esyms.deinit(allocator); |
| 102 | self.global_esyms.deinit(allocator); | 102 | self.global_esyms.deinit(allocator); |
| 103 | self.strtab.deinit(allocator); | 103 | self.strtab.deinit(allocator); |
| ... | @@ -512,25 +512,13 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: * | ... | @@ -512,25 +512,13 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: * |
| 512 | const global = elf_file.symbol(global_index); | 512 | const global = elf_file.symbol(global_index); |
| 513 | const file_ptr = global.file(elf_file).?; | 513 | const file_ptr = global.file(elf_file).?; |
| 514 | assert(file_ptr.index() == self.index); | 514 | assert(file_ptr.index() == self.index); |
| 515 | if (global.type(elf_file) == elf.SHN_UNDEF) continue; | 515 | if (global.outputShndx() == null) continue; |
| 516 | 516 | ||
| 517 | const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file)); | 517 | const off = try ar_symtab.strtab.insert(gpa, global.name(elf_file)); |
| 518 | ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); | 518 | ar_symtab.symtab.appendAssumeCapacity(.{ .off = off, .file_index = self.index }); |
| 519 | } | 519 | } |
| 520 | } | 520 | } |
| 521 | 521 | ||
| 522 | pub fn updateArStrtab( | ||
| 523 | self: *ZigObject, | ||
| 524 | allocator: Allocator, | ||
| 525 | ar_strtab: *Archive.ArStrtab, | ||
| 526 | ) error{OutOfMemory}!void { | ||
| 527 | const name = try std.fmt.allocPrint(allocator, "{s}.o", .{std.fs.path.stem(self.path)}); | ||
| 528 | defer allocator.free(name); | ||
| 529 | if (name.len <= 15) return; | ||
| 530 | const name_off = try ar_strtab.insert(allocator, name); | ||
| 531 | self.output_ar_state.name_off = name_off; | ||
| 532 | } | ||
| 533 | |||
| 534 | pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void { | 522 | pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void { |
| 535 | var end_pos: u64 = elf_file.shdr_table_offset.?; | 523 | var end_pos: u64 = elf_file.shdr_table_offset.?; |
| 536 | for (elf_file.shdrs.items) |shdr| { | 524 | for (elf_file.shdrs.items) |shdr| { |
| ... | @@ -549,11 +537,12 @@ pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { | ... | @@ -549,11 +537,12 @@ pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void { |
| 549 | const amt = try elf_file.base.file.?.preadAll(contents, 0); | 537 | const amt = try elf_file.base.file.?.preadAll(contents, 0); |
| 550 | if (amt != self.output_ar_state.size) return error.InputOutput; | 538 | if (amt != self.output_ar_state.size) return error.InputOutput; |
| 551 | 539 | ||
| 552 | const name = try std.fmt.allocPrint(gpa, "{s}.o", .{std.fs.path.stem(self.path)}); | 540 | const name = self.path; |
| 553 | defer gpa.free(name); | ||
| 554 | |||
| 555 | const hdr = Archive.setArHdr(.{ | 541 | const hdr = Archive.setArHdr(.{ |
| 556 | .name = if (name.len <= 15) .{ .name = name } else .{ .name_off = self.output_ar_state.name_off }, | 542 | .name = if (name.len <= Archive.max_member_name_len) |
| 543 | .{ .name = name } | ||
| 544 | else | ||
| 545 | .{ .name_off = self.output_ar_state.name_off }, | ||
| 557 | .size = @intCast(size), | 546 | .size = @intCast(size), |
| 558 | }); | 547 | }); |
| 559 | try writer.writeAll(mem.asBytes(&hdr)); | 548 | try writer.writeAll(mem.asBytes(&hdr)); |
src/link/Elf/file.zig+34-3| ... | @@ -161,7 +161,7 @@ pub const File = union(enum) { | ... | @@ -161,7 +161,7 @@ pub const File = union(enum) { |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | pub fn writeSymtab(file: File, elf_file: *Elf, ctx: anytype) void { | 163 | pub fn writeSymtab(file: File, elf_file: *Elf, ctx: anytype) void { |
| 164 | var ilocal = ctx.ilocal; | 164 | var ilocal: usize = ctx.ilocal; |
| 165 | for (file.locals()) |local_index| { | 165 | for (file.locals()) |local_index| { |
| 166 | const local = elf_file.symbol(local_index); | 166 | const local = elf_file.symbol(local_index); |
| 167 | if (!local.flags.output_symtab) continue; | 167 | if (!local.flags.output_symtab) continue; |
| ... | @@ -173,7 +173,7 @@ pub const File = union(enum) { | ... | @@ -173,7 +173,7 @@ pub const File = union(enum) { |
| 173 | ilocal += 1; | 173 | ilocal += 1; |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | var iglobal = ctx.iglobal; | 176 | var iglobal: usize = ctx.iglobal; |
| 177 | for (file.globals()) |global_index| { | 177 | for (file.globals()) |global_index| { |
| 178 | const global = elf_file.symbol(global_index); | 178 | const global = elf_file.symbol(global_index); |
| 179 | const file_ptr = global.file(elf_file) orelse continue; | 179 | const file_ptr = global.file(elf_file) orelse continue; |
| ... | @@ -199,7 +199,38 @@ pub const File = union(enum) { | ... | @@ -199,7 +199,38 @@ pub const File = union(enum) { |
| 199 | pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { | 199 | pub fn updateArSymtab(file: File, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) !void { |
| 200 | return switch (file) { | 200 | return switch (file) { |
| 201 | .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file), | 201 | .zig_object => |x| x.updateArSymtab(ar_symtab, elf_file), |
| 202 | .object => @panic("TODO"), | 202 | .object => |x| x.updateArSymtab(ar_symtab, elf_file), |
| 203 | inline else => unreachable, | ||
| 204 | }; | ||
| 205 | } | ||
| 206 | |||
| 207 | pub fn updateArStrtab(file: File, allocator: Allocator, ar_strtab: *Archive.ArStrtab) !void { | ||
| 208 | const path = switch (file) { | ||
| 209 | .zig_object => |x| x.path, | ||
| 210 | .object => |x| x.path, | ||
| 211 | inline else => unreachable, | ||
| 212 | }; | ||
| 213 | const state = switch (file) { | ||
| 214 | .zig_object => |x| &x.output_ar_state, | ||
| 215 | .object => |x| &x.output_ar_state, | ||
| 216 | inline else => unreachable, | ||
| 217 | }; | ||
| 218 | if (path.len <= Archive.max_member_name_len) return; | ||
| 219 | state.name_off = try ar_strtab.insert(allocator, path); | ||
| 220 | } | ||
| 221 | |||
| 222 | pub fn updateArSize(file: File, elf_file: *Elf) void { | ||
| 223 | return switch (file) { | ||
| 224 | .zig_object => |x| x.updateArSize(elf_file), | ||
| 225 | .object => |x| x.updateArSize(), | ||
| 226 | inline else => unreachable, | ||
| 227 | }; | ||
| 228 | } | ||
| 229 | |||
| 230 | pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void { | ||
| 231 | return switch (file) { | ||
| 232 | .zig_object => |x| x.writeAr(elf_file, writer), | ||
| 233 | .object => |x| x.writeAr(writer), | ||
| 203 | inline else => unreachable, | 234 | inline else => unreachable, |
| 204 | }; | 235 | }; |
| 205 | } | 236 | } |
test/link/elf.zig+74-16| ... | @@ -21,6 +21,9 @@ pub fn build(b: *Build) void { | ... | @@ -21,6 +21,9 @@ pub fn build(b: *Build) void { |
| 21 | .abi = .gnu, | 21 | .abi = .gnu, |
| 22 | }; | 22 | }; |
| 23 | 23 | ||
| 24 | // Exercise linker in ar mode | ||
| 25 | elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target })); | ||
| 26 | |||
| 24 | // Exercise linker with self-hosted backend (no LLVM) | 27 | // Exercise linker with self-hosted backend (no LLVM) |
| 25 | elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target })); | 28 | elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target })); |
| 26 | elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target })); | 29 | elf_step.dependOn(testLinkingObj(b, .{ .use_llvm = false, .target = default_target })); |
| ... | @@ -626,6 +629,65 @@ fn testDsoUndef(b: *Build, opts: Options) *Step { | ... | @@ -626,6 +629,65 @@ fn testDsoUndef(b: *Build, opts: Options) *Step { |
| 626 | return test_step; | 629 | return test_step; |
| 627 | } | 630 | } |
| 628 | 631 | ||
| 632 | fn testEmitStaticLib(b: *Build, opts: Options) *Step { | ||
| 633 | const test_step = addTestStep(b, "emit-static-lib", opts); | ||
| 634 | |||
| 635 | const obj1 = addObject(b, "obj1", opts); | ||
| 636 | addCSourceBytes(obj1, | ||
| 637 | \\int foo = 0; | ||
| 638 | \\int bar = 2; | ||
| 639 | \\int fooBar() { | ||
| 640 | \\ return foo + bar; | ||
| 641 | \\} | ||
| 642 | , &.{}); | ||
| 643 | |||
| 644 | const obj2 = addObject(b, "obj2", opts); | ||
| 645 | addCSourceBytes(obj2, "int tentative;", &.{"-fcommon"}); | ||
| 646 | |||
| 647 | const obj3 = addObject(b, "a_very_long_file_name_so_that_it_ends_up_in_strtab", opts); | ||
| 648 | addZigSourceBytes(obj3, | ||
| 649 | \\fn weakFoo() callconv(.C) usize { | ||
| 650 | \\ return 42; | ||
| 651 | \\} | ||
| 652 | \\export var strongBar: usize = 100; | ||
| 653 | \\comptime { | ||
| 654 | \\ @export(weakFoo, .{ .name = "weakFoo", .linkage = .Weak }); | ||
| 655 | \\ @export(strongBar, .{ .name = "strongBarAlias", .linkage = .Strong }); | ||
| 656 | \\} | ||
| 657 | ); | ||
| 658 | |||
| 659 | const lib = addStaticLibrary(b, "lib", opts); | ||
| 660 | lib.addObject(obj1); | ||
| 661 | lib.addObject(obj2); | ||
| 662 | lib.addObject(obj3); | ||
| 663 | |||
| 664 | const check = lib.checkObject(); | ||
| 665 | check.checkInArchiveSymtab(); | ||
| 666 | check.checkExactPath("in object", obj1.getEmittedBin()); | ||
| 667 | check.checkExact("foo"); | ||
| 668 | check.checkInArchiveSymtab(); | ||
| 669 | check.checkExactPath("in object", obj1.getEmittedBin()); | ||
| 670 | check.checkExact("bar"); | ||
| 671 | check.checkInArchiveSymtab(); | ||
| 672 | check.checkExactPath("in object", obj1.getEmittedBin()); | ||
| 673 | check.checkExact("fooBar"); | ||
| 674 | check.checkInArchiveSymtab(); | ||
| 675 | check.checkExactPath("in object", obj2.getEmittedBin()); | ||
| 676 | check.checkExact("tentative"); | ||
| 677 | check.checkInArchiveSymtab(); | ||
| 678 | check.checkExactPath("in object", obj3.getEmittedBin()); | ||
| 679 | check.checkExact("weakFoo"); | ||
| 680 | check.checkInArchiveSymtab(); | ||
| 681 | check.checkExactPath("in object", obj3.getEmittedBin()); | ||
| 682 | check.checkExact("strongBar"); | ||
| 683 | check.checkInArchiveSymtab(); | ||
| 684 | check.checkExactPath("in object", obj3.getEmittedBin()); | ||
| 685 | check.checkExact("strongBarAlias"); | ||
| 686 | test_step.dependOn(&check.step); | ||
| 687 | |||
| 688 | return test_step; | ||
| 689 | } | ||
| 690 | |||
| 629 | fn testEmptyObject(b: *Build, opts: Options) *Step { | 691 | fn testEmptyObject(b: *Build, opts: Options) *Step { |
| 630 | const test_step = addTestStep(b, "empty-object", opts); | 692 | const test_step = addTestStep(b, "empty-object", opts); |
| 631 | 693 | ||
| ... | @@ -1858,34 +1920,30 @@ fn testLinkingObj(b: *Build, opts: Options) *Step { | ... | @@ -1858,34 +1920,30 @@ fn testLinkingObj(b: *Build, opts: Options) *Step { |
| 1858 | fn testLinkingStaticLib(b: *Build, opts: Options) *Step { | 1920 | fn testLinkingStaticLib(b: *Build, opts: Options) *Step { |
| 1859 | const test_step = addTestStep(b, "linking-static-lib", opts); | 1921 | const test_step = addTestStep(b, "linking-static-lib", opts); |
| 1860 | 1922 | ||
| 1861 | const lib = b.addStaticLibrary(.{ | 1923 | const obj = addObject(b, "bobj", opts); |
| 1862 | .name = "alib", | 1924 | addZigSourceBytes(obj, "export var bar: i32 = -42;"); |
| 1863 | .target = opts.target, | 1925 | |
| 1864 | .optimize = opts.optimize, | 1926 | const lib = addStaticLibrary(b, "alib", opts); |
| 1865 | .use_llvm = opts.use_llvm, | ||
| 1866 | .use_lld = false, | ||
| 1867 | }); | ||
| 1868 | addZigSourceBytes(lib, | 1927 | addZigSourceBytes(lib, |
| 1869 | \\extern var mod: usize; | 1928 | \\export fn foo() i32 { |
| 1870 | \\export fn callMe() usize { | 1929 | \\ return 42; |
| 1871 | \\ return me * mod; | ||
| 1872 | \\} | 1930 | \\} |
| 1873 | \\var me: usize = 42; | ||
| 1874 | ); | 1931 | ); |
| 1932 | lib.addObject(obj); | ||
| 1875 | 1933 | ||
| 1876 | const exe = addExecutable(b, "testlib", opts); | 1934 | const exe = addExecutable(b, "testlib", opts); |
| 1877 | addZigSourceBytes(exe, | 1935 | addZigSourceBytes(exe, |
| 1878 | \\const std = @import("std"); | 1936 | \\const std = @import("std"); |
| 1879 | \\extern fn callMe() usize; | 1937 | \\extern fn foo() i32; |
| 1880 | \\export var mod: usize = 2; | 1938 | \\extern var bar: i32; |
| 1881 | \\pub fn main() void { | 1939 | \\pub fn main() void { |
| 1882 | \\ std.debug.print("{d}\n", .{callMe()}); | 1940 | \\ std.debug.print("{d}\n", .{foo() + bar}); |
| 1883 | \\} | 1941 | \\} |
| 1884 | ); | 1942 | ); |
| 1885 | exe.linkLibrary(lib); | 1943 | exe.linkLibrary(lib); |
| 1886 | 1944 | ||
| 1887 | const run = addRunArtifact(exe); | 1945 | const run = addRunArtifact(exe); |
| 1888 | run.expectStdErrEqual("84\n"); | 1946 | run.expectStdErrEqual("0\n"); |
| 1889 | test_step.dependOn(&run.step); | 1947 | test_step.dependOn(&run.step); |
| 1890 | 1948 | ||
| 1891 | return test_step; | 1949 | return test_step; |
| ... | @@ -3332,7 +3390,7 @@ fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile { | ... | @@ -3332,7 +3390,7 @@ fn addStaticLibrary(b: *Build, name: []const u8, opts: Options) *Compile { |
| 3332 | .target = opts.target, | 3390 | .target = opts.target, |
| 3333 | .optimize = opts.optimize, | 3391 | .optimize = opts.optimize, |
| 3334 | .use_llvm = opts.use_llvm, | 3392 | .use_llvm = opts.use_llvm, |
| 3335 | .use_lld = true, | 3393 | .use_lld = opts.use_lld, |
| 3336 | }); | 3394 | }); |
| 3337 | } | 3395 | } |
| 3338 | 3396 |