From 028a9a10549e59e84105515ffb50c491f487c91a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 14 Oct 2024 15:24:13 -0700 Subject: [PATCH] link.Elf.LdScript: eliminate dependency on Elf.File this allows it to be used by the frontend --- src/link/Elf.zig | 7 +- src/link/Elf/LdScript.zig | 137 ++++++++++++++++++++------------------ 2 files changed, 76 insertions(+), 68 deletions(-) diff --git a/src/link/Elf.zig b/src/link/Elf.zig index dfb4a614fa..c8822d3f29 100644 --- a/src/link/Elf.zig +++ b/src/link/Elf.zig @@ -1524,9 +1524,8 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { const data = try in_file.readToEndAlloc(gpa, std.math.maxInt(u32)); defer gpa.free(data); - var script: LdScript = .{ .path = lib.path }; + var script = try LdScript.parse(gpa, diags, lib.path, data); defer script.deinit(gpa); - try script.parse(data, self); var arena_allocator = std.heap.ArenaAllocator.init(gpa); defer arena_allocator.deinit(); @@ -1535,15 +1534,13 @@ fn parseLdScript(self: *Elf, lib: SystemLib) ParseError!void { var test_path = std.ArrayList(u8).init(arena); var checked_paths = std.ArrayList([]const u8).init(arena); - for (script.args.items) |script_arg| { + for (script.args) |script_arg| { checked_paths.clearRetainingCapacity(); success: { if (mem.startsWith(u8, script_arg.path, "-l")) { const lib_name = script_arg.path["-l".len..]; - // TODO I think technically we should re-use the mechanism used by the frontend here. - // Maybe we should hoist search-strategy all the way here? for (self.lib_dirs) |lib_dir| { if (!self.base.isStatic()) { if (try self.accessLibPath(arena, &test_path, &checked_paths, lib_dir, lib_name, .dynamic)) diff --git a/src/link/Elf/LdScript.zig b/src/link/Elf/LdScript.zig index 349011a20a..ff7c01b119 100644 --- a/src/link/Elf/LdScript.zig +++ b/src/link/Elf/LdScript.zig @@ -1,14 +1,15 @@ path: Path, -cpu_arch: ?std.Target.Cpu.Arch = null, -args: std.ArrayListUnmanaged(Arg) = .empty, +cpu_arch: ?std.Target.Cpu.Arch, +args: []const Arg, pub const Arg = struct { needed: bool = false, path: []const u8, }; -pub fn deinit(scr: *LdScript, allocator: Allocator) void { - scr.args.deinit(allocator); +pub fn deinit(ls: *LdScript, gpa: Allocator) void { + gpa.free(ls.args); + ls.* = undefined; } pub const Error = error{ @@ -18,28 +19,30 @@ pub const Error = error{ OutOfMemory, }; -pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { - const comp = elf_file.base.comp; - const gpa = comp.gpa; - const diags = &comp.link_diags; - +pub fn parse( + gpa: Allocator, + diags: *Diags, + /// For error reporting. + path: Path, + data: []const u8, +) Error!LdScript { var tokenizer = Tokenizer{ .source = data }; - var tokens = std.ArrayList(Token).init(gpa); - defer tokens.deinit(); - var line_col = std.ArrayList(LineColumn).init(gpa); - defer line_col.deinit(); + var tokens: std.ArrayListUnmanaged(Token) = .empty; + defer tokens.deinit(gpa); + var line_col: std.ArrayListUnmanaged(LineColumn) = .empty; + defer line_col.deinit(gpa); var line: usize = 0; var prev_line_last_col: usize = 0; while (true) { const tok = tokenizer.next(); - try tokens.append(tok); + try tokens.append(gpa, tok); const column = tok.start - prev_line_last_col; - try line_col.append(.{ .line = line, .column = column }); + try line_col.append(gpa, .{ .line = line, .column = column }); switch (tok.id) { .invalid => { - return diags.failParse(scr.path, "invalid token in LD script: '{s}' ({d}:{d})", .{ + return diags.failParse(path, "invalid token in LD script: '{s}' ({d}:{d})", .{ std.fmt.fmtSliceEscapeLower(tok.get(data)), line, column, }); }, @@ -52,18 +55,22 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { } } - var it = TokenIterator{ .tokens = tokens.items }; - var parser = Parser{ .source = data, .it = &it }; - var args = std.ArrayList(Arg).init(gpa); - scr.doParse(.{ - .parser = &parser, - .args = &args, - }) catch |err| switch (err) { + var it: TokenIterator = .{ .tokens = tokens.items }; + var parser: Parser = .{ + .gpa = gpa, + .source = data, + .it = &it, + .args = .empty, + .cpu_arch = null, + }; + defer parser.args.deinit(gpa); + + parser.start() catch |err| switch (err) { error.UnexpectedToken => { const last_token_id = parser.it.pos - 1; const last_token = parser.it.get(last_token_id); const lcol = line_col.items[last_token_id]; - return diags.failParse(scr.path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{ + return diags.failParse(path, "unexpected token in LD script: {s}: '{s}' ({d}:{d})", .{ @tagName(last_token.id), last_token.get(data), lcol.line, @@ -72,30 +79,10 @@ pub fn parse(scr: *LdScript, data: []const u8, elf_file: *Elf) Error!void { }, else => |e| return e, }; - scr.args = args.moveToUnmanaged(); -} - -fn doParse(scr: *LdScript, ctx: struct { - parser: *Parser, - args: *std.ArrayList(Arg), -}) !void { - while (true) { - ctx.parser.skipAny(&.{ .comment, .new_line }); - - if (ctx.parser.maybe(.command)) |cmd_id| { - const cmd = ctx.parser.getCommand(cmd_id); - switch (cmd) { - .output_format => scr.cpu_arch = try ctx.parser.outputFormat(), - // TODO we should verify that group only contains libraries - .input, .group => try ctx.parser.group(ctx.args), - else => return error.UnexpectedToken, - } - } else break; - } - - if (ctx.parser.it.next()) |tok| switch (tok.id) { - .eof => {}, - else => return error.UnexpectedToken, + return .{ + .path = path, + .cpu_arch = parser.cpu_arch, + .args = try parser.args.toOwnedSlice(gpa), }; } @@ -126,9 +113,34 @@ const Command = enum { }; const Parser = struct { + gpa: Allocator, source: []const u8, it: *TokenIterator, + cpu_arch: ?std.Target.Cpu.Arch, + args: std.ArrayListUnmanaged(Arg), + + fn start(parser: *Parser) !void { + while (true) { + parser.skipAny(&.{ .comment, .new_line }); + + if (parser.maybe(.command)) |cmd_id| { + const cmd = parser.getCommand(cmd_id); + switch (cmd) { + .output_format => parser.cpu_arch = try parser.outputFormat(), + // TODO we should verify that group only contains libraries + .input, .group => try parser.group(), + else => return error.UnexpectedToken, + } + } else break; + } + + if (parser.it.next()) |tok| switch (tok.id) { + .eof => {}, + else => return error.UnexpectedToken, + }; + } + fn outputFormat(p: *Parser) !std.Target.Cpu.Arch { const value = value: { if (p.skip(&.{.lparen})) { @@ -149,18 +161,19 @@ const Parser = struct { return error.UnknownCpuArch; } - fn group(p: *Parser, args: *std.ArrayList(Arg)) !void { + fn group(p: *Parser) !void { + const gpa = p.gpa; if (!p.skip(&.{.lparen})) return error.UnexpectedToken; while (true) { if (p.maybe(.literal)) |tok_id| { const tok = p.it.get(tok_id); const path = tok.get(p.source); - try args.append(.{ .path = path, .needed = true }); + try p.args.append(gpa, .{ .path = path, .needed = true }); } else if (p.maybe(.command)) |cmd_id| { const cmd = p.getCommand(cmd_id); switch (cmd) { - .as_needed => try p.asNeeded(args), + .as_needed => try p.asNeeded(), else => return error.UnexpectedToken, } } else break; @@ -169,13 +182,14 @@ const Parser = struct { _ = try p.require(.rparen); } - fn asNeeded(p: *Parser, args: *std.ArrayList(Arg)) !void { + fn asNeeded(p: *Parser) !void { + const gpa = p.gpa; if (!p.skip(&.{.lparen})) return error.UnexpectedToken; while (p.maybe(.literal)) |tok_id| { const tok = p.it.get(tok_id); const path = tok.get(p.source); - try args.append(.{ .path = path, .needed = false }); + try p.args.append(gpa, .{ .path = path, .needed = false }); } _ = try p.require(.rparen); @@ -227,21 +241,19 @@ const Token = struct { end: usize, const Id = enum { - // zig fmt: off eof, invalid, new_line, - lparen, // ( - rparen, // ) - lbrace, // { - rbrace, // } + lparen, // ( + rparen, // ) + lbrace, // { + rbrace, // } - comment, // /* */ + comment, // /* */ - command, // literal with special meaning, see Command + command, // literal with special meaning, see Command literal, - // zig fmt: on }; const Index = usize; @@ -430,10 +442,9 @@ const TokenIterator = struct { }; const LdScript = @This(); +const Diags = @import("../../link.zig").Diags; const std = @import("std"); const assert = std.debug.assert; const Path = std.Build.Cache.Path; - const Allocator = std.mem.Allocator; -const Elf = @import("../Elf.zig");