Skip to content

Commit

Permalink
Implement InterlockedCompareExchange using CAS
Browse files Browse the repository at this point in the history
This implementation is only usable on 486 or newer CPUs, so there is
also code that detects at runtime whether the AC bit of the EFLAGS
register can be set. On 386, this register always returns the same
value.

The CAS implementation uses the "lock cmpxchg" instruction, which is
only executed if the above runtime check is satisfied. The overhead of
calling this implementation is a "cmp" followed by a "jnz" instruction,
which should have a negligible cost when execution already had to cross
a DLL boundary to get here.

Using the command

find -name "*.dll" -o -name "*.vxd" | perl -nE "print if grep { $_ =~ /InterlockedCompareExchange/ } `dumpbin /imports $_`;"

Here is a list of .dll and .vxd files in the installer that import the
"InterlockedCompareExchange" symbol and thus benefit from this:

$WINDIR/Microsoft.NET/Framework/sbscmp10.dll
$WINDIR/Microsoft.NET/Framework/sbscmp20_mscorwks.dll
$WINDIR/Microsoft.NET/Framework/sbscmp20_perfcounter.dll
$WINDIR/Microsoft.NET/Framework/SharedReg12.dll
$WINDIR/Microsoft.NET/Framework/v1.0.3705/mscormmc.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/AdoNetDiag.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/alink.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/aspnet_filter.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/aspnet_isapi.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/Aspnet_perf.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/CORPerfMonExt.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/cscomp.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/Culture.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/dfdll.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/diasymreader.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/fusion.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/MmcAspExt.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscordacwks.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscordbc.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscordbi.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscorie.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscorjit.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscorld.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscorpe.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscorsec.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscorsn.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscorsvc.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscortim.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/mscorwks.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/normalization.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/PerfCounter.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/peverify.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/sbscmp20_mscorlib.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/shfusion.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/ShFusRes.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/System.Data.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/System.Data.OracleClient.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/System.EnterpriseServices.Wrapper.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/System.Transactions.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/VsaVb7rt.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/webengine.dll
$WINDIR/Microsoft.NET/Framework/v2.0.50727/WMINet_Utils.dll
$WINDIR/RegisteredPackages/{D5D40355-5FB0-48fb-A231-CDC637FA16E0}/NETFXMigration.dll
$WINDIR/System/dfshim.dll
$WINDIR/System/mscoree.dll
$WINDIR/System/mscories.dll
$WINDIR/System/msvcm80.dll
$WINDIR/System/msvcp80.dll
$WINDIR/System/WBEM/Wmidcad.dll
$WINDIR/winsxs/x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.42_none_db5f52fb98cb24ad/msvcm80.dll
$WINDIR/winsxs/x86_microsoft.vc80.crt_1fc8b3b9a1e18e3b_8.0.50727.42_none_db5f52fb98cb24ad/msvcp80.dll
$WINDIR/winsxs/x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_0de06acd/msvcm80.dll
$WINDIR/winsxs/x86_Microsoft.VC80.CRT_1fc8b3b9a1e18e3b_8.0.50727.42_x-ww_0de06acd/msvcp80.dll
  • Loading branch information
friendlyanon committed Apr 14, 2024
1 parent 59e5b67 commit ea182f8
Show file tree
Hide file tree
Showing 10 changed files with 119 additions and 16 deletions.
38 changes: 31 additions & 7 deletions wrappers/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,30 +1,54 @@
cmake_minimum_required(VERSION 3.13)

project(corkel32 C)
project(corkel32 C ASM_MASM)

set(CMAKE_BUILD_TYPE Release)
add_compile_definitions(_WIN32_WINNT=0x0400)

