Jump to content

How VS makes working code still incompatible


user57

Recommended Posts

there was a recent update (my previos version was visual studio 2019 v16.4 updated to vs 2019 v16.10 (the exact version is not known so it must be between those versions)

the code i used is windows xp compatible however this "build in trick" still makes my code still incompatible

in the past your code useally gone to winmain() in some other codes i reconized that some code was added to my executable that actually was not needed at all

so i was wondering why did my executable (what only printed MessageboxA) gone a lot bigger

going after that i could understand that this code was added by something else and is executed before my code in winmain() is executed

in other compilers or maybe settings? you didnt see this problem and it gone to winmain() directly then the executable was smaller too in my test executable it was just MessageboxA function and no need of executing some "other/more" code

this time i had the same problem but this time it made the executable not only inefficient it also made it incompatible
(to see here is that microsoft can add this code before your code and make it lets say windows 10 only "this runtime code then gets executed and says "windows 10 only" or "access denied" before your working code is executed)

 

so i took a look what this code actually does do

all begins with the function that is called before winmain()

extern "C" DWORD WinMainCRTStartup(LPVOID) // <- called before winmain
{

    __security_init_cookie // (unimportant for us)
    return __scrt_common_main(); // <-- calls next functions
}

the filename for this code is exe_winmain.cpp and useally is in a directory (or something close) called
C:\Program Files (x86)\Microsoft Visual Studio\2019\Professional\VC\Tools\MSVC\14.24.28314\crt\src\vcruntime\exe_winmain.cpp

CRT means: C runtime, so we actually know what we deal with

in my case it use a unwanted function InitializeCriticalSectionEx (that i actually use nowhere in my code but the c runtime use it)

so i looked how and where it do that

the code cain is as follows:

calls __scrt_common_main ->__scrt_common_main_seh
src\vctools\crt\vcstartup\src\eh\i386\secchk.c
: (call chain)
0, __SEH_prolog4

1, __scrt_initialize_crt // -> has function __cdecl __vcrt_initialize_locks(VS 16.4works with InitializeCriticalSectionEx and InitializeCriticalSectionAndSpinCount)  (VS 16.10 only work with InitializeCriticalSectionEx) -> the VS 16.4 version checks if it can use InitializeCriticalSectionEx if it cant it use InitializeCriticalSectionAndSpinCount (the code use a function called try_get_function() to do so)

VC\Tools\MSVC\14.29.30037\crt\src\vcruntime\locks.cpp

this is the function with more details:

(static void* __cdecl try_get_function(
        function_id      const id,
        char      const* const name,
        module_id const* const first_module_id,
        module_id const* const last_module_id
        ) noexcept )

but that is only important if we want to use InitializeCriticalSectionEx, if we want we just could use InitializeCriticalSectionAndSpinCount and this part would be solved

2, __scrt_acquire_startup_lock

3, _initterm_e (dont have InitializeCriticalSectionEx yet)

(then _initterm is done a second time and has InitializeCriticalSectionEx in internal)

4, _initterm <--- second time without _e

the function "_initterm"

has 2 functions 1 is called __guard_check_icall_fptr (this function checks if the pointer is valid and is unimportant for this case)

the other function calls several functions (__xc_a beginning list of functions and __xc_z (end of functions))

the second function in list of __xc_a has InitializeCriticalSectionEx in internal and is called:

static _Init_lock (is a class constructor) and leads to _Init_locks
_Init_locks is in a file called "xlock.cpp"
folder:
VC\Tools\MSVC\14.29.30037\crt\src\stl\xlock.cpp

(c code)
__thiscall _Init_locks::_Init_locks() noexcept { // initialize locks
    if (InterlockedIncrement(&init) == 0) {
        for (auto& elem : mtx) {
            _Mtxinit(&elem); // <- next step we looking for
        }
    }
}

here we look at function _Mtxinit
filename that contain _Mtxinit "xmtx.cpp" folder:
VC\Tools\MSVC\14.24.28314\crt\src\stl\xmtx.cpp
(c code)
void __CLRCALL_PURE_OR_CDECL _Mtxinit(_Rmtx* _Mtx) noexcept { // initialize mutex
    InitializeCriticalSectionEx(_Mtx, 4000, 0); // this variant
}

here also is the diffrens what has been changed:

void __CLRCALL_PURE_OR_CDECL _Mtxinit(_Rmtx* _Mtx) noexcept { // initialize mutex
    __crtInitializeCriticalSectionEx(_Mtx, 4000, 0); // variant before
}


the function __crtInitializeCriticalSectionEx has a check if it do use InitializeCriticalSectionEx or if it do use InitializeCriticalSectionAndSpinCount (xp compatible)

extern "C" BOOL __cdecl __crtInitializeCriticalSectionEx(
    LPCRITICAL_SECTION const lpCriticalSection, DWORD const dwSpinCount, DWORD const Flags) {
    // use InitializeCriticalSectionEx if it is available (only on Windows Vista+)...
    IFDYNAMICGETCACHEDFUNCTION(
        PFNINITIALIZECRITICALSECTIONEX, InitializeCriticalSectionEx, pfInitializeCriticalSectionEx) {
        return pfInitializeCriticalSectionEx(lpCriticalSection, dwSpinCount, Flags);
    }

    // ...otherwise fall back to using InitializeCriticalSectionAndSpinCount.
    InitializeCriticalSectionAndSpinCount(lpCriticalSection, dwSpinCount);
    return TRUE;
}

 

so even if your entire code would just use a single MessageboxA this CRT or "C runtime" starting/calling WinMainCRTStartup beforehand

makes your application still incompatible, even if your code 100 % would work


noticeable is that "they" can do this not only with a limit to windows vista, 7, 8, 8.1, or 10
they can add incompatible functions to that CRT code
thus you might just upgrade your VS/Compiler and your code wont work even if the code is compatible


its like a check "do this computer have windows 10" -> access denied -> no code is spawned (even tho your code would work)

shame to microsoft for this one

(the other code i tryed has a header file that use c++20 style (it are just type converters like you do with templates) but it doesnt use any windows functions))

in that sence that is a compiler question of the c++ style (type converter) not a "missing api" question, but still that CRT used that api even tho i dont have that InitializeCriticalSectionEx in my code

and to fix up different types such as handles, int, float etc. for example over a template or type converter, again is a compiler question

here is the rest of the callchain (before your code is executed)

5,  __scrt_release_startup_lock
6,  __scrt_get_dyn_tls_init_callback
7,  __scrt_get_dyn_tls_dtor_callback
8 , __scrt_get_show_window_mode
9,  _get_narrow_winmain_command_line
10, WinMain(our executable/our code is then executed) <-- here begins/comes our code/programm/app

so everybody can easy understand why your code got "blocked" or will just maybe in future, so we know the trick beforehand :-)

