Commit 6705cbd5eb

Jacob Young <jacobly0@users.noreply.github.com>
2025-03-23 02:56:32
codegen: fix packed byte-aligned relocations
Closes #23131
1 parent f45f964
src/link/Coff.zig
@@ -1134,7 +1134,7 @@ pub fn updateFunc(
     ) catch |err| switch (err) {
         error.CodegenFail => return error.CodegenFail,
         error.OutOfMemory => return error.OutOfMemory,
-        error.Overflow => |e| {
+        error.Overflow, error.RelocationNotByteAligned => |e| {
             try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
                 gpa,
                 zcu.navSrcLoc(nav_index),
src/link/Dwarf.zig
@@ -23,12 +23,11 @@ debug_str: StringSection,
 pub const UpdateError = error{
     ReinterpretDeclRef,
     Unimplemented,
-    OutOfMemory,
     EndOfStream,
-    Overflow,
     Underflow,
     UnexpectedEndOfFile,
 } ||
+    codegen.GenerateSymbolError ||
     std.fs.File.OpenError ||
     std.fs.File.SetEndPosError ||
     std.fs.File.CopyRangeError ||
src/link/Plan9.zig
@@ -1090,7 +1090,9 @@ fn updateLazySymbolAtom(
     ) catch |err| switch (err) {
         error.OutOfMemory => return error.OutOfMemory,
         error.CodegenFail => return error.LinkFailure,
-        error.Overflow => return diags.fail("codegen failure: encountered number too big for compiler", .{}),
+        error.Overflow,
+        error.RelocationNotByteAligned,
+        => return diags.fail("unable to codegen: {s}", .{@errorName(err)}),
     };
     const code = code_buffer.items;
     // duped_code is freed when the atom is freed
src/Zcu/PerThread.zig
@@ -1705,7 +1705,7 @@ pub fn linkerUpdateFunc(pt: Zcu.PerThread, func_index: InternPool.Index, air: Ai
         lf.updateFunc(pt, func_index, air, liveness) catch |err| switch (err) {
             error.OutOfMemory => return error.OutOfMemory,
             error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)),
-            error.Overflow => {
+            error.Overflow, error.RelocationNotByteAligned => {
                 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
                     gpa,
                     zcu.navSrcLoc(nav_index),
@@ -3131,7 +3131,7 @@ pub fn linkerUpdateNav(pt: Zcu.PerThread, nav_index: InternPool.Nav.Index) error
         lf.updateNav(pt, nav_index) catch |err| switch (err) {
             error.OutOfMemory => return error.OutOfMemory,
             error.CodegenFail => assert(zcu.failed_codegen.contains(nav_index)),
-            error.Overflow => {
+            error.Overflow, error.RelocationNotByteAligned => {
                 try zcu.failed_codegen.putNoClobber(gpa, nav_index, try Zcu.ErrorMsg.create(
                     gpa,
                     zcu.navSrcLoc(nav_index),
src/codegen.zig
@@ -23,10 +23,7 @@ const Zir = std.zig.Zir;
 const Alignment = InternPool.Alignment;
 const dev = @import("dev.zig");
 
-pub const CodeGenError = error{
-    OutOfMemory,
-    /// Compiler was asked to operate on a number larger than supported.
-    Overflow,
+pub const CodeGenError = GenerateSymbolError || error{
     /// Indicates the error is already stored in Zcu `failed_codegen`.
     CodegenFail,
 };
@@ -177,6 +174,8 @@ pub const GenerateSymbolError = error{
     OutOfMemory,
     /// Compiler was asked to operate on a number larger than supported.
     Overflow,
+    /// Compiler was asked to produce a non-byte-aligned relocation.
+    RelocationNotByteAligned,
 };
 
 pub fn generateSymbol(
@@ -481,12 +480,18 @@ pub fn generateSymbol(
                             // pointer may point to a decl which must be marked used
                             // but can also result in a relocation. Therefore we handle those separately.
                             if (Type.fromInterned(field_ty).zigTypeTag(zcu) == .pointer) {
-                                const field_size = math.cast(usize, Type.fromInterned(field_ty).abiSize(zcu)) orelse
-                                    return error.Overflow;
-                                var tmp_list = try std.ArrayListUnmanaged(u8).initCapacity(gpa, field_size);
-                                defer tmp_list.deinit(gpa);
-                                try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), &tmp_list, reloc_parent);
-                                @memcpy(code.items[current_pos..][0..tmp_list.items.len], tmp_list.items);
+                                const field_offset = std.math.divExact(u16, bits, 8) catch |err| switch (err) {
+                                    error.DivisionByZero => unreachable,
+                                    error.UnexpectedRemainder => return error.RelocationNotByteAligned,
+                                };
+                                code.items.len = current_pos + field_offset;
+                                // TODO: code.lockPointers();
+                                defer {
+                                    assert(code.items.len == current_pos + field_offset + @divExact(target.ptrBitWidth(), 8));
+                                    // TODO: code.unlockPointers();
+                                    code.items.len = current_pos + abi_size;
+                                }
+                                try generateSymbol(bin_file, pt, src_loc, Value.fromInterned(field_val), code, reloc_parent);
                             } else {
                                 Value.fromInterned(field_val).writeToPackedMemory(Type.fromInterned(field_ty), pt, code.items[current_pos..], bits) catch unreachable;
                             }
src/link.zig
@@ -26,6 +26,7 @@ const Package = @import("Package.zig");
 const dev = @import("dev.zig");
 const ThreadSafeQueue = @import("ThreadSafeQueue.zig").ThreadSafeQueue;
 const target_util = @import("target.zig");
+const codegen = @import("codegen.zig");
 
 pub const LdScript = @import("link/LdScript.zig");
 
@@ -683,13 +684,7 @@ pub const File = struct {
 
     /// Note that `LinkFailure` is not a member of this error set because the error message
     /// must be attached to `Zcu.failed_codegen` rather than `Compilation.link_diags`.
-    pub const UpdateNavError = error{
-        Overflow,
-        OutOfMemory,
-        /// Indicates the error is already reported and stored in
-        /// `failed_codegen` on the Zcu.
-        CodegenFail,
-    };
+    pub const UpdateNavError = codegen.CodeGenError;
 
     /// Called from within CodeGen to retrieve the symbol index of a global symbol.
     /// If no symbol exists yet with this name, a new undefined global symbol will
@@ -920,7 +915,7 @@ pub const File = struct {
         decl_val: InternPool.Index,
         decl_align: InternPool.Alignment,
         src_loc: Zcu.LazySrcLoc,
-    ) !@import("codegen.zig").GenResult {
+    ) !codegen.GenResult {
         switch (base.tag) {
             .c => unreachable,
             .spirv => unreachable,
src/register_manager.zig
@@ -14,14 +14,7 @@ const link = @import("link.zig");
 
 const log = std.log.scoped(.register_manager);
 
-pub const AllocationError = error{
-    OutOfRegisters,
-    OutOfMemory,
-    /// Compiler was asked to operate on a number larger than supported.
-    Overflow,
-    /// Indicates the error is already stored in `failed_codegen` on the Zcu.
-    CodegenFail,
-};
+pub const AllocationError = @import("codegen.zig").CodeGenError || error{OutOfRegisters};
 
 pub fn RegisterManager(
     comptime Function: type,
test/behavior/packed-struct.zig
@@ -1335,3 +1335,17 @@ test "assign packed struct initialized with RLS to packed struct literal field"
     try expect(outer.inner.x == x);
     try expect(outer.x == x);
 }
+
+test "byte-aligned packed relocation" {
+    if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_llvm) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_spirv64) return error.SkipZigTest;
+    if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
+
+    const S = struct {
+        var global: u8 align(2) = 0;
+        var packed_value: packed struct { x: u8, y: *align(2) u8 } = .{ .x = 111, .y = &global };
+    };
+    try expect(S.packed_value.x == 111);
+    try expect(S.packed_value.y == &S.global);
+}