add_library(corkdebug OBJECT
add_library(
corkdebug OBJECT
debug.c
)
link_libraries(corkdebug)
target_compile_definitions(corkdebug PRIVATE _CRT_SECURE_NO_WARNINGS=1)

add_library(
detect486 OBJECT
detect486.asm
)

add_library(
cas OBJECT
cas.asm
)

include(CheckSymbolExists)
check_symbol_exists(RtlUnwind Windows.h HAS_RTL_UNWIND)

# KERNEL32 => CORKEL32
add_library(corkel32 SHARED
add_library(
corkel32 MODULE
kernel32.c
advapi32.c
comdlg32.c
corkel32.def
)
target_link_libraries(corkel32 PRIVATE corkdebug detect486 cas)
target_compile_definitions(corkel32 PRIVATE _CRT_SECURE_NO_WARNINGS=1)

# NTDLL => CORNT
add_library(cornt SHARED
add_library(
cornt MODULE
ntdll.c
cornt.def
)
target_link_libraries(cornt PRIVATE corkdebug)
if(HAS_RTL_UNWIND)
target_compile_definitions(cornt PRIVATE HAS_RTL_UNWIND=1)
endif()

# USER32 => CORUSR
add_library(corusr SHARED
add_library(
corusr MODULE
user32.c
corusr.def
)
target_link_libraries(corusr PRIVATE corkdebug)
1 change: 0 additions & 1 deletion wrappers/advapi32.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#define _WIN32_WINNT 0x0400
#include <windows.h>

#include "debug.h"
Expand Down
17 changes: 17 additions & 0 deletions wrappers/cas.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.486
.MODEL FLAT

.CODE

OPTION PROLOGUE:NONE
OPTION EPILOGUE:NONE

_InterlockedCompareExchange_486@12 PROC
mov ecx, DWORD PTR [esp + 4] ; dest
mov edx, DWORD PTR [esp + 8] ; exchange
mov eax, DWORD PTR [esp + 12] ; compare
lock cmpxchg DWORD PTR [ecx], edx
ret 12
_InterlockedCompareExchange_486@12 ENDP

END
10 changes: 10 additions & 0 deletions wrappers/cas.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef CAS
#define CAS

#ifndef STDCALL
#define STDCALL __stdcall
#endif

long STDCALL InterlockedCompareExchange_486(long* dest, long exchange, long compare);

#endif // CAS
1 change: 0 additions & 1 deletion wrappers/comdlg32.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#define _WIN32_WINNT 0x0400
#include <windows.h>

#include "debug.h"
Expand Down
28 changes: 28 additions & 0 deletions wrappers/detect486.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.386
.MODEL FLAT, STDCALL

.CODE

is_cpu_486_or_newer PROC
pushfd
pop eax
mov ebx, eax
xor eax, 40000h ; toggle the AC bit in EFLAGS (only available in 486 or newer)
push eax
popfd
pushfd
pop eax
cmp eax, ebx
jz is_386
push ebx
popfd
xor eax, eax
inc eax
ret

is_386:
xor eax, eax
ret
is_cpu_486_or_newer ENDP

END
10 changes: 10 additions & 0 deletions wrappers/detect486.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef DETECT_486
#define DETECT_486

#ifndef STDCALL
#define STDCALL __stdcall
#endif

int STDCALL is_cpu_486_or_newer(void);

#endif // DETECT_486
23 changes: 19 additions & 4 deletions wrappers/kernel32.c
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
#define _WIN32_WINNT 0x0400

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

#include "cas.h"
#include "debug.h"
#include "detect486.h"

static int has_cmpxchg = 0;

DWORD WINAPI CORKEL32_GetLastError()
{
Expand Down Expand Up @@ -154,10 +156,14 @@ BOOL WINAPI CORKEL32_DeviceIoControl(HANDLE hDevice, DWORD dwIoControlCode, LPVO
}

// Reimplemented
LONG WINAPI CORKEL32_InterlockedCompareExchange(LONG *dest, LONG xchg, LONG compare)
LONG WINAPI CORKEL32_InterlockedCompareExchange(LONG* dest, LONG xchg, LONG compare)
{
LONG temp = *dest;
LONG temp;
if (has_cmpxchg) {
return InterlockedCompareExchange_486(dest, xchg, compare);
}

temp = *dest;
Trace(TRACE_FORCE_DONT_PRINT, "InterlockedCompareExchange");

if (compare == *dest) {
Expand Down Expand Up @@ -303,3 +309,12 @@ BOOL WINAPI CORKEL32_InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION cr

return TRUE;
}

BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if (fdwReason == DLL_PROCESS_ATTACH) {
has_cmpxchg = is_cpu_486_or_newer();
}

return TRUE;
}
6 changes: 4 additions & 2 deletions wrappers/ntdll.c
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
#define _WIN32_WINNT 0x0400
#include <windows.h>

#include "debug.h"

ULONG WINAPI CORNT_RtlNtStatusToDosError(NTSTATUS)
ULONG WINAPI CORNT_RtlNtStatusToDosError(NTSTATUS param_0)
{
Trace(TRACE_UNIMPLEMENTED, "RtlNtStatusToDosError");
// TODO: Stub
return 0;
}

#ifndef HAS_RTL_UNWIND
extern NTAPI RtlUnwind(void* param_0, void* param_1, struct _EXCEPTION_RECORD* param_2, void* param_3);
#endif

VOID NTAPI CORNT_RtlUnwind(void *p0,void *p1,struct _EXCEPTION_RECORD *p2, void *p3)
{
Trace(TRACE_PASSTHROUGH, "RtlUnwind");
Expand Down
1 change: 0 additions & 1 deletion wrappers/user32.c
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
#define _WIN32_WINNT 0x0400
#include <windows.h>

#include "debug.h"
Expand Down

0 comments on commit ea182f8

Please sign in to comment.