about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/doc/unstable-book/src/compiler-flags/emit-stack-sizes.md167
-rw-r--r--src/librustc/session/config.rs2
-rw-r--r--src/librustc_codegen_llvm/back/write.rs2
-rw-r--r--src/librustc_codegen_llvm/llvm/ffi.rs3
-rw-r--r--src/rustllvm/PassWrapper.cpp5
-rw-r--r--src/test/run-make-fulldeps/emit-stack-sizes/Makefile31
-rw-r--r--src/test/run-make-fulldeps/emit-stack-sizes/foo.rs13
7 files changed, 221 insertions, 2 deletions
diff --git a/src/doc/unstable-book/src/compiler-flags/emit-stack-sizes.md b/src/doc/unstable-book/src/compiler-flags/emit-stack-sizes.md
new file mode 100644
index 00000000000..47f45a0b91f
--- /dev/null
+++ b/src/doc/unstable-book/src/compiler-flags/emit-stack-sizes.md
@@ -0,0 +1,167 @@
+# `emit-stack-sizes`
+
+The tracking issue for this feature is: [#54192]
+
+[#54192]: https://github.com/rust-lang/rust/issues/54192
+
+------------------------
+
+The rustc flag `-Z emit-stack-sizes` makes LLVM emit stack size metadata.
+
+> **NOTE**: This LLVM feature only supports the ELF object format as of LLVM
+> 8.0. Using this flag with targets that use other object formats (e.g. macOS
+> and Windows) will result in it being ignored.
+
+Consider this crate:
+
+```
+#![crate_type = "lib"]
+
+use std::ptr;
+
+pub fn foo() {
+    // this function doesn't use the stack
+}
+
+pub fn bar() {
+    let xs = [0u32; 2];
+
+    // force LLVM to allocate `xs` on the stack
+    unsafe { ptr::read_volatile(&xs.as_ptr()); }
+}
+```
+
+Using the `-Z emit-stack-sizes` flag produces extra linker sections in the
+output *object file*.
+
+``` console
+$ rustc -C opt-level=3 --emit=obj foo.rs
+
+$ size -A foo.o
+foo.o  :
+section                                 size   addr
+.text                                      0      0
+.text._ZN3foo3foo17he211d7b4a3a0c16eE      1      0
+.text._ZN3foo3bar17h1acb594305f70c2eE     22      0
+.note.GNU-stack                            0      0
+.eh_frame                                 72      0
+Total                                     95
+
+$ rustc -C opt-level=3 --emit=obj -Z emit-stack-sizes foo.rs
+
+$ size -A foo.o
+foo.o  :
+section                                 size   addr
+.text                                      0      0
+.text._ZN3foo3foo17he211d7b4a3a0c16eE      1      0
+.stack_sizes                               9      0
+.text._ZN3foo3bar17h1acb594305f70c2eE     22      0
+.stack_sizes                               9      0
+.note.GNU-stack                            0      0
+.eh_frame                                 72      0
+Total                                    113
+```
+
+As of LLVM 7.0 the data will be written into a section named `.stack_sizes` and
+the format is "an array of pairs of function symbol values (pointer size) and
+stack sizes (unsigned LEB128)".
+
+``` console
+$ objdump -d foo.o
+
+foo.o:     file format elf64-x86-64
+
+Disassembly of section .text._ZN3foo3foo17he211d7b4a3a0c16eE:
+
+0000000000000000 <_ZN3foo3foo17he211d7b4a3a0c16eE>:
+   0:   c3                      retq
+
+Disassembly of section .text._ZN3foo3bar17h1acb594305f70c2eE:
+
+0000000000000000 <_ZN3foo3bar17h1acb594305f70c2eE>:
+   0:   48 83 ec 10             sub    $0x10,%rsp
+   4:   48 8d 44 24 08          lea    0x8(%rsp),%rax
+   9:   48 89 04 24             mov    %rax,(%rsp)
+   d:   48 8b 04 24             mov    (%rsp),%rax
+  11:   48 83 c4 10             add    $0x10,%rsp
+  15:   c3                      retq
+
+$ objdump -s -j .stack_sizes foo.o
+
+foo.o:     file format elf64-x86-64
+
+Contents of section .stack_sizes:
+ 0000 00000000 00000000 00                 .........
+Contents of section .stack_sizes:
+ 0000 00000000 00000000 10                 .........
+```
+
+It's important to note that linkers will discard this linker section by default.
+To preserve the section you can use a linker script like the one shown below.
+
+``` text
+/* file: keep-stack-sizes.x */
+SECTIONS
+{
+  /* `INFO` makes the section not allocatable so it won't be loaded into memory */
+  .stack_sizes (INFO) :
+  {
+    KEEP(*(.stack_sizes));
+  }
+}
+```
+
+The linker script must be passed to the linker using a rustc flag like `-C
+link-arg`.
+
+```
+// file: src/main.rs
+use std::ptr;
+
+#[inline(never)]
+fn main() {
+    let xs = [0u32; 2];
+
+    // force LLVM to allocate `xs` on the stack
+    unsafe { ptr::read_volatile(&xs.as_ptr()); }
+}
+```
+
+``` console
+$ RUSTFLAGS="-Z emit-stack-sizes" cargo build --release
+
+$ size -A target/release/hello | grep stack_sizes || echo section was not found
+section was not found
+
+$ RUSTFLAGS="-Z emit-stack-sizes" cargo rustc --release -- \
+    -C link-arg=-Wl,-Tkeep-stack-sizes.x \
+    -C link-arg=-N
+
+$ size -A target/release/hello | grep stack_sizes
+.stack_sizes                               90   176272
+
+$ # non-allocatable section (flags don't contain the "A" (alloc) flag)
+$ readelf -S target/release/hello
+Section Headers:
+  [Nr]   Name              Type             Address           Offset
+       Size              EntSize            Flags  Link  Info  Align
+(..)
+  [1031] .stack_sizes      PROGBITS         000000000002b090  0002b0f0
+       000000000000005a  0000000000000000   L       5     0     1
+
+$ objdump -s -j .stack_sizes target/release/hello
+
+target/release/hello:     file format elf64-x86-64
+
+Contents of section .stack_sizes:
+ 2b090 c0040000 00000000 08f00400 00000000  ................
+ 2b0a0 00080005 00000000 00000810 05000000  ................
+ 2b0b0 00000000 20050000 00000000 10400500  .... ........@..
+ 2b0c0 00000000 00087005 00000000 00000080  ......p.........
+ 2b0d0 05000000 00000000 90050000 00000000  ................
+ 2b0e0 00a00500 00000000 0000               ..........
+```
+
+> Author note: I'm not entirely sure why, in *this* case, `-N` is required in
+> addition to `-Tkeep-stack-sizes.x`. For example, it's not required when
+> producing statically linked files for the ARM Cortex-M architecture.
diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs
index 8fa15d48a5d..eb779e6382f 100644
--- a/src/librustc/session/config.rs
+++ b/src/librustc/session/config.rs
@@ -1385,6 +1385,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
           "run the self profiler"),
     profile_json: bool = (false, parse_bool, [UNTRACKED],
           "output a json file with profiler results"),
+    emit_stack_sizes: bool = (false, parse_bool, [UNTRACKED],
+          "emits a section containing stack size metadata"),
 }
 
 pub fn default_lib_output() -> CrateType {
diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs
index 447b505e79c..02ef690b942 100644
--- a/src/librustc_codegen_llvm/back/write.rs
+++ b/src/librustc_codegen_llvm/back/write.rs
@@ -196,6 +196,7 @@ pub fn target_machine_factory(sess: &Session, find_features: bool)
     let features = CString::new(features).unwrap();
     let is_pie_binary = !find_features && is_pie_binary(sess);
     let trap_unreachable = sess.target.target.options.trap_unreachable;
+    let emit_stack_size_section = sess.opts.debugging_opts.emit_stack_sizes;
 
     let asm_comments = sess.asm_comments();
 
@@ -213,6 +214,7 @@ pub fn target_machine_factory(sess: &Session, find_features: bool)
                 trap_unreachable,
                 singlethread,
                 asm_comments,
+                emit_stack_size_section,
             )
         };
 
