GetThreadContext - x64

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
ToniPB
User
User
Posts: 13
Joined: Tue Dec 16, 2014 3:44 am

GetThreadContext - x64

Post by ToniPB »

In AMD64 the CONTEXT structure must start at a 16-bit boundary, otherwise the call fails.

Unfortunately PB can't perform boundary alignment outside of structures.

We need another optional parameter like this:

Code: Select all

Structure CONTEXT Align #PB_Structure_AlignC BAlign 16
any.l
EndStructure
BAligin would be similar to __declspec(align(#)) -> https://msdn.microsoft.com/en-us/library/83ythb65.aspx

Bug Example(s):

Code: Select all

EnableExplicit

; typedef struct DECLSPEC_ALIGN(16) _CONTEXT {

CompilerIf #PB_Compiler_Processor <> #PB_Processor_x64
  MessageRequester("", "This is a x64 bug only!")
CompilerEndIf

Procedure.s win_FormatErrorCode(lngErrorCode.l)
  Protected str_buf.s = Space(255)
  Protected tchar_count.l
  tchar_count = FormatMessage_(#FORMAT_MESSAGE_FROM_SYSTEM, #Null, lngErrorCode, 0, @str_buf, 255, #Null)
  If tchar_count > 0
    str_buf = Trim(Left(str_buf, tchar_count))
  Else
    str_buf = ""
  EndIf
  ProcedureReturn str_buf
EndProcedure

Procedure ProcTest1()
  Protected ctx.CONTEXT
  
  ctx\ContextFlags = #CONTEXT_FULL
  
  If GetThreadContext_(-2, @ctx)
    PrintN("EFlags: "+Hex(ctx\EFlags))
  Else
    Protected last_err.l
    last_err = GetLastError_()
    PrintN("Error - LastError Code: "+Str(last_err)+#CRLF$+#TAB$+"LastError Msg: "+win_FormatErrorCode(last_err))
  EndIf

EndProcedure

Procedure ProcTest2()
  Protected align1.i
  Protected ctx.CONTEXT
  
  ctx\ContextFlags = #CONTEXT_FULL
  
  If GetThreadContext_(-2, @ctx)
    PrintN("EFlags: "+Hex(ctx\EFlags))
  Else
    Protected last_err.l
    last_err = GetLastError_()
    PrintN("Error - LastError Code: "+Str(last_err)+#CRLF$+#TAB$+"LastError Msg: "+win_FormatErrorCode(last_err))
  EndIf

EndProcedure

Procedure.l ProcTest3()
  Protected result.l ; <- 16-byte alignment fucked
  Protected align1.i
  Protected ctx.CONTEXT
  
  ctx\ContextFlags = #CONTEXT_FULL
  
  If GetThreadContext_(-2, @ctx)
    PrintN("EFlags: "+Hex(ctx\EFlags))
  Else
    Protected last_err.l
    last_err = GetLastError_()
    PrintN("Error - LastError Code: "+Str(last_err)+#CRLF$+#TAB$+"LastError Msg: "+win_FormatErrorCode(last_err))
  EndIf
  
  ProcedureReturn result
EndProcedure

If OpenConsole()
  PrintN("Running ProcTest1...")
  ProcTest1()
  PrintN("Running ProcTest2...")
  ProcTest2()
  PrintN("Running ProcTest3...")
  ProcTest3()
  PrintN("")
  PrintN("Press enter to exit.")
  Input()
EndIf
Workaround:

Code: Select all

CompilerIf #PB_Compiler_Processor = #PB_Processor_x64
  
  Procedure.l GetThreadContext(ThreadHandle.i, *ThreadContext.CONTEXT)
    Protected result.l
    Protected ctx.CONTEXT
    
    ; In AMD64 the CONTEXT structure must start at a 16-bit boundary, otherwise the call fails.
    
    If *ThreadContext
      CopyMemory(*ThreadContext, @ctx, SizeOf(CONTEXT))
      result = GetThreadContext_(ThreadHandle, @ctx)
      CopyMemory(@ctx, *ThreadContext, SizeOf(CONTEXT))
    Else
      result = GetThreadContext_(ThreadHandle, *ThreadContext)
    EndIf
    
    ProcedureReturn result
  EndProcedure
  
CompilerElse
  
  Procedure.l GetThreadContext(ThreadHandle.i, *ThreadContext.CONTEXT)
    ProcedureReturn GetThreadContext_(ThreadHandle, *ThreadContext)
  EndProcedure
  
CompilerEndIf

Macro GetThreadContext_
  GetThreadContext
EndMacro
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Re: GetThreadContext Bug - x64

Post by Fred »

It's not a bug, moved to feature and request.
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: GetThreadContext Bug - x64

Post by Keya »

ToniPB I was confused by what your workaround is, but it seems you're simply inserting other variable(s) before the CONTEXT variable to force it into 16-byte alignment? sounds like something that might not work from one compile/compiler to the next, so I was thinking maybe create an AllocateMemory(Sizeof(CONTEXT)+16), then find the first 16-byte aligned address within that, use that in call to GetThreadContext, and then point *ctx.CONTEXT at it :)

i agree with you an addition for alignment instead of a workaround would be great! :)
ToniPB
User
User
Posts: 13
Joined: Tue Dec 16, 2014 3:44 am

Re: GetThreadContext Bug - x64

Post by ToniPB »

Fred wrote:It's not a bug, moved to feature and request.
The API doesn't work properly in 64-bit, so it's clearly a bug or at least a compiler deficiency.

If we don't have the BAlign Parameter then the compiler should handle cases like this automatically.
User avatar
skywalk
Addict
Addict
Posts: 4211
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: GetThreadContext - x64

Post by skywalk »

Have you tried creating your own FLOATING_SAVE_AREA + CONTEXT structures with dummy fields for padding?
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
User avatar
Keya
Addict
Addict
Posts: 1890
Joined: Thu Jun 04, 2015 7:10 am

Re: GetThreadContext - x64

Post by Keya »

maybe something like this as a workaround:

Code: Select all

Define *ctxfull = AllocateMemory(SizeOf(CONTEXT)+16)  ;extra space for 16-byte alignment
Define *ctx.CONTEXT = *ctxfull + (*ctxfull % 16)      ;*ctx is the first 16-byte aligned address within *ctxfull

hThread = GetCurrentThread_()
*ctx\ContextFlags = #CONTEXT_FULL
If GetThreadContext_(hThread, *ctx)
  Debug "RIP=0x" + Hex(*ctx\RIP)
EndIf
FreeMemory(*ctxfull)
Post Reply