about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2018-03-09 07:25:54 -0800
committerAlex Crichton <alex@alexcrichton.com>2018-03-13 08:29:19 -0700
commit0e0f74bcf93341ff3b1d8b59f3639416a159a140 (patch)
tree119a2c795c2cb96c87cf74771ec9b50949573086
parent1999a3fb4154961329ecfff7d70a6303471b996a (diff)
downloadrust-0e0f74bcf93341ff3b1d8b59f3639416a159a140.tar.gz
rust-0e0f74bcf93341ff3b1d8b59f3639416a159a140.zip
rustc: Embed LLVM bitcode by default on iOS
This commit updates rustc to embed bitcode in each object file generated by
default when compiling for iOS. This was determined in #35968 as a step
towards better compatibility with the iOS toolchain, so let's give it a spin and
see how it turns out!

Note that this also updates the `cc` dependency which should propagate this
change of embedding bitcode for C dependencies as well.
-rw-r--r--src/librustc/session/config.rs2
-rw-r--r--src/librustc_back/target/mod.rs6
-rw-r--r--src/librustc_trans/back/write.rs78
-rw-r--r--src/libstd/build.rs3
4 files changed, 87 insertions, 2 deletions
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 1c5cfa87ef4..3e894abd17c 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1288,6 +1288,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "run `dsymutil` and delete intermediate object files"),
     ui_testing: bool = (false, parse_bool, [UNTRACKED],
           "format compiler diagnostics in a way that's better suitable for UI testing"),
+    embed_bitcode: bool = (false, parse_bool, [TRACKED],
+          "embed LLVM bitcode in object files"),
 }
 
 pub fn default_lib_output() -> CrateType {
diff --git a/src/librustc_back/target/mod.rs b/src/librustc_back/target/mod.rs
index 250d85d4520..f53eeb86a9c 100644
--- a/src/librustc_back/target/mod.rs
+++ b/src/librustc_back/target/mod.rs
@@ -473,6 +473,9 @@ pub struct TargetOptions {
     /// The default visibility for symbols in this target should be "hidden"
     /// rather than "default"
     pub default_hidden_visibility: bool,
+
+    /// Whether or not bitcode is embedded in object files
+    pub embed_bitcode: bool,
 }
 
 impl Default for TargetOptions {
@@ -544,6 +547,7 @@ impl Default for TargetOptions {
             i128_lowering: false,
             codegen_backend: "llvm".to_string(),
             default_hidden_visibility: false,
+            embed_bitcode: false,
         }
     }
 }
