Skip to content

Commit b668d14

Browse files
committed
Avoid using fork() when probing system libstdc++
Rather than relying on the linker to do all of the heavy lifting, this changes our probing technique to directly parse the `ld.so.cache` and `libstdc++.so.6` files. As long as we can expect `ld.so.cache` to be available in `/etc` on all systems, this seems to be a reliable way to locate system libraries on Linux systems. That allows us to avoid `fork()`. For a small application that has just started up `fork()` is no big deal, but it's quite heavy-handed for Julia- as-a-library scenarios where resident memory may already be large. Many soft-embedded targets also do not support fork() well, so it is good for our compatibility to adjust this.
1 parent 24d8ab5 commit b668d14

File tree

7 files changed

+4969
-109
lines changed

7 files changed

+4969
-109
lines changed

cli/Makefile

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ include $(JULIAHOME)/Make.inc
55
include $(JULIAHOME)/deps/llvm-ver.make
66

77

8-
HEADERS := $(addprefix $(SRCDIR)/,jl_exports.h loader.h) $(addprefix $(JULIAHOME)/src/,julia_fasttls.h support/platform.h support/dirpath.h jl_exported_data.inc jl_exported_funcs.inc)
8+
HEADERS := $(addprefix $(SRCDIR)/,jl_exports.h loader.h elf.h dl-cache.h) $(addprefix $(JULIAHOME)/src/,julia_fasttls.h support/platform.h support/dirpath.h jl_exported_data.inc jl_exported_funcs.inc)
99

1010
LOADER_CFLAGS = $(JCFLAGS) -I$(BUILDROOT)/src -I$(JULIAHOME)/src -I$(JULIAHOME)/src/support -I$(build_includedir) -ffreestanding
1111
LOADER_LDFLAGS = $(JLDFLAGS) -ffreestanding -L$(build_shlibdir) -L$(build_libdir)
@@ -41,8 +41,8 @@ endif # MSYS2
4141

4242
EXE_OBJS := $(BUILDDIR)/loader_exe.o
4343
EXE_DOBJS := $(BUILDDIR)/loader_exe.dbg.obj
44-
LIB_OBJS := $(BUILDDIR)/loader_lib.o
45-
LIB_DOBJS := $(BUILDDIR)/loader_lib.dbg.obj
44+
LIB_OBJS := $(BUILDDIR)/loader_lib.o $(BUILDDIR)/loader_symbol_probe.o $(BUILDDIR)/loader_library_probe.o
45+
LIB_DOBJS := $(BUILDDIR)/loader_lib.dbg.obj $(BUILDDIR)/loader_symbol_probe.o $(BUILDDIR)/loader_library_probe.o
4646

4747
# If this is an architecture that supports dynamic linking, link in a trampoline definition
4848
ifneq (,$(wildcard $(SRCDIR)/trampolines/trampolines_$(ARCH).S))
@@ -66,6 +66,14 @@ $(BUILDDIR)/loader_trampolines.o : $(SRCDIR)/trampolines/trampolines_$(ARCH).S $
6666
@$(call PRINT_CC, $(CC) $(SHIPFLAGS) $(LOADER_CFLAGS) $< -c -o $@)
6767
$(BUILDDIR)/loader_trampolines.dbg.obj : $(SRCDIR)/trampolines/trampolines_$(ARCH).S $(HEADERS) $(SRCDIR)/trampolines/common.h
6868
@$(call PRINT_CC, $(CC) $(DEBUGFLAGS) $(LOADER_CFLAGS) $< -c -o $@)
69+
$(BUILDDIR)/loader_library_probe.o : $(SRCDIR)/loader_library_probe.c $(HEADERS) $(JULIAHOME)/VERSION
70+
@$(call PRINT_CC, $(CC) -DJL_LIBRARY_EXPORTS $(SHIPFLAGS) $(LOADER_CFLAGS) -c $< -o $@)
71+
$(BUILDDIR)/loader_library_probe.dbg.obj : $(SRCDIR)/loader_library_probe.c $(HEADERS) $(JULIAHOME)/VERSION
72+
@$(call PRINT_CC, $(CC) -DJL_LIBRARY_EXPORTS $(DEBUGFLAGS) $(LOADER_CFLAGS) -c $< -o $@)
73+
$(BUILDDIR)/loader_symbol_probe.o : $(SRCDIR)/loader_symbol_probe.c $(HEADERS) $(JULIAHOME)/VERSION
74+
@$(call PRINT_CC, $(CC) -DJL_LIBRARY_EXPORTS $(SHIPFLAGS) $(LOADER_CFLAGS) -c $< -o $@)
75+
$(BUILDDIR)/loader_symbol_probe.dbg.obj : $(SRCDIR)/loader_symbol_probe.c $(HEADERS) $(JULIAHOME)/VERSION
76+
@$(call PRINT_CC, $(CC) -DJL_LIBRARY_EXPORTS $(DEBUGFLAGS) $(LOADER_CFLAGS) -c $< -o $@)
6977

