Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC

Commits:

21 changed files:

Changes:

  • changelog.d/fix-finalizers-27072
    1
    +section: codegen
    
    2
    +synopsis: Fix module finalizers on multiple platforms
    
    3
    +description: {
    
    4
    +  GHC-generated module finalizers (e.g. ``hs_spt_remove`` for the Static
    
    5
    +  Pointer Table) now run correctly on ELF platforms, darwin, wasm32 and
    
    6
    +  Windows. Also fixes running finalizers when unloading objects with the
    
    7
    +  RTS linker.
    
    8
    +}
    
    9
    +issues: #27072
    
    10
    +mrs: !15762

  • compiler/GHC/Driver/CodeOutput.hs
    ... ... @@ -119,6 +119,7 @@ codeOutput logger tmpfs llvm_config dflags unit_state this_mod filenm location g
    119 119
                       { a <- linted_cmm_stream
    
    120 120
                       ; let stubs = genForeignStubs a
    
    121 121
                       ; emitInitializerDecls this_mod stubs
    
    122
    +                  ; emitFinalizerDecls this_mod stubs
    
    122 123
                       ; return (stubs, a) }
    
    123 124
     
    
    124 125
             ; let dus1 = newTagDUniqSupply CodeGenTag dus0
    
    ... ... @@ -133,19 +134,23 @@ codeOutput logger tmpfs llvm_config dflags unit_state this_mod filenm location g
    133 134
             }
    
    134 135
     
    
    135 136
     -- | See Note [Initializers and finalizers in Cmm] in GHC.Cmm.InitFini for details.
    
    136
    -emitInitializerDecls :: Module -> ForeignStubs -> CgStream RawCmmGroup ()
    
    137
    -emitInitializerDecls this_mod (ForeignStubs _ cstub)
    
    138
    -  | initializers <- getInitializers cstub
    
    139
    -  , not $ null initializers =
    
    140
    -      let init_array = CmmData sect statics
    
    141
    -          lbl = mkInitializerArrayLabel this_mod
    
    142
    -          sect = Section InitArray lbl
    
    137
    +emitInitializerDecls, emitFinalizerDecls :: Module -> ForeignStubs -> CgStream RawCmmGroup ()
    
    138
    +emitInitializerDecls = emitInitFiniArrayDecls InitArray mkInitializerArrayLabel getInitializers
    
    139
    +emitFinalizerDecls   = emitInitFiniArrayDecls FiniArray mkFinalizerArrayLabel   getFinalizers
    
    140
    +
    
    141
    +emitInitFiniArrayDecls :: SectionType -> (Module -> CLabel) -> (CStub -> [CLabel])
    
    142
    +                       -> Module -> ForeignStubs -> CgStream RawCmmGroup ()
    
    143
    +emitInitFiniArrayDecls sect_type mk_lbl get_labels this_mod (ForeignStubs _ cstub)
    
    144
    +  | labels <- get_labels cstub
    
    145
    +  , not $ null labels =
    
    146
    +      let lbl     = mk_lbl this_mod
    
    147
    +          sect    = Section sect_type lbl
    
    143 148
               statics = CmmStaticsRaw lbl
    
    144 149
                 [ CmmStaticLit $ CmmLabel fn_name
    
    145
    -            | fn_name <- initializers
    
    150
    +            | fn_name <- labels
    
    146 151
                 ]
    
    147
    -    in Stream.yield [init_array]
    
    148
    -emitInitializerDecls _ _ = return ()
    
    152
    +    in Stream.yield [CmmData sect statics]
    
    153
    +emitInitFiniArrayDecls _ _ _ _ _ = return ()
    
    149 154
     
    
    150 155
     doOutput :: String -> (Handle -> IO a) -> IO a
    
    151 156
     doOutput filenm io_action = bracket (openFile filenm WriteMode) hClose io_action
    

  • compiler/GHC/Linker/Executable.hs
    ... ... @@ -300,7 +300,19 @@ linkExecutable logger tmpfs opts unit_env o_files dep_units = do
    300 300
                          then ["-Wl,--gc-sections"]
    
    301 301
                          else [])
    
    302 302
     
    
    303
    +                 -- On Windows, module .o files may be archives (see
    
    304
    +                 -- Note [Object merging] in GHC.Driver.Pipeline.Execute).
    
    305
    +                 -- Use --whole-archive to ensure all archive members are
    
    306
    +                 -- included, especially those containing .ctors/.dtors
    
    307
    +                 -- initializer/finalizer sections. See Note [Initializers and
    
    308
    +                 -- finalizers in Cmm] in GHC.Cmm.InitFini.
    
    309
    +                 ++ (if platformOS platform == OSMinGW32
    
    310
    +                     then ["-Wl,--whole-archive"]
    
    311
    +                     else [])
    
    303 312
                      ++ o_files
    
    313
    +                 ++ (if platformOS platform == OSMinGW32
    
    314
    +                     then ["-Wl,--no-whole-archive"]
    
    315
    +                     else [])
    
    304 316
                      ++ lib_path_opts)
    
    305 317
                      ++ extra_ld_inputs
    
    306 318
                      ++ map GHC.SysTools.Option (
    

  • compiler/GHC/Types/ForeignStubs.hs
    ... ... @@ -59,11 +59,85 @@ initializerCStub platform clbl declarations body =
    59 59
     -- | @finalizerCStub fn_nm decls body@ is a 'CStub' containing C finalizer
    
    60 60
     -- function (e.g. an entry of the @.fini_array@ section) named
    
    61 61
     -- @fn_nm@ with the given body and the given set of declarations.
    
    62
    +--
    
    63
    +-- See Note [Finalizers via __cxa_atexit]
    
    62 64
     finalizerCStub :: Platform -> CLabel -> SDoc -> SDoc -> CStub
    
    63
    -finalizerCStub platform clbl declarations body =
    
    64
    -    functionCStub platform clbl declarations body
    
    65
    +finalizerCStub platform clbl declarations body
    
    66
    +  | ArchWasm32 <- platformArch platform
    
    67
    +  = -- See Note [Finalizers via __cxa_atexit]
    
    68
    +    cxaAtexitFinalizerCStub platform clbl declarations body
    
    69
    +finalizerCStub platform clbl declarations body
    
    70
    +  | OSDarwin <- platformOS platform
    
    71
    +  = -- See Note [Finalizers via __cxa_atexit]
    
    72
    +    cxaAtexitFinalizerCStub platform clbl declarations body
    
    73
    +finalizerCStub platform clbl declarations body
    
    74
    +  = functionCStub platform clbl declarations body
    
    65 75
         `mappend` CStub empty [] [clbl]
    
    66 76
     
    
    77
    +-- | Generate a @__cxa_atexit@-based finalizer.
    
    78
    +-- See Note [Finalizers via __cxa_atexit]
    
    79
    +cxaAtexitFinalizerCStub :: Platform -> CLabel -> SDoc -> SDoc -> CStub
    
    80
    +cxaAtexitFinalizerCStub platform clbl declarations body =
    
    81
    +    let clbl_pretty = pprCLabel platform clbl
    
    82
    +        fini_name    = hcat [clbl_pretty, text "$fini"]
    
    83
    +        wrapper_name = hcat [clbl_pretty, text "$fini_atexit"]
    
    84
    +        c_code = vcat
    
    85
    +          [ declarations
    
    86
    +          , text "int __cxa_atexit(void (*)(void *), void *, void *);"
    
    87
    +          , hcat [text "static void ", fini_name, text "(void)"]
    
    88
    +          , braces body
    
    89
    +          , hcat [text "static void ", wrapper_name, text "(void *arg __attribute__((unused)))"]
    
    90
    +          , braces (hcat [fini_name, text "();"])
    
    91
    +          , hsep [text "void", clbl_pretty, text "(void)"]
    
    92
    +          , braces (hcat [text "__cxa_atexit(", wrapper_name, text ", 0, 0);"])
    
    93
    +          ]
    
    94
    +    in CStub c_code [clbl] []
    
    95
    +
    
    96
    +{-
    
    97
    +Note [Finalizers via __cxa_atexit]
    
    98
    +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    99
    +On some platforms, placing a function pointer in the .fini_array /
    
    100
    +__mod_term_func section is not sufficient to have it called on exit.
    
    101
    +On these platforms we instead lower finalizers as initializers that register
    
    102
    +the actual finalizer function via __cxa_atexit.
    
    103
    +
    
    104
    +Affected platforms:
    
    105
    +
    
    106
    +  Wasm32: does not support .fini_array sections.
    
    107
    +
    
    108
    +  Darwin: modern macOS dyld no longer processes __DATA,__mod_term_func entries.
    
    109
    +  Clang now lowers __attribute__((destructor)) as an initializer that calls
    
    110
    +  __cxa_atexit, placing the initializer in __DATA,__mod_init_func (which the
    
    111
    +  linker converts to __TEXT,__init_offsets). GHC must follow the same pattern.
    
    112
    +
    
    113
    +For a finalizer with label `clbl` and body `body`, on these platforms we
    
    114
    +generate:
    
    115
    +
    
    116
    +    static void clbl$fini(void) {
    
    117
    +        <body>
    
    118
    +    }
    
    119
    +    static void clbl$fini_atexit(void *arg) {
    
    120
    +        clbl$fini();
    
    121
    +    }
    
    122
    +    void clbl(void) {
    
    123
    +        __cxa_atexit(clbl$fini_atexit, 0, 0);
    
    124
    +    }
    
    125
    +
    
    126
    +The function `clbl` is placed in the initializers list (getInitializers)
    
    127
    +instead of the finalizers list (getFinalizers). During code output,
    
    128
    +emitInitializerDecls places it in .init_array / __mod_init_func, so the
    
    129
    +registration runs at startup.
    
    130
    +
    
    131
    +The actual finalizer body is in the static helper `clbl$fini`. A separate
    
    132
    +wrapper `clbl$fini_atexit` with the void(*)(void*) signature expected by
    
    133
    +__cxa_atexit is needed because some platforms (e.g. wasm32) enforce exact
    
    134
    +function signature matching at call sites — a simple cast would trap at
    
    135
    +runtime.
    
    136
    +
    
    137
    +This matches what clang does when lowering __attribute__((destructor)) on
    
    138
    +these platforms.
    
    139
    +-}
    
    140
    +
    
    67 141
     newtype CHeader = CHeader { getCHeader :: SDoc }
    
    68 142
     
    
    69 143
     instance Monoid CHeader where
    

  • rts/Linker.c
    ... ... @@ -1117,6 +1117,27 @@ freePreloadObjectFile (ObjectCode *oc)
    1117 1117
         oc->fileSize = 0;
    
    1118 1118
     }
    
    1119 1119
     
    
    1120
    +/* Note [Object unloading and finalizers]
    
    1121
    + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    1122
    + * An ObjectCode may contain .fini_array/.dtors sections with finalizers that
    
    1123
    + * should run when the object is unloaded. However, we must only run these
    
    1124
    + * finalizers if the corresponding initializers (.init_array/.ctors) have
    
    1125
    + * actually been executed.
    
    1126
    + *
    
    1127
    + * Archive members start in OBJECT_LOADED state and only progress to
    
    1128
    + * OBJECT_NEEDED -> OBJECT_RESOLVED -> OBJECT_READY when a symbol from
    
    1129
    + * them is actually required. An archive member that was never needed never
    
    1130
    + * has its relocations applied, so its .fini_array section data still
    
    1131
    + * contains zeros (unresolved relocation targets). Running those finalizers
    
    1132
    + * would dereference NULL function pointers.
    
    1133
    + *
    
    1134
    + * When unloadObj sets an object's status to OBJECT_UNLOADED, it does so
    
    1135
    + * regardless of the previous state, so we cannot rely on the status alone
    
    1136
    + * to decide whether finalizers should run. Instead, we track whether
    
    1137
    + * initializers were executed via the initializersRan flag, which is set in
    
    1138
    + * ocRunInit after successfully running the initializers.
    
    1139
    + */
    
    1140
    +
    
    1120 1141
     /*
    
    1121 1142
      * freeObjectCode() releases all the pieces of an ObjectCode.  It is called by
    
    1122 1143
      * the GC when a previously unloaded ObjectCode has been determined to be
    
    ... ... @@ -1126,11 +1147,9 @@ void freeObjectCode (ObjectCode *oc)
    1126 1147
     {
    
    1127 1148
         IF_DEBUG(linker, ocDebugBelch(oc, "freeObjectCode: start\n"));
    
    1128 1149
     
    
    1129
    -    // Run finalizers
    
    1130
    -    if (oc->type == STATIC_OBJECT &&
    
    1131
    -            (oc->status == OBJECT_READY || oc->status == OBJECT_UNLOADED)) {
    
    1132
    -        // Only run finalizers if the initializers have also been run, which
    
    1133
    -        // happens when we resolve the object.
    
    1150
    +    // Run finalizers only if initializers have been run.
    
    1151
    +    // See Note [Object unloading and finalizers].
    
    1152
    +    if (oc->type == STATIC_OBJECT && oc->initializersRan) {
    
    1134 1153
     #if defined(OBJFORMAT_ELF)
    
    1135 1154
             ocRunFini_ELF(oc);
    
    1136 1155
     #elif defined(OBJFORMAT_PEi386)
    
    ... ... @@ -1295,6 +1314,7 @@ mkOc( ObjectType type, pathchar *path, char *image, int imageSize,
    1295 1314
        oc->imageMapped       = mapped;
    
    1296 1315
     
    
    1297 1316
        oc->misalignment      = misalignment;
    
    1317
    +   oc->initializersRan   = false;
    
    1298 1318
        oc->cxa_finalize      = NULL;
    
    1299 1319
        oc->extraInfos        = NULL;
    
    1300 1320
     
    
    ... ... @@ -1691,6 +1711,7 @@ int ocRunInit(ObjectCode *oc)
    1691 1711
         foreignExportsFinishedLoadingObject();
    
    1692 1712
     
    
    1693 1713
         if (!r) { return r; }
    
    1714
    +    oc->initializersRan = true;
    
    1694 1715
         oc->status = OBJECT_READY;
    
    1695 1716
     
    
    1696 1717
         return 1;
    

  • rts/LinkerInternals.h
    ... ... @@ -268,6 +268,12 @@ struct _ObjectCode {
    268 268
            after allocation, so that we can use realloc */
    
    269 269
         int        misalignment;
    
    270 270
     
    
    271
    +    /* Set to true after initializers (.init_array, .ctors, etc.) have been
    
    272
    +     * executed. Used by freeObjectCode to decide whether finalizers should
    
    273
    +     * run: only objects whose initializers ran should have their finalizers
    
    274
    +     * executed. See Note [Object unloading and finalizers]. */
    
    275
    +    bool initializersRan;
    
    276
    +
    
    271 277
         /* The address of __cxa_finalize; set when at least one finalizer was
    
    272 278
          * register and therefore we must call __cxa_finalize before unloading.
    
    273 279
          * See Note [Resolving __dso_handle]. */
    

  • rts/linker/Elf.c
    ... ... @@ -1308,6 +1308,16 @@ do_Elf_Rel_relocations ( ObjectCode* oc, char* ehdrC,
    1308 1308
            case COMPAT_R_386_NONE:                  break;
    
    1309 1309
            case COMPAT_R_386_32:   *pP = value;     break;
    
    1310 1310
            case COMPAT_R_386_PC32: *pP = value - P; break;
    
    1311
    +       case COMPAT_R_386_PLT32: *pP = value - P; break;
    
    1312
    +       case COMPAT_R_386_GOTOFF: *pP = value - (Elf_Addr)oc->info->got_start; break;
    
    1313
    +       case COMPAT_R_386_GOTPC:  *pP = (Elf_Addr)oc->info->got_start + A - P; break;
    
    1314
    +       case COMPAT_R_386_GOT32:
    
    1315
    +       case COMPAT_R_386_GOT32X:
    
    1316
    +           CHECK(symbol);
    
    1317
    +           CHECK(symbol->got_addr);
    
    1318
    +           *pP = (Elf_Addr)symbol->got_addr
    
    1319
    +               - (Elf_Addr)oc->info->got_start + A;
    
    1320
    +           break;
    
    1311 1321
     #        endif
    
    1312 1322
     
    
    1313 1323
     #        if defined(arm_HOST_ARCH)
    

  • testsuite/tests/codeGen/should_run/T27072d.hs
    1
    +{-# LANGUAGE StaticPointers #-}
    
    2
    +module T27072d where
    
    3
    +
    
    4
    +import GHC.StaticPtr
    
    5
    +
    
    6
    +f :: StaticPtr Int
    
    7
    +f = static 1
    
    8
    +
    
    9
    +g :: StaticPtr Int
    
    10
    +g = static 2

  • testsuite/tests/codeGen/should_run/T27072d.stdout
    1
    +SPT entries after init: 2
    
    2
    +SPT entries after finalizer: 0

  • testsuite/tests/codeGen/should_run/T27072d_c.c
    1
    +// Test that GHC-generated module initializers and finalizer registrations
    
    2
    +// work correctly on Darwin.
    
    3
    +//
    
    4
    +// On Darwin, GHC lowers finalizers as __cxa_atexit registrations from an
    
    5
    +// initializer placed in __DATA,__mod_init_func (see Note [Finalizers via
    
    6
    +// __cxa_atexit] in GHC.Types.ForeignStubs).
    
    7
    +//
    
    8
    +// This test verifies the mechanism by checking that:
    
    9
    +//  1. The SPT initializer runs at load time (entries are inserted).
    
    10
    +//  2. The SPT finalizer (registered via __cxa_atexit from __mod_init_func)
    
    11
    +//     fires during exit() and removes the entries.
    
    12
    +//
    
    13
    +// We verify (2) by registering our own __cxa_atexit checker from a
    
    14
    +// constructor in a dylib that is loaded before the main executable's
    
    15
    +// initializers run. Since __cxa_atexit handlers fire in LIFO order,
    
    16
    +// a handler registered earlier runs later — so our checker runs after the
    
    17
    +// GHC-generated finalizer, and can observe that SPT entries were removed.
    
    18
    +//
    
    19
    +// The Apple linker does not support --wrap, so this is the Darwin
    
    20
    +// equivalent of T27072w's approach.
    
    21
    +
    
    22
    +#include "Rts.h"
    
    23
    +#include <stdio.h>
    
    24
    +
    
    25
    +extern int hs_spt_key_count(void);
    
    26
    +
    
    27
    +int main(int argc, char *argv[]) {
    
    28
    +    RtsConfig conf = defaultRtsConfig;
    
    29
    +    conf.rts_opts_enabled = RtsOptsAll;
    
    30
    +    hs_init_ghc(&argc, &argv, conf);
    
    31
    +
    
    32
    +    printf("SPT entries after init: %d\n", hs_spt_key_count());
    
    33
    +    fflush(stdout);
    
    34
    +
    
    35
    +    // Do NOT call hs_exit(). Return normally so __cxa_atexit handlers fire,
    
    36
    +    // which includes the GHC-generated finalizer registered during init.
    
    37
    +    return 0;
    
    38
    +}

  • testsuite/tests/codeGen/should_run/T27072d_check.c
    1
    +// Checker dylib for T27072d.
    
    2
    +//
    
    3
    +// Compiled as a dylib and linked against the test executable. Because dylib
    
    4
    +// initializers run before the main executable's __mod_init_func entries,
    
    5
    +// our __cxa_atexit registration happens first. Since __cxa_atexit handlers
    
    6
    +// fire in LIFO order, our checker runs *after* the GHC-generated finalizer,
    
    7
    +// allowing us to observe that SPT entries were removed.
    
    8
    +
    
    9
    +#include <stdio.h>
    
    10
    +
    
    11
    +// Provided by the RTS.
    
    12
    +extern int hs_spt_key_count(void);
    
    13
    +
    
    14
    +static void check_spt_finalizer(void *arg __attribute__((unused))) {
    
    15
    +    int count = hs_spt_key_count();
    
    16
    +    printf("SPT entries after finalizer: %d\n", count);
    
    17
    +    fflush(stdout);
    
    18
    +}
    
    19
    +
    
    20
    +// Register the checker. This constructor runs during dylib initialization,
    
    21
    +// which happens before the main executable's initializers.
    
    22
    +__attribute__((constructor))
    
    23
    +static void register_spt_checker(void) {
    
    24
    +    // Use __cxa_atexit so we participate in the same LIFO chain as the
    
    25
    +    // GHC-generated finalizer.
    
    26
    +    extern int __cxa_atexit(void (*)(void *), void *, void *);
    
    27
    +    extern void *__dso_handle;
    
    28
    +    __cxa_atexit(check_spt_finalizer, (void *)0, &__dso_handle);
    
    29
    +}

  • testsuite/tests/codeGen/should_run/T27072w.hs
    1
    +{-# LANGUAGE StaticPointers #-}
    
    2
    +module T27072w where
    
    3
    +
    
    4
    +import GHC.StaticPtr
    
    5
    +
    
    6
    +f :: StaticPtr Int
    
    7
    +f = static 1
    
    8
    +
    
    9
    +g :: StaticPtr Int
    
    10
    +g = static 2

  • testsuite/tests/codeGen/should_run/T27072w.stdout
    1
    +SPT entries after init: 2
    
    2
    +finalizer: hs_spt_remove called
    
    3
    +finalizer: hs_spt_remove called

  • testsuite/tests/codeGen/should_run/T27072w_c.c
    1
    +// Test that GHC-generated finalizers actually run on wasm32
    
    2
    +//
    
    3
    +// We use --wrap=hs_spt_remove to intercept calls from the GHC-generated
    
    4
    +// finalizer and verify they happen during exit().
    
    5
    +
    
    6
    +#include "Rts.h"
    
    7
    +#include <stdio.h>
    
    8
    +
    
    9
    +extern int hs_spt_key_count(void);
    
    10
    +
    
    11
    +// --wrap=hs_spt_remove: the linker redirects all calls to hs_spt_remove
    
    12
    +// through our wrapper, and provides __real_hs_spt_remove for the original.
    
    13
    +extern void __real_hs_spt_remove(StgWord64 key[2]);
    
    14
    +
    
    15
    +void __wrap_hs_spt_remove(StgWord64 key[2]) {
    
    16
    +    printf("finalizer: hs_spt_remove called\n");
    
    17
    +    fflush(stdout);
    
    18
    +    __real_hs_spt_remove(key);
    
    19
    +}
    
    20
    +
    
    21
    +int main(int argc, char *argv[]) {
    
    22
    +    RtsConfig conf = defaultRtsConfig;
    
    23
    +    conf.rts_opts_enabled = RtsOptsAll;
    
    24
    +    hs_init_ghc(&argc, &argv, conf);
    
    25
    +
    
    26
    +    printf("SPT entries after init: %d\n", hs_spt_key_count());
    
    27
    +    fflush(stdout);
    
    28
    +
    
    29
    +    // Do NOT call hs_exit(). Return normally so exit() fires the
    
    30
    +    // __cxa_atexit registered handlers.
    
    31
    +    return 0;
    
    32
    +}

  • testsuite/tests/codeGen/should_run/all.T
    ... ... @@ -260,3 +260,22 @@ test('T25364', normal, compile_and_run, [''])
    260 260
     test('T26061', normal, compile_and_run, [''])
    
    261 261
     test('T26537', normal, compile_and_run, ['-O2 -fregs-graph'])
    
    262 262
     test('T24016', normal, compile_and_run, ['-O1 -fPIC'])
    
    263
    +
    
    264
    +# Check that GHC-generated finalizers run on Darwin. The Apple linker doesn't
    
    265
    +# support --wrap, so we can't intercept hs_spt_remove directly.  Instead we
    
    266
    +# compile a small checker dylib (T27072d_check.c) whose constructor registers
    
    267
    +# a __cxa_atexit handler *before* the executable's __mod_init_func entries run.
    
    268
    +# LIFO ordering ensures the checker fires after the GHC-generated finalizer,
    
    269
    +# so it can observe that SPT entries were removed.
    
    270
    +# Requires dynamic way so the RTS is a dylib (avoids archive conflicts).
    
    271
    +test('T27072d', [req_c, only_ways(['dyn']), when(not opsys('darwin'), skip),
    
    272
    +     pre_cmd('{compiler} -shared -no-hs-main'
    
    273
    +             ' -optl -undefined -optl dynamic_lookup'
    
    274
    +             ' -o T27072d_check.dylib T27072d_check.c')],
    
    275
    +     compile_and_run,
    
    276
    +     ['T27072d_c.c -no-hs-main'
    
    277
    +      ' -optl -Wl,-needed_library,T27072d_check.dylib -optl -rpath -optl .'])
    
    278
    +# check that finalizers are being run, using --wrap to intercept hs_spt_remove.
    
    279
    +# Skipped on Darwin (Apple linker doesn't support --wrap).
    
    280
    +test('T27072w', [req_c, js_skip, when(opsys('darwin'), skip)],
    
    281
    +     compile_and_run, ['T27072w_c.c -no-hs-main -optl-Wl,--wrap=hs_spt_remove'])

  • testsuite/tests/rts/linker/T27072/Lib.c
    1
    +// Minimal module with an initializer and finalizer.
    
    2
    +// The compiler places the function pointers in .init_array/.fini_array
    
    3
    +// (ELF) or __mod_init_func/__mod_term_func (Mach-O).
    
    4
    +//
    
    5
    +// The counter lives in the main binary so it survives after this
    
    6
    +// object is unloaded.
    
    7
    +
    
    8
    +extern int init_counter;
    
    9
    +
    
    10
    +__attribute__((constructor))
    
    11
    +static void lib_init(void) {
    
    12
    +    init_counter++;
    
    13
    +}
    
    14
    +
    
    15
    +__attribute__((destructor))
    
    16
    +static void lib_fini(void) {
    
    17
    +    init_counter--;
    
    18
    +}

  • testsuite/tests/rts/linker/T27072/Makefile
    1
    +.PHONY: clean_build_and_run build_and_run clean build
    
    2
    +
    
    3
    +clean_build_and_run:
    
    4
    +	$(MAKE) clean
    
    5
    +	$(MAKE) build_and_run
    
    6
    +
    
    7
    +build_and_run: build
    
    8
    +	./main
    
    9
    +
    
    10
    +clean:
    
    11
    +	$(RM) Lib.o main.o main
    
    12
    +
    
    13
    +build: Lib.o main
    
    14
    +
    
    15
    +Lib.o: Lib.c
    
    16
    +	$(CC) -c -fPIC Lib.c -o Lib.o
    
    17
    +
    
    18
    +main: main.c
    
    19
    +	"$(TEST_HC)" $(filter-out -rtsopts, $(TEST_HC_OPTS)) \
    
    20
    +		-no-hs-main -optc-Werror \
    
    21
    +		main.c -o main

  • testsuite/tests/rts/linker/T27072/T27072.stdout
    1
    +counter before load: 0
    
    2
    +counter after load: 1
    
    3
    +counter after unload: 0

  • testsuite/tests/rts/linker/T27072/all.T
    1
    +test('T27072',
    
    2
    +     [req_rts_linker,
    
    3
    +      js_skip,
    
    4
    +      extra_files(['Lib.c', 'main.c'])],
    
    5
    +     makefile_test,
    
    6
    +     ['clean_build_and_run'])

  • testsuite/tests/rts/linker/T27072/main.c
    1
    +// Test that the RTS linker executes .init_array entries on load and
    
    2
    +// .fini_array entries on unload.  The loaded module increments a
    
    3
    +// counter in its initializer and decrements it in its finalizer.
    
    4
    +
    
    5
    +#include "Rts.h"
    
    6
    +#include <stdio.h>
    
    7
    +
    
    8
    +#if defined(mingw32_HOST_OS)
    
    9
    +#define PATH_STR(str) L##str
    
    10
    +#else
    
    11
    +#define PATH_STR(str) str
    
    12
    +#endif
    
    13
    +
    
    14
    +int init_counter = 0;
    
    15
    +
    
    16
    +int main(int argc, char *argv[]) {
    
    17
    +    RtsConfig conf = defaultRtsConfig;
    
    18
    +    conf.rts_opts_enabled = RtsOptsAll;
    
    19
    +    hs_init_ghc(&argc, &argv, conf);
    
    20
    +
    
    21
    +    initLinker_(0);
    
    22
    +    insertSymbol(PATH_STR("main"), "init_counter", &init_counter);
    
    23
    +
    
    24
    +    printf("counter before load: %d\n", init_counter);
    
    25
    +    fflush(stdout);
    
    26
    +
    
    27
    +    int ok;
    
    28
    +    ok = loadObj(PATH_STR("Lib.o"));
    
    29
    +    if (!ok) {
    
    30
    +        errorBelch("loadObj(Lib.o) failed");
    
    31
    +        return 1;
    
    32
    +    }
    
    33
    +    ok = resolveObjs();
    
    34
    +    if (!ok) {
    
    35
    +        errorBelch("resolveObjs() failed");
    
    36
    +        return 1;
    
    37
    +    }
    
    38
    +
    
    39
    +    printf("counter after load: %d\n", init_counter);
    
    40
    +    fflush(stdout);
    
    41
    +
    
    42
    +    ok = unloadObj(PATH_STR("Lib.o"));
    
    43
    +    if (!ok) {
    
    44
    +        errorBelch("unloadObj(Lib.o) failed");
    
    45
    +        return 1;
    
    46
    +    }
    
    47
    +
    
    48
    +    // GC triggers actual unloading and finalizer execution.
    
    49
    +    performMajorGC();
    
    50
    +    performMajorGC();
    
    51
    +
    
    52
    +    printf("counter after unload: %d\n", init_counter);
    
    53
    +    fflush(stdout);
    
    54
    +
    
    55
    +    hs_exit();
    
    56
    +    return 0;
    
    57
    +}

  • testsuite/tests/th/all.T
    ... ... @@ -636,7 +636,6 @@ test('T25209', normal, compile, ['-v0 -ddump-splices -dsuppress-uniques'])
    636 636
     test('TH_MultilineStrings', normal, compile_and_run, [''])
    
    637 637
     test('T25252',
    
    638 638
       [extra_files(['T25252B.hs', 'T25252_c.c']),
    
    639
    -   when(arch('i386'), expect_broken_for(25260,['ext-interp'])),
    
    640 639
        req_th,
    
    641 640
        req_c],
    
    642 641
       compile_and_run, ['-fPIC T25252_c.c'])