Skip to content

Commit 8977f28

Browse files
committed
Enable -shared via a new FAKE_DYLIBS setting. NFC
This this change the default behaviour does not change since `FAKE_DYLIBS` defaults to false. However for users who want to try out a more traditional shared library workflow `-sFAKE_DYLIBS=0` can be used enable shared library output with `-shared`. At some point in the future we can consider making this the default. Split out from emscripten-core#25817
1 parent 91fefbf commit 8977f28

File tree

5 files changed

+69
-15
lines changed

5 files changed

+69
-15
lines changed

ChangeLog.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ See docs/process.md for more on how version tagging works.
2020

2121
4.0.21 (in development)
2222
-----------------------
23+
- A new `-sFAKE_DYLIBS` setting was added. This is enabled by default but can
24+
be disabled order to change the behaviour of the ``-shared`` flag to produce
25+
actual shared libraries (side modules).
2326

2427
4.0.20 - 11/18/25
2528
-----------------

site/source/docs/tools_reference/settings_reference.rst

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3415,6 +3415,19 @@ indirectly using `importScripts`
34153415

34163416
Default value: false
34173417

3418+
.. _fake_dylibs:
3419+
3420+
FAKE_DYLIBS
3421+
===========
3422+
3423+
This settings changes the behaviour of the ``-shared`` flag. They default
3424+
setting of ``true`` means the ``-shared`` flag actually produces a normal
3425+
object file (i.e. ``ld -r``). Setting this to false will cause ``-shared``
3426+
to behavour like :ref:`SIDE_MODULE` and produce and dynamically linked
3427+
library.
3428+
3429+
Default value: true
3430+
34183431
.. _deprecated-settings:
34193432

34203433
===================

src/settings.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2230,3 +2230,10 @@ var WASM_JS_TYPES = false;
22302230
// CROSS_ORIGIN uses an inline worker to instead load the worker script
22312231
// indirectly using `importScripts`
22322232
var CROSS_ORIGIN = false;
2233+
2234+
// This settings changes the behaviour of the ``-shared`` flag. They default
2235+
// setting of ``true`` means the ``-shared`` flag actually produces a normal
2236+
// object file (i.e. ``ld -r``). Setting this to false will cause ``-shared``
2237+
// to behavour like :ref:`SIDE_MODULE` and produce and dynamically linked
2238+
// library.
2239+
var FAKE_DYLIBS = true;

tools/cmdline.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ class EmccOptions:
6565
dash_M = False
6666
dash_S = False
6767
dash_c = False
68+
dylibs: List[str] = []
6869
embed_files: List[str] = []
6970
emit_symbol_map = False
7071
emit_tsd = ''

tools/link.py

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -829,6 +829,22 @@ def setup_sanitizers(options):
829829
settings.LOAD_SOURCE_MAP = 1
830830

831831

