Commit 66a7c09def

David Rubin <daviru007@icloud.com>
2024-10-20 10:44:25
link: use target to determine risc-v eflag validity
1 parent b2c62bc
Changed files (3)
src/link/Elf/Object.zig
@@ -99,7 +99,6 @@ pub fn parseCommon(
     path: Path,
     handle: fs.File,
     target: std.Target,
-    first_eflags: *?elf.Word,
 ) !void {
     const offset = if (self.archive) |ar| ar.offset else 0;
     const file_size = (try handle.stat()).size;
@@ -114,7 +113,7 @@ pub fn parseCommon(
             @tagName(self.header.?.e_machine),
         });
     }
-    try validateEFlags(diags, path, target, self.header.?.e_flags, first_eflags);
+    try validateEFlags(diags, path, target, self.header.?.e_flags);
 
     if (self.header.?.e_shnum == 0) return;
 
@@ -180,39 +179,81 @@ pub fn parseCommon(
     }
 }
 
-fn validateEFlags(
+pub fn validateEFlags(
     diags: *Diags,
     path: Path,
     target: std.Target,
     e_flags: elf.Word,
-    first_eflags: *?elf.Word,
-) error{LinkFailure}!void {
-    if (first_eflags.*) |*self_eflags| {
-        switch (target.cpu.arch) {
-            .riscv64 => {
-                if (e_flags != self_eflags.*) {
-                    const riscv_eflags: riscv.RiscvEflags = @bitCast(e_flags);
-                    const self_riscv_eflags: *riscv.RiscvEflags = @ptrCast(self_eflags);
-
-                    self_riscv_eflags.rvc = self_riscv_eflags.rvc or riscv_eflags.rvc;
-                    self_riscv_eflags.tso = self_riscv_eflags.tso or riscv_eflags.tso;
-
-                    var any_errors: bool = false;
-                    if (self_riscv_eflags.fabi != riscv_eflags.fabi) {
-                        any_errors = true;
-                        diags.addParseError(path, "cannot link object files with different float-point ABIs", .{});
-                    }
-                    if (self_riscv_eflags.rve != riscv_eflags.rve) {
-                        any_errors = true;
-                        diags.addParseError(path, "cannot link object files with different RVEs", .{});
-                    }
-                    if (any_errors) return error.LinkFailure;
-                }
-            },
-            else => {},
-        }
-    } else {
-        first_eflags.* = e_flags;
+) !void {
+    switch (target.cpu.arch) {
+        .riscv64 => {
+            const features = target.cpu.features;
+            const flags: riscv.Eflags = @bitCast(e_flags);
+            var any_errors: bool = false;
+
+            // For an input object to target an ABI that the target CPU doesn't have enabled
+            // is invalid, and will throw an error.
+
+            // Invalid when
+            // 1. The input uses C and we do not.
+            if (flags.rvc and !std.Target.riscv.featureSetHas(features, .c)) {
+                any_errors = true;
+                diags.addParseError(
+                    path,
+                    "cannot link object file targeting the C feature without having the C feature enabled",
+                    .{},
+                );
+            }
+
+            // Invalid when
+            // 1. We use E and the input does not.
+            // 2. The input uses E and we do not.
+            if (std.Target.riscv.featureSetHas(features, .e) != flags.rve) {
+                any_errors = true;
+                diags.addParseError(
+                    path,
+                    "{s}",
+                    .{
+                        if (flags.rve)
+                            "cannot link object file targeting the E feature without having the E feature enabled"
+                        else
+                            "cannot link object file not targeting the E feature while having the E feature enabled",
+                    },
+                );
+            }
+
+            // Invalid when
+            // 1. We use total store order and the input does not.
+            // 2. The input uses total store order and we do not.
+            if (flags.tso != std.Target.riscv.featureSetHas(features, .ztso)) {
+                any_errors = true;
+                diags.addParseError(
+                    path,
+                    "cannot link object file targeting the TSO memory model without having the ztso feature enabled",
+                    .{},
+                );
+            }
+
+            const fabi: riscv.Eflags.FloatAbi =
+                if (std.Target.riscv.featureSetHas(features, .d))
+                .double
+            else if (std.Target.riscv.featureSetHas(features, .f))
+                .single
+            else
+                .soft;
+
+            if (flags.fabi != fabi) {
+                any_errors = true;
+                diags.addParseError(
+                    path,
+                    "cannot link object file targeting a different floating-point ABI. targeting {s}, found {s}",
+                    .{ @tagName(fabi), @tagName(flags.fabi) },
+                );
+            }
+
+            if (any_errors) return error.LinkFailure;
+        },
+        else => {},
     }
 }
 
src/link/Elf.zig
@@ -113,8 +113,6 @@ thunks: std.ArrayListUnmanaged(Thunk) = .empty,
 merge_sections: std.ArrayListUnmanaged(Merge.Section) = .empty,
 comment_merge_section_index: ?Merge.Section.Index = null,
 
-first_eflags: ?elf.Word = null,
-
 /// `--verbose-link` output.
 /// Initialized on creation, appended to as inputs are added, printed during `flush`.
 dump_argv_list: std.ArrayListUnmanaged([]const u8),
@@ -791,7 +789,7 @@ pub fn loadInput(self: *Elf, input: link.Input) !void {
         .res => unreachable,
         .dso_exact => @panic("TODO"),
         .object => |obj| try parseObject(self, obj),
-        .archive => |obj| try parseArchive(gpa, diags, &self.file_handles, &self.files, &self.first_eflags, target, debug_fmt_strip, default_sym_version, &self.objects, obj, is_static_lib),
+        .archive => |obj| try parseArchive(gpa, diags, &self.file_handles, &self.files, target, debug_fmt_strip, default_sym_version, &self.objects, obj, is_static_lib),
         .dso => |dso| try parseDso(gpa, diags, dso, &self.shared_objects, &self.files, target),
     }
 }
@@ -1124,7 +1122,6 @@ fn parseObject(self: *Elf, obj: link.Input.Object) !void {
 
     const gpa = self.base.comp.gpa;
     const diags = &self.base.comp.link_diags;
-    const first_eflags = &self.first_eflags;
     const target = self.base.comp.root_mod.resolved_target.result;
     const debug_fmt_strip = self.base.comp.config.debug_format == .strip;
     const default_sym_version = self.default_sym_version;
@@ -1145,7 +1142,7 @@ fn parseObject(self: *Elf, obj: link.Input.Object) !void {
     try self.objects.append(gpa, index);
 
     const object = self.file(index).?.object;
-    try object.parseCommon(gpa, diags, obj.path, handle, target, first_eflags);
+    try object.parseCommon(gpa, diags, obj.path, handle, target);
     if (!self.base.isStaticLib()) {
         try object.parse(gpa, diags, obj.path, handle, target, debug_fmt_strip, default_sym_version);
     }
@@ -1156,7 +1153,6 @@ fn parseArchive(
     diags: *Diags,
     file_handles: *std.ArrayListUnmanaged(File.Handle),
     files: *std.MultiArrayList(File.Entry),
-    first_eflags: *?elf.Word,
     target: std.Target,
     debug_fmt_strip: bool,
     default_sym_version: elf.Versym,
@@ -1179,7 +1175,7 @@ fn parseArchive(
         const object = &files.items(.data)[index].object;
         object.index = index;
         object.alive = init_alive;
-        try object.parseCommon(gpa, diags, obj.path, obj.file, target, first_eflags);
+        try object.parseCommon(gpa, diags, obj.path, obj.file, target);
         if (!is_static_lib)
             try object.parse(gpa, diags, obj.path, obj.file, target, debug_fmt_strip, default_sym_version);
         try objects.append(gpa, index);
src/link/riscv.zig
@@ -70,18 +70,20 @@ fn bitSlice(
     return @truncate((value >> low) & (1 << (high - low + 1)) - 1);
 }
 
-pub const RiscvEflags = packed struct(u32) {
+pub const Eflags = packed struct(u32) {
     rvc: bool,
-    fabi: enum(u2) {
+    fabi: FloatAbi,
+    rve: bool,
+    tso: bool,
+    _reserved: u19 = 0,
+    _unused: u8 = 0,
+
+    pub const FloatAbi = enum(u2) {
         soft = 0b00,
         single = 0b01,
         double = 0b10,
         quad = 0b11,
-    },
-    rve: bool,
-    tso: bool,
-    _reserved: u19,
-    _unused: u8,
+    };
 };
 
 const mem = std.mem;