0

I'm trying to compile Python on Windows in an unusual context, and running into this problem:

a.o : error LNK2019: unresolved external symbol strnicmp referenced in function connection_clear

Okay, so I'm not linking in the proper library containing strnicmp, which one is that? Couldn't find it, so I wrote a script to search through every .lib file on my entire hard disk, run dumpbin /exports on all of them, and collect the results.

And the result was that as far as this process could tell, there is no library containing strnicmp.

A bit more searching led me to https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/strnicmp-wcsnicmp?view=msvc-170

The Microsoft-specific function names strnicmp and wcsnicmp are deprecated aliases for the _strnicmp and _wcsnicmp functions.

On the face of it, that would seem to be the beginning of an explanation. Neither the linker nor I nor my search script can find strnicmp because at the object code level, it doesn't exist; it's probably just a #define in some header file, though that raises the question of why this was not working on the source file containing connection_clear.

Just to make sure, I wrote a test program.

(c1) R:\>type call-strnicmp.c
#include <stdio.h>
#include <string.h>

int main(int argc, char** argv) {
        if (argc == 2 && !strnicmp(argv[1], "foo", 3)) puts("you said foo");
        else
                puts("you did not say foo");
        return 0;
}

(c1) R:\>cl call-strnicmp.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.31.31104 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

call-strnicmp.c
Microsoft (R) Incremental Linker Version 14.31.31104.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:call-strnicmp.exe
call-strnicmp.obj

(c1) R:\>call-strnicmp.exe foo
you said foo

Okay, so strnicmp is being called correctly, presumably via the aforementioned header. Just to make doubly sure:

(c1) R:\>dumpbin /symbols call-strnicmp.obj
Microsoft (R) COFF/PE Dumper Version 14.31.31104.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file call-strnicmp.obj

File Type: COFF OBJECT

COFF SYMBOL TABLE
000 01047980 ABS    notype       Static       | @comp.id
001 80010190 ABS    notype       Static       | @feat.00
002 00000002 ABS    notype       Static       | @vol.md
003 00000000 SECT1  notype       Static       | .drectve
    Section length   2F, #relocs    0, #linenums    0, checksum        0
005 00000000 SECT2  notype       Static       | .debug$S
    Section length   68, #relocs    0, #linenums    0, checksum        0
007 00000000 SECT3  notype       Static       | .text$mn
    Section length   5D, #relocs    6, #linenums    0, checksum D202F0CD
009 00000000 UNDEF  notype ()    External     | puts
00A 00000000 UNDEF  notype ()    External     | strnicmp
00B 00000000 SECT3  notype ()    External     | main
00C 00000000 SECT3  notype       Label        | $LN5
00D 00000000 SECT4  notype       Static       | .xdata
    Section length    8, #relocs    0, #linenums    0, checksum E7553388
00F 00000000 SECT4  notype       Static       | $unwind$main
010 00000000 SECT5  notype       Static       | .pdata
    Section length    C, #relocs    3, #linenums    0, checksum CE23E617
012 00000000 SECT5  notype       Static       | $pdata$main
013 00000000 SECT6  notype       Static       | .data
    Section length   2C, #relocs    0, #linenums    0, checksum C7B9A925
015 00000000 SECT6  notype       Static       | $SG10717
016 00000008 SECT6  notype       Static       | $SG10718
017 00000018 SECT6  notype       Static       | $SG10719
018 00000000 SECT7  notype       Static       | .chks64
    Section length   38, #relocs    0, #linenums    0, checksum        0

String Table Size = 0x1D bytes

  Summary

          38 .chks64
          2C .data
          68 .debug$S
          2F .drectve
           C .pdata
          5D .text$mn
           8 .xdata

... er? strnicmp is being imported, but not with the prepended underscore suggested by the Microsoft documentation. The reference is to the unadorned name.

So what's going on? Presumably I am misunderstanding something in the chain here. Is there a way for a function to be exported such that it does not show up in the dumpbin /exports output of any library?

rwallace
  • 31,405
  • 40
  • 123
  • 242

2 Answers2

1

The _strnicmp function is a Microsoft Visual C extension that is found in the Microsoft C runtime. ucrt.lib or libucrt.lib. The older strnicmp is a deprecated alias for it. (Update: Based on what you told me, it’s actually defined as a weak symbol in oldnames.lib. I originally thought, based on a MSDN article, that it was vcruntime.lib, but I was mistaken.)

You will normally want to link a program that needs it by compiling with the compiler flags listed here, or linking to the correct version of the libraries. For dynamic linkage, this is /MD. It is also possible to specify which libraries to include (e.g. ucrt.lib or libucrt.lib) with linker options.

In a test with DUMPBIN /IMPORTS, however, a simple test program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#undef strnicmp

int main(void)
{
  puts(0 == strnicmp( "Hello", "hello", 5 ) ? "Success\n"
                                            : "Failure\n"
      );
  return EXIT_SUCCESS;
}

when compiled with /MD, resolved the external symbol strnicmp as an alias to _strnicmp in api-ms-win-crt-string-l1-1-0.dll and called that. Therefore, make sure you are compiling your source with the correct header files, from the MSVC command prompt. You may also set CL.EXE compiler flags with the CL environment variable.

Davislor
  • 14,674
  • 2
  • 34
  • 49
  • When I specify linking statically to `libvcruntime.lib` the error message is still there. Are you taking into account that the function in question is not `_stricmp` but `strnicmp`? – rwallace Jun 17 '22 at 09:35
  • @rwallace They should both be there. Edited to clarify that, and that you **might need** the `/MD` compiler flag to use `vcruntime.lib`. – Davislor Jun 17 '22 at 09:51
  • You might also get better results installing Clang with the `x86_64-pc-windows-msvc` target and running it from the MSVC X64 native tools command prompt. – Davislor Jun 17 '22 at 09:54
1

It's in oldnames.lib. It didn't show up in the listing because it turns out that for a static library, you need to use dumpbin /symbols instead of /exports, otherwise it silently gives no output.

rwallace
  • 31,405
  • 40
  • 123
  • 242
  • 1
    I’m still not sure exactly what the source you were compiling did to break this, but I was able to get an `.OBJ` file with a reference to `strnicmp` by declaring my own prototype for it, rather than including any system header. It still linked just fine, but maybe the build script set some custom linker flag that broke it. – Davislor Jun 18 '22 at 01:43
  • @Davislor Right, I intentionally disabled the default linker flags for context-specific reasons. – rwallace Jun 18 '22 at 23:11