1- use std:: env;
21use std:: error:: Error ;
3- use std:: ffi:: OsString ;
42use std:: fs:: { self , File } ;
53use std:: io:: { self , BufWriter , Write } ;
64use std:: path:: { Path , PathBuf } ;
@@ -22,13 +20,9 @@ use tracing::trace;
2220
2321use super :: metadata:: { create_compressed_metadata_file, search_for_section} ;
2422use super :: rmeta_link;
25- use crate :: common;
2623// Public for ArchiveBuilderBuilder::extract_bundled_libs
2724pub use crate :: errors:: ExtractBundledLibsError ;
28- use crate :: errors:: {
29- ArchiveBuildFailure , DlltoolFailImportLibrary , ErrorCallingDllTool , ErrorCreatingImportLibrary ,
30- ErrorWritingDEFFile , UnknownArchiveKind ,
31- } ;
25+ use crate :: errors:: { ArchiveBuildFailure , ErrorCreatingImportLibrary , UnknownArchiveKind } ;
3226
3327/// An item to be included in an import library.
3428/// This is a slimmed down version of `COFFShortExport` from `ar-archive-writer`.
@@ -85,66 +79,57 @@ pub trait ArchiveBuilderBuilder {
8579 items : Vec < ImportLibraryItem > ,
8680 output_path : & Path ,
8781 ) {
88- if common:: is_mingw_gnu_toolchain ( & sess. target ) {
89- // The binutils linker used on -windows-gnu targets cannot read the import
90- // libraries generated by LLVM: in our attempts, the linker produced an .EXE
91- // that loaded but crashed with an AV upon calling one of the imported
92- // functions. Therefore, use binutils to create the import library instead,
93- // by writing a .DEF file to the temp dir and calling binutils's dlltool.
94- create_mingw_dll_import_lib ( sess, lib_name, items, output_path) ;
95- } else {
96- trace ! ( "creating import library" ) ;
97- trace ! ( " dll_name {:#?}" , lib_name) ;
98- trace ! ( " output_path {}" , output_path. display( ) ) ;
99- trace ! (
100- " import names: {}" ,
101- items
102- . iter( )
103- . map( |ImportLibraryItem { name, .. } | name. clone( ) )
104- . collect:: <Vec <_>>( )
105- . join( ", " ) ,
106- ) ;
107-
108- // All import names are Rust identifiers and therefore cannot contain \0 characters.
109- // FIXME: when support for #[link_name] is implemented, ensure that the import names
110- // still don't contain any \0 characters. Also need to check that the names don't
111- // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved
112- // in definition files.
113-
114- let mut file = match fs:: File :: create_new ( & output_path) {
115- Ok ( file) => file,
116- Err ( error) => sess
117- . dcx ( )
118- . emit_fatal ( ErrorCreatingImportLibrary { lib_name, error : error. to_string ( ) } ) ,
119- } ;
82+ trace ! ( "creating import library" ) ;
83+ trace ! ( " dll_name {:#?}" , lib_name) ;
84+ trace ! ( " output_path {}" , output_path. display( ) ) ;
85+ trace ! (
86+ " import names: {}" ,
87+ items
88+ . iter( )
89+ . map( |ImportLibraryItem { name, .. } | name. clone( ) )
90+ . collect:: <Vec <_>>( )
91+ . join( ", " ) ,
92+ ) ;
93+
94+ // All import names are Rust identifiers and therefore cannot contain \0 characters.
95+ // FIXME: when support for #[link_name] is implemented, ensure that the import names
96+ // still don't contain any \0 characters. Also need to check that the names don't
97+ // contain substrings like " @" or "NONAME" that are keywords or otherwise reserved
98+ // in definition files.
99+
100+ let mut file = match fs:: File :: create_new ( & output_path) {
101+ Ok ( file) => file,
102+ Err ( error) => sess
103+ . dcx ( )
104+ . emit_fatal ( ErrorCreatingImportLibrary { lib_name, error : error. to_string ( ) } ) ,
105+ } ;
120106
121- let exports =
122- items. into_iter ( ) . map ( |item| item. into_coff_short_export ( sess) ) . collect :: < Vec < _ > > ( ) ;
123- let machine = match & sess. target . arch {
124- Arch :: X86_64 => MachineTypes :: AMD64 ,
125- Arch :: X86 => MachineTypes :: I386 ,
126- Arch :: AArch64 => MachineTypes :: ARM64 ,
127- Arch :: Arm64EC => MachineTypes :: ARM64EC ,
128- Arch :: Arm => MachineTypes :: ARMNT ,
129- cpu => panic ! ( "unsupported cpu type {cpu}" ) ,
130- } ;
107+ let exports =
108+ items. into_iter ( ) . map ( |item| item. into_coff_short_export ( sess) ) . collect :: < Vec < _ > > ( ) ;
109+ let machine = match & sess. target . arch {
110+ Arch :: X86_64 => MachineTypes :: AMD64 ,
111+ Arch :: X86 => MachineTypes :: I386 ,
112+ Arch :: AArch64 => MachineTypes :: ARM64 ,
113+ Arch :: Arm64EC => MachineTypes :: ARM64EC ,
114+ Arch :: Arm => MachineTypes :: ARMNT ,
115+ cpu => panic ! ( "unsupported cpu type {cpu}" ) ,
116+ } ;
131117
132- if let Err ( error) = ar_archive_writer:: write_import_library (
133- & mut file,
134- lib_name,
135- & exports,
136- machine,
137- !sess. target . is_like_msvc ,
138- // Enable compatibility with MSVC's `/WHOLEARCHIVE` flag.
139- // Without this flag a duplicate symbol error would be emitted
140- // when linking a rust staticlib using `/WHOLEARCHIVE`.
141- // See #129020
142- true ,
143- & [ ] ,
144- ) {
145- sess. dcx ( )
146- . emit_fatal ( ErrorCreatingImportLibrary { lib_name, error : error. to_string ( ) } ) ;
147- }
118+ if let Err ( error) = ar_archive_writer:: write_import_library (
119+ & mut file,
120+ lib_name,
121+ & exports,
122+ machine,
123+ !sess. target . is_like_msvc ,
124+ // Enable compatibility with MSVC's `/WHOLEARCHIVE` flag.
125+ // Without this flag a duplicate symbol error would be emitted
126+ // when linking a rust staticlib using `/WHOLEARCHIVE`.
127+ // See #129020
128+ true ,
129+ & [ ] ,
130+ ) {
131+ sess. dcx ( )
132+ . emit_fatal ( ErrorCreatingImportLibrary { lib_name, error : error. to_string ( ) } ) ;
148133 }
149134 }
150135
@@ -185,129 +170,6 @@ pub trait ArchiveBuilderBuilder {
185170 }
186171}
187172
188- fn create_mingw_dll_import_lib (
189- sess : & Session ,
190- lib_name : & str ,
191- items : Vec < ImportLibraryItem > ,
192- output_path : & Path ,
193- ) {
194- let def_file_path = output_path. with_extension ( "def" ) ;
195-
196- let def_file_content = format ! (
197- "EXPORTS\n {}" ,
198- items
199- . into_iter( )
200- . map( |ImportLibraryItem { name, ordinal, .. } | {
201- match ordinal {
202- Some ( n) => format!( "{name} @{n} NONAME" ) ,
203- None => name,
204- }
205- } )
206- . collect:: <Vec <String >>( )
207- . join( "\n " )
208- ) ;
209-
210- match std:: fs:: write ( & def_file_path, def_file_content) {
211- Ok ( _) => { }
212- Err ( e) => {
213- sess. dcx ( ) . emit_fatal ( ErrorWritingDEFFile { error : e } ) ;
214- }
215- } ;
216-
217- // --no-leading-underscore: For the `import_name_type` feature to work, we need to be
218- // able to control the *exact* spelling of each of the symbols that are being imported:
219- // hence we don't want `dlltool` adding leading underscores automatically.
220- let dlltool = find_binutils_dlltool ( sess) ;
221- // temp_prefix doesn't handle paths with spaces so
222- // use a relative path and set the current working directory
223- let cwd = output_path. parent ( ) . unwrap_or ( output_path) ;
224- let temp_prefix = lib_name;
225- // dlltool target architecture args from:
226- // https://github.com/llvm/llvm-project-release-prs/blob/llvmorg-15.0.6/llvm/lib/ToolDrivers/llvm-dlltool/DlltoolDriver.cpp#L69
227- let ( dlltool_target_arch, dlltool_target_bitness) = match & sess. target . arch {
228- Arch :: X86_64 => ( "i386:x86-64" , "--64" ) ,
229- Arch :: X86 => ( "i386" , "--32" ) ,
230- Arch :: AArch64 => ( "arm64" , "--64" ) ,
231- Arch :: Arm => ( "arm" , "--32" ) ,
232- arch => panic ! ( "unsupported arch {arch}" ) ,
233- } ;
234- let mut dlltool_cmd = std:: process:: Command :: new ( & dlltool) ;
235- dlltool_cmd
236- . arg ( "-d" )
237- . arg ( def_file_path)
238- . arg ( "-D" )
239- . arg ( lib_name)
240- . arg ( "-l" )
241- . arg ( & output_path)
242- . arg ( "-m" )
243- . arg ( dlltool_target_arch)
244- . arg ( "-f" )
245- . arg ( dlltool_target_bitness)
246- . arg ( "--no-leading-underscore" )
247- . arg ( "--temp-prefix" )
248- . arg ( temp_prefix)
249- . current_dir ( cwd) ;
250-
251- match dlltool_cmd. output ( ) {
252- Err ( e) => {
253- sess. dcx ( ) . emit_fatal ( ErrorCallingDllTool {
254- dlltool_path : dlltool. to_string_lossy ( ) ,
255- error : e,
256- } ) ;
257- }
258- // dlltool returns '0' on failure, so check for error output instead.
259- Ok ( output) if !output. stderr . is_empty ( ) => {
260- sess. dcx ( ) . emit_fatal ( DlltoolFailImportLibrary {
261- dlltool_path : dlltool. to_string_lossy ( ) ,
262- dlltool_args : dlltool_cmd
263- . get_args ( )
264- . map ( |arg| arg. to_string_lossy ( ) )
265- . collect :: < Vec < _ > > ( )
266- . join ( " " ) ,
267- stdout : String :: from_utf8_lossy ( & output. stdout ) ,
268- stderr : String :: from_utf8_lossy ( & output. stderr ) ,
269- } )
270- }
271- _ => { }
272- }
273- }
274-
275- fn find_binutils_dlltool ( sess : & Session ) -> OsString {
276- assert ! ( sess. target. options. is_like_windows && !sess. target. options. is_like_msvc) ;
277- if let Some ( dlltool_path) = & sess. opts . cg . dlltool {
278- return dlltool_path. clone ( ) . into_os_string ( ) ;
279- }
280-
281- let tool_name: OsString = if sess. host . options . is_like_windows {
282- // If we're compiling on Windows, always use "dlltool.exe".
283- "dlltool.exe"
284- } else {
285- // On other platforms, use the architecture-specific name.
286- match sess. target . arch {
287- Arch :: X86_64 => "x86_64-w64-mingw32-dlltool" ,
288- Arch :: X86 => "i686-w64-mingw32-dlltool" ,
289- Arch :: AArch64 => "aarch64-w64-mingw32-dlltool" ,
290-
291- // For non-standard architectures (e.g., aarch32) fallback to "dlltool".
292- _ => "dlltool" ,
293- }
294- }
295- . into ( ) ;
296-
297- // NOTE: it's not clear how useful it is to explicitly search PATH.
298- for dir in env:: split_paths ( & env:: var_os ( "PATH" ) . unwrap_or_default ( ) ) {
299- let full_path = dir. join ( & tool_name) ;
300- if full_path. is_file ( ) {
301- return full_path. into_os_string ( ) ;
302- }
303- }
304-
305- // The user didn't specify the location of the dlltool binary, and we weren't able
306- // to find the appropriate one on the PATH. Just return the name of the tool
307- // and let the invocation fail with a hopefully useful error message.
308- tool_name
309- }
310-
311173pub enum AddArchiveKind < ' a > {
312174 Rlib ( /*skip*/ & ' a dyn Fn ( & str , ArchiveEntryKind ) -> bool ) ,
313175 Other ,
0 commit comments