unresolved external symbol __imp__fprintf and __imp____iob_func, SDL2

C++Sdl 2Visual Studio-2015Unresolved External

C++ Problem Overview


Could someone explain what the

> __imp__fprintf

and

> __imp____iob_func

unresolved external means?

Because I get these errors when I'm trying to compile:

1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _ShowError
1>SDL2main.lib(SDL_windows_main.obj) : error LNK2019: unresolved external symbol __imp____iob_func referenced in function _ShowError
1>E:\Documents\Visual Studio 2015\Projects\SDL2_Test\Debug\SDL2_Test.exe : fatal error LNK1120: 2 unresolved externals

I can already say that the problem is not from linking wrong. I have linked everything up correctly, but for some reason it won't compile.

I'm trying to use SDL2.

I'm using Visual Studio 2015 as compiler.

I have linked to SDL2.lib and SDL2main.lib in Linker -> Input -> Additional Dependencies and I have made sure that the VC++ Directories are correct.

C++ Solutions


Solution 1 - C++

I have finally figured out why this is happening !

In visual studio 2015, stdin, stderr, stdout are defined as follow :

#define stdin  (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))

But previously, they were defined as:

#define stdin  (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])

So now __iob_func is not defined anymore which leads to a link error when using a .lib file compiled with previous versions of visual studio.

To solve the issue, you can try defining __iob_func() yourself which should return an array containing {*stdin,*stdout,*stderr}.

Regarding the other link errors about stdio functions (in my case it was sprintf()), you can add legacy_stdio_definitions.lib to your linker options.

Solution 2 - C++

To Milan Babuškov, IMO, this is exactly what the replacement function should look like :-)

FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}

Solution 3 - C++

