mirror of
https://github.com/HyperDbg/HyperDbg.git
synced 2026-10-19 01:59:00 +00:00
commit
f165ff26bc
11 changed files with 716 additions and 126 deletions
|
|
@ -119,6 +119,8 @@ typedef void * HMODULE;
|
|||
// Win32 wait/event constants (used by the cross-platform sync wrappers)
|
||||
# define INFINITE 0xFFFFFFFF
|
||||
# define WAIT_OBJECT_0 0x00000000
|
||||
# define WAIT_TIMEOUT 0x00000102
|
||||
# define WAIT_FAILED 0xFFFFFFFF
|
||||
|
||||
// Win32 invalid handle sentinel (returned by the cross-platform file/serial wrappers)
|
||||
# define INVALID_HANDLE_VALUE ((HANDLE)(SIZE_T)-1)
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
#if defined(__linux__)
|
||||
# include "../header/PlatformMem.h"
|
||||
# include <linux/io.h> // ioremap / iounmap / virt_to_phys / phys_to_virt
|
||||
#endif // defined(__linux__)
|
||||
|
||||
#if defined(_WIN32) || defined(_WIN64)
|
||||
|
|
@ -269,10 +270,10 @@ PlatformMemFreePool(PVOID BufferAddress)
|
|||
//
|
||||
// -------------------------------------------------------------------------
|
||||
// Cross-platform wrappers for the memory-manager / pool APIs the shared sources
|
||||
// use. Windows forwards to the WDK; the Linux arm is a placeholder stub for now.
|
||||
// use. Windows forwards to the WDK; the Linux arm forwards to the equivalent
|
||||
// kernel API where one exists, and is still a placeholder stub where it does not
|
||||
// (see the remaining TODO(Linux) markers below).
|
||||
// The #ifdef lives INSIDE each wrapper so the call sites stay OS-agnostic.
|
||||
// TODO(Linux): replace each Linux arm with its real equivalent (ioremap,
|
||||
// virt_to_phys, phys_to_virt, ...) as the callers are brought up.
|
||||
// -------------------------------------------------------------------------
|
||||
//
|
||||
|
||||
|
|
@ -282,7 +283,24 @@ PlatformMemMapIoSpace(PHYSICAL_ADDRESS PhysicalAddress, SIZE_T NumberOfBytes, ME
|
|||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return MmMapIoSpace(PhysicalAddress, NumberOfBytes, CacheType);
|
||||
#elif defined(__linux__)
|
||||
return NULL; // TODO(Linux): ioremap()
|
||||
//
|
||||
// MmMapIoSpace takes the cacheability as an argument, so pick the ioremap
|
||||
// flavour that matches rather than defaulting everything to uncached —
|
||||
// plain ioremap() is UC- on x86, which would silently be the wrong
|
||||
// attribute for an MmCached or MmWriteCombined request.
|
||||
//
|
||||
switch (CacheType)
|
||||
{
|
||||
case MmCached:
|
||||
return (PVOID)ioremap_cache((phys_addr_t)PhysicalAddress.QuadPart, NumberOfBytes);
|
||||
|
||||
case MmWriteCombined:
|
||||
return (PVOID)ioremap_wc((phys_addr_t)PhysicalAddress.QuadPart, NumberOfBytes);
|
||||
|
||||
case MmNonCached:
|
||||
default:
|
||||
return (PVOID)ioremap((phys_addr_t)PhysicalAddress.QuadPart, NumberOfBytes);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +320,13 @@ PlatformMemUnmapIoSpace(PVOID BaseAddress, SIZE_T NumberOfBytes)
|
|||
#if defined(_WIN32) || defined(_WIN64)
|
||||
MmUnmapIoSpace(BaseAddress, NumberOfBytes);
|
||||
#elif defined(__linux__)
|
||||
// no-op // TODO(Linux): iounmap()
|
||||
//
|
||||
// iounmap() recovers the length from the vm_area it created, so the
|
||||
// NumberOfBytes the WDK needs is unused here.
|
||||
//
|
||||
UNREFERENCED_PARAMETER(NumberOfBytes);
|
||||
|
||||
iounmap((void __iomem *)BaseAddress);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -312,9 +336,16 @@ PlatformMemGetPhysicalAddress(PVOID BaseAddress)
|
|||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return MmGetPhysicalAddress(BaseAddress);
|
||||
#elif defined(__linux__)
|
||||
//
|
||||
// virt_to_phys() is valid for direct-map (kmalloc/kzalloc) addresses, which
|
||||
// is what every Platform* allocator in this file hands out. It is NOT valid
|
||||
// for vmalloc or ioremap addresses — MmGetPhysicalAddress resolves those on
|
||||
// Windows, so a caller that starts passing one will need vmalloc_to_pfn()
|
||||
// here instead.
|
||||
//
|
||||
PHYSICAL_ADDRESS Pa;
|
||||
Pa.QuadPart = 0;
|
||||
return Pa; // TODO(Linux): virt_to_phys()
|
||||
Pa.QuadPart = (LONGLONG)virt_to_phys(BaseAddress);
|
||||
return Pa;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -324,7 +355,7 @@ PlatformMemGetVirtualForPhysical(PHYSICAL_ADDRESS PhysicalAddress)
|
|||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return MmGetVirtualForPhysical(PhysicalAddress);
|
||||
#elif defined(__linux__)
|
||||
return NULL; // TODO(Linux): phys_to_virt()
|
||||
return phys_to_virt((phys_addr_t)PhysicalAddress.QuadPart);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -364,7 +395,11 @@ PlatformMemFreeContiguousMemory(PVOID BaseAddress)
|
|||
#if defined(_WIN32) || defined(_WIN64)
|
||||
MmFreeContiguousMemory(BaseAddress);
|
||||
#elif defined(__linux__)
|
||||
// no-op // TODO(Linux): pair with the contiguous allocator used by callers
|
||||
//
|
||||
// Pairs with PlatformMemAllocateContiguousZeroedMemory above, which backs
|
||||
// the contiguous allocation with kzalloc().
|
||||
//
|
||||
kfree(BaseAddress);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -387,7 +422,11 @@ PlatformMemFreePoolUntagged(PVOID P)
|
|||
#if defined(_WIN32) || defined(_WIN64)
|
||||
ExFreePool(P);
|
||||
#elif defined(__linux__)
|
||||
// no-op // TODO(Linux): kfree(), paired with the matching allocation stub
|
||||
//
|
||||
// Pairs with the kmalloc/kzalloc-backed PlatformMemAllocate* wrappers above
|
||||
// (kfree tolerates NULL, matching the callers' expectations).
|
||||
//
|
||||
kfree(P);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#if defined(__linux__)
|
||||
# include "../header/PlatformProcess.h"
|
||||
# include <linux/sched/task.h> // init_task
|
||||
|
||||
//
|
||||
// Backing token for the NT global PsProcessType declared in PlatformProcess.h —
|
||||
|
|
@ -182,8 +183,11 @@ PlatformProcessGetCurrentThreadTeb(VOID)
|
|||
//
|
||||
|
||||
/**
|
||||
* @brief The System process' EPROCESS. Windows: PsInitialSystemProcess. Linux:
|
||||
* NULL stub. TODO(Linux): a PEPROCESS shim over &init_task.
|
||||
* @brief The System process' EPROCESS. Windows: PsInitialSystemProcess.
|
||||
* Linux: init_task, the swapper/0 task every other task descends from,
|
||||
* which is the closest counterpart to the NT System process. PEPROCESS
|
||||
* is opaque and backed by task_struct on Linux (see WdkTypes.h), so the
|
||||
* address is handed straight back.
|
||||
*/
|
||||
PEPROCESS
|
||||
PlatformProcessGetInitialSystemProcess(VOID)
|
||||
|
|
@ -191,7 +195,7 @@ PlatformProcessGetInitialSystemProcess(VOID)
|
|||
#if defined(_WIN32) || defined(_WIN64)
|
||||
return PsInitialSystemProcess;
|
||||
#elif defined(__linux__)
|
||||
return NULL; // TODO(Linux)
|
||||
return (PEPROCESS)&init_task;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -127,11 +127,10 @@ PlatformTimeConvertToTimeFields(PLARGE_INTEGER Time, PTIME_FIELDS TimeFields)
|
|||
|
||||
/**
|
||||
* @brief Read the performance counter (and, optionally, its frequency).
|
||||
* @details Windows: KeQueryPerformanceCounter(). Linux arm is a stub that
|
||||
* reports 0 for both counter and frequency.
|
||||
*
|
||||
* TODO(Linux): back with ktime_get_ns()/tsc so busy-wait loops using this
|
||||
* actually elapse.
|
||||
* @details Windows: KeQueryPerformanceCounter(). Linux: ktime_get_ns(), whose
|
||||
* tick is one nanosecond — so the reported frequency is fixed at
|
||||
* NSEC_PER_SEC. Both platforms only promise a monotonic counter plus
|
||||
* the frequency needed to convert it, which is all the callers use.
|
||||
*/
|
||||
LARGE_INTEGER
|
||||
PlatformTimeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFrequency)
|
||||
|
|
@ -143,12 +142,12 @@ PlatformTimeQueryPerformanceCounter(PLARGE_INTEGER PerformanceFrequency)
|
|||
#elif defined(__linux__)
|
||||
|
||||
LARGE_INTEGER Counter;
|
||||
Counter.QuadPart = 0;
|
||||
Counter.QuadPart = (LONGLONG)ktime_get_ns();
|
||||
|
||||
if (PerformanceFrequency != NULL)
|
||||
PerformanceFrequency->QuadPart = 0;
|
||||
PerformanceFrequency->QuadPart = NSEC_PER_SEC;
|
||||
|
||||
return Counter; // TODO(Linux)
|
||||
return Counter;
|
||||
|
||||
#else
|
||||
|
||||
|
|
|
|||
|
|
@ -22,7 +22,128 @@
|
|||
# include <strings.h>
|
||||
# include <signal.h>
|
||||
# include <dlfcn.h>
|
||||
# include <time.h> // clock_gettime / CLOCK_MONOTONIC (PlatformQueryPerformanceCounter)
|
||||
# include <sys/mman.h> // munmap (PlatformUnmapFile)
|
||||
# include <time.h> // clock_gettime / CLOCK_MONOTONIC (PlatformQueryPerformanceCounter)
|
||||
# include <pthread.h> // events and threads
|
||||
#endif // defined(__linux__)
|
||||
|
||||
#if defined(__linux__)
|
||||
|
||||
//
|
||||
// Events and threads are heap objects behind the HANDLE. Other HANDLEs on Linux
|
||||
// are file descriptors, FILE pointers, etc., so every object created here is
|
||||
// kept in a registry; PlatformCloseHandle and the event routines only touch a
|
||||
// HANDLE after finding it there
|
||||
//
|
||||
typedef enum _PLATFORM_HANDLE_TYPE
|
||||
{
|
||||
PLATFORM_HANDLE_EVENT,
|
||||
PLATFORM_HANDLE_THREAD,
|
||||
} PLATFORM_HANDLE_TYPE;
|
||||
|
||||
typedef struct _PLATFORM_HANDLE_OBJECT
|
||||
{
|
||||
struct _PLATFORM_HANDLE_OBJECT * Next;
|
||||
PLATFORM_HANDLE_TYPE Type;
|
||||
|
||||
//
|
||||
// PLATFORM_HANDLE_EVENT
|
||||
//
|
||||
pthread_mutex_t Lock;
|
||||
pthread_cond_t Cond;
|
||||
BOOLEAN ManualReset;
|
||||
BOOLEAN Signaled;
|
||||
|
||||
//
|
||||
// PLATFORM_HANDLE_THREAD
|
||||
//
|
||||
pthread_t Thread;
|
||||
|
||||
} PLATFORM_HANDLE_OBJECT, *PPLATFORM_HANDLE_OBJECT;
|
||||
|
||||
static pthread_mutex_t g_PlatformHandleRegistryLock = PTHREAD_MUTEX_INITIALIZER;
|
||||
static PPLATFORM_HANDLE_OBJECT g_PlatformHandleRegistry = NULL;
|
||||
|
||||
/**
|
||||
* @brief Add an object to the handle registry
|
||||
*/
|
||||
static VOID
|
||||
PlatformHandleRegister(PPLATFORM_HANDLE_OBJECT Object)
|
||||
{
|
||||
pthread_mutex_lock(&g_PlatformHandleRegistryLock);
|
||||
Object->Next = g_PlatformHandleRegistry;
|
||||
g_PlatformHandleRegistry = Object;
|
||||
pthread_mutex_unlock(&g_PlatformHandleRegistryLock);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find an object in the handle registry
|
||||
*
|
||||
* @param Handle
|
||||
* @param Remove TRUE to also unlink it from the registry
|
||||
*
|
||||
* @return PPLATFORM_HANDLE_OBJECT the object, or NULL if Handle isn't one
|
||||
*/
|
||||
static PPLATFORM_HANDLE_OBJECT
|
||||
PlatformHandleLookup(HANDLE Handle, BOOLEAN Remove)
|
||||
{
|
||||
PPLATFORM_HANDLE_OBJECT * Link;
|
||||
PPLATFORM_HANDLE_OBJECT Found = NULL;
|
||||
|
||||
pthread_mutex_lock(&g_PlatformHandleRegistryLock);
|
||||
|
||||
for (Link = &g_PlatformHandleRegistry; *Link != NULL; Link = &(*Link)->Next)
|
||||
{
|
||||
if ((HANDLE)*Link == Handle)
|
||||
{
|
||||
Found = *Link;
|
||||
|
||||
if (Remove)
|
||||
*Link = Found->Next;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&g_PlatformHandleRegistryLock);
|
||||
|
||||
return Found;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Find an event in the handle registry
|
||||
*/
|
||||
static PPLATFORM_HANDLE_OBJECT
|
||||
PlatformEventLookup(HANDLE Handle)
|
||||
{
|
||||
PPLATFORM_HANDLE_OBJECT Object = PlatformHandleLookup(Handle, FALSE);
|
||||
|
||||
return (Object != NULL && Object->Type == PLATFORM_HANDLE_EVENT) ? Object : NULL;
|
||||
}
|
||||
|
||||
//
|
||||
// Start parameters of a thread, freed by the thread itself
|
||||
//
|
||||
typedef struct _PLATFORM_THREAD_START
|
||||
{
|
||||
PLATFORM_THREAD_ROUTINE Routine;
|
||||
PVOID Param;
|
||||
} PLATFORM_THREAD_START, *PPLATFORM_THREAD_START;
|
||||
|
||||
/**
|
||||
* @brief pthread entry point that calls the Win32-style thread routine
|
||||
*/
|
||||
static void *
|
||||
PlatformThreadTrampoline(void * Arg)
|
||||
{
|
||||
PLATFORM_THREAD_START Start = *(PPLATFORM_THREAD_START)Arg;
|
||||
|
||||
free(Arg);
|
||||
Start.Routine(Start.Param);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif // defined(__linux__)
|
||||
|
||||
/**
|
||||
|
|
@ -536,14 +657,29 @@ PlatformCreateEvent(BOOLEAN ManualReset, BOOLEAN InitialState)
|
|||
#if defined(_WIN32)
|
||||
return CreateEvent(NULL, ManualReset, InitialState, NULL);
|
||||
#elif defined(__linux__)
|
||||
PPLATFORM_HANDLE_OBJECT Event;
|
||||
pthread_condattr_t CondAttr;
|
||||
|
||||
Event = (PPLATFORM_HANDLE_OBJECT)calloc(1, sizeof(PLATFORM_HANDLE_OBJECT));
|
||||
if (Event == NULL)
|
||||
return NULL;
|
||||
|
||||
Event->Type = PLATFORM_HANDLE_EVENT;
|
||||
Event->ManualReset = ManualReset;
|
||||
Event->Signaled = InitialState;
|
||||
|
||||
//
|
||||
// TODO: back this with a pthread mutex+cond (or eventfd) when the Linux
|
||||
// kernel-debugger transport is implemented. For now return a dummy
|
||||
// non-NULL handle so existing NULL-checks treat creation as success.
|
||||
// Timed waits use the monotonic clock so wall-clock changes don't affect them
|
||||
//
|
||||
(void)ManualReset;
|
||||
(void)InitialState;
|
||||
return (HANDLE)(uintptr_t)1;
|
||||
pthread_condattr_init(&CondAttr);
|
||||
pthread_condattr_setclock(&CondAttr, CLOCK_MONOTONIC);
|
||||
pthread_cond_init(&Event->Cond, &CondAttr);
|
||||
pthread_condattr_destroy(&CondAttr);
|
||||
pthread_mutex_init(&Event->Lock, NULL);
|
||||
|
||||
PlatformHandleRegister(Event);
|
||||
|
||||
return (HANDLE)Event;
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
|
@ -558,7 +694,24 @@ PlatformSetEvent(HANDLE EventHandle)
|
|||
#if defined(_WIN32)
|
||||
return (BOOLEAN)SetEvent(EventHandle);
|
||||
#elif defined(__linux__)
|
||||
(void)EventHandle; // TODO: signal the underlying cond/eventfd
|
||||
PPLATFORM_HANDLE_OBJECT Event = PlatformEventLookup(EventHandle);
|
||||
|
||||
if (Event == NULL)
|
||||
return FALSE;
|
||||
|
||||
pthread_mutex_lock(&Event->Lock);
|
||||
Event->Signaled = TRUE;
|
||||
|
||||
//
|
||||
// A manual-reset event releases every waiter, an auto-reset event only one
|
||||
//
|
||||
if (Event->ManualReset)
|
||||
pthread_cond_broadcast(&Event->Cond);
|
||||
else
|
||||
pthread_cond_signal(&Event->Cond);
|
||||
|
||||
pthread_mutex_unlock(&Event->Lock);
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
|
|
@ -574,7 +727,15 @@ PlatformResetEvent(HANDLE EventHandle)
|
|||
#if defined(_WIN32)
|
||||
return (BOOLEAN)ResetEvent(EventHandle);
|
||||
#elif defined(__linux__)
|
||||
(void)EventHandle; // TODO: clear the underlying cond/eventfd
|
||||
PPLATFORM_HANDLE_OBJECT Event = PlatformEventLookup(EventHandle);
|
||||
|
||||
if (Event == NULL)
|
||||
return FALSE;
|
||||
|
||||
pthread_mutex_lock(&Event->Lock);
|
||||
Event->Signaled = FALSE;
|
||||
pthread_mutex_unlock(&Event->Lock);
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
|
|
@ -584,7 +745,9 @@ PlatformResetEvent(HANDLE EventHandle)
|
|||
/**
|
||||
* @brief Platform independent wrapper for WaitForSingleObject
|
||||
*
|
||||
* @return 0 (WAIT_OBJECT_0) on success
|
||||
* @details On Linux only events can be waited on
|
||||
*
|
||||
* @return WAIT_OBJECT_0 when signaled, WAIT_TIMEOUT, or WAIT_FAILED
|
||||
*/
|
||||
DWORD
|
||||
PlatformWaitForSingleObject(HANDLE Handle, DWORD TimeoutMilliseconds)
|
||||
|
|
@ -592,13 +755,51 @@ PlatformWaitForSingleObject(HANDLE Handle, DWORD TimeoutMilliseconds)
|
|||
#if defined(_WIN32)
|
||||
return WaitForSingleObject(Handle, TimeoutMilliseconds);
|
||||
#elif defined(__linux__)
|
||||
PPLATFORM_HANDLE_OBJECT Event = PlatformEventLookup(Handle);
|
||||
struct timespec Deadline;
|
||||
int Ret = 0;
|
||||
|
||||
if (Event == NULL)
|
||||
return WAIT_FAILED;
|
||||
|
||||
if (TimeoutMilliseconds != INFINITE)
|
||||
{
|
||||
clock_gettime(CLOCK_MONOTONIC, &Deadline);
|
||||
Deadline.tv_sec += TimeoutMilliseconds / 1000;
|
||||
Deadline.tv_nsec += (long)(TimeoutMilliseconds % 1000) * 1000000L;
|
||||
|
||||
if (Deadline.tv_nsec >= 1000000000L)
|
||||
{
|
||||
Deadline.tv_sec += 1;
|
||||
Deadline.tv_nsec -= 1000000000L;
|
||||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&Event->Lock);
|
||||
|
||||
while (!Event->Signaled && Ret != ETIMEDOUT)
|
||||
{
|
||||
if (TimeoutMilliseconds == INFINITE)
|
||||
pthread_cond_wait(&Event->Cond, &Event->Lock);
|
||||
else
|
||||
Ret = pthread_cond_timedwait(&Event->Cond, &Event->Lock, &Deadline);
|
||||
}
|
||||
|
||||
if (!Event->Signaled)
|
||||
{
|
||||
pthread_mutex_unlock(&Event->Lock);
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
//
|
||||
// TODO: wait on the underlying cond/eventfd. For now return immediately as
|
||||
// success — no real transport exists yet to wait on.
|
||||
// An auto-reset event is consumed by the waiter it releases
|
||||
//
|
||||
(void)Handle;
|
||||
(void)TimeoutMilliseconds;
|
||||
return 0;
|
||||
if (!Event->ManualReset)
|
||||
Event->Signaled = FALSE;
|
||||
|
||||
pthread_mutex_unlock(&Event->Lock);
|
||||
|
||||
return WAIT_OBJECT_0;
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
|
@ -613,7 +814,30 @@ PlatformCloseHandle(HANDLE Handle)
|
|||
#if defined(_WIN32)
|
||||
return (BOOLEAN)CloseHandle(Handle);
|
||||
#elif defined(__linux__)
|
||||
(void)Handle; // TODO: free the underlying cond/eventfd or close the fd
|
||||
PPLATFORM_HANDLE_OBJECT Object = PlatformHandleLookup(Handle, TRUE);
|
||||
|
||||
//
|
||||
// Not an event or a thread, nothing is released for other handle kinds yet
|
||||
//
|
||||
if (Object == NULL)
|
||||
return TRUE;
|
||||
|
||||
if (Object->Type == PLATFORM_HANDLE_EVENT)
|
||||
{
|
||||
pthread_cond_destroy(&Object->Cond);
|
||||
pthread_mutex_destroy(&Object->Lock);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Like CloseHandle on a Win32 thread, the thread keeps running, it
|
||||
// just releases its resources itself once it exits
|
||||
//
|
||||
pthread_detach(Object->Thread);
|
||||
}
|
||||
|
||||
free(Object);
|
||||
|
||||
return TRUE;
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
|
|
@ -633,14 +857,33 @@ PlatformCreateThread(PLATFORM_THREAD_ROUTINE Routine, PVOID Param)
|
|||
#if defined(_WIN32)
|
||||
return CreateThread(NULL, 0, Routine, Param, 0, NULL);
|
||||
#elif defined(__linux__)
|
||||
//
|
||||
// TODO: back this with pthread_create when the Linux kernel-debugger
|
||||
// transport is implemented. Returning NULL leaves the listening
|
||||
// thread unstarted, which is all the callers check for.
|
||||
//
|
||||
(void)Routine;
|
||||
(void)Param;
|
||||
return NULL;
|
||||
PPLATFORM_HANDLE_OBJECT Object;
|
||||
PPLATFORM_THREAD_START Start;
|
||||
|
||||
Object = (PPLATFORM_HANDLE_OBJECT)calloc(1, sizeof(PLATFORM_HANDLE_OBJECT));
|
||||
Start = (PPLATFORM_THREAD_START)malloc(sizeof(PLATFORM_THREAD_START));
|
||||
|
||||
if (Object == NULL || Start == NULL)
|
||||
{
|
||||
free(Object);
|
||||
free(Start);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Object->Type = PLATFORM_HANDLE_THREAD;
|
||||
Start->Routine = Routine;
|
||||
Start->Param = Param;
|
||||
|
||||
if (pthread_create(&Object->Thread, NULL, PlatformThreadTrampoline, Start) != 0)
|
||||
{
|
||||
free(Object);
|
||||
free(Start);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PlatformHandleRegister(Object);
|
||||
|
||||
return (HANDLE)Object;
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
|
@ -665,9 +908,9 @@ PlatformCreateThread(PLATFORM_THREAD_ROUTINE Routine, PVOID Param)
|
|||
* (teardown before thread-kill, and SD_SEND -> SHUT_RDWR to wake a
|
||||
* blocked reader), which the port's no-logic-changes rule defers.
|
||||
*
|
||||
* Returning TRUE is consistent with PlatformCreateThread, which
|
||||
* returns NULL on Linux — the thread is never started there, so
|
||||
* there is nothing to terminate yet.
|
||||
* Until then this is a no-op that returns TRUE: the thread keeps
|
||||
* running and leaves its loop on the next receive error, and
|
||||
* PlatformCloseHandle detaches it so it is cleaned up on exit.
|
||||
*
|
||||
* @param Thread handle to the thread to terminate
|
||||
* @param ExitCode exit code for the terminated thread
|
||||
|
|
@ -944,19 +1187,19 @@ PlatformReadFileAtOffset(HANDLE FileHandle, UINT64 Offset, VOID * Buffer, DWORD
|
|||
return (BOOLEAN)ReadFile(FileHandle, Buffer, NumberOfBytes, BytesRead, NULL);
|
||||
#elif defined(__linux__)
|
||||
//
|
||||
// TODO (linux): pread((int)(intptr_t)FileHandle, Buffer, NumberOfBytes, Offset)
|
||||
// once PlatformMapFileReadOnly wraps a real fd. Unreached today
|
||||
// because the map returns NULL on Linux, so callers bail first.
|
||||
// pread() is the positioned read: it takes the offset directly instead of
|
||||
// needing the SetFilePointerEx seek the Windows arm does first. Unreached
|
||||
// today because PlatformMapFileReadOnly still returns NULL on Linux (it is
|
||||
// blocked on the wide-char path), so callers bail before getting here.
|
||||
//
|
||||
(void)FileHandle;
|
||||
(void)Offset;
|
||||
(void)Buffer;
|
||||
(void)NumberOfBytes;
|
||||
ssize_t ReadResult = pread((int)(intptr_t)FileHandle, Buffer, NumberOfBytes, (off_t)Offset);
|
||||
|
||||
if (BytesRead != NULL)
|
||||
{
|
||||
*BytesRead = 0;
|
||||
*BytesRead = (ReadResult < 0) ? 0 : (DWORD)ReadResult;
|
||||
}
|
||||
return FALSE;
|
||||
|
||||
return (BOOLEAN)(ReadResult >= 0);
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
|
@ -984,13 +1227,18 @@ PlatformUnmapFile(VOID * BaseAddress, SIZE_T FileSize, HANDLE FileHandle)
|
|||
}
|
||||
#elif defined(__linux__)
|
||||
//
|
||||
// TODO (linux): munmap(BaseAddress, FileSize) and close the fd behind
|
||||
// FileHandle once PlatformMapFileReadOnly is implemented.
|
||||
// No-op for now since the map always returns NULL.
|
||||
// munmap() needs the length, which is why the size is passed back in here;
|
||||
// the fd handed back by PlatformMapFileReadOnly is closed alongside it.
|
||||
// Unreached today since the map still returns NULL on Linux.
|
||||
//
|
||||
(void)BaseAddress;
|
||||
(void)FileSize;
|
||||
(void)FileHandle;
|
||||
if (BaseAddress != NULL)
|
||||
{
|
||||
munmap(BaseAddress, FileSize);
|
||||
}
|
||||
if (FileHandle != INVALID_HANDLE_VALUE)
|
||||
{
|
||||
close((int)(intptr_t)FileHandle);
|
||||
}
|
||||
#else
|
||||
# error "Unsupported platform"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
* @details See platform-serial.h. The Windows branch wraps the Win32 serial primitives
|
||||
* (CreateFile / Comm* / overlapped ReadFile/WriteFile) and owns the per-direction
|
||||
* OVERLAPPED state internally so the protocol layer never sees it. The Linux
|
||||
* branch is currently stubbed (returns FALSE/NULL) and is the home where the
|
||||
* termios-based implementation over /dev/tty* will live.
|
||||
* branch uses blocking file descriptors: termios for tty devices, and a
|
||||
* UNIX domain socket for VMware's serial-port socket on a Linux host.
|
||||
*
|
||||
* @version 0.20
|
||||
* @date 2026-06-08
|
||||
|
|
@ -28,57 +28,247 @@
|
|||
|
||||
#elif defined(__linux__)
|
||||
|
||||
//
|
||||
// TODO: implement the serial transport on Linux using termios over /dev/tty*:
|
||||
// - PlatformSerialOpen -> open(PortName, O_RDWR | O_NOCTTY)
|
||||
// - PlatformSerialConfigure -> tcgetattr/cfsetspeed/tcsetattr (raw, 8-N-1)
|
||||
// - PlatformSerialReadByte -> read() (with VTIME/VMIN or poll() for the timeout role)
|
||||
// - PlatformSerialWrite -> write()
|
||||
// - PlatformSerialClose -> close()
|
||||
// Named-pipe transport would map onto a UNIX domain socket / FIFO.
|
||||
//
|
||||
// Until then these return failure so the kernel-debugger connection simply
|
||||
// reports "not supported on Linux yet" rather than crashing.
|
||||
//
|
||||
# include <errno.h>
|
||||
# include <fcntl.h>
|
||||
# include <stdint.h>
|
||||
# include <string.h>
|
||||
# include <termios.h>
|
||||
# include <unistd.h>
|
||||
# include <sys/socket.h>
|
||||
# include <sys/stat.h>
|
||||
# include <sys/un.h>
|
||||
|
||||
//
|
||||
// The transport handle wraps a file descriptor. fd 0 is a valid descriptor but
|
||||
// a NULL HANDLE means failure, so the handle stores fd + 1
|
||||
//
|
||||
# define SERIAL_FD_TO_HANDLE(Fd) ((HANDLE)(intptr_t)((Fd) + 1))
|
||||
# define SERIAL_HANDLE_TO_FD(H) ((int)(intptr_t)(H) - 1)
|
||||
|
||||
/**
|
||||
* @brief Open the serial transport
|
||||
* @details PortName is either a tty device (/dev/ttyS*, /dev/ttyUSB*, a pty)
|
||||
* or a UNIX domain socket, which is how VMware exposes a VM's serial
|
||||
* port ("named pipe") on a Linux host
|
||||
*
|
||||
* @param PortName path of the device or socket
|
||||
* @param Role debugger or debuggee side (both use blocking I/O on Linux)
|
||||
*
|
||||
* @return HANDLE transport handle, or NULL on failure (errno is set)
|
||||
*/
|
||||
HANDLE
|
||||
PlatformSerialOpen(const char * PortName, PLATFORM_SERIAL_IO_ROLE Role)
|
||||
{
|
||||
(void)PortName;
|
||||
struct stat St;
|
||||
int Fd;
|
||||
|
||||
(void)Role;
|
||||
return NULL;
|
||||
|
||||
if (stat(PortName, &St) != 0)
|
||||
return NULL;
|
||||
|
||||
if (S_ISSOCK(St.st_mode))
|
||||
{
|
||||
struct sockaddr_un Addr = {0};
|
||||
|
||||
if (strlen(PortName) >= sizeof(Addr.sun_path))
|
||||
{
|
||||
errno = ENAMETOOLONG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||
if (Fd < 0)
|
||||
return NULL;
|
||||
|
||||
Addr.sun_family = AF_UNIX;
|
||||
strcpy(Addr.sun_path, PortName);
|
||||
|
||||
if (connect(Fd, (struct sockaddr *)&Addr, sizeof(Addr)) != 0)
|
||||
{
|
||||
int SavedErrno = errno;
|
||||
close(Fd);
|
||||
errno = SavedErrno;
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Fd = open(PortName, O_RDWR | O_NOCTTY | O_CLOEXEC);
|
||||
if (Fd < 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return SERIAL_FD_TO_HANDLE(Fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert a baud rate to its termios speed constant
|
||||
*
|
||||
* @param BaudRate
|
||||
*
|
||||
* @return speed_t the speed, or 0 if termios has no constant for it
|
||||
*/
|
||||
static speed_t
|
||||
PlatformSerialBaudToSpeed(DWORD BaudRate)
|
||||
{
|
||||
switch (BaudRate)
|
||||
{
|
||||
case 110:
|
||||
return B110;
|
||||
case 300:
|
||||
return B300;
|
||||
case 600:
|
||||
return B600;
|
||||
case 1200:
|
||||
return B1200;
|
||||
case 2400:
|
||||
return B2400;
|
||||
case 4800:
|
||||
return B4800;
|
||||
case 9600:
|
||||
return B9600;
|
||||
case 19200:
|
||||
return B19200;
|
||||
case 38400:
|
||||
return B38400;
|
||||
case 57600:
|
||||
return B57600;
|
||||
case 115200:
|
||||
return B115200;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Configure baud rate and raw 8-N-1 framing
|
||||
* @details A socket has no line settings, so non-tty handles are accepted as-is
|
||||
*
|
||||
* @param Handle
|
||||
* @param BaudRate
|
||||
*
|
||||
* @return BOOLEAN
|
||||
*/
|
||||
BOOLEAN
|
||||
PlatformSerialConfigure(HANDLE Handle, DWORD BaudRate)
|
||||
{
|
||||
(void)Handle;
|
||||
(void)BaudRate;
|
||||
return FALSE;
|
||||
int Fd = SERIAL_HANDLE_TO_FD(Handle);
|
||||
struct termios Tio;
|
||||
speed_t Speed;
|
||||
|
||||
if (!isatty(Fd))
|
||||
return TRUE;
|
||||
|
||||
Speed = PlatformSerialBaudToSpeed(BaudRate);
|
||||
if (Speed == 0)
|
||||
{
|
||||
errno = EINVAL;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (tcgetattr(Fd, &Tio) != 0)
|
||||
return FALSE;
|
||||
|
||||
cfmakeraw(&Tio); // 8 data bits, no parity, no echo, no line processing
|
||||
Tio.c_cflag &= ~(CSTOPB | CRTSCTS);
|
||||
Tio.c_cflag |= CLOCAL | CREAD;
|
||||
Tio.c_cc[VMIN] = 1;
|
||||
Tio.c_cc[VTIME] = 0;
|
||||
|
||||
if (cfsetispeed(&Tio, Speed) != 0 || cfsetospeed(&Tio, Speed) != 0)
|
||||
return FALSE;
|
||||
|
||||
if (tcsetattr(Fd, TCSANOW, &Tio) != 0)
|
||||
return FALSE;
|
||||
|
||||
tcflush(Fd, TCIOFLUSH);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a single byte (blocking)
|
||||
*
|
||||
* @param Handle
|
||||
* @param OutByte
|
||||
* @param BytesRead
|
||||
* @param Role
|
||||
*
|
||||
* @return BOOLEAN TRUE if a byte was read
|
||||
*/
|
||||
BOOLEAN
|
||||
PlatformSerialReadByte(HANDLE Handle,
|
||||
CHAR * OutByte,
|
||||
DWORD * BytesRead,
|
||||
PLATFORM_SERIAL_IO_ROLE Role)
|
||||
{
|
||||
(void)Handle;
|
||||
(void)OutByte;
|
||||
ssize_t Ret;
|
||||
|
||||
(void)Role;
|
||||
|
||||
do
|
||||
{
|
||||
Ret = read(SERIAL_HANDLE_TO_FD(Handle), OutByte, 1);
|
||||
} while (Ret < 0 && errno == EINTR);
|
||||
|
||||
if (BytesRead)
|
||||
*BytesRead = 0;
|
||||
return FALSE;
|
||||
*BytesRead = Ret > 0 ? (DWORD)Ret : 0;
|
||||
|
||||
return Ret == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Write a buffer (blocking, until everything is sent)
|
||||
*
|
||||
* @param Handle
|
||||
* @param Buffer
|
||||
* @param Length
|
||||
* @param Synchronous unused, all writes are blocking on Linux
|
||||
*
|
||||
* @return BOOLEAN
|
||||
*/
|
||||
BOOLEAN
|
||||
PlatformSerialWrite(HANDLE Handle, const void * Buffer, UINT32 Length, BOOLEAN Synchronous)
|
||||
{
|
||||
(void)Handle;
|
||||
(void)Buffer;
|
||||
(void)Length;
|
||||
const char * Ptr = (const char *)Buffer;
|
||||
ssize_t Ret;
|
||||
|
||||
(void)Synchronous;
|
||||
return FALSE;
|
||||
|
||||
while (Length > 0)
|
||||
{
|
||||
Ret = write(SERIAL_HANDLE_TO_FD(Handle), Ptr, Length);
|
||||
|
||||
if (Ret < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Ptr += Ret;
|
||||
Length -= (UINT32)Ret;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close the transport handle
|
||||
*
|
||||
* @param Handle
|
||||
*
|
||||
* @return BOOLEAN
|
||||
*/
|
||||
BOOLEAN
|
||||
PlatformSerialClose(HANDLE Handle)
|
||||
{
|
||||
if (Handle == NULL)
|
||||
return FALSE;
|
||||
|
||||
return close(SERIAL_HANDLE_TO_FD(Handle)) == 0;
|
||||
}
|
||||
|
||||
#else
|
||||
|
|
|
|||
|
|
@ -191,13 +191,23 @@ set(SourceFiles
|
|||
include_directories(
|
||||
"../dependencies/phnt"
|
||||
"../dependencies"
|
||||
"../dependencies/zydis/dependencies/zycore/include"
|
||||
"../include"
|
||||
"../dependencies/zydis/include"
|
||||
"."
|
||||
"../script-eval"
|
||||
"../dependencies/keystone/include"
|
||||
)
|
||||
|
||||
#
|
||||
# On Linux libhyperdbg links the system Zydis/Zycore (see the top-level
|
||||
# CMakeLists.txt), so it must also use the system headers; the bundled headers
|
||||
# are a different Zydis version and fail the ZYDIS_VERSION check at runtime
|
||||
#
|
||||
if(NOT UNIX)
|
||||
include_directories(
|
||||
"../dependencies/zydis/dependencies/zycore/include"
|
||||
"../dependencies/zydis/include"
|
||||
)
|
||||
endif()
|
||||
set_source_files_properties(
|
||||
"../include/platform/user/code/platform-intrinsics.c"
|
||||
"../include/platform/user/code/platform-lib-calls.c"
|
||||
|
|
|
|||
|
|
@ -78,6 +78,17 @@ CommandDebugCheckComPort(const CHAR * ComPort, UINT32 * Port)
|
|||
*Port = COM4_PORT;
|
||||
return TRUE;
|
||||
}
|
||||
#if defined(__linux__)
|
||||
else if (ComPort[0] == '/')
|
||||
{
|
||||
//
|
||||
// A device or socket path (e.g., /dev/ttyS0, or the VMware serial
|
||||
// port socket), only usable by the debugger, it has no I/O port
|
||||
//
|
||||
*Port = 0;
|
||||
return TRUE;
|
||||
}
|
||||
#endif // defined(__linux__)
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
|
@ -186,10 +197,10 @@ HyperDbgDebugCurrentDeviceUsingComPort(const CHAR * PortName, DWORD Baudrate)
|
|||
//
|
||||
// check if com port address is valid or not
|
||||
//
|
||||
if (!CommandDebugCheckComPort(PortName, &Port))
|
||||
if (!CommandDebugCheckComPort(PortName, &Port) || Port == 0)
|
||||
{
|
||||
//
|
||||
// com port is invalid
|
||||
// com port is invalid (a device path has no I/O port for the debuggee)
|
||||
//
|
||||
return FALSE;
|
||||
}
|
||||
|
|
@ -412,6 +423,18 @@ CommandDebug(vector<CommandToken> CommandTokens, string Command)
|
|||
return;
|
||||
}
|
||||
|
||||
#if defined(__linux__)
|
||||
//
|
||||
// A device or socket path has no I/O port, so it can't be used by the debuggee
|
||||
//
|
||||
if (IsSerial && IsPrepare && Port == 0)
|
||||
{
|
||||
ShowMessages("err, a device path can only be used with 'remote', use COM1-COM4 with 'prepare'\n\n");
|
||||
CommandDebugHelp();
|
||||
return;
|
||||
}
|
||||
#endif // defined(__linux__)
|
||||
|
||||
//
|
||||
// Perform connecting to the remote machine or prepare to send
|
||||
//
|
||||
|
|
|
|||
|
|
@ -2432,14 +2432,34 @@ KdSendResponseOfThePingPacket()
|
|||
//
|
||||
// ShowMessages("the ping request is received\n");
|
||||
|
||||
CHAR * Signature = (CHAR *)BuildSignature;
|
||||
UINT32 SignatureLength = sizeof(BuildSignature);
|
||||
|
||||
#if defined(__linux__)
|
||||
//
|
||||
// TODO(Linux): DEV ONLY, remove before merging. Lets the signature sent to a
|
||||
// separately built (Windows) debuggee be overridden, e.g.,
|
||||
// HYPERDBG_BUILD_SIGNATURE=0.2.0-20261004.0959
|
||||
//
|
||||
CHAR * OverrideSignature = getenv("HYPERDBG_BUILD_SIGNATURE");
|
||||
|
||||
if (OverrideSignature != NULL && OverrideSignature[0] != '\0')
|
||||
{
|
||||
ShowMessages("sending overridden build signature '%s' (dev only)\n", OverrideSignature);
|
||||
|
||||
Signature = OverrideSignature;
|
||||
SignatureLength = (UINT32)strlen(OverrideSignature) + 1;
|
||||
}
|
||||
#endif // defined(__linux__)
|
||||
|
||||
//
|
||||
// Send the handshake packet to debuggee
|
||||
//
|
||||
if (!KdCommandPacketAndBufferToDebuggee(
|
||||
DEBUGGER_REMOTE_PACKET_TYPE_DEBUGGER_TO_DEBUGGEE_EXECUTE_ON_USER_MODE,
|
||||
DEBUGGER_REMOTE_PACKET_REQUESTED_ACTION_ON_USER_MODE_DEBUGGER_VERSION,
|
||||
(CHAR *)BuildSignature,
|
||||
sizeof(BuildSignature)))
|
||||
Signature,
|
||||
SignatureLength))
|
||||
{
|
||||
ShowMessages("err, unable to send response to the ping packet\n");
|
||||
return FALSE;
|
||||
|
|
@ -2524,10 +2544,8 @@ KdPrepareSerialConnectionToRemoteSystem(HANDLE SerialHandle,
|
|||
}
|
||||
#else
|
||||
//
|
||||
// TODO(Linux): waiting for the first byte on the serial port is
|
||||
// Win32-only here (SetCommMask/WaitCommEvent); the Linux home for it is
|
||||
// platform-serial.c. Unreachable for now, as the Linux serial path is
|
||||
// refused in KdPrepareAndConnectDebugPort.
|
||||
// Linux: no need to wait for the first byte explicitly, the serial
|
||||
// handle is blocking so the reads that follow wait for it
|
||||
//
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
|
@ -2770,6 +2788,23 @@ StartAgain:
|
|||
return Result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Close the serial (or named pipe) handle of the kernel debugger
|
||||
*
|
||||
* @param Handle
|
||||
*
|
||||
* @return BOOLEAN
|
||||
*/
|
||||
static BOOLEAN
|
||||
KdCloseSerialHandle(HANDLE Handle)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
return PlatformCloseHandle(Handle);
|
||||
#else
|
||||
return PlatformSerialClose(Handle);
|
||||
#endif // _WIN32
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Prepare and initialize COM port
|
||||
*
|
||||
|
|
@ -2940,13 +2975,24 @@ KdPrepareAndConnectDebugPort(const CHAR * PortName,
|
|||
*/
|
||||
#else
|
||||
//
|
||||
// TODO(Linux): opening and configuring the serial port is Win32-only
|
||||
// here (CreateFile/PurgeComm/GetCommState/SetCommState). The Linux
|
||||
// home for this is platform-serial.c, whose serial routines are still
|
||||
// stubs. Until they are implemented, refuse the serial path.
|
||||
// It's a serial, PortName is a tty device or a UNIX socket (e.g., the
|
||||
// VMware serial port socket)
|
||||
//
|
||||
ShowMessages("err, serial connection is not supported on Linux yet\n");
|
||||
return FALSE;
|
||||
Comm = PlatformSerialOpen(PortName,
|
||||
IsPreparing ? PLATFORM_SERIAL_IO_DEBUGGEE : PLATFORM_SERIAL_IO_DEBUGGER);
|
||||
|
||||
if (Comm == NULL)
|
||||
{
|
||||
ShowMessages("err, unable to open '%s' (%s)\n", PortName, strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!PlatformSerialConfigure(Comm, Baudrate))
|
||||
{
|
||||
ShowMessages("err, unable to configure '%s' (%s)\n", PortName, strerror(errno));
|
||||
PlatformSerialClose(Comm);
|
||||
return FALSE;
|
||||
}
|
||||
#endif // _WIN32
|
||||
}
|
||||
else
|
||||
|
|
@ -2987,7 +3033,7 @@ KdPrepareAndConnectDebugPort(const CHAR * PortName,
|
|||
//
|
||||
if (!KdCheckIfDebuggerIsListening(Comm))
|
||||
{
|
||||
PlatformCloseHandle(Comm);
|
||||
KdCloseSerialHandle(Comm);
|
||||
g_SerialRemoteComPortHandle = NULL;
|
||||
g_IsDebuggeeInHandshakingPhase = FALSE;
|
||||
|
||||
|
|
@ -3012,7 +3058,7 @@ KdPrepareAndConnectDebugPort(const CHAR * PortName,
|
|||
//
|
||||
if (HyperDbgInstallKdDriver() == 1 || HyperDbgLoadVmmModule() == 1)
|
||||
{
|
||||
PlatformCloseHandle(Comm);
|
||||
KdCloseSerialHandle(Comm);
|
||||
g_SerialRemoteComPortHandle = NULL;
|
||||
g_IsConnectedToHyperDbgLocally = FALSE;
|
||||
|
||||
|
|
@ -3026,7 +3072,7 @@ KdPrepareAndConnectDebugPort(const CHAR * PortName,
|
|||
//
|
||||
if (!g_DeviceHandle)
|
||||
{
|
||||
PlatformCloseHandle(Comm);
|
||||
KdCloseSerialHandle(Comm);
|
||||
g_SerialRemoteComPortHandle = NULL;
|
||||
g_IsConnectedToHyperDbgLocally = FALSE;
|
||||
|
||||
|
|
@ -3041,7 +3087,7 @@ KdPrepareAndConnectDebugPort(const CHAR * PortName,
|
|||
|
||||
if (DebuggeeRequest == NULL)
|
||||
{
|
||||
PlatformCloseHandle(Comm);
|
||||
KdCloseSerialHandle(Comm);
|
||||
g_SerialRemoteComPortHandle = NULL;
|
||||
g_IsConnectedToHyperDbgLocally = FALSE;
|
||||
|
||||
|
|
@ -3091,7 +3137,7 @@ KdPrepareAndConnectDebugPort(const CHAR * PortName,
|
|||
|
||||
if (!StatusIoctl)
|
||||
{
|
||||
PlatformCloseHandle(Comm);
|
||||
KdCloseSerialHandle(Comm);
|
||||
g_SerialRemoteComPortHandle = NULL;
|
||||
g_IsConnectedToHyperDbgLocally = FALSE;
|
||||
|
||||
|
|
@ -3124,7 +3170,7 @@ KdPrepareAndConnectDebugPort(const CHAR * PortName,
|
|||
}
|
||||
else
|
||||
{
|
||||
PlatformCloseHandle(Comm);
|
||||
KdCloseSerialHandle(Comm);
|
||||
g_SerialRemoteComPortHandle = NULL;
|
||||
g_IsConnectedToHyperDbgLocally = FALSE;
|
||||
|
||||
|
|
@ -3899,7 +3945,7 @@ KdUninitializeConnection()
|
|||
//
|
||||
if (g_SerialRemoteComPortHandle != NULL)
|
||||
{
|
||||
PlatformCloseHandle(g_SerialRemoteComPortHandle);
|
||||
KdCloseSerialHandle(g_SerialRemoteComPortHandle);
|
||||
g_SerialRemoteComPortHandle = NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1554,10 +1554,8 @@ StartAgain:
|
|||
}
|
||||
#else
|
||||
//
|
||||
// TODO(Linux): waiting for the first byte on the serial port is
|
||||
// Win32-only here (SetCommMask/WaitCommEvent); the Linux home for it is
|
||||
// platform-serial.c. Unreachable for now, as the Linux serial path is
|
||||
// refused in KdPrepareAndConnectDebugPort.
|
||||
// Linux: no need to wait for the first byte explicitly, the serial
|
||||
// handle is blocking so the read below waits for it
|
||||
//
|
||||
#endif // _WIN32
|
||||
|
||||
|
|
|
|||
|
|
@ -41,24 +41,55 @@ SymbolLoadOrDownloadSymbols(BOOLEAN IsDownload, BOOLEAN SilentLoad)
|
|||
}
|
||||
|
||||
/**
|
||||
* @brief Attempt to resolve a name or expression to an address.
|
||||
* @brief check and convert string to a 64 bit unsigned integer and also
|
||||
* check for symbol object names and evaluate expressions
|
||||
* @details Same as the Windows implementation in symbol.cpp. Symbol names
|
||||
* aren't resolved on Linux yet (the name lookup always reports not
|
||||
* found), but numbers and expressions (e.g., @rax, @rsp+10) are
|
||||
*
|
||||
* On Linux, full symbol resolution (ELF/DWARF) is not yet implemented.
|
||||
* This stub handles the common case of a plain hex/decimal literal so that
|
||||
* numeric addresses still work everywhere in the debugger.
|
||||
* @param TextToConvert the target string
|
||||
* @param Result result will be save to the pointer
|
||||
*
|
||||
* @return BOOLEAN shows whether the conversion was successful or not
|
||||
*/
|
||||
BOOLEAN
|
||||
SymbolConvertNameOrExprToAddress(const string & TextToConvert, PUINT64 Result)
|
||||
{
|
||||
try
|
||||
BOOLEAN IsFound = FALSE;
|
||||
BOOLEAN HasError = FALSE;
|
||||
UINT64 Address = 0;
|
||||
|
||||
if (ConvertStringToUInt64(TextToConvert, &Address))
|
||||
{
|
||||
*Result = std::stoull(TextToConvert, nullptr, 0);
|
||||
return TRUE;
|
||||
//
|
||||
// It's a hex number
|
||||
//
|
||||
IsFound = TRUE;
|
||||
}
|
||||
catch (...)
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
//
|
||||
// Check for symbol object names
|
||||
//
|
||||
Address = ScriptEngineConvertNameToAddressWrapper(TextToConvert.c_str(), &IsFound);
|
||||
|
||||
if (!IsFound)
|
||||
{
|
||||
//
|
||||
// As the last resort, test whether it's an expression; in the
|
||||
// Debugger Mode it's sent to the debuggee to be evaluated
|
||||
//
|
||||
Address = ScriptEngineEvalSingleExpression(TextToConvert, &HasError);
|
||||
IsFound = !HasError;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsFound)
|
||||
{
|
||||
*Result = Address;
|
||||
}
|
||||
|
||||
return IsFound;
|
||||
}
|
||||
|
||||
BOOLEAN
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue