44//!
55//! Also see the docs in `asm.rs`.
66
7- use process:: Stdio ;
7+ use object:: {
8+ read:: { Object as _, ObjectSection as _} ,
9+ write:: { Object , Symbol , SymbolSection } ,
10+ SymbolFlags ,
11+ } ;
812use std:: env:: current_dir;
913use std:: {
1014 collections:: BTreeMap ,
1115 fs:: { self , File } ,
12- process:: { self , Command } ,
16+ process:: { Command , Stdio } ,
1317} ;
1418
1519fn toolchain ( ) -> String {
@@ -25,6 +29,88 @@ fn rustc() -> Command {
2529 cmd
2630}
2731
32+ /// Patches an object file so that it doesn't contain a panic handler.
33+ ///
34+ /// The panic handler defined in `asm/lib.rs` should never get linked to the final program.
35+ /// Unfortunately, Rust uses the same symbol for all panic handlers, and doesn't really like it if
36+ /// that ends up with multiple ones. It also demands that we define a panic handler for the inline
37+ /// assembly shim, even though none of that code should ever be able to panic. The result of this is
38+ /// that the supposedly unreachable panic handler does end up getting linked into the final program,
39+ /// unless it is built with optimizations enabled.
40+ ///
41+ /// To fix that, we put the never-to-be-used panic handler into its own section via
42+ /// `#[link_section]`, and then use this function to delete that section.
43+ fn trim_panic_handler ( obj_file : & str ) {
44+ let objdata = fs:: read ( & obj_file) . unwrap ( ) ;
45+ let obj = object:: File :: parse ( & objdata) . unwrap ( ) ;
46+
47+ let mut writer = Object :: new ( obj. format ( ) , obj. architecture ( ) , obj. endianness ( ) ) ;
48+ for ( sec_index, section) in obj. sections ( ) . enumerate ( ) {
49+ assert_eq ! ( section. index( ) . 0 , sec_index) ;
50+
51+ let name = section. name ( ) . unwrap ( ) ;
52+ if name. starts_with ( ".ARM" )
53+ || name. starts_with ( ".rel.ARM" )
54+ || name. contains ( "cortex_m_asm_panic" )
55+ || name == ".strtab"
56+ || name == ".symtab"
57+ {
58+ // We drop the ARM exception handling tables since they refer back to the panic handler
59+ // symbol. They aren't used either way. We also drop `.strtab` and `.symtab` since they
60+ // otherwise end up having the wrong section type. The object crate should rebuild any
61+ // index tables when writing the file.
62+ continue ;
63+ }
64+
65+ let segment = section
66+ . segment_name ( )
67+ . unwrap ( )
68+ . map ( |s| s. as_bytes ( ) )
69+ . unwrap_or ( & [ ] ) ;
70+ let sec_id = writer. add_section ( segment. to_vec ( ) , name. as_bytes ( ) . to_vec ( ) , section. kind ( ) ) ;
71+
72+ let align = if section. align ( ) == 0 {
73+ // Not sure why but `section.align()` can return 0.
74+ 1
75+ } else {
76+ section. align ( )
77+ } ;
78+ writer. append_section_data ( sec_id, section. data ( ) . unwrap ( ) , align) ;
79+
80+ // Import all symbols from the section.
81+ for ( _sym_idx, symbol) in obj. symbols ( ) {
82+ if symbol. section_index ( ) == Some ( section. index ( ) ) {
83+ writer. add_symbol ( Symbol {
84+ name : symbol. name ( ) . unwrap_or ( "" ) . as_bytes ( ) . to_vec ( ) ,
85+ value : symbol. address ( ) ,
86+ size : symbol. size ( ) ,
87+ kind : symbol. kind ( ) ,
88+ scope : symbol. scope ( ) ,
89+ weak : symbol. is_weak ( ) ,
90+ section : match symbol. section ( ) {
91+ object:: SymbolSection :: Unknown => unimplemented ! ( ) ,
92+ object:: SymbolSection :: None => SymbolSection :: None ,
93+ object:: SymbolSection :: Undefined => SymbolSection :: Undefined ,
94+ object:: SymbolSection :: Absolute => SymbolSection :: Absolute ,
95+ object:: SymbolSection :: Common => SymbolSection :: Common ,
96+ object:: SymbolSection :: Section ( _) => SymbolSection :: Section ( sec_id) ,
97+ } ,
98+ flags : match symbol. flags ( ) {
99+ SymbolFlags :: None => SymbolFlags :: None ,
100+ SymbolFlags :: Elf { st_info, st_other } => {
101+ SymbolFlags :: Elf { st_info, st_other }
102+ }
103+ _ => unimplemented ! ( ) ,
104+ } ,
105+ } ) ;
106+ }
107+ }
108+ }
109+
110+ let obj = writer. write ( ) . unwrap ( ) ;
111+ fs:: write ( & obj_file, obj) . unwrap ( ) ;
112+ }
113+
28114fn assemble_really ( target : & str , cfgs : & [ & str ] , plugin_lto : bool ) {
29115 let mut cmd = rustc ( ) ;
30116
@@ -74,6 +160,11 @@ fn assemble_really(target: &str, cfgs: &[&str], plugin_lto: bool) {
74160 let status = cmd. status ( ) . unwrap ( ) ;
75161 assert ! ( status. success( ) ) ;
76162
163+ if !plugin_lto {
164+ // Post-process the object file.
165+ trim_panic_handler ( & obj_file) ;
166+ }
167+
77168 // Archive `target.o` -> `bin/target.a`.
78169 let mut builder = ar:: Builder :: new ( File :: create ( format ! ( "bin/{}.a" , file_stub) ) . unwrap ( ) ) ;
79170
0 commit comments