ld cannot find shared libraries on non-ASCII paths in mingw64

Jan Beulich jbeulich@suse.com
Tue Nov 18 11:39:47 GMT 2025


On 18.11.2025 09:38, Jan Beulich wrote:
> On 15.11.2025 16:23, tuug@gmx.us wrote:
>> Merely attempting to link a shared library with g++ results in this error message:
>> $ g++ -lkernel32
>> C:/Users/שזדס/scoop/apps/msys2/2025-08-30/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.2.0/../../../../x86_64-w64-mingw32/bin/ld.exe: cannot find C:/Users/שזדס/scoop/apps/msys2/2025-08-30/ucrt64/bin/../lib/gcc/x86_64-w64-mingw32/15.2.0/../../../../lib/crt2.o: No such file or directory◀
> 
> The path appearing correct (from all I can tell) in the error message makes me
> wonder if this really is an issue with ld. And indeed potentially problematic
> code lives in libbfd. _bfd_real_fopen() tries to convert the input string to
> wchar_t[], using MultiByteToWideChar() and passing as the code page whatever
> MinGW's ___lc_codepage_func() returns (but using hard-coded CP_UTF8 for e.g.
> Cygwin). That's an aspect that needs checking on a MinGW installation (I use
> Cygwin, so won't easily be able to.)
> 
> Following the MultiByteToWideChar() invocation there is indeed questionable
> code, though: The loop assumes that it can index the wchar_t[] using the same
> indexes that apply to filename[]. That's obviously not going to work when any
> multi-byte UTF8 characters are present (as is the case for you). I should be
> able to get you a patch for that; would you be able to actually give it a try
> (on the hope that this is what is causing the issue for you)?

Below is the patch. I compile-tested in on Cygwin, but as said I have no MinGW
available to actually test the effect of the change.

Jan

bfd: correct dir separator conversion for Win32

Iterating a wchar_t array holding the conversion of multi-byte (likely
UTF-8) input using array indexes from the corresponding char array isn't
going to work as soon as any characters wider than a single char are
present. Simply walk the wchar_t array all by itself.

While looking at that code I also noticed a wrong argument being passed to
a later MultiByteToWideChar() invocation: This needs to be number of
characters, which isn't sizeof() when the array is of wchar_t elements.
---
Note: Error handling is completely lacking here; this will need taking
      care of separately.

--- a/bfd/bfdio.c
+++ b/bfd/bfdio.c
@@ -122,7 +122,6 @@ _bfd_real_fopen (const char *filename, c
   const wchar_t   prefixDOS[] = L"\\\\?\\";
   const wchar_t   prefixUNC[] = L"\\\\?\\UNC\\";
   const wchar_t   prefixNone[] = L"";
-  const size_t    partPathLen = strlen (filename) + 1;
   const wchar_t * prefix;
   size_t          sizeof_prefix;
   bool            strip_network_prefix = false;
@@ -207,10 +206,12 @@ _bfd_real_fopen (const char *filename, c
 
   MultiByteToWideChar (cp, 0, filename, -1, partPath, partPathWSize);
 
-  /* Convert any UNIX style path separators into the DOS i.e. backslash separator.  */
-  for (ix = 0; ix < partPathLen; ix++)
-    if (IS_UNIX_DIR_SEPARATOR(filename[ix]))
-      partPath[ix] = '\\';
+  /* Convert any UNIX style path separators into the DOS i.e. backslash
+     separator.  Short of a TOASCII()- or ISASCII()-like helper (taking
+     wchar_t as input) in libiberty, open-code that here for now.  */
+  for (ix = 0; partPath[ix] != L'\0'; ix++)
+    if (partPath[ix] <= L'\x7f' && IS_UNIX_DIR_SEPARATOR ((char)partPath[ix]))
+      partPath[ix] = L'\\';
 
   /* Getting the full path from the provided partial path.
      1) Get the length.
@@ -245,7 +246,7 @@ _bfd_real_fopen (const char *filename, c
   /* It is non-standard for modes to exceed 16 characters.  */
   wchar_t  modesW[16];
 
-  MultiByteToWideChar (cp, 0, modes, -1, modesW, sizeof(modesW));
+  MultiByteToWideChar (cp, 0, modes, -1, modesW, ARRAY_SIZE (modesW));
 
   FILE *  file = _wfopen (fullPath, modesW);
   free (fullPath);



More information about the Binutils mailing list