7078
# Debugging target to help us see what kind of code is being generated for our trampolines
7179
dump-trampolines: $(SRCDIR)/trampolines/trampolines_$(ARCH).S

cli/dl-cache.h

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
/* Support for reading /etc/ld.so.cache files written by Linux ldconfig.
2+
Copyright (C) 1999-2019 Free Software Foundation, Inc.
3+
This file is part of the GNU C Library.
4+
5+
The GNU C Library is free software; you can redistribute it and/or
6+
modify it under the terms of the GNU Lesser General Public
7+
License as published by the Free Software Foundation; either
8+
version 2.1 of the License, or (at your option) any later version.
9+
10+
The GNU C Library is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13+
Lesser General Public License for more details.
14+
15+
You should have received a copy of the GNU Lesser General Public
16+
License along with the GNU C Library; if not, see
17+
<http://www.gnu.org/licenses/>. */
18+
19+
#include <stdint.h>
20+
21+
#define FLAG_ANY -1
22+
#define FLAG_TYPE_MASK 0x00ff
23+
#define FLAG_LIBC4 0x0000
24+
#define FLAG_ELF 0x0001
25+
#define FLAG_ELF_LIBC5 0x0002
26+
#define FLAG_ELF_LIBC6 0x0003
27+
#define FLAG_REQUIRED_MASK 0xff00
28+
#define FLAG_SPARC_LIB64 0x0100
29+
#define FLAG_IA64_LIB64 0x0200
30+
#define FLAG_X8664_LIB64 0x0300
31+
#define FLAG_S390_LIB64 0x0400
32+
#define FLAG_POWERPC_LIB64 0x0500
33+
#define FLAG_MIPS64_LIBN32 0x0600
34+
#define FLAG_MIPS64_LIBN64 0x0700
35+
#define FLAG_X8664_LIBX32 0x0800
36+
#define FLAG_ARM_LIBHF 0x0900
37+
#define FLAG_AARCH64_LIB64 0x0a00
38+
#define FLAG_ARM_LIBSF 0x0b00
39+
#define FLAG_MIPS_LIB32_NAN2008 0x0c00
40+
#define FLAG_MIPS64_LIBN32_NAN2008 0x0d00
41+
#define FLAG_MIPS64_LIBN64_NAN2008 0x0e00
42+
#define FLAG_RISCV_FLOAT_ABI_SOFT 0x0f00
43+
#define FLAG_RISCV_FLOAT_ABI_DOUBLE 0x1000
44+
45+
#if defined(_CPU_X86_64_)
46+
47+
#define _DL_CACHE_DEFAULT_ID 0x303
48+
#define _dl_cache_check_flags(flags) ((flags) == _DL_CACHE_DEFAULT_ID)
49+
50+
#elif defined(_CPU_AARCH64_)
51+
52+
#ifdef __LP64__
53+
# define _DL_CACHE_DEFAULT_ID (FLAG_AARCH64_LIB64 | FLAG_ELF_LIBC6)
54+
#else
55+
# define _DL_CACHE_DEFAULT_ID (FLAG_AARCH64_LIB32 | FLAG_ELF_LIBC6)
56+
#endif
57+
58+
#define _dl_cache_check_flags(flags) ((flags) == _DL_CACHE_DEFAULT_ID)
59+
60+
#elif defined(_CPU_RISCV64_)
61+
62+
/* For now we only support the natural XLEN ABI length on all targets, so the
63+
only bits that need to go into ld.so.cache are the FLEG ABI length. */
64+
#if defined __riscv_float_abi_double
65+
# define _DL_CACHE_DEFAULT_ID (FLAG_RISCV_FLOAT_ABI_DOUBLE | FLAG_ELF_LIBC6)
66+
#else
67+
# define _DL_CACHE_DEFAULT_ID (FLAG_RISCV_FLOAT_ABI_SOFT | FLAG_ELF_LIBC6)
68+
#endif
69+
70+
#define _dl_cache_check_flags(flags) ((flags) == _DL_CACHE_DEFAULT_ID)
71+
72+
#elif defined(_CPU_ARM_)
73+
74+
/* In order to support the transition from unmarked objects
75+
to marked objects we must treat unmarked objects as
76+
compatible with either FLAG_ARM_LIBHF or FLAG_ARM_LIBSF. */
77+
#ifdef __ARM_PCS_VFP
78+
# define _dl_cache_check_flags(flags) \
79+
((flags) == (FLAG_ARM_LIBHF | FLAG_ELF_LIBC6) \
80+
|| (flags) == FLAG_ELF_LIBC6)
81+
#else
82+
# define _dl_cache_check_flags(flags) \
83+
((flags) == (FLAG_ARM_LIBSF | FLAG_ELF_LIBC6) \
84+
|| (flags) == FLAG_ELF_LIBC6)
85+
#endif
86+
87+
#elif defined(_CPU_X86_)
88+
89+
/* Defined as (FLAG_ELF_LIBC6 | FLAG_X8664_LIBX32). */
90+
#undef _DL_CACHE_DEFAULT_ID
91+
#define _DL_CACHE_DEFAULT_ID 0x803
92+
93+
#elif defined(_CPU_PPC64_)
94+
95+
#define _DL_CACHE_DEFAULT_ID 0x503
96+
97+
#define _dl_cache_check_flags(flags) \
98+
((flags) == _DL_CACHE_DEFAULT_ID)
99+
100+
#else
101+
102+
#error "Missing CPU arch-specific definitions in dl-cache.h"
103+
104+
#endif
105+
106+
#ifndef _DL_CACHE_DEFAULT_ID
107+
# define _DL_CACHE_DEFAULT_ID 3
108+
#endif
109+
110+
#ifndef _dl_cache_check_flags
111+
# define _dl_cache_check_flags(flags) \
112+
((flags) == 1 || (flags) == _DL_CACHE_DEFAULT_ID)
113+
#endif
114+
115+
#ifndef LD_SO_CACHE
116+
# define LD_SO_CACHE SYSCONFDIR "/ld.so.cache"
117+
#endif
118+
119+
#define CACHEMAGIC "ld.so-1.7.0"
120+
121+
/* libc5 and glibc 2.0/2.1 use the same format. For glibc 2.2 another
122+
format has been added in a compatible way:
123+
The beginning of the string table is used for the new table:
124+
old_magic
125+
nlibs
126+
libs[0]
127+
...
128+
libs[nlibs-1]
129+
pad, new magic needs to be aligned
130+
- this is string[0] for the old format
131+
new magic - this is string[0] for the new format
132+
newnlibs
133+
...
134+
newlibs[0]
135+
...
136+
newlibs[newnlibs-1]
137+
string 1
138+
string 2
139+
...
140+
*/
141+
struct file_entry
142+
{
143+
int flags; /* This is 1 for an ELF library. */
144+
unsigned int key, value; /* String table indices. */
145+
};
146+
147+
struct cache_file
148+
{
149+
char magic[sizeof CACHEMAGIC - 1];
150+
unsigned int nlibs;
151+
struct file_entry libs[0];
152+
};
153+
154+
#define CACHEMAGIC_NEW "glibc-ld.so.cache"
155+
#define CACHE_VERSION "1.1"
156+
#define CACHEMAGIC_VERSION_NEW CACHEMAGIC_NEW CACHE_VERSION
157+
158+
159+
struct file_entry_new
160+
{
161+
int32_t flags; /* This is 1 for an ELF library. */
162+
uint32_t key, value; /* String table indices. */
163+
uint32_t osversion; /* Required OS version. */
164+
uint64_t hwcap; /* Hwcap entry. */
165+
};
166+
167+
struct cache_file_new
168+
{
169+
char magic[sizeof CACHEMAGIC_NEW - 1];
170+
char version[sizeof CACHE_VERSION - 1];
171+
uint32_t nlibs; /* Number of entries. */
172+
uint32_t len_strings; /* Size of string table. */
173+
uint32_t unused[5]; /* Leave space for future extensions
174+
and align to 8 byte boundary. */
175+
struct file_entry_new libs[0]; /* Entries describing libraries. */
176+
/* After this the string table of size len_strings is found. */
177+
};
178+
179+
/* Used to align cache_file_new. */
180+
#define ALIGN_CACHE(addr) \
181+
(((addr) + __alignof__ (struct cache_file_new) -1) \
182+
& (~(__alignof__ (struct cache_file_new) - 1)))
183+
184+
// extern int _dl_cache_libcmp (const char *p1, const char *p2) attribute_hidden;

0 commit comments

Comments
 (0)