Skip to content

Commit

Permalink
Refactor system library claculation to better handle -nostdlib and …
Browse files Browse the repository at this point in the history
…friends. (#15618)

This refactoring splits out `get_libs_to_link` into its own function
which can early return once the list is complete.

The follow gcc/clang flags are now (kind of) supported:

- `-nostartfiles` - don't link crt1 and other startup files
- `-nolibc`       - don't link libc
- `-nodefualtlibs`- don't link libc or compiler-rt or other stdlibs
                    (but do link startup files)
- `-nostdlib`     - don't link any system libraries or startup files.
  • Loading branch information
sbc100 authored Jan 8, 2022
1 parent 1934a98 commit fd23b8c
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 73 deletions.
6 changes: 3 additions & 3 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -2460,7 +2460,7 @@ def get_full_import_name(name):
@ToolchainProfiler.profile_block('compile inputs')
def phase_compile_inputs(options, state, newargs, input_files):
def is_link_flag(flag):
if flag.startswith('-nostdlib'):
if flag in ('-nostdlib', '-nostartfiles', '-nolibc', '-nodefaultlibs'):
return True
return flag.startswith(('-l', '-L', '-Wl,'))

Expand Down Expand Up @@ -2626,8 +2626,8 @@ def phase_calculate_system_libraries(state, linker_arguments, linker_inputs, new
if not settings.SIDE_MODULE:
# Ports are always linked into the main module, never the size module.
extra_files_to_link += ports.get_libs(settings)
if '-nostdlib' not in newargs and '-nodefaultlibs' not in newargs:
extra_files_to_link += system_libs.calculate([f for _, f in sorted(linker_inputs)] + extra_files_to_link, forced=state.forced_stdlibs)
all_linker_inputs = [f for _, f in sorted(linker_inputs)] + extra_files_to_link
extra_files_to_link += system_libs.calculate(all_linker_inputs, newargs, forced=state.forced_stdlibs)
linker_arguments.extend(extra_files_to_link)


Expand Down
2 changes: 2 additions & 0 deletions tests/test_other.py
Original file line number Diff line number Diff line change
Expand Up @@ -10131,6 +10131,8 @@ def test_nostdlib(self):
libs = ['-lc', '-lcompiler_rt', '-lc_rt']
self.run_process([EMCC, test_file('unistd/close.c'), '-nostdlib'] + libs)
self.run_process([EMCC, test_file('unistd/close.c'), '-nodefaultlibs'] + libs)
self.run_process([EMCC, test_file('unistd/close.c'), '-nolibc', '-lc'])
self.run_process([EMCC, test_file('unistd/close.c'), '-nostartfiles'])

def test_argument_match(self):
# Verify that emcc arguments match precisely. We had a bug where only the prefix
Expand Down
156 changes: 86 additions & 70 deletions tools/system_libs.py
Original file line number Diff line number Diff line change
Expand Up @@ -1651,22 +1651,12 @@ def add_reverse_deps(need):
add_reverse_deps(symbols)


def calculate(input_files, forced):
# Setting this will only use the forced libs in EMCC_FORCE_STDLIBS. This avoids spending time checking
# for unresolved symbols in your project files, which can speed up linking, but if you do not have
# the proper list of actually needed libraries, errors can occur. See below for how we must
# export all the symbols in deps_info when using this option.
only_forced = os.environ.get('EMCC_ONLY_FORCED_STDLIBS')
if only_forced:
# One of the purposes EMCC_ONLY_FORCED_STDLIBS was to skip the scanning
# of the input files for reverse dependencies.
diagnostics.warning('deprecated', 'EMCC_ONLY_FORCED_STDLIBS is deprecated. Use `-nostdlib` and/or `-s REVERSE_DEPS=none` depending on the desired result')
settings.REVERSE_DEPS = 'all'
def get_libs_to_link(args, forced, only_forced):
libs_to_link = []

handle_reverse_deps(input_files)
if '-nostdlib' in args:
return libs_to_link

force_include = []
libs_to_link = []
already_included = set()
system_libs_map = Library.get_usable_variations()

Expand All @@ -1675,6 +1665,7 @@ def calculate(input_files, forced):
# it can be the name of a lib (libc++, etc.).
# You can provide 1 to include everything, or a comma-separated list with the
# ones you want
force_include = []
force = os.environ.get('EMCC_FORCE_STDLIBS')
if force == '1':
force_include = [name for name, lib in system_libs_map.items() if not lib.never_force]
Expand All @@ -1695,93 +1686,118 @@ def add_library(libname):
need_whole_archive = lib.name in force_include and lib.get_ext() == '.a'
libs_to_link.append((lib.get_link_flag(), need_whole_archive))

if settings.USE_PTHREADS:
add_library('crtbegin')
if '-nostartfiles' not in args:
if settings.USE_PTHREADS:
add_library('crtbegin')

if settings.SIDE_MODULE:
return [l[0] for l in libs_to_link]
if settings.STANDALONE_WASM:
if settings.EXPECT_MAIN:
add_library('crt1')
else:
add_library('crt1_reactor')

if settings.STANDALONE_WASM:
if settings.EXPECT_MAIN:
add_library('crt1')
else:
add_library('crt1_reactor')
if settings.SIDE_MODULE:
return libs_to_link

for forced in force_include:
if forced not in system_libs_map:
shared.exit_with_error('invalid forced library: %s', forced)
add_library(forced)

if '-nodefaultlibs' in args:
return libs_to_link

if only_forced:
add_library('libc_rt')
add_library('libcompiler_rt')
else:
if settings.AUTO_NATIVE_LIBRARIES:
add_library('libGL')
add_library('libal')
add_library('libhtml5')
return libs_to_link

sanitize = settings.USE_LSAN or settings.USE_ASAN or settings.UBSAN_RUNTIME
if settings.AUTO_NATIVE_LIBRARIES:
add_library('libGL')
add_library('libal')
add_library('libhtml5')

# JS math must come before anything else, so that it overrides the normal
# libc math.
if settings.JS_MATH:
add_library('libjsmath')
sanitize = settings.USE_LSAN or settings.USE_ASAN or settings.UBSAN_RUNTIME

# to override the normal libc printf, we must come before it
if settings.PRINTF_LONG_DOUBLE:
add_library('libprintf_long_double')
# JS math must come before anything else, so that it overrides the normal
# libc math.
if settings.JS_MATH:
add_library('libjsmath')

if settings.ALLOW_UNIMPLEMENTED_SYSCALLS:
add_library('libstubs')
# to override the normal libc printf, we must come before it
if settings.PRINTF_LONG_DOUBLE:
add_library('libprintf_long_double')

if settings.ALLOW_UNIMPLEMENTED_SYSCALLS:
add_library('libstubs')
if '-nolibc' not in args:
if not settings.EXIT_RUNTIME:
add_library('libnoexit')
add_library('libc')
add_library('libcompiler_rt')
if settings.LINK_AS_CXX:
add_library('libc++')
if settings.LINK_AS_CXX or sanitize:
add_library('libc++abi')
if settings.EXCEPTION_HANDLING:
add_library('libunwind')
if settings.MALLOC != 'none':
add_library('libmalloc')
if settings.STANDALONE_WASM:
add_library('libstandalonewasm')
add_library('libc_rt')
add_library('libcompiler_rt')
if settings.LINK_AS_CXX:
add_library('libc++')
if settings.LINK_AS_CXX or sanitize:
add_library('libc++abi')
if settings.EXCEPTION_HANDLING:
add_library('libunwind')
if settings.STANDALONE_WASM:
add_library('libstandalonewasm')
add_library('libc_rt')

if settings.USE_LSAN:
force_include.append('liblsan_rt')
add_library('liblsan_rt')
if settings.USE_LSAN:
force_include.append('liblsan_rt')
add_library('liblsan_rt')

if settings.USE_ASAN:
force_include.append('libasan_rt')
add_library('libasan_rt')
add_library('libasan_js')
if settings.USE_ASAN:
force_include.append('libasan_rt')
add_library('libasan_rt')
add_library('libasan_js')

if settings.UBSAN_RUNTIME == 1:
add_library('libubsan_minimal_rt')
elif settings.UBSAN_RUNTIME == 2:
add_library('libubsan_rt')
if settings.UBSAN_RUNTIME == 1:
add_library('libubsan_minimal_rt')
elif settings.UBSAN_RUNTIME == 2:
add_library('libubsan_rt')

if settings.USE_LSAN or settings.USE_ASAN:
add_library('liblsan_common_rt')
if settings.USE_LSAN or settings.USE_ASAN:
add_library('liblsan_common_rt')

if sanitize:
add_library('libsanitizer_common_rt')
if sanitize:
add_library('libsanitizer_common_rt')

if settings.PROXY_POSIX_SOCKETS:
add_library('libsockets_proxy')
else:
add_library('libsockets')
if settings.PROXY_POSIX_SOCKETS:
add_library('libsockets_proxy')
else:
add_library('libsockets')

if settings.USE_WEBGPU:
add_library('libwebgpu_cpp')

return libs_to_link


def calculate(input_files, args, forced):
# Setting this will only use the forced libs in EMCC_FORCE_STDLIBS. This avoids spending time checking
# for unresolved symbols in your project files, which can speed up linking, but if you do not have
# the proper list of actually needed libraries, errors can occur. See below for how we must
# export all the symbols in deps_info when using this option.
only_forced = os.environ.get('EMCC_ONLY_FORCED_STDLIBS')
if only_forced:
# One of the purposes EMCC_ONLY_FORCED_STDLIBS was to skip the scanning
# of the input files for reverse dependencies.
diagnostics.warning('deprecated', 'EMCC_ONLY_FORCED_STDLIBS is deprecated. Use `-nostdlib` and/or `-s REVERSE_DEPS=none` depending on the desired result')
settings.REVERSE_DEPS = 'all'

handle_reverse_deps(input_files)

if settings.USE_WEBGPU:
add_library('libwebgpu_cpp')
libs_to_link = get_libs_to_link(args, forced, only_forced)

# When LINKABLE is set the entire link command line is wrapped in --whole-archive by
# building.link_ldd. And since --whole-archive/--no-whole-archive processing does not nest we
# shouldn't add any extra `--no-whole-archive` or we will undo the intent of building.link_ldd.
if settings.LINKABLE:
if settings.LINKABLE or settings.SIDE_MODULE:
return [l[0] for l in libs_to_link]

# Wrap libraries in --whole-archive, as needed. We need to do this last
Expand Down

0 comments on commit fd23b8c

Please sign in to comment.