diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index b5b63942e2c6e..719b459909842 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -641,9 +641,12 @@ impl<'a> Linker for GccLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility in object files typically takes care of this. - if crate_type == CrateType::Executable && self.sess.target.override_export_symbols.is_none() - { - return; + if crate_type == CrateType::Executable { + if self.sess.target.override_export_symbols.is_none() + && !self.sess.opts.debugging_opts.export_executable_symbols + { + return; + } } // We manually create a list of exported symbols to ensure we don't expose any more. @@ -970,7 +973,9 @@ impl<'a> Linker for MsvcLinker<'a> { fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]) { // Symbol visibility takes care of this typically if crate_type == CrateType::Executable { - return; + if !self.sess.opts.debugging_opts.export_executable_symbols { + return; + } } let path = tmpdir.join("lib.def"); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 30a29ed6ed38f..efddae2333077 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -732,6 +732,7 @@ fn test_debugging_options_tracking_hash() { tracked!(debug_macros, true); tracked!(dep_info_omit_d_target, true); tracked!(drop_tracking, true); + tracked!(export_executable_symbols, true); tracked!(dual_proc_macros, true); tracked!(fewer_names, Some(true)); tracked!(force_unstable_if_unmarked, true); diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index 441e1f9f6a2b8..84914173376d4 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -1271,6 +1271,8 @@ options! { an additional `.html` file showing the computed coverage spans."), emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED], "emit a section containing stack size metadata (default: no)"), + export_executable_symbols: bool = (false, parse_bool, [TRACKED], + "export symbols from executables, as if they were dynamic libraries"), fewer_names: Option = (None, parse_opt_bool, [TRACKED], "reduce memory use by retaining fewer names within compilation artifacts (LLVM-IR) \ (default: no)"), diff --git a/src/test/run-make/export-executable-symbols/Makefile b/src/test/run-make/export-executable-symbols/Makefile new file mode 100644 index 0000000000000..78bf3a0167a83 --- /dev/null +++ b/src/test/run-make/export-executable-symbols/Makefile @@ -0,0 +1,7 @@ +-include ../../run-make-fulldeps/tools.mk + +all: + $(RUSTC) --crate-type=cdylib foo.rs --target $(TARGET) + $(RUSTC) -Zexport-executable-symbols -lfoo -L "$(TMPDIR)" main.rs --target $(TARGET) + $(call $(TMPDIR)/main) + diff --git a/src/test/run-make/export-executable-symbols/foo.rs b/src/test/run-make/export-executable-symbols/foo.rs new file mode 100644 index 0000000000000..4585fc5abead0 --- /dev/null +++ b/src/test/run-make/export-executable-symbols/foo.rs @@ -0,0 +1,8 @@ +extern "C" { + fn exported_symbol() -> i8; +} + +#[no_mangle] +pub extern "C" fn call_exported_symbol() -> i8 { + unsafe { exported_symbol() } +} diff --git a/src/test/run-make/export-executable-symbols/main.rs b/src/test/run-make/export-executable-symbols/main.rs new file mode 100644 index 0000000000000..6041648caab6b --- /dev/null +++ b/src/test/run-make/export-executable-symbols/main.rs @@ -0,0 +1,37 @@ +// edition:2018 + +#![feature(rustc_private)] + +extern crate libc; +use std::ffi::*; +use std::os::unix::ffi::*; + +fn main() { + let path = std::env::var("TMPDIR").unwrap(); + let path = std::path::PathBuf::from(path).join("libfoo.so"); + + let s = CString::new(path.as_os_str().as_bytes()).unwrap(); + let handle = unsafe { libc::dlopen(s.as_ptr(), libc::RTLD_LAZY | libc::RTLD_GLOBAL) }; + if handle.is_null() { + let msg = unsafe { CStr::from_ptr(libc::dlerror() as *const _) }; + panic!("failed to dlopen lib {:?}", msg); + } + + unsafe { + libc::dlerror(); + } + + let raw_string = CString::new("call_exported_symbol").unwrap(); + let symbol = unsafe { libc::dlsym(handle as *mut libc::c_void, raw_string.as_ptr()) }; + if symbol.is_null() { + let msg = unsafe { CStr::from_ptr(libc::dlerror() as *const _) }; + panic!("failed to load symbol {:?}", msg); + } + let func: extern "C" fn() -> i8 = unsafe { std::mem::transmute(symbol) }; + assert_eq!(func(), 42); +} + +#[no_mangle] +pub fn exported_symbol() -> i8 { + 42 +}