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
10411041 }
10421042
10431043 for (positionals.items) |obj| {
1044 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1045 self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
1046 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1044 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1045 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1046 else => |e| try self.reportParseError(
1047 obj.path,
1048 "unexpected error: parsing input file failed with error {s}",
1049 .{@errorName(e)},
1050 ),
1051 };
10471052 }
10481053
10491054 var system_libs = std.ArrayList(SystemLib).init(arena);
......@@ -1122,9 +1127,14 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11221127 }
11231128
11241129 for (system_libs.items) |lib| {
1125 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1126 self.parseLibrary(lib, false, &parse_ctx) catch |err|
1127 try self.handleAndReportParseError(lib.path, err, &parse_ctx);
1130 self.parseLibrary(lib, false) catch |err| switch (err) {
1131 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1132 else => |e| try self.reportParseError(
1133 lib.path,
1134 "unexpected error: parsing library failed with error {s}",
1135 .{@errorName(e)},
1136 ),
1137 };
11281138 }
11291139
11301140 // 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
11401150 if (csu.crtn) |v| try positionals.append(.{ .path = v });
11411151
11421152 for (positionals.items) |obj| {
1143 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1144 self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
1145 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1153 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1154 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1155 else => |e| try self.reportParseError(
1156 obj.path,
1157 "unexpected error: parsing input file failed with error {s}",
1158 .{@errorName(e)},
1159 ),
1160 };
11461161 }
11471162
1163 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1164
11481165 // Init all objects
11491166 for (self.objects.items) |index| {
11501167 try self.file(index).?.object.init(self);
......@@ -1153,6 +1170,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
11531170 try self.file(index).?.shared_object.init(self);
11541171 }
11551172
1173 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1174
11561175 // Dedup shared objects
11571176 {
11581177 var seen_dsos = std.StringHashMap(void).init(gpa);
......@@ -1279,6 +1298,8 @@ pub fn flushModule(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node
12791298 self.error_flags.no_entry_point_found = false;
12801299 try self.writeElfHeader();
12811300 }
1301
1302 if (self.misc_errors.items.len > 0) return error.FlushFailure;
12821303}
12831304
12841305pub 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
13001321 if (module_obj_path) |path| try positionals.append(.{ .path = path });
13011322
13021323 for (positionals.items) |obj| {
1303 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1304 self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
1305 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1324 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1325 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1326 else => |e| try self.reportParseError(
1327 obj.path,
1328 "unexpected error: parsing input file failed with error {s}",
1329 .{@errorName(e)},
1330 ),
1331 };
13061332 }
13071333
1334 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1335
13081336 // First, we flush relocatable object file generated with our backends.
13091337 if (self.zigObjectPtr()) |zig_object| {
13101338 zig_object.resolveSymbols(self);
......@@ -1316,6 +1344,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
13161344 try zig_object.addAtomsToRelaSections(self);
13171345 try self.updateSectionSizesObject();
13181346
1347 try self.allocateAllocSectionsObject();
13191348 try self.allocateNonAllocSections();
13201349
13211350 if (build_options.enable_logging) {
......@@ -1325,14 +1354,15 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
13251354 try self.writeSyntheticSectionsObject();
13261355 try self.writeShdrTable();
13271356 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);
13281361 }
13291362
13301363 var files = std.ArrayList(File.Index).init(gpa);
13311364 defer files.deinit();
13321365 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.
13361366 if (self.zigObjectPtr()) |zig_object| files.appendAssumeCapacity(zig_object.index);
13371367 for (self.objects.items) |index| files.appendAssumeCapacity(index);
13381368
......@@ -1353,7 +1383,7 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
13531383 for (files.items) |index| {
13541384 const file_ptr = self.file(index).?;
13551385 try file_ptr.updateArStrtab(gpa, &ar_strtab);
1356 file_ptr.updateArSize(self);
1386 file_ptr.updateArSize();
13571387 }
13581388
13591389 // Update file offsets of contributing objects.
......@@ -1405,13 +1435,15 @@ pub fn flushStaticLib(self: *Elf, comp: *Compilation, module_obj_path: ?[]const
14051435 // Write object files
14061436 for (files.items) |index| {
14071437 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());
14091439 }
14101440
14111441 assert(buffer.items.len == total_size);
14121442
14131443 try self.base.file.?.setEndPos(total_size);
14141444 try self.base.file.?.pwriteAll(buffer.items, 0);
1445
1446 if (self.misc_errors.items.len > 0) return error.FlushFailure;
14151447}
14161448
14171449pub 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)
14321464 if (module_obj_path) |path| try positionals.append(.{ .path = path });
14331465
14341466 for (positionals.items) |obj| {
1435 var parse_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
1436 self.parsePositional(obj.path, obj.must_link, &parse_ctx) catch |err|
1437 try self.handleAndReportParseError(obj.path, err, &parse_ctx);
1467 self.parsePositional(obj.path, obj.must_link) catch |err| switch (err) {
1468 error.MalformedObject, error.MalformedArchive, error.InvalidCpuArch => continue, // already reported
1469 else => |e| try self.reportParseError(
1470 obj.path,
1471 "unexpected error: parsing input file failed with error {s}",
1472 .{@errorName(e)},
1473 ),
1474 };
14381475 }
14391476
1477 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1478
14401479 // Init all objects
14411480 for (self.objects.items) |index| {
14421481 try self.file(index).?.object.init(self);
14431482 }
14441483
1484 if (self.misc_errors.items.len > 0) return error.FlushFailure;
1485
14451486 // Now, we are ready to resolve the symbols across all input files.
14461487 // We will first resolve the files in the ZigObject, next in the parsed
14471488 // input Object files.
......@@ -1473,6 +1514,8 @@ pub fn flushObject(self: *Elf, comp: *Compilation, module_obj_path: ?[]const u8)
14731514 try self.writeSyntheticSectionsObject();
14741515 try self.writeShdrTable();
14751516 try self.writeElfHeader();
1517
1518 if (self.misc_errors.items.len > 0) return error.FlushFailure;
14761519}
14771520
14781521/// --verbose-link output
......@@ -1760,7 +1803,8 @@ fn dumpArgv(self: *Elf, comp: *Compilation) !void {
17601803}
17611804
17621805const ParseError = error{
1763 UnknownFileType,
1806 MalformedObject,
1807 MalformedArchive,
17641808 InvalidCpuArch,
17651809 OutOfMemory,
17661810 Overflow,
......@@ -1771,34 +1815,30 @@ const ParseError = error{
17711815 InvalidCharacter,
17721816} || 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 {
17751819 const tracy = trace(@src());
17761820 defer tracy.end();
17771821 if (try Object.isObject(path)) {
1778 try self.parseObject(path, ctx);
1822 try self.parseObject(path);
17791823 } else {
1780 try self.parseLibrary(.{ .path = path }, must_link, ctx);
1824 try self.parseLibrary(.{ .path = path }, must_link);
17811825 }
17821826}
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 {
17851829 const tracy = trace(@src());
17861830 defer tracy.end();
17871831
17881832 if (try Archive.isArchive(lib.path)) {
1789 try self.parseArchive(lib.path, must_link, ctx);
1833 try self.parseArchive(lib.path, must_link);
17901834 } else if (try SharedObject.isSharedObject(lib.path)) {
1791 try self.parseSharedObject(lib, ctx);
1835 try self.parseSharedObject(lib);
17921836 } else {
1793 // TODO if the script has a top-level comment identifying it as GNU ld script,
1794 // then report parse errors. Otherwise return UnknownFileType.
1795 self.parseLdScript(lib, ctx) catch |err| switch (err) {
1796 else => return error.UnknownFileType,
1797 };
1837 try self.parseLdScript(lib);
17981838 }
17991839}
18001840
1801fn parseObject(self: *Elf, path: []const u8, ctx: *ParseErrorCtx) ParseError!void {
1841fn parseObject(self: *Elf, path: []const u8) ParseError!void {
18021842 const tracy = trace(@src());
18031843 defer tracy.end();
18041844
......@@ -1816,12 +1856,9 @@ fn parseObject(self: *Elf, path: []const u8, ctx: *ParseErrorCtx) ParseError!voi
18161856
18171857 const object = self.file(index).?.object;
18181858 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;
18221859}
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 {
18251862 const tracy = trace(@src());
18261863 defer tracy.end();
18271864
......@@ -1844,13 +1881,10 @@ fn parseArchive(self: *Elf, path: []const u8, must_link: bool, ctx: *ParseErrorC
18441881 object.alive = must_link;
18451882 try object.parse(self);
18461883 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;
18501884 }
18511885}
18521886
1853fn parseSharedObject(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void {
1887fn parseSharedObject(self: *Elf, lib: SystemLib) ParseError!void {
18541888 const tracy = trace(@src());
18551889 defer tracy.end();
18561890
......@@ -1870,12 +1904,9 @@ fn parseSharedObject(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError
18701904
18711905 const shared_object = self.file(index).?.shared_object;
18721906 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;
18761907}
18771908
1878fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!void {
1909fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void {
18791910 const tracy = trace(@src());
18801911 defer tracy.end();
18811912
......@@ -1885,15 +1916,10 @@ fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!voi
18851916 const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32));
18861917 defer gpa.free(data);
18871918
1888 var script = LdScript{};
1919 var script = LdScript{ .path = lib.path };
18891920 defer script.deinit(gpa);
18901921 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
18971923 const lib_dirs = self.base.options.lib_dirs;
18981924
18991925 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
......@@ -1947,11 +1973,17 @@ fn parseLdScript(self: *Elf, lib: SystemLib, ctx: *ParseErrorCtx) ParseError!voi
19471973 }
19481974
19491975 const full_path = test_path.items;
1950 var scr_ctx: ParseErrorCtx = .{ .detected_cpu_arch = undefined };
19511976 self.parseLibrary(.{
19521977 .needed = scr_obj.needed,
19531978 .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 };
19551987 }
19561988}
19571989
......@@ -2177,7 +2209,7 @@ fn scanRelocs(self: *Elf) !void {
21772209 try object.scanRelocs(self, &undefs);
21782210 }
21792211
2180 try self.reportUndefined(&undefs);
2212 try self.reportUndefinedSymbols(&undefs);
21812213
21822214 for (self.symbols.items, 0..) |*sym, i| {
21832215 const index = @as(u32, @intCast(i));
......@@ -2789,7 +2821,6 @@ fn linkWithLLD(self: *Elf, comp: *Compilation, prog_node: *std.Progress.Node) !v
27892821 }));
27902822 } else {
27912823 self.error_flags.missing_libc = true;
2792 return error.FlushFailure;
27932824 }
27942825 }
27952826 }
......@@ -2945,6 +2976,7 @@ fn writeShdrTable(self: *Elf) !void {
29452976 defer gpa.free(buf);
29462977
29472978 for (buf, 0..) |*shdr, i| {
2979 assert(self.shdrs.items[i].sh_offset != math.maxInt(u64));
29482980 shdr.* = shdrTo32(self.shdrs.items[i]);
29492981 if (foreign_endian) {
29502982 mem.byteSwapAllFields(elf.Elf32_Shdr, shdr);
......@@ -2957,6 +2989,7 @@ fn writeShdrTable(self: *Elf) !void {
29572989 defer gpa.free(buf);
29582990
29592991 for (buf, 0..) |*shdr, i| {
2992 assert(self.shdrs.items[i].sh_offset != math.maxInt(u64));
29602993 shdr.* = self.shdrs.items[i];
29612994 if (foreign_endian) {
29622995 mem.byteSwapAllFields(elf.Elf64_Shdr, shdr);
......@@ -3007,6 +3040,8 @@ fn writePhdrTable(self: *Elf) !void {
30073040}
30083041
30093042fn 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
30103045 var hdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined;
30113046
30123047 var index: usize = 0;
......@@ -4740,7 +4775,7 @@ fn writeAtoms(self: *Elf) !void {
47404775 try self.base.file.?.pwriteAll(buffer, sh_offset);
47414776 }
47424777
4743 try self.reportUndefined(&undefs);
4778 try self.reportUndefinedSymbols(&undefs);
47444779}
47454780
47464781fn writeAtomsObject(self: *Elf) !void {
......@@ -6003,7 +6038,7 @@ pub fn insertDynString(self: *Elf, name: []const u8) error{OutOfMemory}!u32 {
60036038 return off;
60046039}
60056040
6006fn reportUndefined(self: *Elf, undefs: anytype) !void {
6041fn reportUndefinedSymbols(self: *Elf, undefs: anytype) !void {
60076042 const gpa = self.base.allocator;
60086043 const max_notes = 4;
60096044
......@@ -6045,41 +6080,26 @@ fn reportMissingLibraryError(
60456080 }
60466081}
60476082
6048const ParseErrorCtx = struct {
6049 detected_cpu_arch: std.Target.Cpu.Arch,
6050};
6051
6052fn handleAndReportParseError(
6083pub fn reportParseError(
60536084 self: *Elf,
60546085 path: []const u8,
6055 err: ParseError,
6056 ctx: *const ParseErrorCtx,
6086 comptime format: []const u8,
6087 args: anytype,
60576088) error{OutOfMemory}!void {
6058 const cpu_arch = self.base.options.target.cpu.arch;
6059 switch (err) {
6060 error.UnknownFileType => try self.reportParseError(path, "unknown file type", .{}),
6061 error.InvalidCpuArch => try self.reportParseError(
6062 path,
6063 "invalid cpu architecture: expected '{s}', but found '{s}'",
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(
6089 var err = try self.addErrorWithNotes(1);
6090 try err.addMsg(self, format, args);
6091 try err.addNote(self, "while parsing {s}", .{path});
6092}
6093
6094pub fn reportParseError2(
60756095 self: *Elf,
6076 path: []const u8,
6096 file_index: File.Index,
60776097 comptime format: []const u8,
60786098 args: anytype,
60796099) error{OutOfMemory}!void {
60806100 var err = try self.addErrorWithNotes(1);
60816101 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()});
60836103}
60846104
60856105const FormatShdrCtx = struct {
src/link/Elf/Archive.zig+4-6
......@@ -33,12 +33,10 @@ pub fn parse(self: *Archive, elf_file: *Elf) !void {
3333 const hdr = try reader.readStruct(elf.ar_hdr);
3434
3535 if (!mem.eql(u8, &hdr.ar_fmag, elf.ARFMAG)) {
36 // TODO convert into an error
37 log.debug(
38 "{s}: invalid header delimiter: expected '{s}', found '{s}'",
39 .{ self.path, std.fmt.fmtSliceEscapeLower(elf.ARFMAG), std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag) },
40 );
41 return;
36 try elf_file.reportParseError(self.path, "invalid archive header delimiter: {s}", .{
37 std.fmt.fmtSliceEscapeLower(&hdr.ar_fmag),
38 });
39 return error.MalformedArchive;
4240 }
4341
4442 const size = try hdr.size();
src/link/Elf/LdScript.zig+18-19
......@@ -1,3 +1,4 @@
1path: []const u8,
12cpu_arch: ?std.Target.Cpu.Arch = null,
23args: std.ArrayListUnmanaged(Elf.SystemLib) = .{},
34
......@@ -6,7 +7,7 @@ pub fn deinit(scr: *LdScript, allocator: Allocator) void {
67}
78
89pub const Error = error{
9 InvalidScript,
10 InvalidLdScript,
1011 UnexpectedToken,
1112 UnknownCpuArch,
1213 OutOfMemory,
......@@ -30,13 +31,12 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void {
3031 try line_col.append(.{ .line = line, .column = column });
3132 switch (tok.id) {
3233 .invalid => {
33 // TODO errors
34 // elf_file.base.fatal("invalid token in ld script: '{s}' ({d}:{d})", .{
35 // tok.get(data),
36 // line,
37 // column,
38 // });
39 return error.InvalidScript;
34 try elf_file.reportParseError(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{
35 std.fmt.fmtSliceEscapeLower(tok.get(data)),
36 line,
37 column,
38 });
39 return error.InvalidLdScript;
4040 },
4141 .new_line => {
4242 line += 1;
......@@ -55,17 +55,16 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void {
5555 .args = &args,
5656 }) catch |err| switch (err) {
5757 error.UnexpectedToken => {
58 // const last_token_id = parser.it.pos - 1;
59 // const last_token = parser.it.get(last_token_id);
60 // const lcol = line_col.items[last_token_id];
61 // TODO errors
62 // elf_file.base.fatal("unexpected token in ld script: {s} : '{s}' ({d}:{d})", .{
63 // @tagName(last_token.id),
64 // last_token.get(data),
65 // lcol.line,
66 // lcol.column,
67 // });
68 return error.InvalidScript;
58 const last_token_id = parser.it.pos - 1;
59 const last_token = parser.it.get(last_token_id);
60 const lcol = line_col.items[last_token_id];
61 try elf_file.reportParseError(scr.path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{
62 @tagName(last_token.id),
63 last_token.get(data),
64 lcol.line,
65 lcol.column,
66 });
67 return error.InvalidLdScript;
6968 },
7069 else => |e| return e,
7170 };
src/link/Elf/Object.zig+38-2
......@@ -54,10 +54,30 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
5454
5555 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
5766 if (self.header.?.e_shnum == 0) return;
5867
5968 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
6181 const shoff = math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
6282 const shdrs = @as(
6383 [*]align(1) const elf.Elf64_Shdr,
......@@ -66,10 +86,23 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
6686 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
6787
6888 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 }
6995 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
7096 }
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
74107 const symtab_index = for (self.shdrs.items, 0..) |shdr, i| switch (shdr.sh_type) {
75108 elf.SHT_SYMTAB => break @as(u16, @intCast(i)),
......@@ -81,7 +114,10 @@ pub fn parse(self: *Object, elf_file: *Elf) !void {
81114 self.first_global = shdr.sh_info;
82115
83116 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 };
85121 const symtab = @as([*]align(1) const elf.Elf64_Sym, @ptrCast(raw_symtab.ptr))[0..nsyms];
86122
87123 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 {
5252 const reader = stream.reader();
5353
5454 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
5576 const shoff = std.math.cast(usize, self.header.?.e_shoff) orelse return error.Overflow;
5677
5778 const shdrs = @as(
......@@ -61,6 +82,10 @@ pub fn parse(self: *SharedObject, elf_file: *Elf) !void {
6182 try self.shdrs.ensureTotalCapacityPrecise(gpa, shdrs.len);
6283
6384 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 }
6489 self.shdrs.appendAssumeCapacity(try ElfShdr.fromElf64Shdr(shdr));
6590 switch (shdr.sh_type) {
6691 elf.SHT_DYNSYM => self.dynsym_sect_index = @as(u16, @intCast(i)),
src/link/Elf/ZigObject.zig+28-18
......@@ -3,6 +3,7 @@
33//! and any relocations that may have been emitted.
44//! Think about this as fake in-memory Object file for the Zig module.
55
6data: std.ArrayListUnmanaged(u8) = .{},
67path: []const u8,
78index: File.Index,
89
......@@ -101,6 +102,7 @@ pub fn init(self: *ZigObject, elf_file: *Elf) !void {
101102}
102103
103104pub fn deinit(self: *ZigObject, allocator: Allocator) void {
105 self.data.deinit(allocator);
104106 allocator.free(self.path);
105107 self.local_esyms.deinit(allocator);
106108 self.global_esyms.deinit(allocator);
......@@ -441,6 +443,27 @@ pub fn markLive(self: *ZigObject, elf_file: *Elf) void {
441443 }
442444}
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
444467pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *Elf) error{OutOfMemory}!void {
445468 const gpa = elf_file.base.allocator;
446469
......@@ -457,34 +480,21 @@ pub fn updateArSymtab(self: ZigObject, ar_symtab: *Archive.ArSymtab, elf_file: *
457480 }
458481}
459482
460pub fn updateArSize(self: *ZigObject, elf_file: *Elf) void {
461 var end_pos: u64 = elf_file.shdr_table_offset.?;
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;
483pub fn updateArSize(self: *ZigObject) void {
484 self.output_ar_state.size = self.data.items.len;
466485}
467486
468pub fn writeAr(self: ZigObject, elf_file: *Elf, 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
487pub fn writeAr(self: ZigObject, writer: anytype) !void {
478488 const name = self.path;
479489 const hdr = Archive.setArHdr(.{
480490 .name = if (name.len <= Archive.max_member_name_len)
481491 .{ .name = name }
482492 else
483493 .{ .name_off = self.output_ar_state.name_off },
484 .size = @intCast(size),
494 .size = @intCast(self.data.items.len),
485495 });
486496 try writer.writeAll(mem.asBytes(&hdr));
487 try writer.writeAll(contents);
497 try writer.writeAll(self.data.items);
488498}
489499
490500pub fn addAtomsToRelaSections(self: ZigObject, elf_file: *Elf) !void {
src/link/Elf/file.zig+4-4
......@@ -162,17 +162,17 @@ pub const File = union(enum) {
162162 state.name_off = try ar_strtab.insert(allocator, path);
163163 }
164164
165 pub fn updateArSize(file: File, elf_file: *Elf) void {
165 pub fn updateArSize(file: File) void {
166166 return switch (file) {
167 .zig_object => |x| x.updateArSize(elf_file),
167 .zig_object => |x| x.updateArSize(),
168168 .object => |x| x.updateArSize(),
169169 inline else => unreachable,
170170 };
171171 }
172172
173 pub fn writeAr(file: File, elf_file: *Elf, writer: anytype) !void {
173 pub fn writeAr(file: File, writer: anytype) !void {
174174 return switch (file) {
175 .zig_object => |x| x.writeAr(elf_file, writer),
175 .zig_object => |x| x.writeAr(writer),
176176 .object => |x| x.writeAr(writer),
177177 inline else => unreachable,
178178 };
test/link/elf.zig+41-4
......@@ -29,6 +29,7 @@ pub fn testAll(b: *Build) *Step {
2929
3030 // Exercise linker in ar mode
3131 elf_step.dependOn(testEmitStaticLib(b, .{ .target = musl_target }));
32 elf_step.dependOn(testEmitStaticLibZig(b, .{ .use_llvm = false, .target = musl_target }));
3233
3334 // Exercise linker with self-hosted backend (no LLVM)
3435 elf_step.dependOn(testGcSectionsZig(b, .{ .use_llvm = false, .target = default_target }));
......@@ -743,6 +744,42 @@ fn testEmitStaticLib(b: *Build, opts: Options) *Step {
743744 return test_step;
744745}
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
746783fn testEmptyObject(b: *Build, opts: Options) *Step {
747784 const test_step = addTestStep(b, "empty-object", opts);
748785
......@@ -1875,7 +1912,7 @@ fn testMismatchedCpuArchitectureError(b: *Build, opts: Options) *Step {
18751912 exe.linkLibC();
18761913
18771914 expectLinkErrors(exe, test_step, .{ .exact = &.{
1878 "invalid cpu architecture: expected 'x86_64', but found 'aarch64'",
1915 "invalid cpu architecture: aarch64",
18791916 "note: while parsing /?/a.o",
18801917 } });
18811918
......@@ -3305,10 +3342,10 @@ fn testUnknownFileTypeError(b: *Build, opts: Options) *Step {
33053342 exe.linkLibC();
33063343
33073344 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",
33093348 "note: while parsing /?/liba.dylib",
3310 "undefined symbol: foo",
3311 "note: referenced by /?/a.o:.text",
33123349 } });
33133350
33143351 return test_step;