I agree with your conclusion that llvm-nm.exe cannot be used. This is 
unfortunate. At least it is known for sure now.

This makes me once again question libtool's "auto-export" feature on MSVC. It 
seems to do more harm than good, but some libraries still may rely on it to 
export their symbols. Also recall that there is no auto-import for variables on 
MSVC.

I am not sure if _NO_CRT_STDIO_INLINE is a user feature macro since it does not 
seem to be documented at all.

- Kirill Makurin
________________________________
From: Bruno Haible <br...@clisp.org>
Sent: Tuesday, June 17, 2025 12:34 AM
To: Kirill Makurin <maiddais...@outlook.com>
Cc: bug-gnulib@gnu.org <bug-gnulib@gnu.org>
Subject: Re: clang-cl: undeclared library function 'sprintf'

Kirill Makurin wrote:
> This issue was coming from "NM=llvm-nm.exe".

Well, I think the issue has two causes:

1) The MSVC <stdio.h> defines some functions, such as sprintf, as
non-static inline functions by default.

You can see that it is independent of the compiler and independent
of whether 'dumpbin' or 'llvm-nm' is used to inspect the object file:

$ cat hello.c
#include <stdio.h>
#include <stdlib.h>
int main () {
  printf("Hello world\n");
  fflush(stdout);
  return 0;
}

$ cl -c -Os hello.c
$ dumpbin -nologo -symbols hello.obj | grep External | grep -v UNDEF
011 00000000 SECT5  notype ()    External     | __local_stdio_printf_options
015 00000000 SECT6  notype ()    External     | _vfprintf_l
016 00000000 SECT7  notype ()    External     | printf
017 00000000 SECT4  notype ()    External     | main
$ llvm-nm hello.obj | grep ' [A-TV-Z] '
00000008 C ?_OptionsStorage@?1??__local_stdio_printf_options@@9@9
00000000 T __local_stdio_printf_options
00000000 T _vfprintf_l
00000000 T main
00000000 T printf

$ clang-cl -c -Os hello.c
$ dumpbin -nologo -symbols hello.obj | grep External | grep -v UNDEF
00A 00000000 SECT5  notype ()    External     | printf
00F 00000000 SECT6  notype ()    External     | __local_stdio_printf_options
012 00000000 SECT7  notype       External     | 
??_C@_0N@NLPDAPMJ@Hello?5world?6?$AA@ (`string')
01C 00000000 SECT1  notype ()    External     | main
$ llvm-nm hello.obj | grep ' [A-TV-Z] '
00000000 R ??_C@_0N@NLPDAPMJ@Hello?5world?6?$AA@
00000000 T __local_stdio_printf_options
00000000 T main
00000000 T printf

And it can be turned off by defining the C macro _NO_CRT_STDIO_INLINE:

$ cl -c -Os hello.c -D_NO_CRT_STDIO_INLINE
$ dumpbin -nologo -symbols hello.obj | grep External | grep -v UNDEF
00E 00000000 SECT4  notype ()    External     | main
$ llvm-nm hello.obj | grep ' [A-TV-Z] '
00000000 T main

$ clang-cl -c -Os hello.c -D_NO_CRT_STDIO_INLINE
$ dumpbin -nologo -symbols hello.obj | grep External | grep -v UNDEF
011 00000000 SECT1  notype ()    External     | main
$ llvm-nm hello.obj | grep ' [A-TV-Z] '
00000000 T main

2)
> After changing to NM="dumpbin.exe -nologo -symbols" (which I use with cl.exe),
> both libtextstyle and libunistring build successfully.

In libtool invocations, the use of $NM is followed by a variable
'global_symbol_pipe'. In libtool.m4 [1] two possible values are defined
for this variable. Since you say that with 'dumpbin' the exports of the
DLL are controlled correctly whereas with 'llvm-nm' they are not, there
are two possibilities:
  * Either the 'global_symbol_pipe' value for this case (libtool.m4 line 4211)
    needs to be corrected.
  * Or, possibly, there is no way to do so, because the output of 'llvm-nm'
    does not contain the necessary information.

Looking at the complete output of 'llvm-nm hello.obj', even with various
command-line options like --no-weak, I conclude that the cause is the latter.

That is, you *can't* use "NM=llvm-nm.exe", and NM="dumpbin -symbols" is the
only choice that works. Like I documented in INSTALL.windows and repeated in
[2].

Bruno

[1] 
https://gitweb.git.savannah.gnu.org/gitweb/?p=libtool.git;a=blob;f=m4/libtool.m4;h=99b53ad046fb46afeabc3824b6f2f6ddec9b6935;hb=HEAD#l4189
[2] https://lists.gnu.org/archive/html/bug-gnulib/2025-06/msg00098.html



Reply via email to