832+
def get_dylibs(options, linker_args):
833+
"""Find all the Wasm dynanamic libraries specified on the command line,
834+
either via `-lfoo` or vis `libfoo.so` directly."""
835+
836+
dylibs = []
837+
for arg in linker_args:
838+
if arg.startswith('-l'):
839+
for ext in DYLIB_EXTENSIONS:
840+
path = find_library('lib' + arg[2:] + ext, options.lib_dirs)
841+
if path and building.is_wasm_dylib(path):
842+
dylibs.append(path)
843+
elif building.is_wasm_dylib(arg):
844+
dylibs.append(arg)
845+
return dylibs
846+
847+
832848
@ToolchainProfiler.profile_block('linker_setup')
833849
def phase_linker_setup(options, linker_args): # noqa: C901, PLR0912, PLR0915
834850
"""Future modifications should consider refactoring to reduce complexity.
@@ -843,6 +859,21 @@ def phase_linker_setup(options, linker_args): # noqa: C901, PLR0912, PLR0915
843859
setup_environment_settings()
844860

845861
apply_library_settings(linker_args)
862+
863+
if settings.SIDE_MODULE or settings.MAIN_MODULE:
864+
default_setting('FAKE_DYLIBS', 0)
865+
866+
if options.shared and not settings.FAKE_DYLIBS:
867+
default_setting('SIDE_MODULE', 1)
868+
default_setting('RELOCATABLE', 1)
869+
870+
# If there are any dynamically linked libraries on the command line then
871+
# need to enable `MAIN_MODULE` in order to produce JS code that can load them.
872+
if not settings.FAKE_DYLIBS:
873+
options.dylibs = get_dylibs(options, linker_args)
874+
if not settings.MAIN_MODULE and not settings.SIDE_MODULE and options.dylibs:
875+
default_setting('MAIN_MODULE', 2)
876+
846877
linker_args += calc_extra_ldflags(options)
847878

848879
# We used to do this check during on startup during `check_sanity`, but
@@ -936,16 +967,14 @@ def phase_linker_setup(options, linker_args): # noqa: C901, PLR0912, PLR0915
936967

937968
# If no output format was specified we try to deduce the format based on
938969
# the output filename extension
939-
if not options.oformat and (options.relocatable or (options.shared and not settings.SIDE_MODULE)):
940-
# Until we have a better story for actually producing runtime shared libraries
941-
# we support a compatibility mode where shared libraries are actually just
942-
# object files linked with `wasm-ld --relocatable` or `llvm-link` in the case
943-
# of LTO.
970+
if not options.oformat and (options.relocatable or (options.shared and settings.FAKE_DYLIBS and not settings.SIDE_MODULE)):
971+
# With FAKE_DYLIBS we generate an normal object file rather than an shared object.
972+
# This is linked with `wasm-ld --relocatable` or (`llvm-link` in the case of LTO).
944973
if final_suffix in EXECUTABLE_EXTENSIONS:
945974
diagnostics.warning('emcc', '-shared/-r used with executable output suffix. This behaviour is deprecated. Please remove -shared/-r to build an executable or avoid the executable suffix (%s) when building object files.' % final_suffix)
946975
else:
947-
if options.shared:
948-
diagnostics.warning('emcc', 'linking a library with `-shared` will emit a static object file. This is a form of emulation to support existing build systems. If you want to build a runtime shared library use the SIDE_MODULE setting.')
976+
if options.shared and 'FAKE_DYLIBS' not in user_settings:
977+
diagnostics.warning('emcc', 'linking a library with `-shared` will emit a static object file (FAKE_DYLIBS defaults to true). If you want to build a runtime shared library use the SIDE_MODULE or FAKE_DYLIBS=0.')
949978
options.oformat = OFormat.OBJECT
950979

951980
if not options.oformat:
@@ -2752,10 +2781,10 @@ def map_to_js_libs(library_name):
27522781

27532782

27542783
def process_libraries(options, flags):
2784+
"""Process `-l` and `--js-library` flags."""
27552785
new_flags = []
27562786
system_libs_map = system_libs.Library.get_usable_variations()
27572787

2758-
# Process `-l` and `--js-library` flags
27592788
for flag in flags:
27602789
if flag.startswith('--js-library='):
27612790
js_lib = flag.split('=', 1)[1]
@@ -2876,7 +2905,7 @@ def replacement(self):
28762905

28772906

28782907
def filter_out_fake_dynamic_libs(options, inputs):
2879-
# Filters out "fake" dynamic libraries that are really just intermediate object files.
2908+
"""Filter out "fake" dynamic libraries that are really just intermediate object files."""
28802909
def is_fake_dylib(input_file):
28812910
if get_file_suffix(input_file) in DYLIB_EXTENSIONS and os.path.exists(input_file) and not building.is_wasm_dylib(input_file):
28822911
if not options.ignore_dynamic_linking:
@@ -2888,11 +2917,13 @@ def is_fake_dylib(input_file):
28882917
return [f for f in inputs if not is_fake_dylib(f)]
28892918

28902919

2891-
def filter_out_duplicate_dynamic_libs(inputs):
2920+
def filter_out_duplicate_fake_dynamic_libs(inputs):
2921+
"""Filter out duplicate "fake" shared libraries (intermediate object files).
2922+
2923+
See test_core.py:test_redundant_link
2924+
"""
28922925
seen = set()
28932926

2894-
# Filter out duplicate "fake" shared libraries (intermediate object files).
2895-
# See test_core.py:test_redundant_link
28962927
def check(input_file):
28972928
if get_file_suffix(input_file) in DYLIB_EXTENSIONS and not building.is_wasm_dylib(input_file):
28982929
abspath = os.path.abspath(input_file)
@@ -3108,11 +3139,10 @@ def phase_calculate_linker_inputs(options, linker_args):
31083139
if options.oformat == OFormat.OBJECT or options.ignore_dynamic_linking:
31093140
linker_args = filter_out_fake_dynamic_libs(options, linker_args)
31103141
else:
3111-
linker_args = filter_out_duplicate_dynamic_libs(linker_args)
3142+
linker_args = filter_out_duplicate_fake_dynamic_libs(linker_args)
31123143

31133144
if settings.MAIN_MODULE:
3114-
dylibs = [a for a in linker_args if building.is_wasm_dylib(a)]
3115-
process_dynamic_libs(dylibs, options.lib_dirs)
3145+
process_dynamic_libs(options.dylibs, options.lib_dirs)
31163146

31173147
return linker_args
31183148

0 commit comments

Comments
 (0)