@@ -33,7 +33,6 @@ void jl_loader_print_stderr3(const char * msg1, const char * msg2, const char *
33
33
/* Wrapper around dlopen(), with extra relative pathing thrown in*/
34
34
static void * load_library (const char * rel_path , const char * src_dir , int err ) {
35
35
void * handle = NULL ;
36
-
37
36
// See if a handle is already open to the basename
38
37
const char * basename = rel_path + strlen (rel_path );
39
38
while (basename -- > rel_path )
@@ -153,6 +152,181 @@ JL_DLLEXPORT const char * jl_get_libdir()
153
152
return lib_dir ;
154
153
}
155
154
155
+ #ifdef _OS_LINUX_
156
+ #ifndef GLIBCXX_LEAST_VERSION_SYMBOL /* Should always be defined in the makefile. This appeases the linter. */
157
+ #define GLIBCXX_LEAST_VERSION_SYMBOL "GLIBCXX_a.b.c"
158
+ #endif
159
+
160
+ #include <link.h>
161
+ #include <sys/wait.h>
162
+
163
+ static void write_wrapper (int fd , char * str , size_t len )
164
+ {
165
+ size_t written_sofar = 0 ;
166
+ while (len ) {
167
+ ssize_t bytes_written = write (fd , str + written_sofar , len );
168
+ if (bytes_written == -1 && errno == EINTR ) continue ;
169
+ if (bytes_written == -1 && errno != EINTR ) {
170
+ perror ("Error in write wrapper:\nwrite" );
171
+ _exit (1 );
172
+ }
173
+ len -= bytes_written ;
174
+ written_sofar += bytes_written ;
175
+ }
176
+ }
177
+
178
+ // Where the buf passed in was malloced
179
+ static void read_wrapper (int fd , char * * ret , size_t * ret_len )
180
+ {
181
+ // Allocate an initial buffer
182
+ size_t len = JL_PATH_MAX ;
183
+ char * buf = (char * )malloc (len + 1 );
184
+ if (!buf ) {
185
+ char err_str [] = "Error in read wrapper:\nmalloc() returned NULL.\n" ;
186
+ size_t err_strlen = strlen (err_str );
187
+ write_wrapper (STDERR_FILENO , err_str , err_strlen );
188
+ exit (1 );
189
+ }
190
+
191
+ // Read into it, reallocating as necessary
192
+ size_t have_read = 0 ;
193
+ while (1 ) {
194
+ ssize_t n = read (fd , buf + have_read , len - have_read );
195
+ have_read += n ;
196
+ if (n == 0 ) break ;
197
+ if (n == -1 && errno != EINTR ) {
198
+ perror ("Error in read wrapper:\nread" );
199
+ exit (1 );
200
+ }
201
+ if (n == -1 && errno == EINTR ) continue ;
202
+ if (have_read == len ) {
203
+ buf = (char * )realloc (buf , 1 + (len *= 2 ));
204
+ if (!buf ) {
205
+ char err_str [] = "Error in read wrapper:\nrealloc() returned NULL.\n" ;
206
+ size_t err_strlen = strlen (err_str );
207
+ write_wrapper (STDERR_FILENO , err_str , err_strlen );
208
+ exit (1 );
209
+ }
210
+ }
211
+ }
212
+
213
+ * ret = buf ;
214
+ * ret_len = have_read ;
215
+ }
216
+
217
+ // On Linux, it can happen that the system has a newer libstdc++ than the one we ship,
218
+ // which can break loading of some system libraries: <https://github.com/JuliaLang/julia/issues/34276>.
219
+
220
+ // Return the path to the libstdcxx to load.
221
+ // If the path is found, return it.
222
+ // Otherwise, print the error and exit.
223
+ // The path returned must be freed.
224
+ static char * libstdcxxprobe (void )
225
+ {
226
+ // Create the pipe and child process.
227
+ int fork_pipe [2 ];
228
+ int ret = pipe (fork_pipe );
229
+ if (ret == -1 ) {
230
+ perror ("Error during libstdcxxprobe:\npipe" );
231
+ exit (1 );
232
+ }
233
+ pid_t pid = fork ();
234
+ if (pid == -1 ) {
235
+ perror ("Error during libstdcxxprobe:\nfork" );
236
+ exit (1 );
237
+ }
238
+ if (pid == (pid_t ) 0 ) { // Child process.
239
+ ret = close (fork_pipe [0 ]);
240
+ if (ret == -1 ) {
241
+ perror ("Error during libstdcxxprobe in child process:\nclose" );
242
+ _exit (1 );
243
+ }
244
+
245
+ // Open the first available libstdc++.so.
246
+ // If it can't be found, report so by exiting zero.
247
+ void * handle = dlopen ("libstdc++.so.6" , RTLD_LAZY );
248
+ if (!handle ) {
249
+ _exit (0 );
250
+ }
251
+
252
+ // See if the version is compatible
253
+ char * dlerr = dlerror (); // clear out dlerror
254
+ void * sym = dlsym (handle , GLIBCXX_LEAST_VERSION_SYMBOL );
255
+ dlerr = dlerror ();
256
+ if (dlerr ) {
257
+ // We can't use the library that was found, so don't write anything.
258
+ // The main process will see that nothing was written,
259
+ // then exit the function and return null.
260
+ _exit (0 );
261
+ }
262
+
263
+ // No error means the symbol was found, we can use this library.
264
+ // Get the path to it, and write it to the parent process.
265
+ struct link_map * lm ;
266
+ ret = dlinfo (handle , RTLD_DI_LINKMAP , & lm );
267
+ if (ret == -1 ) {
268
+ char errbuf [2048 ];
269
+ int err_len = snprintf (errbuf , 2048 , "Error during libstdcxxprobe in child process:\ndlinfo() - %s\n" , dlerror ());
270
+ write_wrapper (STDOUT_FILENO , errbuf , (size_t )err_len );
271
+ _exit (1 );
272
+ }
273
+ char * libpath = lm -> l_name ;
274
+ write_wrapper (fork_pipe [1 ], libpath , strlen (libpath ));
275
+ _exit (0 );
276
+ }
277
+ else { // Parent process.
278
+ ret = close (fork_pipe [1 ]);
279
+ if (ret == -1 ) {
280
+ perror ("Error during libstdcxxprobe in parent process:\nclose" );
281
+ _exit (1 );
282
+ }
283
+
284
+ // Wait for the child to complete.
285
+ while (1 ) {
286
+ int wstatus ;
287
+ pid_t npid = waitpid (pid , & wstatus , 0 );
288
+ if (npid == -1 ) {
289
+ if (errno == EINTR ) continue ;
290
+ if (errno != EINTR ) {
291
+ perror ("Error during libstdcxxprobe in parent process:\nwaitpid" );
292
+ exit (1 );
293
+ }
294
+ }
295
+ else if (!WIFEXITED (wstatus )) {
296
+ char err_str [] = "Error during libstdcxxprobe in parent process:\n"
297
+ "The child process did not exit normally.\n" ;
298
+ size_t err_strlen = strlen (err_str );
299
+ write_wrapper (STDERR_FILENO , err_str , err_strlen );
300
+ exit (1 );
301
+ }
302
+ else if (WEXITSTATUS (wstatus )) {
303
+ // The child has printed an error and exited, so the parent should exit too.
304
+ exit (1 );
305
+ }
306
+ break ;
307
+ }
308
+
309
+ // Read the absolute path to the lib from the child process.
310
+ char * path ;
311
+ size_t pathlen ;
312
+ read_wrapper (fork_pipe [0 ], & path , & pathlen );
313
+
314
+ // Close the read end of the pipe
315
+ ret = close (fork_pipe [0 ]);
316
+ if (ret == -1 ) {
317
+ perror ("Error during libstdcxxprobe in parent process:\nclose" );
318
+ _exit (1 );
319
+ }
320
+
321
+ if (!pathlen ) {
322
+ free (path );
323
+ return NULL ;
324
+ }
325
+ return path ;
326
+ }
327
+ }
328
+ #endif
329
+
156
330
void * libjulia_internal = NULL ;
157
331
__attribute__((constructor )) void jl_load_libjulia_internal (void ) {
158
332
// Only initialize this once
@@ -161,11 +335,46 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) {
161
335
}
162
336
163
337
// Introspect to find our own path
164
- const char * lib_dir = jl_get_libdir ();
338
+ const char * lib_dir = jl_get_libdir ();
165
339
166
340
// Pre-load libraries that libjulia-internal needs.
167
341
int deps_len = strlen (dep_libs );
168
- char * curr_dep = & dep_libs [0 ];
342
+ char * curr_dep = & dep_libs [0 ];
343
+
344
+ void * cxx_handle ;
345
+ int done_probe = 0 ;
346
+ #if defined(_OS_LINUX_ )
347
+ int do_probe = 1 ;
348
+ char * probevar = getenv ("JULIA_PROBE_LIBSTDCXX" );
349
+ if (probevar ) {
350
+ if (strcmp (probevar , "1" ) == 0 || strcmp (probevar , "yes" ))
351
+ do_probe = 1 ;
352
+ else if (strcmp (probevar , "0" ) == 0 || strcmp (probevar , "no" ))
353
+ do_probe = 0 ;
354
+ }
355
+ if (do_probe ) {
356
+ char * cxxpath = libstdcxxprobe ();
357
+ if (cxxpath ) {
358
+ cxx_handle = dlopen (cxxpath , RTLD_LAZY );
359
+ char * dlr = dlerror ();
360
+ if (dlr ) {
361
+ size_t buflen = strlen (cxxpath ) + 2048 ;
362
+ char * errbuf = malloc (buflen );
363
+ int err_len = snprintf (errbuf , buflen , "Error, cannot load \"%s\"\n"
364
+ "dlinfo() - %s\n" , cxxpath , dlr );
365
+ write_wrapper (STDOUT_FILENO , errbuf , (size_t )err_len );
366
+ free (errbuf );
367
+ exit (1 );
368
+ }
369
+ free (cxxpath );
370
+ done_probe = 1 ;
371
+ }
372
+ }
373
+ #endif
374
+ // If not on linux, or the probe does not finish successfully, load the bundled version.
375
+ if (!done_probe ) {
376
+ load_library ("libstdc++.so" , jl_get_libdir (), 1 );
377
+ }
169
378
170
379
// We keep track of "special" libraries names (ones whose name is prefixed with `@`)
171
380
// which are libraries that we want to load in some special, custom way, such as
@@ -189,7 +398,8 @@ __attribute__((constructor)) void jl_load_libjulia_internal(void) {
189
398
}
190
399
special_library_names [special_idx ] = curr_dep + 1 ;
191
400
special_idx += 1 ;
192
- } else {
401
+ }
402
+ else {
193
403
load_library (curr_dep , lib_dir , 1 );
194
404
}
195
405
@@ -278,7 +488,7 @@ JL_DLLEXPORT int jl_load_repl(int argc, char * argv[]) {
278
488
}
279
489
280
490
#ifdef _OS_WINDOWS_
281
- int __stdcall DllMainCRTStartup (void * instance , unsigned reason , void * reserved ) {
491
+ int __stdcall DllMainCRTStartup (void * instance , unsigned reason , void * reserved ) {
282
492
setup_stdio ();
283
493
284
494
// Because we override DllMainCRTStartup, we have to manually call our constructor methods
0 commit comments