c++ - अनसुलझे बाहरी प्रतीक__imp__fprintf और__imp____iob_func, SDL2




sdl-2 visual-studio-2015 (11)

क्या कोई समझा सकता है

__imp__fprintf

तथा

__imp____iob_func

अनसुलझे बाहरी साधन?

क्योंकि मुझे ये गलतियाँ तब लगती हैं जब मैं संकलन करने की कोशिश कर रहा हूँ:

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

मैं पहले ही कह सकता हूं कि समस्या गलत लिंकिंग से नहीं है। मैंने सब कुछ सही ढंग से जोड़ा है, लेकिन किसी कारण से यह संकलन नहीं होगा।

मैं SDL2 का उपयोग करने की कोशिश कर रहा हूं।

मैं संकलक के रूप में विज़ुअल स्टूडियो 2015 का उपयोग कर रहा हूं।

मैं लिंकर में SDL2.lib और SDL2main.lib से जुड़ा हूं -> इनपुट -> अतिरिक्त निर्भरताएं और मैंने यह सुनिश्चित किया है कि VC ++ निर्देशिकाएँ सही हैं।


VS2015 में मेरा भी यही मुद्दा था। मैंने इसे VS2015 में SDL2 स्रोतों को संकलित करके हल किया है।

  1. http://libsdl.org/download-2.0.php जाएं और एसडीएल 2 सोर्स कोड डाउनलोड करें।
  2. VS2015 में SDL_VS2013.sl खोलें। आपको परियोजनाओं को परिवर्तित करने के लिए कहा जाएगा। कर दो।
  3. SDL2 प्रोजेक्ट संकलित करें।
  4. संकलन SDL2main परियोजना।
  5. VS2015 में अपने SDL 2 प्रोजेक्ट में नई उत्पन्न आउटपुट फाइल SDL2main.lib, SDL2.lib और SDL2.dll का उपयोग करें।


इस पहले से ही समृद्ध धागे में और अधिक भ्रम लाने के लिए, मैं fprintf पर उसी अनसुलझे बाहरी में टकरा गया

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

यहां तक ​​कि अगर मेरे मामले में यह एक अलग संदर्भ में था: विज़ुअल स्टूडियो 2005 (विज़ुअल स्टूडियो 8.0) के तहत और त्रुटि मेरे स्वयं के कोड में हो रही थी (बहुत ही मैं इसे संकलित कर रहा था), न कि एक तीसरी पार्टी।

ऐसा हुआ कि यह त्रुटि मेरे संकलक झंडे में / एमडी विकल्प द्वारा ट्रिगर की गई थी। / MT पर स्विच करने से समस्या दूर हो गई। यह अजीब है क्योंकि आमतौर पर, स्टेटिकली (एमटी) को जोड़ने से डायनेमिकली (एमडी) की तुलना में अधिक समस्या पैदा होती है ... लेकिन सिर्फ अगर यह अन्य कार्य करता है, तो मैंने इसे वहां रखा।


इस समस्या का एक और हालिया समाधान: अधिक हाल के sdl libs का उपयोग करें

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

उन्हें लगता है कि यह समस्या ठीक हो गई है, हालांकि यह केवल 32 बिट लाइब्रेरी है (मुझे लगता है)।


जैसा कि ऊपर कहा गया है, सही उत्तर सब कुछ VS2015 के साथ संकलित करना है, लेकिन ब्याज के लिए निम्नलिखित समस्या का मेरा विश्लेषण है।

यह प्रतीक Microsoft द्वारा VS2015 के भाग के रूप में प्रदान की गई किसी भी स्थिर लाइब्रेरी में परिभाषित नहीं किया गया है, जो कि अन्य लोगों के मुकाबले अजीब है। क्यों की खोज करने के लिए, हमें उस फ़ंक्शन की घोषणा को देखने की आवश्यकता है और, इससे भी महत्वपूर्ण बात यह है कि इसका उपयोग कैसे किया जाता है।

यहाँ दृश्य स्टूडियो 2008 हेडर से एक स्निपेट दिया गया है:

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

तो हम देख सकते हैं कि फ़ंक्शन का काम FILE ऑब्जेक्ट्स की एक सरणी की शुरुआत को वापस करना है (हैंडल नहीं, "FILE *" हैंडल है, FILE महत्वपूर्ण स्टेट गुडियों को संग्रहीत करने वाला अंतर्निहित अपारदर्शी डेटा संरचना है)। इस फ़ंक्शन के उपयोगकर्ता तीन मैक्रो स्टडिन, स्टडआउट और स्टेडर हैं जो कि विभिन्न fscanf, fprintf स्टाइल कॉल के लिए उपयोग किए जाते हैं।

अब आइए नजर डालते हैं कि विजुअल स्टूडियो 2015 कैसे चीजों को परिभाषित करता है:

_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))

