Skip to content

[cDAC] Remove native components #117024

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jul 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 13 additions & 104 deletions src/coreclr/unwinder/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,121 +1,30 @@
# helper to add set of include directories to unwinder targets
macro(add_unwinder_include_directories TARGET)
target_include_directories(${TARGET} BEFORE PRIVATE ${VM_DIR})
target_include_directories(${TARGET} BEFORE PRIVATE ${VM_DIR}/${ARCH_SOURCES_DIR})
target_include_directories(${TARGET} BEFORE PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(${TARGET} BEFORE PRIVATE ${CLR_DIR}/unwinder)
target_include_directories(${TARGET} PRIVATE ${CLR_DIR}/debug/ee)
target_include_directories(${TARGET} PRIVATE ${CLR_DIR}/gc)
target_include_directories(${TARGET} PRIVATE ${CLR_DIR}/gcdump)
target_include_directories(${TARGET} PRIVATE ${CLR_DIR}/debug/daccess)
target_include_directories(${TARGET} PRIVATE ${ARCH_SOURCES_DIR})
endmacro()
include_directories(BEFORE ${VM_DIR})
include_directories(BEFORE ${VM_DIR}/${ARCH_SOURCES_DIR})
include_directories(BEFORE ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(BEFORE ${CLR_DIR}/unwinder)
include_directories(${CLR_DIR}/debug/ee)
include_directories(${CLR_DIR}/gc)
include_directories(${CLR_DIR}/gcdump)
include_directories(${CLR_DIR}/debug/daccess)

set(UNWINDER_SOURCES
baseunwinder.cpp
)

# Include platform specific unwinder for applicable (native and cross-target) builds.
include_directories(${ARCH_SOURCES_DIR})
list(APPEND UNWINDER_SOURCES
${ARCH_SOURCES_DIR}/unwinder.cpp
)

convert_to_absolute_path(UNWINDER_SOURCES ${UNWINDER_SOURCES})

if(CLR_CMAKE_HOST_UNIX)
add_library_clr(unwinder_wks OBJECT ${UNWINDER_SOURCES})
add_unwinder_include_directories(unwinder_wks)
add_dependencies(unwinder_wks eventing_headers)
endif(CLR_CMAKE_HOST_UNIX)

add_library_clr(unwinder_dac ${UNWINDER_SOURCES})
add_unwinder_include_directories(unwinder_dac)
add_dependencies(unwinder_dac eventing_headers)
set_target_properties(unwinder_dac PROPERTIES DAC_COMPONENT TRUE)
target_compile_definitions(unwinder_dac PRIVATE FEATURE_NO_HOST)

### cDAC Unwinders ####

set(BASE_UNWINDER_SOURCES baseunwinder.cpp)
convert_to_absolute_path(BASE_UNWINDER_SOURCES ${BASE_UNWINDER_SOURCES})
add_library_clr(unwinder_cdac_base STATIC ${BASE_UNWINDER_SOURCES})

target_include_directories(unwinder_cdac_base BEFORE PUBLIC ${VM_DIR})
target_include_directories(unwinder_cdac_base BEFORE PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_include_directories(unwinder_cdac_base BEFORE PUBLIC ${CLR_DIR}/unwinder)
target_include_directories(unwinder_cdac_base PUBLIC ${CLR_DIR}/debug/ee)
target_include_directories(unwinder_cdac_base PUBLIC ${CLR_DIR}/gc)
target_include_directories(unwinder_cdac_base PUBLIC ${CLR_DIR}/gcdump)
target_include_directories(unwinder_cdac_base PUBLIC ${CLR_DIR}/debug/daccess)
target_compile_definitions(unwinder_cdac_base PUBLIC FEATURE_NO_HOST FEATURE_CDAC_UNWINDER)

if (CLR_CMAKE_TARGET_WIN32)
# cDAC unwinders are statically linked into the NativeAOT runtime which is built with
# release version of the statically linked CRT. Therefore we do the same here.
set_property(TARGET unwinder_cdac_base PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded)

# _DEBUG is always passed as a parameter if the build is a debug build.
# This causes the debug CRT on MSVC to be used so we need to undefine it.
target_compile_options(unwinder_cdac_base PRIVATE -U_DEBUG)
endif()

install_clr(TARGETS unwinder_cdac_base DESTINATIONS cdaclibs COMPONENT cdac)

# Helper function for platform specific cDAC uwninder builds.
function(create_platform_unwinder)
set(oneValueArgs TARGET ARCH)
set(multiValueArgs DESTINATIONS)
cmake_parse_arguments(TARGETDETAILS "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

if(TARGETDETAILS_ARCH STREQUAL "x64")
set(ARCH_SOURCES_DIR amd64)
elseif((TARGETDETAILS_ARCH STREQUAL "arm") OR (TARGETDETAILS_ARCH STREQUAL "armel"))
set(ARCH_SOURCES_DIR arm)
elseif(TARGETDETAILS_ARCH STREQUAL "x86")
set(ARCH_SOURCES_DIR i386)
elseif(TARGETDETAILS_ARCH STREQUAL "arm64")
set(ARCH_SOURCES_DIR arm64)
else()
clr_unknown_arch()
endif()

set(UNWINDER_SOURCES ${ARCH_SOURCES_DIR}/unwinder.cpp)
convert_to_absolute_path(UNWINDER_SOURCES ${UNWINDER_SOURCES})
add_library_clr(${TARGETDETAILS_TARGET} STATIC ${UNWINDER_SOURCES})

target_include_directories(${TARGETDETAILS_TARGET} BEFORE PRIVATE ${VM_DIR}/${ARCH_SOURCES_DIR})
target_include_directories(${TARGETDETAILS_TARGET} PRIVATE ${ARCH_SOURCES_DIR})

target_link_libraries(${TARGETDETAILS_TARGET} PRIVATE unwinder_cdac_base)
if (CLR_CMAKE_TARGET_WIN32)
# cDAC unwinders are statically linked into the NativeAOT runtime which is built with
# release version of the statically linked CRT. Therefore we do the same here.
set_property(TARGET ${TARGETDETAILS_TARGET} PROPERTY MSVC_RUNTIME_LIBRARY MultiThreaded)

# _DEBUG is always passed as a parameter if the build is a debug build.
# This causes the debug CRT on MSVC to be used so we need to undefine it.
target_compile_options(${TARGETDETAILS_TARGET} PRIVATE -U_DEBUG)
endif()

# add the install targets
install_clr(TARGETS ${TARGETDETAILS_TARGET} DESTINATIONS ${TARGETDETAILS_DESTINATIONS} COMPONENT cdac)

# Set the target to be built for the specified OS and ARCH
set_target_definitions_to_custom_os_and_arch(TARGET ${TARGETDETAILS_TARGET} OS win ARCH ${TARGETDETAILS_ARCH})

target_compile_definitions(${TARGETDETAILS_TARGET} PRIVATE FEATURE_NO_HOST FEATURE_CDAC_UNWINDER)
endfunction()

if(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64)
create_platform_unwinder(TARGET unwinder_cdac_amd64 ARCH x64 DESTINATIONS cdaclibs)
create_platform_unwinder(TARGET unwinder_cdac_arm64 ARCH arm64 DESTINATIONS cdaclibs)
endif(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64)

if(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_ARM64)
create_platform_unwinder(TARGET unwinder_cdac_arm64 ARCH arm64 DESTINATIONS cdaclibs)
endif(CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_ARM64)

if(NOT CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64)
create_platform_unwinder(TARGET unwinder_cdac_amd64 ARCH x64 DESTINATIONS cdaclibs)
endif(NOT CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_AMD64)

if(NOT CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_ARM64)
create_platform_unwinder(TARGET unwinder_cdac_arm64 ARCH arm64 DESTINATIONS cdaclibs)
endif(NOT CLR_CMAKE_TARGET_WIN32 AND CLR_CMAKE_TARGET_ARCH_ARM64)

156 changes: 32 additions & 124 deletions src/coreclr/unwinder/amd64/unwinder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@

typedef DPTR(M128A) PTR_M128A;

#ifndef FEATURE_CDAC_UNWINDER
//---------------------------------------------------------------------------------------
//
// Read 64 bit unsigned value from the specified address. When the unwinder is built
Expand Down Expand Up @@ -52,29 +51,9 @@ static M128A MemoryRead128(PM128A addr)
{
return *dac_cast<PTR_M128A>((TADDR)addr);
}
#else
// Read 64 bit unsigned value from the specified addres when the unwinder is build
// for the cDAC. This triggers a callback to the cDAC host to read the memory from
// the target process.
static ULONG64 MemoryRead64(PULONG64 addr)
{
ULONG64 value;
t_pCallbacks->readFromTarget((uint64_t)addr, &value, sizeof(value), t_pCallbacks->callbackContext);
return value;
}

// Read 128 bit value from the specified addres when the unwinder is build
// for the cDAC. This triggers a callback to the cDAC host to read the memory from
// the target process.
static M128A MemoryRead128(PM128A addr)
{
M128A value;
t_pCallbacks->readFromTarget((uint64_t)addr, &value, sizeof(value), t_pCallbacks->callbackContext);
return value;
}
#endif // FEATURE_CDAC_UNWINDER
#ifdef DACCESS_COMPILE

#if defined(DACCESS_COMPILE) || defined(FEATURE_CDAC_UNWINDER)
//---------------------------------------------------------------------------------------
//
// The InstructionBuffer class abstracts accessing assembler instructions in the function
Expand All @@ -89,19 +68,6 @@ class InstructionBuffer
UCHAR m_buffer[32];

// Load the instructions from the target process being debugged
#ifdef FEATURE_CDAC_UNWINDER
HRESULT Load()
{
HRESULT hr = t_pCallbacks->readFromTarget(m_address, m_buffer, sizeof(m_buffer), t_pCallbacks->callbackContext);
if (SUCCEEDED(hr))
{
// TODO: Implement breakpoint patching for cDAC
// https://github.com/dotnet/runtime/issues/112273#issue-2838620747
}

return hr;
}
#else // FEATURE_CDAC_UNWINDER
HRESULT Load()
{
HRESULT hr = DacReadAll(TO_TADDR(m_address), m_buffer, sizeof(m_buffer), false);
Expand All @@ -116,7 +82,6 @@ class InstructionBuffer

return hr;
}
#endif // FEATURE_CDAC_UNWINDER

public:

Expand Down Expand Up @@ -161,17 +126,15 @@ class InstructionBuffer
}

// Get the byte at the given index from the current position
// Assert that the index is within the buffer
// Invoke DacError if the index is out of the buffer
UCHAR operator[](int index)
{
int realIndex = m_offset + index;
UNWINDER_ASSERT(realIndex < (int)sizeof(m_buffer));
return m_buffer[realIndex];
}
};
#endif // DACCESS_COMPILE || FEATURE_CDAC_UNWINDER

#ifdef DACCESS_COMPILE
//---------------------------------------------------------------------------------------
//
// Given the target address of an UNWIND_INFO structure, this function retrieves all the memory used for
Expand Down Expand Up @@ -251,57 +214,47 @@ BOOL DacUnwindStackFrame(CONTEXT * pContext, KNONVOLATILE_CONTEXT_POINTERS* pCon
return res;
}

#elif defined(FEATURE_CDAC_UNWINDER)

BOOL amd64Unwind(void* pContext, ReadFromTarget readFromTarget, GetAllocatedBuffer getAllocatedBuffer, GetStackWalkInfo getStackWalkInfo, UnwinderFail unwinderFail, void* callbackContext)
{
CDACCallbacks callbacks { readFromTarget, getAllocatedBuffer, getStackWalkInfo, unwinderFail, callbackContext };
t_pCallbacks = &callbacks;
BOOL res = OOPStackUnwinderAMD64::Unwind((CONTEXT*) pContext);
t_pCallbacks = nullptr;

return res;
}
//---------------------------------------------------------------------------------------
//
// Unwind the given CONTEXT to the caller CONTEXT. The given CONTEXT will be overwritten.
//
// Arguments:
// pContext - in-out parameter storing the specified CONTEXT on entry and the unwound CONTEXT on exit
//
// Return Value:
// TRUE if the unwinding is successful
//

UNWIND_INFO * OOPStackUnwinderAMD64::GetUnwindInfo(TADDR taUnwindInfo)
BOOL OOPStackUnwinderAMD64::Unwind(CONTEXT * pContext)
{
UNWIND_INFO unwindInfo;
if(t_pCallbacks->readFromTarget((uint64_t)taUnwindInfo, &unwindInfo, sizeof(unwindInfo), t_pCallbacks->callbackContext) != S_OK)
{
return NULL;
}
HRESULT hr = E_FAIL;

DWORD cbUnwindInfo = offsetof(UNWIND_INFO, UnwindCode) +
unwindInfo.CountOfUnwindCodes * sizeof(UNWIND_CODE);
ULONG64 uControlPC = (DWORD64)dac_cast<PCODE>(::GetIP(pContext));

// Check if there is a chained unwind info. If so, it has an extra RUNTIME_FUNCTION tagged to the end.
if ((unwindInfo.Flags & UNW_FLAG_CHAININFO) != 0)
// get the module base
ULONG64 uImageBase;
hr = GetModuleBase(uControlPC, &uImageBase);
if (FAILED(hr))
{
// If there is an odd number of UNWIND_CODE, we need to adjust for alignment.
if ((unwindInfo.CountOfUnwindCodes & 1) != 0)
{
cbUnwindInfo += sizeof(UNWIND_CODE);
}
cbUnwindInfo += sizeof(T_RUNTIME_FUNCTION);
return FALSE;
}

// Allocate a buffer for the unwind info from cDAC callback.
// This buffer will be freed by the cDAC host once unwinding is done.
UNWIND_INFO* pUnwindInfo;
if(t_pCallbacks->getAllocatedBuffer(cbUnwindInfo, (void**)&pUnwindInfo, t_pCallbacks->callbackContext) != S_OK)
// get the function entry
IMAGE_RUNTIME_FUNCTION_ENTRY functionEntry;
hr = GetFunctionEntry(uControlPC, &functionEntry, sizeof(functionEntry));
if (FAILED(hr))
{
return NULL;
return FALSE;
}

if(t_pCallbacks->readFromTarget(taUnwindInfo, pUnwindInfo, cbUnwindInfo, t_pCallbacks->callbackContext) != S_OK)
{
return NULL;
}
// call VirtualUnwind() to do the real work
ULONG64 EstablisherFrame;
hr = VirtualUnwind(0, uImageBase, uControlPC, &functionEntry, pContext, NULL, &EstablisherFrame, NULL, NULL);

return pUnwindInfo;
return (hr == S_OK);
}

#else // !DACCESS_COMPILE && !FEATURE_CDAC_UNWINDER
#else // DACCESS_COMPILE

// For unwinding of the jitted code on non-Windows platforms, the Instruction buffer is
// just a plain pointer to the instruction data.
Expand Down Expand Up @@ -385,57 +338,13 @@ PEXCEPTION_ROUTINE RtlVirtualUnwind_Unsafe(
ContextPointers,
&handlerRoutine);

UNWINDER_ASSERT(SUCCEEDED(res));
_ASSERTE(SUCCEEDED(res));

return handlerRoutine;
}

#endif // !DACCESS_COMPILE && !FEATURE_CDAC_UNWINDER

//---------------------------------------------------------------------------------------
//
// Unwind the given CONTEXT to the caller CONTEXT. The given CONTEXT will be overwritten.
//
// Arguments:
// pContext - in-out parameter storing the specified CONTEXT on entry and the unwound CONTEXT on exit
//
// Return Value:
// TRUE if the unwinding is successful
//

BOOL OOPStackUnwinderAMD64::Unwind(CONTEXT * pContext)
{
HRESULT hr = E_FAIL;

ULONG64 uControlPC =
#ifndef FEATURE_CDAC_UNWINDER
(DWORD64)dac_cast<PCODE>(::GetIP(pContext));
#else // FEATURE_CDAC_UNWINDER
pContext->Rip;
#endif // FEATURE_CDAC_UNWINDER

// get the module base
ULONG64 uImageBase;
hr = GetModuleBase(uControlPC, &uImageBase);
if (FAILED(hr))
{
return FALSE;
}

// get the function entry
IMAGE_RUNTIME_FUNCTION_ENTRY functionEntry;
hr = GetFunctionEntry(uControlPC, &functionEntry, sizeof(functionEntry));
if (FAILED(hr))
{
return FALSE;
}

// call VirtualUnwind() to do the real work
ULONG64 EstablisherFrame;
hr = VirtualUnwind(0, uImageBase, uControlPC, &functionEntry, pContext, NULL, &EstablisherFrame, NULL, NULL);

return (hr == S_OK);
}
#endif // DACCESS_COMPILE

//
//
Expand Down Expand Up @@ -1936,4 +1845,3 @@ Return Value:

return Slots;
}

8 changes: 0 additions & 8 deletions src/coreclr/unwinder/amd64/unwinder.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,6 @@

#include "baseunwinder.h"

#ifdef FEATURE_CDAC_UNWINDER
EXTERN_C BOOL amd64Unwind(void* pContext,
ReadFromTarget readFromTarget,
GetAllocatedBuffer getAllocatedBuffer,
GetStackWalkInfo getStackWalkInfo,
UnwinderFail unwinderFail,
void* callbackContext);
#endif // FEATURE_CDAC_UNWINDER

//---------------------------------------------------------------------------------------
//
Expand Down
Loading
Loading