From d3eaabd2855f21d0c7cf4a4e1fe52dfc50a2b07f Mon Sep 17 00:00:00 2001 From: Jakub Konka Date: Tue, 30 Aug 2022 23:07:28 +0200 Subject: [PATCH 1/3] coff: add base relocation related types --- lib/std/coff.zig | 78 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/lib/std/coff.zig b/lib/std/coff.zig index e822416f70..1caec57c4a 100644 --- a/lib/std/coff.zig +++ b/lib/std/coff.zig @@ -310,6 +310,84 @@ pub const ImageDataDirectory = extern struct { size: u32, }; +pub const BaseRelocationDirectoryEntry = extern struct { + /// The image base plus the page RVA is added to each offset to create the VA where the base relocation must be applied. + page_rva: u32, + + /// The total number of bytes in the base relocation block, including the Page RVA and Block Size fields and the Type/Offset fields that follow. + block_size: u32, +}; + +pub const BaseRelocation = packed struct { + /// Stored in the remaining 12 bits of the WORD, an offset from the starting address that was specified in the Page RVA field for the block. + /// This offset specifies where the base relocation is to be applied. + offset: u12, + + /// Stored in the high 4 bits of the WORD, a value that indicates the type of base relocation to be applied. + @"type": BaseRelocationType, +}; + +pub const BaseRelocationType = enum(u4) { + /// The base relocation is skipped. This type can be used to pad a block. + ABSOLUTE = 0, + + /// The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the high value of a 32-bit word. + HIGH = 1, + + /// The base relocation adds the low 16 bits of the difference to the 16-bit field at offset. The 16-bit field represents the low half of a 32-bit word. + LOW = 2, + + /// The base relocation applies all 32 bits of the difference to the 32-bit field at offset. + HIGHLOW = 3, + + /// The base relocation adds the high 16 bits of the difference to the 16-bit field at offset. + /// The 16-bit field represents the high value of a 32-bit word. + /// The low 16 bits of the 32-bit value are stored in the 16-bit word that follows this base relocation. + /// This means that this base relocation occupies two slots. + HIGHADJ = 4, + + /// When the machine type is MIPS, the base relocation applies to a MIPS jump instruction. + MIPS_JMPADDR = 5, + + /// This relocation is meaningful only when the machine type is ARM or Thumb. + /// The base relocation applies the 32-bit address of a symbol across a consecutive MOVW/MOVT instruction pair. + // ARM_MOV32 = 5, + + /// This relocation is only meaningful when the machine type is RISC-V. + /// The base relocation applies to the high 20 bits of a 32-bit absolute address. + // RISCV_HIGH20 = 5, + + /// Reserved, must be zero. + RESERVED = 6, + + /// This relocation is meaningful only when the machine type is Thumb. + /// The base relocation applies the 32-bit address of a symbol to a consecutive MOVW/MOVT instruction pair. + THUMB_MOV32 = 7, + + /// This relocation is only meaningful when the machine type is RISC-V. + /// The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V I-type instruction format. + // RISCV_LOW12I = 7, + + /// This relocation is only meaningful when the machine type is RISC-V. + /// The base relocation applies to the low 12 bits of a 32-bit absolute address formed in RISC-V S-type instruction format. + RISCV_LOW12S = 8, + + /// This relocation is only meaningful when the machine type is LoongArch 32-bit. + /// The base relocation applies to a 32-bit absolute address formed in two consecutive instructions. + // LOONGARCH32_MARK_LA = 8, + + /// This relocation is only meaningful when the machine type is LoongArch 64-bit. + /// The base relocation applies to a 64-bit absolute address formed in four consecutive instructions. + // LOONGARCH64_MARK_LA = 8, + + /// The relocation is only meaningful when the machine type is MIPS. + /// The base relocation applies to a MIPS16 jump instruction. + MIPS_JMPADDR16 = 9, + + /// The base relocation applies the difference to the 64-bit field at offset. + DIR64 = 10, +}; + pub const DebugDirectoryEntry = extern struct { characteristics: u32, time_date_stamp: u32, From 35e0ff7c364487152d786347cf70f47b2a390f12 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Aug 2022 14:59:10 -0700 Subject: [PATCH 2/3] CI: drone: disable failing tests See tracking issue #12689 --- ci/drone/linux_script_finalize | 3 ++- ci/drone/test_linux_behavior | 4 +++- ci/drone/test_linux_misc | 6 ++++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/ci/drone/linux_script_finalize b/ci/drone/linux_script_finalize index 4219bd19ac..43ff5f8f0e 100755 --- a/ci/drone/linux_script_finalize +++ b/ci/drone/linux_script_finalize @@ -19,7 +19,8 @@ pip3 install s3cmd cd build mv ../LICENSE "$INSTALL_PREFIX/" -mv ../zig-cache/langref.html "$INSTALL_PREFIX/" +# https://github.com/ziglang/zig/issues/12689 +# mv ../zig-cache/langref.html "$INSTALL_PREFIX/" mv "$INSTALL_PREFIX/bin/zig" "$INSTALL_PREFIX/" rmdir "$INSTALL_PREFIX/bin" diff --git a/ci/drone/test_linux_behavior b/ci/drone/test_linux_behavior index 53c9d94a1e..7aa9c6dcc0 100755 --- a/ci/drone/test_linux_behavior +++ b/ci/drone/test_linux_behavior @@ -7,8 +7,10 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" ZIG="$INSTALL_PREFIX/bin/zig" export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" +# Tracking issue for the disabled tests: # https://github.com/ziglang/zig/issues/12689 + # $ZIG build test-behavior -Dskip-non-native --zig-lib-dir lib $ZIG build test-compiler-rt -Dskip-non-native --zig-lib-dir lib $ZIG build test-fmt --zig-lib-dir lib -$ZIG build docs --zig-lib-dir lib +# $ZIG build docs --zig-lib-dir lib diff --git a/ci/drone/test_linux_misc b/ci/drone/test_linux_misc index a64edf19b8..ebbeee7576 100755 --- a/ci/drone/test_linux_misc +++ b/ci/drone/test_linux_misc @@ -7,10 +7,12 @@ INSTALL_PREFIX="$DRONE_WORKSPACE/stage3-release" ZIG="$INSTALL_PREFIX/bin/zig" export ZIG_GLOBAL_CACHE_DIR="$DRONE_WORKSPACE/zig-cache" -$ZIG build test-universal-libc -Dskip-non-native --zig-lib-dir lib +# Tracking issue for the disabled tests: # https://github.com/ziglang/zig/issues/12689 + +$ZIG build test-universal-libc -Dskip-non-native --zig-lib-dir lib # $ZIG build test-compare-output -Dskip-non-native --zig-lib-dir lib -$ZIG build test-standalone -Dskip-non-native --zig-lib-dir lib -Dskip-release-safe +# $ZIG build test-standalone -Dskip-non-native --zig-lib-dir lib -Dskip-release-safe $ZIG build test-stack-traces -Dskip-non-native --zig-lib-dir lib $ZIG build test-cli -Dskip-non-native --zig-lib-dir lib $ZIG build test-asm-link -Dskip-non-native --zig-lib-dir lib From 56cfa8f22f69d813efedb1fa01fdcb7077ca0e5a Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 30 Aug 2022 17:35:25 -0700 Subject: [PATCH 3/3] Sema: prevent access of undefined fields When instantiating a generic function, there is a period of time where the function is inserted into monomorphed_funcs map, but is not yet initialized. Despite semantic analysis being single-threaded, generic function instantiation can happen recursively, meaning that the hash and equality functions for monomorphed_funcs entries are potentially invoked for an uninitialized function. This problem was mitigated by pre-setting the hash field on the newly allocated function, however it did not solve the problem for hash collisions in which case the equality function would be invoked. That it was solved for hash() but not eql() explains why the problem was difficult to observe. I tested this patch by temporarily sabotaging the hash and making it always return 0. This fix is centered on adding a new field to Module.Fn which is the one checked by eql() and is populated pre-initialization. closes #12643 --- src/Module.zig | 16 ++++++++++++++++ src/Sema.zig | 16 ++++++++++------ 2 files changed, 26 insertions(+), 6 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index 66c36b939b..9410c4ea4a 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -1496,6 +1496,22 @@ pub const Fn = struct { /// active Sema context. Importantly, this value is also updated when an existing /// generic function instantiation is found and called. branch_quota: u32, + + /// If this is not none, this function is a generic function instantiation, and + /// this is the generic function decl from which the instance was derived. + /// This information is redundant with a combination of checking if comptime_args is + /// not null and looking at the first decl dependency of owner_decl. This redundant + /// information is useful for three reasons: + /// 1. Improved perf of monomorphed_funcs when checking the eql() function because it + /// can do two fewer pointer chases by grabbing the info from this field directly + /// instead of accessing the decl and then the dependencies set. + /// 2. While a generic function instantiation is being initialized, we need hash() + /// and eql() to work before the initialization is complete. Completing the + /// insertion into the decl dependency set has more fallible operations than simply + /// setting this field. + /// 3. I forgot what the third thing was while typing up the other two. + generic_owner_decl: Decl.OptionalIndex, + state: Analysis, is_cold: bool = false, is_noinline: bool, diff --git a/src/Sema.zig b/src/Sema.zig index 0626fd30ee..7a96fd51cd 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -5590,11 +5590,10 @@ const GenericCallAdapter = struct { pub fn eql(ctx: @This(), adapted_key: void, other_key: *Module.Fn) bool { _ = adapted_key; - // The generic function Decl is guaranteed to be the first dependency - // of each of its instantiations. - const other_owner_decl = ctx.module.declPtr(other_key.owner_decl); - const generic_owner_decl = other_owner_decl.dependencies.keys()[0]; - if (ctx.generic_fn.owner_decl != generic_owner_decl) return false; + // Checking for equality may happen on an item that has been inserted + // into the map but is not yet fully initialized. In such case, the + // two initialized fields are `hash` and `generic_owner_decl`. + if (ctx.generic_fn.owner_decl != other_key.generic_owner_decl.unwrap().?) return false; const other_comptime_args = other_key.comptime_args.?; for (other_comptime_args[0..ctx.func_ty_info.param_types.len]) |other_arg, i| { @@ -6447,11 +6446,14 @@ fn instantiateGenericCall( const gop = try mod.monomorphed_funcs.getOrPutAdapted(gpa, {}, adapter); const callee = if (!gop.found_existing) callee: { const new_module_func = try gpa.create(Module.Fn); + errdefer gpa.destroy(new_module_func); + // This ensures that we can operate on the hash map before the Module.Fn // struct is fully initialized. new_module_func.hash = precomputed_hash; + new_module_func.generic_owner_decl = module_fn.owner_decl.toOptional(); + new_module_func.comptime_args = null; gop.key_ptr.* = new_module_func; - errdefer gpa.destroy(new_module_func); errdefer assert(mod.monomorphed_funcs.remove(new_module_func)); try namespace.anon_decls.ensureUnusedCapacity(gpa, 1); @@ -8032,11 +8034,13 @@ fn funcCommon( } else null; const hash = new_func.hash; + const generic_owner_decl = if (comptime_args == null) .none else new_func.generic_owner_decl; const fn_payload = try sema.arena.create(Value.Payload.Function); new_func.* = .{ .state = anal_state, .zir_body_inst = func_inst, .owner_decl = sema.owner_decl_index, + .generic_owner_decl = generic_owner_decl, .comptime_args = comptime_args, .hash = hash, .lbrace_line = src_locs.lbrace_line,