@@ -792,6 +796,7 @@ impl Target {
         key!(no_builtins, bool);
         key!(codegen_backend);
         key!(default_hidden_visibility, bool);
+        key!(embed_bitcode, bool);
 
         if let Some(array) = obj.find("abi-blacklist").and_then(Json::as_array) {
             for name in array.iter().filter_map(|abi| abi.as_string()) {
@@ -990,6 +995,7 @@ impl ToJson for Target {
         target_option_val!(no_builtins);
         target_option_val!(codegen_backend);
         target_option_val!(default_hidden_visibility);
+        target_option_val!(embed_bitcode);
 
         if default.abi_blacklist != self.options.abi_blacklist {
             d.insert("abi-blacklist".to_string(), self.options.abi_blacklist.iter()
diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs
index 7651a8e748e..3e7422557e9 100644
--- a/src/librustc_trans/back/write.rs
+++ b/src/librustc_trans/back/write.rs
@@ -42,6 +42,7 @@ use syntax_pos::MultiSpan;
 use syntax_pos::symbol::Symbol;
 use type_::Type;
 use context::{is_pie_binary, get_reloc_model};
+use common::{C_bytes_in_context, val_ty};
 use jobserver::{Client, Acquired};
 use rustc_demangle;
 
@@ -262,6 +263,8 @@ pub struct ModuleConfig {
     // emscripten's ecc compiler, when used as the linker.
     obj_is_bitcode: bool,
     no_integrated_as: bool,
+    embed_bitcode: bool,
+    embed_bitcode_marker: bool,
 }
 
 impl ModuleConfig {
@@ -279,6 +282,8 @@ impl ModuleConfig {
             emit_asm: false,
             emit_obj: false,
             obj_is_bitcode: false,
+            embed_bitcode: false,
+            embed_bitcode_marker: false,
             no_integrated_as: false,
 
             no_verify: false,
@@ -299,6 +304,17 @@ impl ModuleConfig {
         self.time_passes = sess.time_passes();
         self.inline_threshold = sess.opts.cg.inline_threshold;
         self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode;
+        let embed_bitcode = sess.target.target.options.embed_bitcode ||
+            sess.opts.debugging_opts.embed_bitcode;
+        if embed_bitcode {
+            match sess.opts.optimize {
+                config::OptLevel::No |
+                config::OptLevel::Less => {
+                    self.embed_bitcode_marker = embed_bitcode;
+                }
+                _ => self.embed_bitcode = embed_bitcode,
+            }
+        }
 
         // Copy what clang does by turning on loop vectorization at O2 and
         // slp vectorization at O3. Otherwise configure other optimization aspects
@@ -662,7 +678,7 @@ unsafe fn codegen(cgcx: &CodegenContext,
     let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name);
 
 
-    if write_bc || config.emit_bc_compressed {
+    if write_bc || config.emit_bc_compressed || config.embed_bitcode {
         let thin;
         let old;
         let data = if llvm::LLVMRustThinLTOAvailable() {
@@ -681,6 +697,11 @@ unsafe fn codegen(cgcx: &CodegenContext,
             timeline.record("write-bc");
         }
 
+        if config.embed_bitcode {
+            embed_bitcode(cgcx, llcx, llmod, Some(data));
+            timeline.record("embed-bc");
+        }
+
         if config.emit_bc_compressed {
             let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION);
             let data = bytecode::encode(&mtrans.llmod_id, data);
@@ -689,6 +710,8 @@ unsafe fn codegen(cgcx: &CodegenContext,
             }
             timeline.record("compress-bc");
         }
+    } else if config.embed_bitcode_marker {
+        embed_bitcode(cgcx, llcx, llmod, None);
     }
 
     time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()),
@@ -796,6 +819,59 @@ unsafe fn codegen(cgcx: &CodegenContext,
                                    &cgcx.output_filenames))
 }
 
+/// Embed the bitcode of an LLVM module in the LLVM module itself.
+///
+/// This is done primarily for iOS where it appears to be standard to compile C
+/// code at least with `-fembed-bitcode` which creates two sections in the
+/// executable:
+///
+/// * __LLVM,__bitcode
+/// * __LLVM,__cmdline
+///
+/// It appears *both* of these sections are necessary to get the linker to
+/// recognize what's going on. For us though we just always throw in an empty
+/// cmdline section.
+///
+/// Furthermore debug/O1 builds don't actually embed bitcode but rather just
+/// embed an empty section.
+///
+/// Basically all of this is us attempting to follow in the footsteps of clang
+/// on iOS. See #35968 for lots more info.
+unsafe fn embed_bitcode(cgcx: &CodegenContext,
+                        llcx: ContextRef,
+                        llmod: ModuleRef,
+                        bitcode: Option<&[u8]>) {
+    let llconst = C_bytes_in_context(llcx, bitcode.unwrap_or(&[]));
+    let llglobal = llvm::LLVMAddGlobal(
+        llmod,
+        val_ty(llconst).to_ref(),
+        "rustc.embedded.module\0".as_ptr() as *const _,
+    );
+    llvm::LLVMSetInitializer(llglobal, llconst);
+    let section = if cgcx.opts.target_triple.contains("-ios") {
+        "__LLVM,__bitcode\0"
+    } else {
+        ".llvmbc\0"
+    };
+    llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
+    llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
+
+    let llconst = C_bytes_in_context(llcx, &[]);
+    let llglobal = llvm::LLVMAddGlobal(
+        llmod,
+        val_ty(llconst).to_ref(),
+        "rustc.embedded.cmdline\0".as_ptr() as *const _,
+    );
+    llvm::LLVMSetInitializer(llglobal, llconst);
+    let section = if cgcx.opts.target_triple.contains("-ios") {
+        "__LLVM,__cmdline\0"
+    } else {
+        ".llvmcmd\0"
+    };
+    llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _);
+    llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage);
+}
+
 pub(crate) struct CompiledModules {
     pub modules: Vec<CompiledModule>,
     pub metadata_module: CompiledModule,
diff --git a/src/libstd/build.rs b/src/libstd/build.rs
index b3c71f4d81b..6652ff98201 100644
--- a/src/libstd/build.rs
+++ b/src/libstd/build.rs
@@ -86,6 +86,7 @@ fn main() {
 
 fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> {
     let native = native_lib_boilerplate("libbacktrace", "libbacktrace", "backtrace", ".libs")?;
+    let cflags = env::var("CFLAGS").unwrap_or_default() + " -fvisibility=hidden -O2";
 
     run(Command::new("sh")
                 .current_dir(&native.out_dir)
@@ -98,7 +99,7 @@ fn build_libbacktrace(host: &str, target: &str) -> Result<(), ()> {
                 .arg("--disable-host-shared")
                 .arg(format!("--host={}", build_helper::gnu_target(target)))
                 .arg(format!("--build={}", build_helper::gnu_target(host)))
-                .env("CFLAGS", env::var("CFLAGS").unwrap_or_default() + " -fvisibility=hidden -O2"));
+                .env("CFLAGS", cflags));
 
     run(Command::new(build_helper::make(host))
                 .current_dir(&native.out_dir)