Microsoft has a special note on this (https://msdn.microsoft.com/en-us/library/bb531344.aspx#BK_CRT):

> The printf and scanf family of functions are now defined inline. > > The definitions of all of the printf and scanf functions have been > moved inline into stdio.h, conio.h, and other CRT headers. > This is a breaking change that leads to a linker error (LNK2019, > unresolved external symbol) for any programs that declared these > functions locally without including the appropriate CRT headers. If > possible, you should update the code to include the CRT headers (that > is, add #include ) and the inline functions, but if you do > not want to modify your code to include these header files, an > alternative solution is to add an additional library to your linker > input, legacy_stdio_definitions.lib. > > To add this library to your linker input in the IDE, open the context > menu for the project node, choose Properties, then in the Project > Properties dialog box, choose Linker, and edit the Linker Input to add > legacy_stdio_definitions.lib to the semi-colon-separated list. > > If your project links with static libraries that were compiled with a > release of Visual C++ earlier than 2015, the linker might report an > unresolved external symbol. These errors might reference internal > stdio definitions for _iob, _iob_func, or related imports for > certain stdio functions in the form of _imp*. Microsoft > recommends that you recompile all static libraries with the latest > version of the Visual C++ compiler and libraries when you upgrade a > project. If the library is a third-party library for which source is > not available, you should either request an updated binary from the > third party or encapsulate your usage of that library into a separate > DLL that you compile with the older version of the Visual C++ compiler > and libraries.

Solution 4 - C++

As answered above, the right answer is to compile everything with VS2015, but for interest the following is my analysis of the problem.

This symbol does not appear to be defined in any static library provided by Microsoft as part of VS2015, which is rather peculiar since all others are. To discover why, we need to look at the declaration of that function and, more importantly, how it's used.

Here's a snippet from the Visual Studio 2008 headers:

_CRTIMP FILE * __cdecl __iob_func(void);
#define stdin (&__iob_func()[0])
#define stdout (&__iob_func()[1])
#define stderr (&__iob_func()[2])

So we can see that the job of the function is to return the start of an array of FILE objects (not handles, the "FILE *" is the handle, FILE is the underlying opaque data structure storing the important state goodies). The users of this function are the three macros stdin, stdout and stderr which are used for various fscanf, fprintf style calls.

Now let's take a look at how Visual Studio 2015 defines the same things:

_ACRTIMP_ALT FILE* __cdecl __acrt_iob_func(unsigned);
#define stdin (__acrt_iob_func(0))
#define stdout (__acrt_iob_func(1))
#define stderr (__acrt_iob_func(2))

So the approach has changed for the replacement function to now return the file handle rather than the address of the array of file objects, and the macros have changed to simply call the function passing in an identifying number.

So why can't they/we provide a compatible API? There are two key rules which Microsoft can't contravene in terms of their original implementation via __iob_func:

  1. There must be an array of three FILE structures which can be indexed in the same manner as before.
  2. The structural layout of FILE cannot change.

Any change in either of the above would mean existing compiled code linked against that would go badly wrong if that API is called.

Let's take a look at how FILE was/is defined.

First the VS2008 FILE definition:

struct _iobuf {
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname;
        };
typedef struct _iobuf FILE;

And now the VS2015 FILE definition:

typedef struct _iobuf
{
    void* _Placeholder;
} FILE;

So there is the crux of it: the structure has changed shape. Existing compiled code referring to __iob_func relies upon the fact that the data returned is both an array that can be indexed and that in that array the elements are the same distance apart.

The possible solutions mentioned in the answers above along these lines would not work (if called) for a few reasons:

FILE _iob[] = {*stdin, *stdout, *stderr};

extern "C" FILE * __cdecl __iob_func(void)
{
    return _iob;
}

The FILE array _iob would be compiled with VS2015 and so it would be laid out as a block of structures containing a void*. Assuming 32-bit alignment, these elements would be 4 bytes apart. So _iob[0] is at offset 0, _iob[1] is at offset 4 and _iob[2] is at offset 8. The calling code will instead expect FILE to be much longer, aligned at 32 bytes on my system, and so it will take the address of the returned array and add 0 bytes to get to element zero (that one is okay), but for _iob[1] it will deduce that it needs to add 32 bytes and for _iob[2] it will deduce that it needs to add 64-bytes (because that's how it looked in the VS2008 headers). And indeed the disassembled code for VS2008 demonstrates this.

A secondary issue with the above solution is that it copies the content of the FILE structure (*stdin), not the FILE * handle. So any VS2008 code would be looking at a different underlying structure to VS2015. This might work if the structure only contained pointers, but that's a big risk. In any case the first issue renders this irrelevant.

The only hack I've been able to dream up is one in which __iob_func walks the call stack to work out which actual file handle they are looking for (based on the offset added to the returned address) and returns a computed value such that it gives the right answer. This is every bit as insane as it sounds, but the prototype for x86 only (not x64) is listed below for your amusement. It worked okay in my experiments, but your mileage may vary - not recommended for production use!

#include <windows.h>
#include <stdio.h>
#include <dbghelp.h>

/* #define LOG */

#if defined(_M_IX86)

#define GET_CURRENT_CONTEXT(c, contextFlags) \
  do { \
    c.ContextFlags = contextFlags; \
    __asm    call x \
    __asm x: pop eax \
    __asm    mov c.Eip, eax \
    __asm    mov c.Ebp, ebp \
    __asm    mov c.Esp, esp \
  } while(0);

#else

/* This should work for 64-bit apps, but doesn't */
#define GET_CURRENT_CONTEXT(c, contextFlags) \
  do { \
    c.ContextFlags = contextFlags; \
    RtlCaptureContext(&c); \
} while(0);

#endif

FILE * __cdecl __iob_func(void)
{
	CONTEXT c = { 0 };
	STACKFRAME64 s = { 0 };
	DWORD imageType;
	HANDLE hThread = GetCurrentThread();
	HANDLE hProcess = GetCurrentProcess();

	GET_CURRENT_CONTEXT(c, CONTEXT_FULL);

#ifdef _M_IX86
	imageType = IMAGE_FILE_MACHINE_I386;
	s.AddrPC.Offset = c.Eip;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.Ebp;
	s.AddrFrame.Mode = AddrModeFlat;
	s.AddrStack.Offset = c.Esp;
	s.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
	imageType = IMAGE_FILE_MACHINE_AMD64;
	s.AddrPC.Offset = c.Rip;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.Rsp;
	s.AddrFrame.Mode = AddrModeFlat;
	s.AddrStack.Offset = c.Rsp;
	s.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
	imageType = IMAGE_FILE_MACHINE_IA64;
	s.AddrPC.Offset = c.StIIP;
	s.AddrPC.Mode = AddrModeFlat;
	s.AddrFrame.Offset = c.IntSp;
	s.AddrFrame.Mode = AddrModeFlat;
	s.AddrBStore.Offset = c.RsBSP;
	s.AddrBStore.Mode = AddrModeFlat;
	s.AddrStack.Offset = c.IntSp;
	s.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif

	if (!StackWalk64(imageType, hProcess, hThread, &s, &c, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
	{
#ifdef LOG
		printf("Error: 0x%08X (Address: %p)\n", GetLastError(), (LPVOID)s.AddrPC.Offset);
#endif
		return NULL;
	}

	if (s.AddrReturn.Offset == 0)
	{
		return NULL;
	}

	{
		unsigned char const * assembly = (unsigned char const *)(s.AddrReturn.Offset);
#ifdef LOG
		printf("Code bytes proceeding call to __iob_func: %p: %02X,%02X,%02X\n", assembly, *assembly, *(assembly + 1), *(assembly + 2));
#endif
		if (*assembly == 0x83 && *(assembly + 1) == 0xC0 && (*(assembly + 2) == 0x20 || *(assembly + 2) == 0x40))
		{
			if (*(assembly + 2) == 32)
			{
				return (FILE*)((unsigned char *)stdout - 32);
			}
			if (*(assembly + 2) == 64)
			{
				return (FILE*)((unsigned char *)stderr - 64);
			}

		}
		else
		{
			return stdin;
		}
	}
	return NULL;
}

Solution 5 - C++

I had the same issue in VS2015. I have solved it by compiling the SDL2 sources in VS2015.

  1. Go to http://libsdl.org/download-2.0.php and download SDL 2 source code.
  2. Open SDL_VS2013.sln in VS2015. You will be asked to convert the projects. Do it.
  3. Compile SDL2 project.
  4. Compile SDL2main project.
  5. Use the new generated output files SDL2main.lib, SDL2.lib and SDL2.dll in your SDL 2 project in VS2015.

Solution 6 - C++

I don't know why but:

#ifdef main
#undef main
#endif

After the includes but before your main should fix it from my experience.

Solution 7 - C++

A more recent solution to this problem: Use the more recent sdl libs on

"https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/?C=M;O=D"

They seem to have fixed the problem, although it's only the 32 bit library (I think).

Solution 8 - C++

To link means not to work properly. Digging into stdio.h of VS2012 and VS2015 the following worked for me. Alas, you have to decide if it should work for one of { stdin, stdout, stderr }, never more than one.

extern "C" FILE* __cdecl __iob_func()
{
    struct _iobuf_VS2012 { // ...\Microsoft Visual Studio 11.0\VC\include\stdio.h #56
        char *_ptr;
        int   _cnt;
        char *_base;
        int   _flag;
        int   _file;
        int   _charbuf;
        int   _bufsiz;
        char *_tmpfname; };
    // VS2015 has only FILE = struct {void*}
    
    int const count = sizeof(_iobuf_VS2012) / sizeof(FILE);

    //// stdout
    //return (FILE*)(&(__acrt_iob_func(1)->_Placeholder) - count);

    // stderr
    return (FILE*)(&(__acrt_iob_func(2)->_Placeholder) - 2 * count);
}

Solution 9 - C++

My advice is to not (try to) implement __iob_func.

While fixing these errors:

curllib.v110.lib(mprintf.obj) : error LNK2001: unresolved external symbol ___iob_func```

I tried the other answers' solutions, but in the end, returning a `FILE*` C-array doesn't match up with an array of Windows' internal IOB structs. @Volker is right that it'll never work for more than one of `stdin`, `stdout` or `stderr`.

**If a library actually USES** one of those streams, **it will crash**. As long as your program doesn't cause the lib to use them, **you'll never know**. For example, `png_default_error` writes to `stderr` when the CRC doesn't match in the PNG's metadata. (Normally not a crash-worthy issue)

Conclusion: It's not possible to mix VS2012 (Platform Toolset v110/v110_xp) and VS2015+ libraries, if they use stdin, stdout and/or stderr.

Solution: Recompile your libraries that have `__iob_func` unresolved symbols with your current version of VS and a matching Platform Toolset.

Solution 10 - C++

Use the pre-compiled SDL2main.lib and SDL.lib for your VS2015 project's library : https://buildbot.libsdl.org/sdl-builds/sdl-visualstudio/sdl-visualstudio-2225.zip

Solution 11 - C++

I resolve this problem with following function. I use Visual Studio 2019.

FILE* __cdecl __iob_func(void)
{
    FILE _iob[] = { *stdin, *stdout, *stderr };
    return _iob;
}

because stdin Macro defined function call, "*stdin" expression is cannot used global array initializer. But local array initialier is possible. sorry, I am poor at english.

Solution 12 - C++

For anyone who is still looking for an answer where the above tricks didn't work. Static linking is the way to solve this problem. Change your Runtime library settings as below

Project properties --> C/C++ --> Code generation --> Runtime Library --> Multi-threaded Debug (/MTd) instead of /MDd

Here is a discussion on this solution: https://social.msdn.microsoft.com/Forums/vstudio/en-US/4a1c9610-fa41-45f6-ad39-c9f6795be6f2/msvcrt-iob-disappeared?forum=vclanguage

Solution 13 - C++

I managed to fix the problem.

The source of the error was this line of code, that can be found in the SDLmain source code.

fprintf(stderr, "%s: %s\n", title, message);

So what I did was to edit the source code in SDLmain of that line too:

fprintf("%s: %s\n", title, message);

And then I built the SDLmain and copied and replaced the old SDLmain.lib in my SDL2 library directory with the newly built and edited.

Then when I ran my program with SDL2 no error messages came up and to code ran smoothly.

I don't know if this will bite me later, but so for everything is going great.

Solution 14 - C++

This can happen when you link to msvcrt.dll instead of msvcr10.dll (or similar), which is a good plan. Because it will free you up to redistribute your Visual Studio's runtime library inside your final software package.

That workaround helps me (at Visual Studio 2008):

#if _MSC_VER >= 1400
#undef stdin
#undef stdout
#undef stderr
extern "C" _CRTIMP extern FILE _iob[];
#define stdin	_iob
#define stdout	(_iob+1)
#define stderr	(_iob+2)
#endif

This snippet is not needed for Visual Studio 6 and its compiler. Therefore the #ifdef.

Solution 15 - C++

To bring more confusion in this already rich thread, I happened to bump in the same unresolved external on fprintf

main.obj : error LNK2019: unresolved external symbol __imp__fprintf referenced in function _GenerateInfoFile

Even if in my case it was in a rather different context : under Visual Studio 2005 (Visual Studio 8.0) and the error was happening in my own code (the very same I was compiling), not a third party.

It happened that this error was triggered by /MD option in my compiler flags. Switching to /MT removed the issue. This is weird because usually, linking statically (MT) raises more problem than dynamically (MD)... but just in case it serves other, I put it there.

Solution 16 - C++

In my case, this error comes from my trial to remove dependencies to MSVC-version dependent runtime library DLL (msvcr10.dll or so) and/or remove static runtime library too, to remove excess fat from my executables.

So I use /NODEFAULTLIB linker switch, my self-made "msvcrt-light.lib" (google for it when you need), and mainCRTStartup() / WinMainCRTStartup() entries.

It is IMHO since Visual Studio 2015, so I stuck to older compilers.

However, defining symbol _NO_CRT_STDIO_INLINE removes all hassle, and a simple "Hello World" application is again 3 KB small and doesn't depend to unusual DLLs. Tested in Visual Studio 2017.

Solution 17 - C++

Maybe this might help you. I added Shell32.lib to my Linker --> Input --> Additional Dependencies and it stopped this error. I found out about it from this post: https://discourse.libsdl.org/t/windows-build-fails-with-missing-symbol-imp-commandlinetoargvw/27256/3

Solution 18 - C++

Paste this code in any of your source files and re-build. Worked for me !

 #include <stdio.h>

FILE _iob[3];

FILE* __cdecl __iob_func(void)
{

   _iob[0] = *stdin;

   _iob[0] = *stdout;

   _iob[0] = *stderr;

   return _iob;

}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRockFrenzyView Question on Stackoverflow
Solution 1 - C++godView Answer on Stackoverflow
Solution 2 - C++smartslView Answer on Stackoverflow
Solution 3 - C++kingsbView Answer on Stackoverflow
Solution 4 - C++MarkHView Answer on Stackoverflow
Solution 5 - C++user5157912View Answer on Stackoverflow
Solution 6 - C++The XGoodView Answer on Stackoverflow
Solution 7 - C++Vhaerun Of DecrepitudeView Answer on Stackoverflow
Solution 8 - C++VolkerView Answer on Stackoverflow
Solution 9 - C++Luc BloomView Answer on Stackoverflow
Solution 10 - C++SolutionIsComingView Answer on Stackoverflow
Solution 11 - C++soen.krView Answer on Stackoverflow
Solution 12 - C++SisirView Answer on Stackoverflow
Solution 13 - C++RockFrenzyView Answer on Stackoverflow
Solution 14 - C++user2699548View Answer on Stackoverflow
Solution 15 - C++ZzirconiumView Answer on Stackoverflow
Solution 16 - C++Henrik HaftmannView Answer on Stackoverflow
Solution 17 - C++AeryesView Answer on Stackoverflow
Solution 18 - C++Deep PatelView Answer on Stackoverflow