link.Elf.LdScript: eliminate dependency on Elf.File

this allows it to be used by the frontend
This commit is contained in:
Andrew Kelley 2024-10-14 15:24:13 -07:00
parent 33d07f4b6e
commit 028a9a1054
2 changed files with 76 additions and 68 deletions

View File

@ -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))

View File

@ -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");