@@ -860,7 +860,7 @@ fn link_natively<'a>(
860
860
if !prog. status . success ( ) {
861
861
let mut output = prog. stderr . clone ( ) ;
862
862
output. extend_from_slice ( & prog. stdout ) ;
863
- let escaped_output = escape_string ( & output) ;
863
+ let escaped_output = escape_linker_output ( & output, flavor ) ;
864
864
// FIXME: Add UI tests for this error.
865
865
let err = errors:: LinkingFailed {
866
866
linker_path : & linker_path,
@@ -1052,6 +1052,83 @@ fn escape_string(s: &[u8]) -> String {
1052
1052
}
1053
1053
}
1054
1054
1055
+ #[ cfg( not( windows) ) ]
1056
+ fn escape_linker_output ( s : & [ u8 ] , _flavour : LinkerFlavor ) -> String {
1057
+ escape_string ( s)
1058
+ }
1059
+
1060
+ /// If the output of the msvc linker is not UTF-8 and the host is Windows,
1061
+ /// then try to convert the string from the OEM encoding.
1062
+ #[ cfg( windows) ]
1063
+ fn escape_linker_output ( s : & [ u8 ] , flavour : LinkerFlavor ) -> String {
1064
+ // This only applies to the actual MSVC linker.
1065
+ if flavour != LinkerFlavor :: Msvc ( Lld :: No ) {
1066
+ return escape_string ( s) ;
1067
+ }
1068
+ match str:: from_utf8 ( s) {
1069
+ Ok ( s) => return s. to_owned ( ) ,
1070
+ Err ( _) => match win:: locale_byte_str_to_string ( s, win:: oem_code_page ( ) ) {
1071
+ Some ( s) => s,
1072
+ // The string is not UTF-8 and isn't valid for the OEM code page
1073
+ None => format ! ( "Non-UTF-8 output: {}" , s. escape_ascii( ) ) ,
1074
+ } ,
1075
+ }
1076
+ }
1077
+
1078
+ /// Wrappers around the Windows API.
1079
+ #[ cfg( windows) ]
1080
+ mod win {
1081
+ use windows:: Win32 :: Globalization :: {
1082
+ GetLocaleInfoEx , MultiByteToWideChar , CP_OEMCP , LOCALE_IUSEUTF8LEGACYOEMCP ,
1083
+ LOCALE_NAME_SYSTEM_DEFAULT , LOCALE_RETURN_NUMBER , MB_ERR_INVALID_CHARS ,
1084
+ } ;
1085
+
1086
+ /// Get the Windows system OEM code page. This is most notably the code page
1087
+ /// used for link.exe's output.
1088
+ pub fn oem_code_page ( ) -> u32 {
1089
+ unsafe {
1090
+ let mut cp: u32 = 0 ;
1091
+ // We're using the `LOCALE_RETURN_NUMBER` flag to return a u32.
1092
+ // But the API requires us to pass the data as though it's a [u16] string.
1093
+ let len = std:: mem:: size_of :: < u32 > ( ) / std:: mem:: size_of :: < u16 > ( ) ;
1094
+ let data = std:: slice:: from_raw_parts_mut ( & mut cp as * mut u32 as * mut u16 , len) ;
1095
+ let len_written = GetLocaleInfoEx (
1096
+ LOCALE_NAME_SYSTEM_DEFAULT ,
1097
+ LOCALE_IUSEUTF8LEGACYOEMCP | LOCALE_RETURN_NUMBER ,
1098
+ Some ( data) ,
1099
+ ) ;
1100
+ if len_written as usize == len { cp } else { CP_OEMCP }
1101
+ }
1102
+ }
1103
+ /// Try to convert a multi-byte string to a UTF-8 string using the given code page
1104
+ /// The string does not need to be null terminated.
1105
+ ///
1106
+ /// This is implemented as a wrapper around `MultiByteToWideChar`.
1107
+ /// See <https://learn.microsoft.com/en-us/windows/win32/api/stringapiset/nf-stringapiset-multibytetowidechar>
1108
+ ///
1109
+ /// It will fail if the multi-byte string is longer than `i32::MAX` or if it contains
1110
+ /// any invalid bytes for the expected encoding.
1111
+ pub fn locale_byte_str_to_string ( s : & [ u8 ] , code_page : u32 ) -> Option < String > {
1112
+ // `MultiByteToWideChar` requires a length to be a "positive integer".
1113
+ if s. len ( ) > isize:: MAX as usize {
1114
+ return None ;
1115
+ }
1116
+ // Error if the string is not valid for the expected code page.
1117
+ let flags = MB_ERR_INVALID_CHARS ;
1118
+ // Call MultiByteToWideChar twice.
1119
+ // First to calculate the length then to convert the string.
1120
+ let mut len = unsafe { MultiByteToWideChar ( code_page, flags, s, None ) } ;
1121
+ if len > 0 {
1122
+ let mut utf16 = vec ! [ 0 ; len as usize ] ;
1123
+ len = unsafe { MultiByteToWideChar ( code_page, flags, s, Some ( & mut utf16) ) } ;
1124
+ if len > 0 {
1125
+ return utf16. get ( ..len as usize ) . map ( String :: from_utf16_lossy) ;
1126
+ }
1127
+ }
1128
+ None
1129
+ }
1130
+ }
1131
+
1055
1132
fn add_sanitizer_libraries ( sess : & Session , crate_type : CrateType , linker : & mut dyn Linker ) {
1056
1133
// On macOS the runtimes are distributed as dylibs which should be linked to
1057
1134
// both executables and dynamic shared objects. Everywhere else the runtimes
0 commit comments