Edited by user57
more detail
Link to comment
Share on other sites


Have you found how to fix this?

 

I recently got VS 2019 and wanted to compile a few programs without SSE2, but so far haven't been able to build anything successfully. Not even sure if i set up the damn thing properly. Tried to follow some "Hello world" tutorial, but it shows selecting console project and i don't have this option here, pfff.

I remember getting that update too.

Link to comment
Share on other sites

  • Dave-H changed the title to How VS makes working code still incompatible

I managed to configure a console "hello world" program so it works in XP (and no stupid SSE2, yay!).

I didn't find simple GUI program to test though, and the example when i started a new "Dialog project" was way over my head.

I have been trying to get Miranda-ng, but it seems to have issues even without targeting XP.  And not having touched any Visual Studio before kinda complicates things for me...

 

@user57

Could you share that MessageboxA project? I'd like to try getting it built for XP.

Link to comment
Share on other sites

  • 2 weeks later...

actually i gone for a solution

i editet this cpp file to make use of the previos method

but then something weird happend

it still imported that function so i was like hmm you might forgotten something ? looked no

then i debugged the application

picture 01

then this message apeared (basicly it just says that i used a different c++ file to compile but actually this message always apears even tho i made > 10 compiles and > 10 changes) , i so looked if this file exits 2 or more times (no), i restarted vs (also compiled the same problem)

also a few trys with rebuild solution/project and clean project

deleting the entire project from hardrive and tryed again from beginning also lead to that picture that the c++ files doesnt match the compiled file

in picture 02 (thats the one in the middle with the assembly code) you can see that it still used that old code (aka  InitializeCriticalSectionEx not InitializeCriticalSectionAndSpinCount) even tho the cpp file is edited (picture 03)

in picture 03 thats the one that shows the cpp file that should make that code

as you see i first uncommented the code that creates InitializeCriticalSectionEx and replaced it with the common function InitializeCriticalSectionAndSpinCount

a other method is i tryed to write c errors into that function, but instead of giving me a error message (because the code cant even work) it also compiled that old code with InitializeCriticalSectionEx

do it have some kind of backfile ? maybe staticly skipped ?

 

do somebody actually know why this is happening ? (it let me edit other internal cpp files but for some the code applied/changed does simply not happen, the files itself are changed i gone the folders and looked them with other editors)

01.jpg

02.jpg

03.jpg

Edited by user57
Link to comment
Share on other sites

  • 1 year later...

greetings all, we found the related information that caused that problem

 

even tho we have no inside information, the information i posted is incredible accurate

 

 

so here is what microsoft did, it probaly is useful information to know

https://github.com/microsoft/STL/pull/1194/commits/faa3434d7e748fcfdc253ad2788a0e4fddfea105

 

explain that 

__crtInitializeCriticalSectionEx(&_M_critical_section, 4000, 0);

// to
InitializeCriticalSectionEx(&_M_critical_section, 4000, 0);

 

it also explains why the dependency walker for versions up to 16.7 show a try to search for that functions 

(meant is that there these functions where found on dependency walker but not in the import list)

 

 

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...