@@ -181,6 +181,44 @@ fn create_object_file(
181181 abi_version,
182182 e_flags,
183183 } ;
184+
185+ // Add the COFF `@feat.00` symbol used to communicate linker feature flags.
186+ //
187+ // When linking with /SAFESEH on x86, lld requires that all linker inputs be marked as safe
188+ // exception handling compatible. Our metadata objects masquerade as regular COFF objects and
189+ // are treated as linker inputs, so they need the flag too.
190+ //
191+ // This implementation mirrors the rustc's metadata object generation:
192+ // <https://github.com/rust-lang/rust/blob/b90dc1e597db0bbc0cab0eccb39747b1a9d7e607/compiler/rustc_codegen_ssa/src/back/metadata.rs#L224-L252>
193+ //
194+ // See also:
195+ //
196+ // - <https://github.com/rust-lang/rust/issues/96498>
197+ // - <https://learn.microsoft.com/en-us/windows/win32/debug/pe-format>
198+ if binary_format == BinaryFormat :: Coff {
199+ // Disable mangling so the "@feat.00" symbol name is written verbatim.
200+ // CoffI386 mangling adds a `_` prefix which would break this special symbol.
201+ let original_mangling = file. mangling ( ) ;
202+ file. set_mangling ( write:: Mangling :: None ) ;
203+
204+ let mut feature: u64 = 0 ;
205+ if architecture == Architecture :: I386 {
206+ feature |= 1 ; // IMAGE_FILE_SAFE_EXCEPTION_HANDLER
207+ }
208+ file. add_symbol ( Symbol {
209+ name : b"@feat.00" . to_vec ( ) ,
210+ value : feature,
211+ size : 0 ,
212+ kind : SymbolKind :: Data ,
213+ scope : SymbolScope :: Compilation ,
214+ weak : false ,
215+ section : SymbolSection :: Absolute ,
216+ flags : SymbolFlags :: None ,
217+ } ) ;
218+
219+ file. set_mangling ( original_mangling) ;
220+ }
221+
184222 Some ( file)
185223}
186224
@@ -344,6 +382,65 @@ windows
344382 assert_eq ! ( result. architecture( ) , Architecture :: I386 ) ;
345383 }
346384
385+ /// Verify that i686 COFF metadata objects contain an absolute `@feat.00` symbol with
386+ /// `IMAGE_FILE_SAFE_EXCEPTION_HANDLER` (bit 0) set.
387+ ///
388+ /// See <https://github.com/rust-lang/rust/issues/96498>
389+ #[ test]
390+ fn test_create_metadata_file_windows_msvc_i686_has_feat00 ( ) {
391+ let rustc_output = br#"debug_assertions
392+ target_arch="x86"
393+ target_endian="little"
394+ target_env="msvc"
395+ target_family="windows"
396+ target_feature="fxsr"
397+ target_feature="sse"
398+ target_feature="sse2"
399+ target_os="windows"
400+ target_pointer_width="32"
401+ target_vendor="pc"
402+ windows
403+ "# ;
404+ let target_triple = "i686-pc-windows-msvc" ;
405+ let target_info = parse_rustc_target_info ( rustc_output) ;
406+ let contents = b"test audit data" ;
407+ let result = create_metadata_file (
408+ & target_info,
409+ target_triple,
410+ contents,
411+ "AUDITABLE_VERSION_INFO" ,
412+ )
413+ . expect ( "should produce an object file for i686-pc-windows-msvc" ) ;
414+
415+ // Parse the COFF symbol table and verify `@feat.00` has value bit0=1 and absolute section.
416+ let symtab_ptr = u32:: from_le_bytes ( result[ 8 ..12 ] . try_into ( ) . unwrap ( ) ) as usize ;
417+ let sym_count = u32:: from_le_bytes ( result[ 12 ..16 ] . try_into ( ) . unwrap ( ) ) as usize ;
418+ let symbol_size = 18 ;
419+
420+ let feat = ( 0 ..sym_count) . find_map ( |i| {
421+ let start = symtab_ptr + i * symbol_size;
422+ let end = start + symbol_size;
423+ let entry = result. get ( start..end) ?;
424+ if & entry[ 0 ..8 ] != b"@feat.00" {
425+ return None ;
426+ }
427+ let value = u32:: from_le_bytes ( entry[ 8 ..12 ] . try_into ( ) . unwrap ( ) ) ;
428+ let section_number = i16:: from_le_bytes ( entry[ 12 ..14 ] . try_into ( ) . unwrap ( ) ) ;
429+ Some ( ( value, section_number) )
430+ } ) ;
431+
432+ let ( value, section_number) = feat. expect ( "COFF object for i686 must contain @feat.00" ) ;
433+ assert_eq ! (
434+ value & 1 ,
435+ 1 ,
436+ "@feat.00 must set IMAGE_FILE_SAFE_EXCEPTION_HANDLER on i686"
437+ ) ;
438+ assert_eq ! (
439+ section_number, -1 ,
440+ "@feat.00 must be an absolute COFF symbol (section number -1)"
441+ ) ;
442+ }
443+
347444 #[ test]
348445 fn test_create_object_file_windows_gnu ( ) {
349446 let rustc_output = br#"debug_assertions
0 commit comments