इसलिए प्रतिस्थापन फ़ंक्शन के लिए दृष्टिकोण बदल गया है अब फ़ाइल ऑब्जेक्ट्स के सरणी के पते के बजाय फ़ाइल हैंडल को वापस लौटाएं, और मैक्रोज़ केवल एक पहचान संख्या में गुजरने वाले फ़ंक्शन को कॉल करने के लिए बदल गए हैं।

तो वे / हम संगत API क्यों नहीं दे सकते? दो प्रमुख नियम हैं जो Microsoft __iob_func के माध्यम से अपने मूल कार्यान्वयन के संदर्भ में उल्लंघन नहीं कर सकते हैं:

  1. तीन FILE संरचनाओं की एक सरणी होनी चाहिए जिसे पहले की तरह ही अनुक्रमित किया जा सकता है।
  2. FILE का संरचनात्मक लेआउट परिवर्तित नहीं हो सकता।

उपरोक्त में से किसी में भी बदलाव का मतलब मौजूदा संकलित कोड से जुड़ा होगा, जो कि एपीआई कहलाता है तो बुरी तरह गलत हो जाएगा।

आइए नजर डालते हैं कि FILE को कैसे परिभाषित किया गया है।

पहली VS2008 फ़ाइल परिभाषा:

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

और अब VS2015 फ़ाइल परिभाषा:

typedef struct _iobuf
{
    void* _Placeholder;
} FILE;

तो इसका क्रैक्स है: संरचना ने आकार बदल दिया है। मौजूदा संकलित कोड __iob_func का संदर्भ देते हुए इस तथ्य पर निर्भर करता है कि लौटाया गया डेटा दोनों एक सरणी है जिसे अनुक्रमित किया जा सकता है और उस सरणी में तत्व समान दूरी पर हैं।

इन रेखाओं के साथ ऊपर दिए गए उत्तर में वर्णित संभावित समाधान कुछ कारणों से काम नहीं करेंगे (यदि कहा जाता है):

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

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

फ़ाइल सरणी _iob को VS2015 के साथ संकलित किया जाएगा और इसलिए इसे एक शून्य * संरचना वाले ब्लॉक के रूप में रखा जाएगा। 32-बिट संरेखण मानते हुए, ये तत्व अलग-अलग 4 बाइट्स होंगे। तो _iob [0] ऑफ़सेट 0 पर है, _iob [1] ऑफ़सेट 4 पर है और _iob [2] ऑफ़सेट 8 पर है। कॉलिंग कोड इसके बजाय FILE के अधिक लंबे होने की उम्मीद करेगा, मेरे सिस्टम पर 32 बाइट्स में संरेखित है, और इसी तरह यह दिए गए ऐरे का पता लेगा और एलिमेंट को शून्य करने के लिए 0 बाइट्स जोड़ देगा (जो कि ठीक है), लेकिन _iob [1] के लिए यह कटौती करेगा कि उसे 32 बाइट्स जोड़ने की जरूरत है और _iob [2] के लिए यह कटौती करेगा यह 64-बाइट्स जोड़ने की आवश्यकता है (क्योंकि यह कैसे VS2008 हेडर में देखा गया है)। और वास्तव में VS2008 के लिए असंतुष्ट कोड इसे प्रदर्शित करता है।

उपरोक्त समाधान के साथ एक द्वितीयक समस्या यह है कि यह FILE संरचना की सामग्री की प्रतिलिपि बनाता है (* stdin), न कि FILE * हैंडल। इसलिए कोई भी VS2008 कोड VS2015 के लिए एक अलग अंतर्निहित संरचना को देखेगा। यह काम कर सकता है यदि संरचना में केवल संकेत होते हैं, लेकिन यह एक बड़ा जोखिम है। किसी भी स्थिति में पहला मुद्दा इस अप्रासंगिक को प्रस्तुत करता है।

केवल वही हैक जो मैं सपने में देख पा रहा हूं, वह है जिसमें __iob_func कॉल स्टैक चलता है जो वास्तविक फ़ाइल हैंडल की तलाश में है जो वे ढूंढ रहे हैं (लौटे पते पर जोड़े गए ऑफसेट के आधार पर) और एक कम्प्यूटेड मान लौटाता है जैसे कि सही जवाब देता है। यह हर तरह से पागल है क्योंकि यह लगता है, लेकिन केवल x86 के लिए प्रोटोटाइप (x64 नहीं) आपके मनोरंजन के लिए नीचे सूचीबद्ध है। इसने मेरे प्रयोगों में ठीक काम किया है, लेकिन आपका लाभ भिन्न हो सकता है - उत्पादन के उपयोग के लिए अनुशंसित नहीं!

#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;
}

मिलान बाबूसकोव, आईएमओ के लिए, यह वही है जो प्रतिस्थापन फ़ंक्शन को :-) जैसा दिखना चाहिए

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

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

मेरी सलाह यह है कि __iob_func को लागू न करने का प्रयास करें।

इन त्रुटियों को ठीक करते समय:

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

