Commit 028a9a1054

Andrew Kelley <andrew@ziglang.org>
2024-10-15 00:24:13
link.Elf.LdScript: eliminate dependency on Elf.File
this allows it to be used by the frontend
1 parent 33d07f4
Changed files (2)
src
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");
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))