diff --git a/src/librustc_codegen_llvm/llvm/ffi.rs b/src/librustc_codegen_llvm/llvm/ffi.rs
index a5f4137c62b..845f2fa9f45 100644
--- a/src/librustc_codegen_llvm/llvm/ffi.rs
+++ b/src/librustc_codegen_llvm/llvm/ffi.rs
@@ -1460,7 +1460,8 @@ extern "C" {
                                        DataSections: bool,
                                        TrapUnreachable: bool,
                                        Singlethread: bool,
-                                       AsmComments: bool)
+                                       AsmComments: bool,
+                                       EmitStackSizeSection: bool)
                                        -> Option<&'static mut TargetMachine>;
     pub fn LLVMRustDisposeTargetMachine(T: &'static mut TargetMachine);
     pub fn LLVMRustAddAnalysisPasses(T: &'a TargetMachine, PM: &PassManager<'a>, M: &'a Module);
diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp
index 5c4bb61781e..06f75d981e3 100644
--- a/src/rustllvm/PassWrapper.cpp
+++ b/src/rustllvm/PassWrapper.cpp
@@ -373,7 +373,8 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
     bool DataSections,
     bool TrapUnreachable,
     bool Singlethread,
-    bool AsmComments) {
+    bool AsmComments,
+    bool EmitStackSizeSection) {
 
   auto OptLevel = fromRust(RustOptLevel);
   auto RM = fromRust(RustReloc);
@@ -411,6 +412,8 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine(
   }
 
 #if LLVM_VERSION_GE(6, 0)
+  Options.EmitStackSizeSection = EmitStackSizeSection;
+
   Optional<CodeModel::Model> CM;
 #else
   CodeModel::Model CM = CodeModel::Model::Default;
diff --git a/src/test/run-make-fulldeps/emit-stack-sizes/Makefile b/src/test/run-make-fulldeps/emit-stack-sizes/Makefile
new file mode 100644
index 00000000000..c2f643ce24c
--- /dev/null
+++ b/src/test/run-make-fulldeps/emit-stack-sizes/Makefile
@@ -0,0 +1,31 @@
+-include ../tools.mk
+
+# This feature only works when the output object format is ELF so we ignore
+# macOS and Windows
+ifdef IS_WINDOWS
+# Do nothing on Windows.
+all:
+	exit 0
+else ifneq (,$(filter $(TARGET),i686-apple-darwin x86_64-apple-darwin))
+# Do nothing on macOS.
+all:
+	exit 0
+else
+# check that the .stack_sizes section is generated
+# this test requires LLVM >= 6.0.0
+vers = $(shell $(RUSTC) -Vv)
+ifneq (,$(findstring LLVM version: 3,$(vers)))
+all:
+	exit 0
+else ifneq (,$(findstring LLVM version: 4,$(vers)))
+all:
+	exit 0
+else ifneq (,$(findstring LLVM version: 5,$(vers)))
+all:
+	exit 0
+else
+all:
+	$(RUSTC) -C opt-level=3 -Z emit-stack-sizes --emit=obj foo.rs
+	size -A $(TMPDIR)/foo.o | $(CGREP) .stack_sizes
+endif
+endif
diff --git a/src/test/run-make-fulldeps/emit-stack-sizes/foo.rs b/src/test/run-make-fulldeps/emit-stack-sizes/foo.rs
new file mode 100644
index 00000000000..6c81b63963a
--- /dev/null
+++ b/src/test/run-make-fulldeps/emit-stack-sizes/foo.rs
@@ -0,0 +1,13 @@
+// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+#![crate_type = "lib"]
+
+pub fn foo() {}