diff --git a/src/lib/libdylink.js b/src/lib/libdylink.js index 44e349da62a28..26957d843c47d 100644 --- a/src/lib/libdylink.js +++ b/src/lib/libdylink.js @@ -447,7 +447,7 @@ var LibraryDylink = { name = getString(); } - var customSection = { neededDynlibs: [], tlsExports: new Set(), weakImports: new Set() }; + var customSection = { neededDynlibs: [], tlsExports: new Set(), weakImports: new Set(), runtimePaths: [] }; if (name == 'dylink') { customSection.memorySize = getLEB(); customSection.memoryAlign = getLEB(); @@ -457,7 +457,7 @@ var LibraryDylink = { // current module could resolve its imports. (see tools/shared.py // WebAssembly.make_shared_library() for "dylink" section extension format) var neededDynlibsCount = getLEB(); - for (var i = 0; i < neededDynlibsCount; ++i) { + while (neededDynlibsCount--) { var libname = getString(); customSection.neededDynlibs.push(libname); } @@ -467,6 +467,7 @@ var LibraryDylink = { var WASM_DYLINK_NEEDED = 0x2; var WASM_DYLINK_EXPORT_INFO = 0x3; var WASM_DYLINK_IMPORT_INFO = 0x4; + var WASM_DYLINK_RUNTIME_PATH = 0x5; var WASM_SYMBOL_TLS = 0x100; var WASM_SYMBOL_BINDING_MASK = 0x3; var WASM_SYMBOL_BINDING_WEAK = 0x1; @@ -480,8 +481,8 @@ var LibraryDylink = { customSection.tableAlign = getLEB(); } else if (subsectionType === WASM_DYLINK_NEEDED) { var neededDynlibsCount = getLEB(); - for (var i = 0; i < neededDynlibsCount; ++i) { - libname = getString(); + while (neededDynlibsCount--) { + var libname = getString(); customSection.neededDynlibs.push(libname); } } else if (subsectionType === WASM_DYLINK_EXPORT_INFO) { @@ -503,6 +504,12 @@ var LibraryDylink = { customSection.weakImports.add(symname); } } + } else if (subsectionType === WASM_DYLINK_RUNTIME_PATH) { + var runtimePathsCount = getLEB(); + while (runtimePathsCount--) { + var path = getString(); + customSection.runtimePaths.push(path); + } } else { #if ASSERTIONS err(`unknown dylink.0 subsection: ${subsectionType}`) diff --git a/test/other/codesize/test_codesize_hello_dylink.gzsize b/test/other/codesize/test_codesize_hello_dylink.gzsize index e41f31b38121a..9f8c8a0907901 100644 --- a/test/other/codesize/test_codesize_hello_dylink.gzsize +++ b/test/other/codesize/test_codesize_hello_dylink.gzsize @@ -1 +1 @@ -5907 +5911 diff --git a/test/other/codesize/test_codesize_hello_dylink.jssize b/test/other/codesize/test_codesize_hello_dylink.jssize index 759ee160e24c4..75b82ea42066f 100644 --- a/test/other/codesize/test_codesize_hello_dylink.jssize +++ b/test/other/codesize/test_codesize_hello_dylink.jssize @@ -1 +1 @@ -13048 +13085 diff --git a/test/test_other.py b/test/test_other.py index be5e79753e483..0669a990ffb40 100644 --- a/test/test_other.py +++ b/test/test_other.py @@ -2357,6 +2357,55 @@ def test_dylink_dependencies(self): self.run_process(cmd + ['-L.']) self.run_js('a.out.js') + def test_dylink_dependencies_rpath(self): + create_file('side1.c', r''' + #include + #include + + void side2(); + + void side1() { + printf("side1\n"); + side2(); + } + ''') + create_file('side2.c', r''' + #include + #include + + void side2() { + printf("side2\n"); + } + ''') + create_file('main.c', ''' + void side1(); + + int main() { + side1(); + return 0; + } + ''') + self.emcc('side2.c', ['-fPIC', '-sSIDE_MODULE', '-olibside2.so']) + self.emcc('side1.c', ['-fPIC', '-sSIDE_MODULE', '-Wl,-rpath,$ORIGIN', '-olibside1.so', 'libside2.so']) + cmd = [EMCC, 'main.c', '-fPIC', '-sMAIN_MODULE=2', 'libside1.so'] + + # Unless `.` is added to the library path the libside2.so won't be found. + err = self.expect_fail(cmd) + self.assertContained('emcc: error: libside1.so: shared library dependency not found in library path: `libside2.so`.', err) + + # Adding -L. to the library path makes it work. + self.run_process(cmd + ['-L.', '-Wl,-rpath,$ORIGIN']) + self.run_js('a.out.js') + + def get_runtime_paths(path): + with webassembly.Module(path) as module: + dylink_section = module.parse_dylink_section() + return dylink_section.runtime_paths + + self.assertEqual(get_runtime_paths('libside2.so'), []) + self.assertEqual(get_runtime_paths('libside1.so'), ['$ORIGIN']) + self.assertEqual(get_runtime_paths('a.out.wasm'), ['$ORIGIN']) + def test_dylink_LEGACY_GL_EMULATION(self): # LEGACY_GL_EMULATION wraps JS library functions. This test ensure that when it does # so it preserves the `.sig` attributes needed by dynamic linking. @@ -12415,9 +12464,6 @@ def test_missing_stdlibs(self): self.run_process([EMCC, test_file('hello_world.c'), '-lm', '-ldl', '-lrt', '-lpthread']) def test_supported_linker_flags(self): - out = self.run_process([EMCC, test_file('hello_world.c'), '-Wl,-rpath=foo'], stderr=PIPE).stderr - self.assertContained('warning: ignoring unsupported linker flag: `-rpath=foo`', out) - out = self.run_process([EMCC, test_file('hello_world.c'), '-Wl,-rpath-link,foo'], stderr=PIPE).stderr self.assertContained('warning: ignoring unsupported linker flag: `-rpath-link`', out) @@ -12437,7 +12483,7 @@ def test_supported_linker_flags(self): def test_supported_linker_flag_skip_next(self): # Regression test for a bug where skipping an unsupported linker flag # could skip the next unrelated linker flag. - err = self.expect_fail([EMCC, test_file('hello_world.c'), '-Wl,-rpath=foo', '-lbar']) + err = self.expect_fail([EMCC, test_file('hello_world.c'), '-Wl,-version-script,foo', '-lbar']) self.assertContained('error: unable to find library -lbar', err) def test_linker_flags_pass_through(self): diff --git a/tools/link.py b/tools/link.py index 2e82c0cf459da..6f52ba5bc2a48 100644 --- a/tools/link.py +++ b/tools/link.py @@ -61,7 +61,8 @@ '--start-group', '--end-group', '-(', '-)', '--whole-archive', '--no-whole-archive', - '-whole-archive', '-no-whole-archive' + '-whole-archive', '-no-whole-archive', + '-rpath', ) # Unsupported LLD flags which we will ignore. @@ -72,7 +73,6 @@ # wasm-ld doesn't support soname or other dynamic linking flags (yet). Ignore them # in order to aid build systems that want to pass these flags. '-allow-shlib-undefined': False, - '-rpath': True, '-rpath-link': True, '-version-script': True, '-install_name': True, diff --git a/tools/webassembly.py b/tools/webassembly.py index 7bbc03b93fe78..b913867caa14d 100644 --- a/tools/webassembly.py +++ b/tools/webassembly.py @@ -149,6 +149,7 @@ class DylinkType(IntEnum): NEEDED = 2 EXPORT_INFO = 3 IMPORT_INFO = 4 + RUNTIME_PATH = 5 class TargetFeaturePrefix(IntEnum): @@ -165,7 +166,7 @@ class InvalidWasmError(BaseException): Import = namedtuple('Import', ['kind', 'module', 'field', 'type']) Export = namedtuple('Export', ['name', 'kind', 'index']) Global = namedtuple('Global', ['type', 'mutable', 'init']) -Dylink = namedtuple('Dylink', ['mem_size', 'mem_align', 'table_size', 'table_align', 'needed', 'export_info', 'import_info']) +Dylink = namedtuple('Dylink', ['mem_size', 'mem_align', 'table_size', 'table_align', 'needed', 'export_info', 'import_info', 'runtime_paths']) Table = namedtuple('Table', ['elem_type', 'limits']) FunctionBody = namedtuple('FunctionBody', ['offset', 'size']) DataSegment = namedtuple('DataSegment', ['flags', 'init', 'offset', 'size']) @@ -313,6 +314,7 @@ def parse_dylink_section(self): needed = [] export_info = {} import_info = {} + runtime_paths = [] self.read_string() # name if dylink_section.name == 'dylink': @@ -359,6 +361,12 @@ def parse_dylink_section(self): import_info.setdefault(module, {}) import_info[module][field] = flags count -= 1 + elif subsection_type == DylinkType.RUNTIME_PATH: + count = self.read_uleb() + while count: + rpath = self.read_string() + runtime_paths.append(rpath) + count -= 1 else: print(f'unknown subsection: {subsection_type}') # ignore unknown subsections @@ -367,7 +375,7 @@ def parse_dylink_section(self): else: utils.exit_with_error('error parsing shared library') - return Dylink(mem_size, mem_align, table_size, table_align, needed, export_info, import_info) + return Dylink(mem_size, mem_align, table_size, table_align, needed, export_info, import_info, runtime_paths) @memoize def get_exports(self):