Ben Gamari pushed to branch wip/T25943 at Glasgow Haskell Compiler / GHC

Commits:

1 changed file:

Changes:

  • rts/linker/LoadNativeObjPosix.c
    ... ... @@ -88,6 +88,26 @@ void freeNativeCode_POSIX (ObjectCode *nc) {
    88 88
       }
    
    89 89
     }
    
    90 90
     
    
    91
    +/*
    
    92
    + * Note [Don't fail due to RTLD_NOW]
    
    93
    + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    94
    + * If possible we want to load dynamic objects immediately (e.g. using RTLD_NOW)
    
    95
    + * so that we can query their mappings and therefore be able to safely unload
    
    96
    + * them. However, there are some cases where an object cannot be
    
    97
    + * successfully eagerly loaded yet execution can nevertheless
    
    98
    + * succeed with lazy binding. One such instance was found in #25943, where
    
    99
    + * a library referenced undefined symbols. While this pattern is quite dodgy
    
    100
    + * (really, these symbol references should be weakly bound in the library),
    
    101
    + * previous GHC versions accepted such programs. Moreover, it is important that
    
    102
    + * we are able to load such libraries since GHC insists on loading all package
    
    103
    + * dependencies when, e.g., evaluating TemplateHaskell splices.
    
    104
    + *
    
    105
    + * To ensure that we don't fail to load such programs, we first attempt
    
    106
    + * loading with RTLD_NOW and, if this fails, attempt to load again
    
    107
    + * with lazy binding (taking care to mark the object as not unloadable in
    
    108
    + * this case).
    
    109
    + */
    
    110
    +
    
    91 111
     void * loadNativeObj_POSIX (pathchar *path, char **errmsg)
    
    92 112
     {
    
    93 113
        ObjectCode* nc;
    
    ... ... @@ -99,7 +119,6 @@ void * loadNativeObj_POSIX (pathchar *path, char **errmsg)
    99 119
     
    
    100 120
        retval = NULL;
    
    101 121
     
    
    102
    -
    
    103 122
        /* If we load the same object multiple times, just return the
    
    104 123
         * already-loaded handle. Note that this is broken if unloadNativeObj
    
    105 124
         * is used, as we don’t do any reference counting; see #24345.
    
    ... ... @@ -116,6 +135,23 @@ void * loadNativeObj_POSIX (pathchar *path, char **errmsg)
    116 135
     
    
    117 136
        nc = mkOc(DYNAMIC_OBJECT, path, NULL, 0, false, NULL, 0);
    
    118 137
     
    
    138
    +   // If we HAVE_DLINFO, we use RTLD_NOW rather than RTLD_LAZY because we want
    
    139
    +   // to learn eagerly about all external functions. Otherwise, there is no
    
    140
    +   // additional advantage to being eager, so it is better to be lazy and only
    
    141
    +   // bind functions when needed for better performance.
    
    142
    +   //
    
    143
    +   // Moreover, it is possible that loading will fail (e.g. if the library
    
    144
    +   // being loaded depends upon symbols from a library which is not available);
    
    145
    +   // in this case we will retry loading with load_now=false. See
    
    146
    +   // Note [Don't fail due to RTLD_NOW]..
    
    147
    +   bool load_now;
    
    148
    +#if defined(HAVE_DLINFO)
    
    149
    +   load_now = true;
    
    150
    +#else
    
    151
    +   load_now = false;
    
    152
    +#endif
    
    153
    +
    
    154
    +try_again:
    
    119 155
        foreignExportsLoadingObject(nc);
    
    120 156
     
    
    121 157
        // When dlopen() loads a profiled dynamic library, it calls the ctors which
    
    ... ... @@ -129,17 +165,7 @@ void * loadNativeObj_POSIX (pathchar *path, char **errmsg)
    129 165
        ACQUIRE_LOCK(&ccs_mutex);
    
    130 166
     #endif
    
    131 167
     
    
    132
    -   // If we HAVE_DLINFO, we use RTLD_NOW rather than RTLD_LAZY because we want
    
    133
    -   // to learn eagerly about all external functions. Otherwise, there is no
    
    134
    -   // additional advantage to being eager, so it is better to be lazy and only bind
    
    135
    -   // functions when needed for better performance.
    
    136
    -   int dlopen_mode;
    
    137
    -#if defined(HAVE_DLINFO)
    
    138
    -   dlopen_mode = RTLD_NOW;
    
    139
    -#else
    
    140
    -   dlopen_mode = RTLD_LAZY;
    
    141
    -#endif
    
    142
    -
    
    168
    +   const int dlopen_mode = load_now ? RTLD_NOW : RTLD_LAZY;
    
    143 169
        hdl = dlopen(path, dlopen_mode|RTLD_LOCAL); /* see Note [RTLD_LOCAL] */
    
    144 170
        nc->dlopen_handle = hdl;
    
    145 171
        nc->status = OBJECT_READY;
    
    ... ... @@ -151,31 +177,42 @@ void * loadNativeObj_POSIX (pathchar *path, char **errmsg)
    151 177
        foreignExportsFinishedLoadingObject();
    
    152 178
     
    
    153 179
        if (hdl == NULL) {
    
    154
    -     /* dlopen failed; save the message in errmsg */
    
    155
    -     copyErrmsg(errmsg, dlerror());
    
    156
    -     goto dlopen_fail;
    
    180
    +     if (load_now) {
    
    181
    +       // See Note [Don't fail due to RTLD_NOW]
    
    182
    +       load_now = false;
    
    183
    +       goto try_again;
    
    184
    +     } else {
    
    185
    +       /* dlopen failed; save the message in errmsg */
    
    186
    +       copyErrmsg(errmsg, dlerror());
    
    187
    +       goto dlopen_fail;
    
    188
    +     }
    
    157 189
        }
    
    158 190
     
    
    159 191
     #if defined(HAVE_DLINFO)
    
    160
    -   struct link_map *map;
    
    161
    -   if (dlinfo(hdl, RTLD_DI_LINKMAP, &map) == -1) {
    
    162
    -     /* dlinfo failed; save the message in errmsg */
    
    163
    -     copyErrmsg(errmsg, dlerror());
    
    164
    -     goto dlinfo_fail;
    
    165
    -   }
    
    192
    +   if (load_now) {
    
    193
    +     struct link_map *map;
    
    194
    +     if (dlinfo(hdl, RTLD_DI_LINKMAP, &map) == -1) {
    
    195
    +       /* dlinfo failed; save the message in errmsg */
    
    196
    +       copyErrmsg(errmsg, dlerror());
    
    197
    +       goto dlinfo_fail;
    
    198
    +     }
    
    166 199
     
    
    167
    -   hdl = NULL; // pass handle ownership to nc
    
    200
    +     hdl = NULL; // pass handle ownership to nc
    
    168 201
     
    
    169
    -   struct piterate_cb_info piterate_info = {
    
    170
    -     .nc = nc,
    
    171
    -     .l_addr = (void *) map->l_addr
    
    172
    -   };
    
    173
    -   dl_iterate_phdr(loadNativeObjCb_, &piterate_info);
    
    174
    -   if (!nc->nc_ranges) {
    
    175
    -     copyErrmsg(errmsg, "dl_iterate_phdr failed to find obj");
    
    176
    -     goto dl_iterate_phdr_fail;
    
    202
    +     struct piterate_cb_info piterate_info = {
    
    203
    +       .nc = nc,
    
    204
    +       .l_addr = (void *) map->l_addr
    
    205
    +     };
    
    206
    +     dl_iterate_phdr(loadNativeObjCb_, &piterate_info);
    
    207
    +     if (!nc->nc_ranges) {
    
    208
    +       copyErrmsg(errmsg, "dl_iterate_phdr failed to find obj");
    
    209
    +       goto dl_iterate_phdr_fail;
    
    210
    +     }
    
    211
    +     nc->unloadable = true;
    
    212
    +   } else {
    
    213
    +     nc->nc_ranges = NULL;
    
    214
    +     nc->unloadable = false;
    
    177 215
        }
    
    178
    -   nc->unloadable = true;
    
    179 216
     #else
    
    180 217
        nc->nc_ranges = NULL;
    
    181 218
        nc->unloadable = false;