@@ -181,6 +181,38 @@ 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+ // For i386, bit 0 (`IMAGE_FILE_SAFE_EXCEPTION_HANDLER`) marks the object as SAFESEH-compatible.
188+ // Without this, linkers can reject the object with `/safeseh`.
189+ //
190+ // - <https://github.com/rust-lang/rust/issues/96498>
191+ // - <https://learn.microsoft.com/en-us/windows/win32/debug/pe-format>
192+ if binary_format == BinaryFormat :: Coff {
193+ // Disable mangling so the "@feat.00" symbol name is written verbatim.
194+ // CoffI386 mangling adds a `_` prefix which would break this special symbol.
195+ let original_mangling = file. mangling ( ) ;
196+ file. set_mangling ( write:: Mangling :: None ) ;
197+
198+ let mut feature: u64 = 0 ;
199+ if architecture == Architecture :: I386 {
200+ feature |= 1 ; // IMAGE_FILE_SAFE_EXCEPTION_HANDLER
201+ }
202+ file. add_symbol ( Symbol {
203+ name : b"@feat.00" . to_vec ( ) ,
204+ value : feature,
205+ size : 0 ,
206+ kind : SymbolKind :: Data ,
207+ scope : SymbolScope :: Compilation ,
208+ weak : false ,
209+ section : SymbolSection :: Absolute ,
210+ flags : SymbolFlags :: None ,
211+ } ) ;
212+
213+ file. set_mangling ( original_mangling) ;
214+ }
215+
184216 Some ( file)
185217}
186218
@@ -322,6 +354,87 @@ windows
322354 assert_eq ! ( result. architecture( ) , Architecture :: X86_64 ) ;
323355 }
324356
357+ #[ test]
358+ fn test_create_object_file_windows_msvc_i686 ( ) {
359+ let rustc_output = br#"debug_assertions
360+ target_arch="x86"
361+ target_endian="little"
362+ target_env="msvc"
363+ target_family="windows"
364+ target_feature="fxsr"
365+ target_feature="sse"
366+ target_feature="sse2"
367+ target_os="windows"
368+ target_pointer_width="32"
369+ target_vendor="pc"
370+ windows
371+ "# ;
372+ let target_triple = "i686-pc-windows-msvc" ;
373+ let target_info = parse_rustc_target_info ( rustc_output) ;
374+ let result = create_object_file ( & target_info, target_triple) . unwrap ( ) ;
375+ assert_eq ! ( result. format( ) , BinaryFormat :: Coff ) ;
376+ assert_eq ! ( result. architecture( ) , Architecture :: I386 ) ;
377+ }
378+
379+ /// Verify that i686 COFF metadata objects contain an absolute `@feat.00` symbol with
380+ /// `IMAGE_FILE_SAFE_EXCEPTION_HANDLER` (bit 0) set.
381+ ///
382+ /// See <https://github.com/rust-lang/rust/issues/96498>
383+ #[ test]
384+ fn test_create_metadata_file_windows_msvc_i686_has_feat00 ( ) {
385+ let rustc_output = br#"debug_assertions
386+ target_arch="x86"
387+ target_endian="little"
388+ target_env="msvc"
389+ target_family="windows"
390+ target_feature="fxsr"
391+ target_feature="sse"
392+ target_feature="sse2"
393+ target_os="windows"
394+ target_pointer_width="32"
395+ target_vendor="pc"
396+ windows
397+ "# ;
398+ let target_triple = "i686-pc-windows-msvc" ;
399+ let target_info = parse_rustc_target_info ( rustc_output) ;
400+ let contents = b"test audit data" ;
401+ let result = create_metadata_file (
402+ & target_info,
403+ target_triple,
404+ contents,
405+ "AUDITABLE_VERSION_INFO" ,
406+ )
407+ . expect ( "should produce an object file for i686-pc-windows-msvc" ) ;
408+
409+ // Parse the COFF symbol table and verify `@feat.00` has value bit0=1 and absolute section.
410+ let symtab_ptr = u32:: from_le_bytes ( result[ 8 ..12 ] . try_into ( ) . unwrap ( ) ) as usize ;
411+ let sym_count = u32:: from_le_bytes ( result[ 12 ..16 ] . try_into ( ) . unwrap ( ) ) as usize ;
412+ let symbol_size = 18 ;
413+
414+ let feat = ( 0 ..sym_count) . find_map ( |i| {
415+ let start = symtab_ptr + i * symbol_size;
416+ let end = start + symbol_size;
417+ let entry = result. get ( start..end) ?;
418+ if & entry[ 0 ..8 ] != b"@feat.00" {
419+ return None ;
420+ }
421+ let value = u32:: from_le_bytes ( entry[ 8 ..12 ] . try_into ( ) . unwrap ( ) ) ;
422+ let section_number = i16:: from_le_bytes ( entry[ 12 ..14 ] . try_into ( ) . unwrap ( ) ) ;
423+ Some ( ( value, section_number) )
424+ } ) ;
425+
426+ let ( value, section_number) = feat. expect ( "COFF object for i686 must contain @feat.00" ) ;
427+ assert_eq ! (
428+ value & 1 ,
429+ 1 ,
430+ "@feat.00 must set IMAGE_FILE_SAFE_EXCEPTION_HANDLER on i686"
431+ ) ;
432+ assert_eq ! (
433+ section_number, -1 ,
434+ "@feat.00 must be an absolute COFF symbol (section number -1)"
435+ ) ;
436+ }
437+
325438 #[ test]
326439 fn test_create_object_file_windows_gnu ( ) {
327440 let rustc_output = br#"debug_assertions
0 commit comments