... |
... |
@@ -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;
|