Skip to content

Commit b72bd9e

Browse files
foxtaclesclaude
andcommitted
Patch LIBCMT.LIB instead of linking separate goldp_libcmt static lib
Two issues with the previous approach prevented 100% reccmp match on the heap CRT functions and operator new/delete: 1. Linking goldp_libcmt as a separate static lib alongside RTM's LIBCMT.LIB left the linker with two definitions of _calloc/_malloc/etc. With certain optimization combinations the linker pulled the LIBCMT.LIB version, not ours, dropping calloc/malloc/heap_alloc to <30% match. 2. Compiling the SP3 sources with RelWithDebInfo emitted S_GPROC32 PDB entries with the C source name (_nh_malloc, one underscore) which shadowed the PUBLIC entry's linker-decorated name (__nh_malloc, two underscores). LR's binary has no S_GPROC32 for CRT internals, so reccmp displayed the PUBLIC name for LR and the GPROC name for ours — name mismatch even when bytes were byte-identical (operator new dropped to 83%, delete to 75%). Fix: - Compile the SP3 overlay as an OBJECT library so we can grab the raw .obj files. Use lib /REMOVE to strip RTM's heap .obj members from a copy of LIBCMT.LIB, then lib /OUT to add ours. The patched LIBCMT.LIB contains exactly one definition of each heap symbol — ours — and the linker has no other choice. - Suppress MSVC_DEBUG_INFORMATION_FORMAT on goldp_libcmt_objs so its PDB contains only PUBLIC symbol entries (matching LR's PDB granularity). - Use /NODEFAULTLIB:LIBCMT plus an explicit reference to the patched lib so the linker pulls our patched version instead of the system default. Result: _free, _calloc, _malloc, __nh_malloc, __heap_alloc, _realloc, _expand, _msize, all __sbh_* functions, operator new, and operator delete are now at 100% reccmp match. The four remaining sub-100% CRT functions (__dosmaperr, __flsbuf, __NMSG_WRITE, __setmbcp) have layout-dependent address-literal mismatches and are unrelated to this change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent ba658c5 commit b72bd9e

1 file changed

Lines changed: 49 additions & 9 deletions

File tree

CMakeLists.txt

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -566,7 +566,9 @@ if(MSVC_FOR_DECOMP)
566566
enable_language(C)
567567
set(MSVC_CRT_SRC_PATH "" CACHE PATH "Path of MSVC6 CRT/SRC")
568568
if(MSVC_CRT_SRC_PATH)
569-
add_library(goldp_libcmt STATIC
569+
# Compile our SP3 heap overlay as an OBJECT library so we can patch the
570+
# resulting .obj files into a copy of RTM's LIBCMT.LIB.
571+
add_library(goldp_libcmt_objs OBJECT
570572
"${MSVC_CRT_SRC_PATH}/free.c"
571573
"${MSVC_CRT_SRC_PATH}/calloc.c"
572574
"${MSVC_CRT_SRC_PATH}/malloc.c"
@@ -580,13 +582,51 @@ if(MSVC_FOR_DECOMP)
580582
"${MSVC_CRT_SRC_PATH}/new.cpp"
581583
"${MSVC_CRT_SRC_PATH}/delete.cpp"
582584
)
583-
target_include_directories(goldp_libcmt PRIVATE "${MSVC_CRT_SRC_PATH}")
584-
target_compile_definitions(goldp_libcmt PRIVATE WIN32_LEAN_AND_MEAN NOSERVICE)
585-
target_compile_definitions(goldp_libcmt PRIVATE _MBCS _MB_MAP_DIRECT)
586-
target_compile_definitions(goldp_libcmt PRIVATE _CRTBLD)
587-
target_compile_definitions(goldp_libcmt PRIVATE WINHEAP)
588-
target_compile_options(goldp_libcmt PRIVATE /O1 /Zp8 /GFy /GB /Gi- /Zl)
589-
set_property(TARGET goldp_libcmt PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
590-
target_link_libraries(goldp PRIVATE goldp_libcmt)
585+
target_include_directories(goldp_libcmt_objs PRIVATE "${MSVC_CRT_SRC_PATH}")
586+
target_compile_definitions(goldp_libcmt_objs PRIVATE WIN32_LEAN_AND_MEAN NOSERVICE)
587+
target_compile_definitions(goldp_libcmt_objs PRIVATE _MBCS _MB_MAP_DIRECT)
588+
target_compile_definitions(goldp_libcmt_objs PRIVATE _CRTBLD)
589+
target_compile_definitions(goldp_libcmt_objs PRIVATE WINHEAP)
590+
target_compile_options(goldp_libcmt_objs PRIVATE /O1 /Zp8 /GFy /GB /Gi- /Zl)
591+
set_property(TARGET goldp_libcmt_objs PROPERTY MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
592+
# Suppress source-level debug info so the PDB doesn't emit S_GPROC32 entries with C-source
593+
# names for CRT internals — this would shadow PUBLIC symbol names and break reccmp's symbol
594+
# resolution for call targets that LR's PDB only knows by linker-decorated name.
595+
set_property(TARGET goldp_libcmt_objs PROPERTY MSVC_DEBUG_INFORMATION_FORMAT "")
596+
597+
# Locate the RTM LIBCMT.LIB picked up from the LIB env var
598+
find_library(RTM_LIBCMT_LIB NAMES LIBCMT REQUIRED NO_CACHE)
599+
message(STATUS "RTM LIBCMT.LIB: ${RTM_LIBCMT_LIB}")
600+
601+
# Build a patched LIBCMT.LIB: copy RTM's lib, strip heap .obj members, add ours.
602+
set(PATCHED_LIBCMT "${CMAKE_BINARY_DIR}/LIBCMT.LIB")
603+
add_custom_command(
604+
OUTPUT "${PATCHED_LIBCMT}"
605+
DEPENDS goldp_libcmt_objs "${RTM_LIBCMT_LIB}"
606+
COMMAND ${CMAKE_COMMAND} -E copy "${RTM_LIBCMT_LIB}" "${PATCHED_LIBCMT}"
607+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\free.obj "${PATCHED_LIBCMT}"
608+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\calloc.obj "${PATCHED_LIBCMT}"
609+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\malloc.obj "${PATCHED_LIBCMT}"
610+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\realloc.obj "${PATCHED_LIBCMT}"
611+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\expand.obj "${PATCHED_LIBCMT}"
612+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\msize.obj "${PATCHED_LIBCMT}"
613+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\sbheap.obj "${PATCHED_LIBCMT}"
614+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\heapinit.obj "${PATCHED_LIBCMT}"
615+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\heapchk.obj "${PATCHED_LIBCMT}"
616+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\heapmin.obj "${PATCHED_LIBCMT}"
617+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\new.obj "${PATCHED_LIBCMT}"
618+
COMMAND lib /nologo /REMOVE:build\\intel\\mt_obj\\delete.obj "${PATCHED_LIBCMT}"
619+
COMMAND lib /nologo "${PATCHED_LIBCMT}" $<TARGET_OBJECTS:goldp_libcmt_objs>
620+
COMMAND_EXPAND_LISTS
621+
VERBATIM
622+
)
623+
add_custom_target(goldp_libcmt_target DEPENDS "${PATCHED_LIBCMT}")
624+
add_dependencies(goldp goldp_libcmt_target)
625+
626+
# Tell the linker to use our patched LIBCMT instead of RTM's
627+
target_link_options(goldp PRIVATE
628+
"/NODEFAULTLIB:LIBCMT"
629+
"${PATCHED_LIBCMT}"
630+
)
591631
endif()
592632
endif()

0 commit comments

Comments
 (0)