मैंने अन्य उत्तरों के समाधानों की कोशिश की, लेकिन अंत में, एक FILE* सी-सरणी लौटना विंडोज के आंतरिक आईओबी संरचनाओं के एक सरणी के साथ मेल नहीं खाता। @ वोल्कर सही है कि यह कभी भी एक से अधिक स्टड, स्टडआउट या स्टेडर के लिए काम नहीं करेगा।

यदि एक पुस्तकालय वास्तव में उन धाराओं में से एक का उपयोग करता है, तो यह दुर्घटनाग्रस्त हो जाएगा । जब तक आपके कार्यक्रम का उपयोग करने के लिए उन्हें देय नहीं होता है, तो आप कभी नहीं जान पाएंगे । उदाहरण के लिए, png_default_error stderr को लिखता है जब CRC PNG के मेटाडेटा में मेल नहीं खाता है। (आम तौर पर दुर्घटना-योग्य मुद्दा नहीं)

निष्कर्ष: VS2012 (प्लेटफ़ॉर्म टूलसेट v110 / v110_xp) और VS2015 + लाइब्रेरीज़ को मिक्स करना संभव नहीं है, अगर वे स्टडिन, स्टडआउट और / या स्टैडर का उपयोग करते हैं।

समाधान: अपने पुस्तकालयों को फिर से __iob_func जिसमें आपके मौजूदा संस्करण वीएस और मिलान प्लेटफॉर्म टूलसेट के साथ __iob_func अनसुलझे प्रतीक हैं।


मेरे मामले में, यह त्रुटि मेरे परीक्षण से MSVC- संस्करण आश्रित रनटाइम लाइब्रेरी DLL (msvcr10.dll या तो) पर निर्भरता को हटाने और / या मेरे निष्पादनयोग्य से अतिरिक्त वसा को हटाने के लिए स्थैतिक रनटाइम लाइब्रेरी को हटाने के लिए आती है।

इसलिए मैं / NODEFAULTLIB लिंकर स्विच का उपयोग करता हूं, मेरे स्व-निर्मित "msvcrt-light.lib" (इसके लिए Google की आवश्यकता होने पर), और mainCRTStartup() / WinMainCRTStartup() प्रविष्टियाँ।

यह विजुअल स्टूडियो 2015 के बाद से IMHO है, इसलिए मैं पुराने कंपाइलरों से चिपक गया।

हालाँकि, प्रतीक _NO_CRT_STDIO_INLINE को परिभाषित करने से सभी परेशानी दूर हो जाती है, और एक सरल "हैलो वर्ल्ड" एप्लिकेशन फिर से 3 KB छोटा हो जाता है और असामान्य DLL पर निर्भर नहीं होता है। विजुअल स्टूडियो 2017 में परीक्षण किया गया।


मैंने आखिर पता लगा लिया कि ऐसा क्यों हो रहा है!

दृश्य स्टूडियो 2015 में, स्टडिन, स्टैडर, स्टडआउट को निम्नानुसार परिभाषित किया गया है:

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

लेकिन पहले, उन्हें इस रूप में परिभाषित किया गया था:

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

तो अब __iob_func को अब परिभाषित नहीं किया जाता है जो दृश्य स्टूडियो के पिछले संस्करणों के साथ संकलित .lib फ़ाइल का उपयोग करते समय लिंक त्रुटि की ओर जाता है।

समस्या को हल करने के लिए, आप __iob_func() स्वयं परिभाषित करने का प्रयास कर सकते हैं, जिसमें {*stdin,*stdout,*stderr} युक्त सरणी वापस आनी चाहिए।

Stdio फ़ंक्शन के बारे में अन्य लिंक त्रुटियों के बारे में (मेरे मामले में यह स्प्रिंटफ़ sprintf() ) था, आप अपने लिंकर विकल्पों में legacy_stdio_definitions.lib जोड़ सकते हैं।


मैंने समस्या को ठीक करने में कामयाबी हासिल की।

त्रुटि का स्रोत कोड की यह पंक्ति थी, जिसे SDLmain स्रोत कोड में पाया जा सकता है।

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

इसलिए मैंने जो किया वह स्रोत कोड को उस पंक्ति के SDLmain में संपादित करना था:

#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

और फिर मैंने SDLmain का निर्माण किया और अपनी SDL2 लाइब्रेरी डायरेक्टरी में पुराने SDLmain.lib को कॉपी किया और उसकी जगह नया बनाया और संपादित किया।

फिर जब मैंने अपना कार्यक्रम SDL2 के साथ चलाया तो कोई त्रुटि संदेश नहीं आया और कोड आसानी से चला।

मुझे नहीं पता कि यह बाद में मुझे काटेगा, लेकिन इसलिए सब कुछ बहुत अच्छा है।


लिंक करने का मतलब है ठीक से काम न करना। VS2012 और VS2015 के stdio.h में खुदाई करना मेरे लिए निम्न कार्य था। काश, आपको यह तय करना होगा कि क्या यह {स्टड, स्टडआउट, स्ट्रडर} में से एक के लिए काम करना चाहिए, कभी भी एक से अधिक नहीं।

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);
}




unresolved-external