authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-12-05 14:06:37-08:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-12-05 14:06:37-08:00
log559e216f3fd86329c29e9f8ff70e0d00b1e903b9
treebb101d7b0862b739abf48dcb785f580d9f94dfaf
parent72568c131dcfc9303de0a809e02290c7ac464663
parentda417b851b8aa8522abfbd235c82193075054544
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #18207 from ziglang/elf-error-handler

elf: report errors for some detected malformed object contents

8 files changed, 261 insertions(+), 136 deletions(-)

src/link/Elf.zig+103-83
...@@ -1041,9 +1041,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1041,9 +1041,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1041 }1041 }
10421042
1043 for (positionals.items) |obj| {1043 for (positionals.items) |obj| {
1044 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };1044 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1045 self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|1045 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1046 try self.handleAndReportParseError(obj.path, err, &parse_ctx);1046 else => |e| try self.reportParseError(
1047 obj.path,
1048 "unexpected error: parsing input file failed with error {s}",
1049 .{@errorName(e)},
1050 ),
1051 };
1047 }1052 }
10481053
1049 var system_libs = std.ArrayList(SystemLib).init(arena);1054 var system_libs = std.ArrayList(SystemLib).init(arena);
...@@ -1122,9 +1127,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1122,9 +1127,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1122 }1127 }
11231128
1124 for (system_libs.items) |lib| {1129 for (system_libs.items) |lib| {
1125 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };1130 self.parseLibrary(lib, false) catch |err| switch (err) {
1126 self.parseLibrary(lib, false, &parse_ctx) catch |err|1131 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1127 try self.handleAndReportParseError(lib.path, err, &parse_ctx);1132 else => |e| try self.reportParseError(
1133 lib.path,
1134 "unexpected error: parsing library failed with error {s}",
1135 .{@errorName(e)},
1136 ),
1137 };
1128 }1138 }
11291139
1130 // Finally, as the last input objects we add compiler_rt and CSU postlude (if any).1140 // Finally, as the last input objects we add compiler_rt and CSU postlude (if any).
...@@ -1140,11 +1150,18 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1140,11 +1150,18 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1140 if (csu.crtn) |v| try positionals.append(.{ .path = v });1150 if (csu.crtn) |v| try positionals.append(.{ .path = v });
11411151
1142 for (positionals.items) |obj| {1152 for (positionals.items) |obj| {
1143 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };1153 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1144 self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|1154 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1145 try self.handleAndReportParseError(obj.path, err, &parse_ctx);1155 else => |e| try self.reportParseError(
1156 obj.path,
1157 "unexpected error: parsing input file failed with error {s}",
1158 .{@errorName(e)},
1159 ),
1160 };
1146 }1161 }
11471162
1163 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1164
1148 // Init all objects1165 // Init all objects
1149 for (self.objects.items) |index| {1166 for (self.objects.items) |index| {
1150 try self.file(index).?.object.init(self);1167 try self.file(index).?.object.init(self);
...@@ -1153,6 +1170,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1153,6 +1170,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1153 try self.file(index).?.shared_object.init(self);1170 try self.file(index).?.shared_object.init(self);
1154 }1171 }
11551172
1173 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1174
1156 // Dedup shared objects1175 // Dedup shared objects
1157 {1176 {
1158 var seen_dsos = std.StringHashMap(void).init(gpa);1177 var seen_dsos = std.StringHashMap(void).init(gpa);
...@@ -1279,6 +1298,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node...@@ -1279,6 +1298,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
1279 self.error_flags.no_entry_point_found = false;1298 self.error_flags.no_entry_point_found = false;
1280 try self.writeElfHeader();1299 try self.writeElfHeader();
1281 }1300 }
1301
1302 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1282}1303}
12831304
1284pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {1305pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
...@@ -1300,11 +1321,18 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const...@@ -1300,11 +1321,18 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
1300 if (module_obj_path) |path| try positionals.append(.{ .path = path });1321 if (module_obj_path) |path| try positionals.append(.{ .path = path });
13011322
1302 for (positionals.items) |obj| {1323 for (positionals.items) |obj| {
1303 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };1324 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1304 self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|1325 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1305 try self.handleAndReportParseError(obj.path, err, &parse_ctx);1326 else => |e| try self.reportParseError(
1327 obj.path,
1328 "unexpected error: parsing input file failed with error {s}",
1329 .{@errorName(e)},
1330 ),
1331 };
1306 }1332 }
13071333
1334 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1335
1308 // First, we flush relocatable object file generated with our backends.1336 // First, we flush relocatable object file generated with our backends.
1309 if (self.zigObjectPtr()) |zig_object| {1337 if (self.zigObjectPtr()) |zig_object| {
1310 zig_object.resolveSymbols(self);1338 zig_object.resolveSymbols(self);
...@@ -1316,6 +1344,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const...@@ -1316,6 +1344,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
1316 try zig_object.addAtomsToRelaSections(self);1344 try zig_object.addAtomsToRelaSections(self);
1317 try self.updateSectionSizesObject();1345 try self.updateSectionSizesObject();
13181346
1347 try self.allocateAllocSectionsObject();
1319 try self.allocateNonAllocSections();1348 try self.allocateNonAllocSections();
13201349
1321 if (build_options.enable_logging) {1350 if (build_options.enable_logging) {
...@@ -1325,14 +1354,15 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const...@@ -1325,14 +1354,15 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
1325 try self.writeSyntheticSectionsObject();1354 try self.writeSyntheticSectionsObject();
1326 try self.writeShdrTable();1355 try self.writeShdrTable();
1327 try self.writeElfHeader();1356 try self.writeElfHeader();
1357
1358 // TODO we can avoid reading in the file contents we just wrote if we give the linker
1359 // ability to write directly to a buffer.
1360 try zig_object.readFileContents(self);
1328 }1361 }
13291362
1330 var files = std.ArrayList(File.Index).init(gpa);1363 var files = std.ArrayList(File.Index).init(gpa);
1331 defer files.deinit();1364 defer files.deinit();
1332 try files.ensureTotalCapacityPrecise(self.objects.items.len + 1);1365 try files.ensureTotalCapacityPrecise(self.objects.items.len + 1);
1333 // Note to self: we currently must have ZigObject written out first as we write the object
1334 // file into the same file descriptor and then re-read its contents.
1335 // TODO implement writing ZigObject to a buffer instead of file.
1336 if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);1366 if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
1337 for (self.objects.items) |index| files.appendAssumeCapacity(index);1367 for (self.objects.items) |index| files.appendAssumeCapacity(index);
13381368
...@@ -1353,7 +1383,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const...@@ -1353,7 +1383,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
1353 for (files.items) |index| {1383 for (files.items) |index| {
1354 const file_ptr = self.file(index).?;1384 const file_ptr = self.file(index).?;
1355 try file_ptr.updateArStrtab(gpa, &ar_strtab);1385 try file_ptr.updateArStrtab(gpa, &ar_strtab);
1356 file_ptr.updateArSize(self);1386 file_ptr.updateArSize();
1357 }1387 }
13581388
1359 // Update file offsets of contributing objects.1389 // Update file offsets of contributing objects.
...@@ -1405,13 +1435,15 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const...@@ -1405,13 +1435,15 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
1405 // Write object files1435 // Write object files
1406 for (files.items) |index| {1436 for (files.items) |index| {
1407 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);1437 if (!mem.isAligned(buffer.items.len, 2)) try buffer.writer().writeByte(0);
1408 try self.file(index).?.writeAr(self, buffer.writer());1438 try self.file(index).?.writeAr(buffer.writer());
1409 }1439 }
14101440
1411 assert(buffer.items.len == total_size);1441 assert(buffer.items.len == total_size);
14121442
1413 try self.base.file.?.setEndPos(total_size);1443 try self.base.file.?.setEndPos(total_size);
1414 try self.base.file.?.pwriteAll(buffer.items, 0);1444 try self.base.file.?.pwriteAll(buffer.items, 0);
1445
1446 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1415}1447}
14161448
1417pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {1449pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8) link.File.FlushError!void {
...@@ -1432,16 +1464,25 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8)...@@ -1432,16 +1464,25 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8)
1432 if (module_obj_path) |path| try positionals.append(.{ .path = path });1464 if (module_obj_path) |path| try positionals.append(.{ .path = path });
14331465
1434 for (positionals.items) |obj| {1466 for (positionals.items) |obj| {
1435 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };1467 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1436 self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|1468 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1437 try self.handleAndReportParseError(obj.path, err, &parse_ctx);1469 else => |e| try self.reportParseError(
1470 obj.path,
1471 "unexpected error: parsing input file failed with error {s}",
1472 .{@errorName(e)},
1473 ),
1474 };
1438 }1475 }
14391476
1477 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1478
1440 // Init all objects1479 // Init all objects
1441 for (self.objects.items) |index| {1480 for (self.objects.items) |index| {
1442 try self.file(index).?.object.init(self);1481 try self.file(index).?.object.init(self);
1443 }1482 }
14441483
1484 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1485
1445 // Now, we are ready to resolve the symbols across all input files.1486 // Now, we are ready to resolve the symbols across all input files.
1446 // We will first resolve the files in the ZigObject, next in the parsed1487 // We will first resolve the files in the ZigObject, next in the parsed
1447 // input Object files.1488 // input Object files.
...@@ -1473,6 +1514,8 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8)...@@ -1473,6 +1514,8 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8)
1473 try self.writeSyntheticSectionsObject();1514 try self.writeSyntheticSectionsObject();
1474 try self.writeShdrTable();1515 try self.writeShdrTable();
1475 try self.writeElfHeader();1516 try self.writeElfHeader();
1517
1518 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1476}1519}
14771520
1478/// --verbose-link output1521/// --verbose-link output
...@@ -1760,7 +1803,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {...@@ -1760,7 +1803,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
1760}1803}
17611804
1762const ParseError = error{1805const ParseError = error{
1763 UnknownFileType,1806 MalformedObject,
1807 MalformedArchive,
1764 InvalidCpuArch,1808 InvalidCpuArch,
1765 OutOfMemory,1809 OutOfMemory,
1766 Overflow,1810 Overflow,
...@@ -1771,34 +1815,30 @@ const ParseError = error{...@@ -1771,34 +1815,30 @@ const ParseError = error{
1771 InvalidCharacter,1815 InvalidCharacter,
1772} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;1816} || LdScript.Error || std.os.AccessError || std.os.SeekError || std.fs.File.OpenError || std.fs.File.ReadError;
17731817
1774fn parsePositional(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorCtx) ParseError!void {1818fn parsePositional(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
1775 const tracy = trace(@src());1819 const tracy = trace(@src());
1776 defer tracy.end();1820 defer tracy.end();
1777 if (try Object.isObject(path)) {1821 if (try Object.isObject(path)) {
1778 try self.parseObject(path, ctx);1822 try self.parseObject(path);
1779 } else {1823 } else {
1780 try self.parseLibrary(.{ .path = path }, must_link, ctx);1824 try self.parseLibrary(.{ .path = path }, must_link);
1781 }1825 }
1782}1826}
17831827
1784fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool, ctx: *ParseErrorCtx) ParseError!void {1828fn parseLibrary(self: *Elf, lib: SystemLib, must_link: bool) ParseError!void {
1785 const tracy = trace(@src());1829 const tracy = trace(@src());
1786 defer tracy.end();1830 defer tracy.end();
17871831
1788 if (try Archive.isArchive(lib.path)) {1832 if (try Archive.isArchive(lib.path)) {
1789 try self.parseArchive(lib.path, must_link, ctx);1833 try self.parseArchive(lib.path, must_link);
1790 } else if (try SharedObject.isSharedObject(lib.path)) {1834 } else if (try SharedObject.isSharedObject(lib.path)) {
1791 try self.parseSharedObject(lib, ctx);1835 try self.parseSharedObject(lib);
1792 } else {1836 } else {
1793 // TODO if the script has a top-level comment identifying it as GNU ld script,1837 try self.parseLdScript(lib);
1794 // then report parse errors. Otherwise return UnknownFileType.
1795 self.parseLdScript(lib, ctx) catch |err| switch (err) {
1796 else => return error.UnknownFileType,
1797 };
1798 }1838 }
1799}1839}
18001840
1801fn parseObject(self: *Elf, path: []const u8, ctx: *ParseErrorCtx) ParseError!void {1841fn parseObject(self: *Elf, path: []const u8) ParseError!void {
1802 const tracy = trace(@src());1842 const tracy = trace(@src());
1803 defer tracy.end();1843 defer tracy.end();
18041844
...@@ -1816,12 +1856,9 @@ fn parseObject(self: *Elf, path: []const u8, ctx: *ParseErrorCtx) ParseError!voi...@@ -1816,12 +1856,9 @@ fn parseObject(self: *Elf, path: []const u8, ctx: *ParseErrorCtx) ParseError!voi
18161856
1817 const object = self.file(index).?.object;1857 const object = self.file(index).?.object;
1818 try object.parse(self);1858 try object.parse(self);
1819
1820 ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?;
1821 if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
1822}1859}
18231860
1824fn parseArchive(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorCtx) ParseError!void {1861fn parseArchive(self: *Elf, path: []const u8, must_link: bool) ParseError!void {
1825 const tracy = trace(@src());1862 const tracy = trace(@src());
1826 defer tracy.end();1863 defer tracy.end();
18271864
...@@ -1844,13 +1881,10 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorC...@@ -1844,13 +1881,10 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorC
1844 object.alive = must_link;1881 object.alive = must_link;
1845 try object.parse(self);1882 try object.parse(self);
1846 try self.objects.append(gpa, index);1883 try self.objects.append(gpa, index);
1847
1848 ctx.detected_cpu_arch = object.header.?.e_machine.toTargetCpuArch().?;
1849 if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
1850 }1884 }
1851}1885}
18521886
1853fn parseSharedObject(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void {1887fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
1854 const tracy = trace(@src());1888 const tracy = trace(@src());
1855 defer tracy.end();1889 defer tracy.end();
18561890
...@@ -1870,12 +1904,9 @@ fn parseSharedObject(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError...@@ -1870,12 +1904,9 @@ fn parseSharedObject(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError
18701904
1871 const shared_object = self.file(index).?.shared_object;1905 const shared_object = self.file(index).?.shared_object;
1872 try shared_object.parse(self);1906 try shared_object.parse(self);
1873
1874 ctx.detected_cpu_arch = shared_object.header.?.e_machine.toTargetCpuArch().?;
1875 if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
1876}1907}
18771908
1878fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void {1909fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
1879 const tracy = trace(@src());1910 const tracy = trace(@src());
1880 defer tracy.end();1911 defer tracy.end();
18811912
...@@ -1885,15 +1916,10 @@ fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!voi...@@ -1885,15 +1916,10 @@ fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!voi
1885 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));1916 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
1886 defer gpa.free(data);1917 defer gpa.free(data);
18871918
1888 var script = LdScript{};1919 var script = LdScript{ .path = lib.path };
1889 defer script.deinit(gpa);1920 defer script.deinit(gpa);
1890 try script.parse(data, self);1921 try script.parse(data, self);
18911922
1892 if (script.cpu_arch) |cpu_arch| {
1893 ctx.detected_cpu_arch = cpu_arch;
1894 if (ctx.detected_cpu_arch != self.base.options.target.cpu.arch) return error.InvalidCpuArch;
1895 }
1896
1897 const lib_dirs = self.base.options.lib_dirs;1923 const lib_dirs = self.base.options.lib_dirs;
18981924
1899 var arena_allocator = std.heap.ArenaAllocator.init(gpa);1925 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
...@@ -1947,11 +1973,17 @@ fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!voi...@@ -1947,11 +1973,17 @@ fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!voi
1947 }1973 }
19481974
1949 const full_path = test_path.items;1975 const full_path = test_path.items;
1950 var scr_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1951 self.parseLibrary(.{1976 self.parseLibrary(.{
1952 .needed = scr_obj.needed,1977 .needed = scr_obj.needed,
1953 .path = full_path,1978 .path = full_path,
1954 }, false, &scr_ctx) catch |err| try self.handleAndReportParseError(full_path, err, &scr_ctx);1979 }, false) catch |err| switch (err) {
1980 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1981 else => |e| try self.reportParseError(
1982 full_path,
1983 "unexpected error: parsing library failed with error {s}",
1984 .{@errorName(e)},
1985 ),
1986 };
1955 }1987 }
1956}1988}
19571989
...@@ -2177,7 +2209,7 @@ fn scanRelocs(self: *Elf) !void {...@@ -2177,7 +2209,7 @@ fn scanRelocs(self: *Elf) !void {
2177 try object.scanRelocs(self, &undefs);2209 try object.scanRelocs(self, &undefs);
2178 }2210 }
21792211
2180 try self.reportUndefined(&undefs);2212 try self.reportUndefinedSymbols(&undefs);
21812213
2182 for (self.symbols.items, 0..) |*sym, i| {2214 for (self.symbols.items, 0..) |*sym, i| {
2183 const index = @as(u32, @intCast(i));2215 const index = @as(u32, @intCast(i));
...@@ -2789,7 +2821,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v...@@ -2789,7 +2821,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
2789 }));2821 }));
2790 } else {2822 } else {
2791 self.error_flags.missing_libc = true;2823 self.error_flags.missing_libc = true;
2792 return error.FlushFailure;
2793 }2824 }
2794 }2825 }
2795 }2826 }
...@@ -2945,6 +2976,7 @@ fn writeShdrTable(self: *Elf) !void {...@@ -2945,6 +2976,7 @@ fn writeShdrTable(self: *Elf) !void {
2945 defer gpa.free(buf);2976 defer gpa.free(buf);
29462977
2947 for (buf, 0..) |*shdr, i| {2978 for (buf, 0..) |*shdr, i| {
2979 assert(self.shdrs.items[i].sh_offset != math.maxInt(u64));
2948 shdr.* = shdrTo32(self.shdrs.items[i]);2980 shdr.* = shdrTo32(self.shdrs.items[i]);
2949 if (foreign_endian) {2981 if (foreign_endian) {
2950 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);2982 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
...@@ -2957,6 +2989,7 @@ fn writeShdrTable(self: *Elf) !void {...@@ -2957,6 +2989,7 @@ fn writeShdrTable(self: *Elf) !void {
2957 defer gpa.free(buf);2989 defer gpa.free(buf);
29582990
2959 for (buf, 0..) |*shdr, i| {2991 for (buf, 0..) |*shdr, i| {
2992 assert(self.shdrs.items[i].sh_offset != math.maxInt(u64));
2960 shdr.* = self.shdrs.items[i];2993 shdr.* = self.shdrs.items[i];
2961 if (foreign_endian) {2994 if (foreign_endian) {
2962 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);2995 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
...@@ -3007,6 +3040,8 @@ fn writePhdrTable(self: *Elf) !void {...@@ -3007,6 +3040,8 @@ fn writePhdrTable(self: *Elf) !void {
3007}3040}
30083041
3009fn writeElfHeader(self: *Elf) !void {3042fn writeElfHeader(self: *Elf) !void {
3043 if (self.misc_errors.items.len > 0) return; // We had errors, so skip flushing to render the output unusable
3044
3010 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;3045 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
30113046
3012 var index: usize = 0;3047 var index: usize = 0;
...@@ -4740,7 +4775,7 @@ fn writeAtoms(self: *Elf) !void {...@@ -4740,7 +4775,7 @@ fn writeAtoms(self: *Elf) !void {
4740 try self.base.file.?.pwriteAll(buffer, sh_offset);4775 try self.base.file.?.pwriteAll(buffer, sh_offset);
4741 }4776 }
47424777
4743 try self.reportUndefined(&undefs);4778 try self.reportUndefinedSymbols(&undefs);
4744}4779}
47454780
4746fn writeAtomsObject(self: *Elf) !void {4781fn writeAtomsObject(self: *Elf) !void {
...@@ -6003,7 +6038,7 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {...@@ -6003,7 +6038,7 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
6003 return off;6038 return off;
6004}6039}
60056040
6006fn reportUndefined(self: *Elf, undefs: anytype) !void {6041fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
6007 const gpa = self.base.allocator;6042 const gpa = self.base.allocator;
6008 const max_notes = 4;6043 const max_notes = 4;
60096044
...@@ -6045,41 +6080,26 @@ fn reportMissingLibraryError(...@@ -6045,41 +6080,26 @@ fn reportMissingLibraryError(
6045 }6080 }
6046}6081}
60476082
6048const ParseErrorCtx = struct {6083pub fn reportParseError(
6049 detected_cpu_arch: std.Target.Cpu.Arch,
6050};
6051
6052fn handleAndReportParseError(
6053 self: *Elf,6084 self: *Elf,
6054 path: []const u8,6085 path: []const u8,
6055 err: ParseError,6086 comptime format: []const u8,
6056 ctx: *const ParseErrorCtx,6087 args: anytype,
6057) error{OutOfMemory}!void {6088) error{OutOfMemory}!void {
6058 const cpu_arch = self.base.options.target.cpu.arch;6089 var err = try self.addErrorWithNotes(1);
6059 switch (err) {6090 try err.addMsg(self, format, args);
6060 error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),6091 try err.addNote(self, "while parsing {s}", .{path});
6061 error.InvalidCpuArch => try self.reportParseError(6092}
6062 path,6093
6063 "invalid cpu architecture: expected '{s}', but found '{s}'",6094pub fn reportParseError2(
6064 .{ @tagName(cpu_arch), @tagName(ctx.detected_cpu_arch) },
6065 ),
6066 else => |e| try self.reportParseError(
6067 path,
6068 "unexpected error: parsing object failed with error {s}",
6069 .{@errorName(e)},
6070 ),
6071 }
6072}
6073
6074fn reportParseError(
6075 self: *Elf,6095 self: *Elf,
6076 path: []const u8,6096 file_index: File.Index,
6077 comptime format: []const u8,6097 comptime format: []const u8,
6078 args: anytype,6098 args: anytype,
6079) error{OutOfMemory}!void {6099) error{OutOfMemory}!void {
6080 var err = try self.addErrorWithNotes(1);6100 var err = try self.addErrorWithNotes(1);
6081 try err.addMsg(self, format, args);6101 try err.addMsg(self, format, args);
6082 try err.addNote(self, "while parsing {s}", .{path});6102 try err.addNote(self, "while parsing {}", .{self.file(file_index).?.fmtPath()});
6083}6103}
60846104
6085const FormatShdrCtx = struct {6105const FormatShdrCtx = struct {
src/link/Elf/Archive.zig+4-6
...@@ -33,12 +33,10 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void {...@@ -33,12 +33,10 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void {
33 const hdr = try reader.readStruct(elf.ar_hdr);33 const hdr = try reader.readStruct(elf.ar_hdr);
3434
35 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {35 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {
36 // TODO convert into an error36 try elf_file.reportParseError(self.path, "invalid archive header delimiter: {s}", .{
37 log.debug(37 std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
38 "{s}: invalid header delimiter: expected '{s}', found '{s}'",38 });
39 .{ self.path, std.fmt.fmtSliceEscapeLower(elf.ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) },39 return error.MalformedArchive;
40 );
41 return;
42 }40 }
4341
44 const size = try hdr.size();42 const size = try hdr.size();
src/link/Elf/LdScript.zig+18-19
...@@ -1,3 +1,4 @@...@@ -1,3 +1,4 @@
1path: []const u8,
1cpu_arch: ?std.Target.Cpu.Arch = null,2cpu_arch: ?std.Target.Cpu.Arch = null,
2args: std.ArrayListUnmanaged(Elf.SystemLib) = .{},3args: std.ArrayListUnmanaged(Elf.SystemLib) = .{},
34
...@@ -6,7 +7,7 @@ pub fn deinit(scr: *LdScript, allocator: Allocator) void {...@@ -6,7 +7,7 @@ pub fn deinit(scr: *LdScript, allocator: Allocator) void {
6}7}
78
8pub const Error = error{9pub const Error = error{
9 InvalidScript,10 InvalidLdScript,
10 UnexpectedToken,11 UnexpectedToken,
11 UnknownCpuArch,12 UnknownCpuArch,
12 OutOfMemory,13 OutOfMemory,
...@@ -30,13 +31,12 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void {...@@ -30,13 +31,12 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void {
30 try line_col.append(.{ .line = line, .column = column });31 try line_col.append(.{ .line = line, .column = column });
31 switch (tok.id) {32 switch (tok.id) {
32 .invalid => {33 .invalid => {
33 // TODO errors34 try elf_file.reportParseError(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{
34 // elf_file.base.fatal("invalid token in ld script: '{s}' ({d}:{d})", .{35 std.fmt.fmtSliceEscapeLower(tok.get(data)),
35 // tok.get(data),36 line,
36 // line,37 column,
37 // column,38 });
38 // });39 return error.InvalidLdScript;
39 return error.InvalidScript;
40 },40 },
41 .new_line => {41 .new_line => {
42 line += 1;42 line += 1;
...@@ -55,17 +55,16 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void {...@@ -55,17 +55,16 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void {
55 .args = &args,55 .args = &args,
56 }) catch |err| switch (err) {56 }) catch |err| switch (err) {
57 error.UnexpectedToken => {57 error.UnexpectedToken => {
58 // const last_token_id = parser.it.pos - 1;58 const last_token_id = parser.it.pos - 1;
59 // const last_token = parser.it.get(last_token_id);59 const last_token = parser.it.get(last_token_id);
60 // const lcol = line_col.items[last_token_id];60 const lcol = line_col.items[last_token_id];
61 // TODO errors61 try elf_file.reportParseError(scr.path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{
62 // elf_file.base.fatal("unexpected token in ld script: {s} : '{s}' ({d}:{d})", .{62 @tagName(last_token.id),
63 // @tagName(last_token.id),63 last_token.get(data),
64 // last_token.get(data),64 lcol.line,
65 // lcol.line,65 lcol.column,
66 // lcol.column,66 });
67 // });67 return error.InvalidLdScript;
68 return error.InvalidScript;
69 },68 },
70 else => |e| return e,69 else => |e| return e,
71 };70 };
src/link/Elf/Object.zig+38-2
...@@ -54,10 +54,30 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -54,10 +54,30 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
5454
55 self.header = try reader.readStruct(elf.Elf64_Ehdr);55 self.header = try reader.readStruct(elf.Elf64_Ehdr);
5656
57 if (elf_file.base.options.target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) {
58 try elf_file.reportParseError2(
59 self.index,
60 "invalid cpu architecture: {s}",
61 .{@tagName(self.header.?.e_machine.toTargetCpuArch().?)},
62 );
63 return error.InvalidCpuArch;
64 }
65
57 if (self.header.?.e_shnum == 0) return;66 if (self.header.?.e_shnum == 0) return;
5867
59 const gpa = elf_file.base.allocator;68 const gpa = elf_file.base.allocator;
6069
70 if (self.data.len < self.header.?.e_shoff or
71 self.data.len < self.header.?.e_shoff + @as(u64, @intCast(self.header.?.e_shnum)) * @sizeOf(elf.Elf64_Shdr))
72 {
73 try elf_file.reportParseError2(
74 self.index,
75 "corrupt header: section header table extends past the end of file",
76 .{},
77 );
78 return error.MalformedObject;
79 }
80
61 const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;81 const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
62 const shdrs = @as(82 const shdrs = @as(
63 [*]align(1) const elf.Elf64_Shdr,83 [*]align(1) const elf.Elf64_Shdr,
...@@ -66,10 +86,23 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -66,10 +86,23 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
66 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);86 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
6787
68 for (shdrs) |shdr| {88 for (shdrs) |shdr| {
89 if (shdr.sh_type != elf.SHT_NOBITS) {
90 if (self.data.len < shdr.sh_offset or self.data.len < shdr.sh_offset + shdr.sh_size) {
91 try elf_file.reportParseError2(self.index, "corrupt section: extends past the end of file", .{});
92 return error.MalformedObject;
93 }
94 }
69 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));95 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
70 }96 }
7197
72 try self.strtab.appendSlice(gpa, self.shdrContents(self.header.?.e_shstrndx));98 const shstrtab = self.shdrContents(self.header.?.e_shstrndx);
99 for (shdrs) |shdr| {
100 if (shdr.sh_name >= shstrtab.len) {
101 try elf_file.reportParseError2(self.index, "corrupt section name offset", .{});
102 return error.MalformedObject;
103 }
104 }
105 try self.strtab.appendSlice(gpa, shstrtab);
73106
74 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {107 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
75 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),108 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
...@@ -81,7 +114,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {...@@ -81,7 +114,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
81 self.first_global = shdr.sh_info;114 self.first_global = shdr.sh_info;
82115
83 const raw_symtab = self.shdrContents(index);116 const raw_symtab = self.shdrContents(index);
84 const nsyms = @divExact(raw_symtab.len, @sizeOf(elf.Elf64_Sym));117 const nsyms = math.divExact(usize, raw_symtab.len, @sizeOf(elf.Elf64_Sym)) catch {
118 try elf_file.reportParseError2(self.index, "symbol table not evenly divisible", .{});
119 return error.MalformedObject;
120 };
85 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];121 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
86122
87 const strtab_bias = @as(u32, @intCast(self.strtab.items.len));123 const strtab_bias = @as(u32, @intCast(self.strtab.items.len));
src/link/Elf/SharedObject.zig+25
...@@ -52,6 +52,27 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {...@@ -52,6 +52,27 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
52 const reader = stream.reader();52 const reader = stream.reader();
5353
54 self.header = try reader.readStruct(elf.Elf64_Ehdr);54 self.header = try reader.readStruct(elf.Elf64_Ehdr);
55
56 if (elf_file.base.options.target.cpu.arch != self.header.?.e_machine.toTargetCpuArch().?) {
57 try elf_file.reportParseError2(
58 self.index,
59 "invalid cpu architecture: {s}",
60 .{@tagName(self.header.?.e_machine.toTargetCpuArch().?)},
61 );
62 return error.InvalidCpuArch;
63 }
64
65 if (self.data.len < self.header.?.e_shoff or
66 self.data.len < self.header.?.e_shoff + @as(u64, @intCast(self.header.?.e_shnum)) * @sizeOf(elf.Elf64_Shdr))
67 {
68 try elf_file.reportParseError2(
69 self.index,
70 "corrupted header: section header table extends past the end of file",
71 .{},
72 );
73 return error.MalformedObject;
74 }
75
55 const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;76 const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
5677
57 const shdrs = @as(78 const shdrs = @as(
...@@ -61,6 +82,10 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {...@@ -61,6 +82,10 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
61 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);82 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
6283
63 for (shdrs, 0..) |shdr, i| {84 for (shdrs, 0..) |shdr, i| {
85 if (self.data.len < shdr.sh_offset or self.data.len < shdr.sh_offset + shdr.sh_size) {
86 try elf_file.reportParseError2(self.index, "corrupted section header", .{});
87 return error.MalformedObject;
88 }
64 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));89 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
65 switch (shdr.sh_type) {90 switch (shdr.sh_type) {
66 elf.SHT_DYNSYM => self.dynsym_sect_index = @as(u16, @intCast(i)),91 elf.SHT_DYNSYM => self.dynsym_sect_index = @as(u16, @intCast(i)),
src/link/Elf/ZigObject.zig+28-18
...@@ -3,6 +3,7 @@...@@ -3,6 +3,7 @@
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.
55
6data: std.ArrayListUnmanaged(u8) = .{},
6path: []const u8,7path: []const u8,
7index: File.Index,8index: File.Index,
89
...@@ -101,6 +102,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {...@@ -101,6 +102,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
101}102}
102103
103pub fn deinit(self: *ZigObject, allocator: Allocator) void {104pub fn deinit(self: *ZigObject, allocator: Allocator) void {
105 self.data.deinit(allocator);
104 allocator.free(self.path);106 allocator.free(self.path);
105 self.local_esyms.deinit(allocator);107 self.local_esyms.deinit(allocator);
106 self.global_esyms.deinit(allocator);108 self.global_esyms.deinit(allocator);
...@@ -441,6 +443,27 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {...@@ -441,6 +443,27 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
441 }443 }
442}444}
443445
446/// This is just a temporary helper function that allows us to re-read what we wrote to file into a buffer.
447/// We need this so that we can write to an archive.
448/// TODO implement writing ZigObject data directly to a buffer instead.
449pub fn readFileContents(self: *ZigObject, elf_file: *Elf) !void {
450 const gpa = elf_file.base.allocator;
451 const shsize: u64 = switch (elf_file.ptr_width) {
452 .p32 => @sizeOf(elf.Elf32_Shdr),
453 .p64 => @sizeOf(elf.Elf64_Shdr),
454 };
455 var end_pos: u64 = elf_file.shdr_table_offset.? + elf_file.shdrs.items.len * shsize;
456 for (elf_file.shdrs.items) |shdr| {
457 if (shdr.sh_type == elf.SHT_NOBITS) continue;
458 end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
459 }
460 const size = std.math.cast(usize, end_pos) orelse return error.Overflow;
461 try self.data.resize(gpa, size);
462
463 const amt = try elf_file.base.file.?.preadAll(self.data.items, 0);
464 if (amt != size) return error.InputOutput;
465}
466
444pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {467pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {
445 const gpa = elf_file.base.allocator;468 const gpa = elf_file.base.allocator;
446469
...@@ -457,34 +480,21 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *...@@ -457,34 +480,21 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *
457 }480 }
458}481}
459482
460pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void {483pub fn updateArSize(self: *ZigObject) void {
461 var end_pos: u64 = elf_file.shdr_table_offset.?;484 self.output_ar_state.size = self.data.items.len;
462 for (elf_file.shdrs.items) |shdr| {
463 end_pos = @max(end_pos, shdr.sh_offset + shdr.sh_size);
464 }
465 self.output_ar_state.size = end_pos;
466}485}
467486
468pub fn writeAr(self: ZigObject, elf_file: *Elf, writer: anytype) !void {487pub fn writeAr(self: ZigObject, writer: anytype) !void {
469 const gpa = elf_file.base.allocator;
470
471 const size = std.math.cast(usize, self.output_ar_state.size) orelse return error.Overflow;
472 const contents = try gpa.alloc(u8, size);
473 defer gpa.free(contents);
474
475 const amt = try elf_file.base.file.?.preadAll(contents, 0);
476 if (amt != self.output_ar_state.size) return error.InputOutput;
477
478 const name = self.path;488 const name = self.path;
479 const hdr = Archive.setArHdr(.{489 const hdr = Archive.setArHdr(.{
480 .name = if (name.len <= Archive.max_member_name_len)490 .name = if (name.len <= Archive.max_member_name_len)
481 .{ .name = name }491 .{ .name = name }
482 else492 else
483 .{ .name_off = self.output_ar_state.name_off },493 .{ .name_off = self.output_ar_state.name_off },
484 .size = @intCast(size),494 .size = @intCast(self.data.items.len),
485 });495 });
486 try writer.writeAll(mem.asBytes(&hdr));496 try writer.writeAll(mem.asBytes(&hdr));
487 try writer.writeAll(contents);497 try writer.writeAll(self.data.items);
488}498}
489499
490pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void {500pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void {
src/link/Elf/file.zig+4-4
...@@ -162,17 +162,17 @@ pub const File = union(enum) {...@@ -162,17 +162,17 @@ pub const File = union(enum) {
162 state.name_off = try ar_strtab.insert(allocator, path);162 state.name_off = try ar_strtab.insert(allocator, path);
163 }163 }
164164
165 pub fn updateArSize(file: File, elf_file: *Elf) void {165 pub fn updateArSize(file: File) void {
166 return switch (file) {166 return switch (file) {
167 .zig_object => |x| x.updateArSize(elf_file),167 .zig_object => |x| x.updateArSize(),
168 .object => |x| x.updateArSize(),168 .object => |x| x.updateArSize(),
169 inline else => unreachable,169 inline else => unreachable,
170 };170 };
171 }171 }
172172
173 pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void {173 pub fn writeAr(file: File, writer: anytype) !void {
174 return switch (file) {174 return switch (file) {
175 .zig_object => |x| x.writeAr(elf_file, writer),175 .zig_object => |x| x.writeAr(writer),
176 .object => |x| x.writeAr(writer),176 .object => |x| x.writeAr(writer),
177 inline else => unreachable,177 inline else => unreachable,
178 };178 };
test/link/elf.zig+41-4
...@@ -29,6 +29,7 @@ pub fn testAll(b: *Build) *Step {...@@ -29,6 +29,7 @@ pub fn testAll(b: *Build) *Step {
2929
30 // Exercise linker in ar mode30 // Exercise linker in ar mode
31 elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target }));31 elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target }));
32 elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = musl_target }));
3233
33 // Exercise linker with self-hosted backend (no LLVM)34 // Exercise linker with self-hosted backend (no LLVM)
34 elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));35 elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
...@@ -743,6 +744,42 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step {...@@ -743,6 +744,42 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step {
743 return test_step;744 return test_step;
744}745}
745746
747fn testEmitStaticLibZig(b: *Build, opts: Options) *Step {
748 const test_step = addTestStep(b, "emit-static-lib-zig", opts);
749
750 const obj1 = addObject(b, "obj1", opts);
751 addZigSourceBytes(obj1,
752 \\export var foo: i32 = 42;
753 \\export var bar: i32 = 2;
754 );
755
756 const lib = addStaticLibrary(b, "lib", opts);
757 addZigSourceBytes(lib,
758 \\extern var foo: i32;
759 \\extern var bar: i32;
760 \\export fn fooBar() i32 {
761 \\ return foo + bar;
762 \\}
763 );
764 lib.addObject(obj1);
765
766 const exe = addExecutable(b, "test", opts);
767 addZigSourceBytes(exe,
768 \\const std = @import("std");
769 \\extern fn fooBar() i32;
770 \\pub fn main() void {
771 \\ std.debug.print("{d}", .{fooBar()});
772 \\}
773 );
774 exe.linkLibrary(lib);
775
776 const run = addRunArtifact(exe);
777 run.expectStdErrEqual("44");
778 test_step.dependOn(&run.step);
779
780 return test_step;
781}
782
746fn testEmptyObject(b: *Build, opts: Options) *Step {783fn testEmptyObject(b: *Build, opts: Options) *Step {
747 const test_step = addTestStep(b, "empty-object", opts);784 const test_step = addTestStep(b, "empty-object", opts);
748785
...@@ -1875,7 +1912,7 @@ fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {...@@ -1875,7 +1912,7 @@ fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
1875 exe.linkLibC();1912 exe.linkLibC();
18761913
1877 expectLinkErrors(exe, test_step, .{ .exact = &.{1914 expectLinkErrors(exe, test_step, .{ .exact = &.{
1878 "invalid cpu architecture: expected 'x86_64', but found 'aarch64'",1915 "invalid cpu architecture: aarch64",
1879 "note: while parsing /?/a.o",1916 "note: while parsing /?/a.o",
1880 } });1917 } });
18811918
...@@ -3305,10 +3342,10 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {...@@ -3305,10 +3342,10 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
3305 exe.linkLibC();3342 exe.linkLibC();
33063343
3307 expectLinkErrors(exe, test_step, .{ .exact = &.{3344 expectLinkErrors(exe, test_step, .{ .exact = &.{
3308 "unknown file type",3345 "invalid token in LD script: '\\x00\\x00\\x00\\x0c\\x00\\x00\\x00/usr/lib/dyld\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x0d' (0:829)",
3346 "note: while parsing /?/liba.dylib",
3347 "unexpected error: parsing input file failed with error InvalidLdScript",
3309 "note: while parsing /?/liba.dylib",3348 "note: while parsing /?/liba.dylib",
3310 "undefined symbol: foo",
3311 "note: referenced by /?/a.o:.text",
3312 } });3349 } });
33133350
3314 return test_step;3351 return test_step;