2011-11-27 27 views
7

Thư viện MustangPeak chung (http://code.google.com/p/mustangpeakcommonlib/) chứa đoạn mã sau có thể chuyển đổi một phương pháp để một thủ tục có thể được sử dụng trong một callback:Làm thế nào để chuyển một phương thức sang một thủ tục gọi lại trong 64bit Delphi XE2?

const 
    AsmPopEDX = $5A; 
    AsmMovEAX = $B8; 
    AsmPushEAX = $50; 
    AsmPushEDX = $52; 
    AsmJmpShort = $E9; 

type 
    TStub = packed record 
    PopEDX: Byte; 
    MovEAX: Byte; 
    SelfPointer: Pointer; 
    PushEAX: Byte; 
    PushEDX: Byte; 
    JmpShort: Byte; 
    Displacement: Integer; 
    end; 

{ ----------------------------------------------------------------------------- } 
function CreateStub(ObjectPtr: Pointer; MethodPtr: Pointer): Pointer; 
var 
    Stub: ^TStub; 
begin 
    // Allocate memory for the stub 
    // 1/10/04 Support for 64 bit, executable code must be in virtual space 
    Stub := VirtualAlloc(nil, SizeOf(TStub), MEM_COMMIT, PAGE_EXECUTE_READWRITE); 

    // Pop the return address off the stack 
    Stub^.PopEDX := AsmPopEDX; 

    // Push the object pointer on the stack 
    Stub^.MovEAX := AsmMovEAX; 
    Stub^.SelfPointer := ObjectPtr; 
    Stub^.PushEAX := AsmPushEAX; 

    // Push the return address back on the stack 
    Stub^.PushEDX := AsmPushEDX; 

    // Jump to the 'real' procedure, the method. 
    Stub^.JmpShort := AsmJmpShort; 
    Stub^.Displacement := (Integer(MethodPtr) - Integer(@(Stub^.JmpShort))) - 
    (SizeOf(Stub^.JmpShort) + SizeOf(Stub^.Displacement)); 

    // Return a pointer to the stub 
    Result := Stub; 
end; 
{ ----------------------------------------------------------------------------- } 

{ ----------------------------------------------------------------------------- } 
procedure DisposeStub(Stub: Pointer); 
begin 
    // 1/10/04 Support for 64 bit, executable code must be in virtual space 
    VirtualFree(Stub, SizeOf(TStub),MEM_DECOMMIT); 
end; 

tôi sẽ đánh giá cao bất kỳ sự trợ giúp nào trong việc chuyển đổi nó thành 64bit. Tôi biết rằng quy ước gọi trong Win64 là khác nhau và có tới bốn tham số được chuyển vào sổ đăng ký. Vì vậy, CreateStub có thể phải được sửa đổi để bao gồm số lượng tham số. Nó thực sự không được sử dụng với hơn bốn tham số là số nguyên hoặc con trỏ (không có đối số dấu chấm động).

+0

Nhận xét cho biết rằng nó đã hỗ trợ 64 bit. Có lẽ những ý kiến ​​đó là sai lầm! Tất cả như nhau, nhìn vào nguồn VCL cho StdWndProc cho một mã mẫu làm một cái gì đó rất giống nhau. –

+0

Nhận xét là gây hiểu lầm. Nó có nghĩa là để nói rằng nếu bạn cấp phát bộ nhớ với GetMem thay vì VirtualAlloc nó sẽ tạo ra một vấn đề trong bộ vi xử lý 64bit (bảo vệ DEP). – PyScripter

+0

Khi bạn sử dụng mã này trong mã 32 bit, bạn có cần phải tạo con trỏ phương thức 'stdcall' không? –

Trả lời

4

Đây là phiên bản 64 bit của CreateStub. Kudos để Andrey Gruzdev đã cung cấp mã.

type 
    ICallbackStub = interface(IInterface) 
    function GetStubPointer: Pointer; 
    property StubPointer : Pointer read GetStubPointer; 
    end; 

    TCallbackStub = class(TInterfacedObject, ICallbackStub) 
    private 
    fStubPointer : Pointer; 
    fCodeSize : integer; 
    function GetStubPointer: Pointer; 
    public 
    constructor Create(Obj : TObject; MethodPtr: Pointer; NumArgs : integer); 
    destructor Destroy; override; 
    end; 



constructor TCallBackStub.Create(Obj: TObject; MethodPtr: Pointer; 
    NumArgs: integer); 
{$IFNDEF CPUX64} 
// as before 
{$ELSE CPUX64} 
const 
RegParamCount = 4; 
ShadowParamCount = 4; 

Size32Bit = 4; 
Size64Bit = 8; 

ShadowStack = ShadowParamCount * Size64Bit; 
SkipParamCount = RegParamCount - ShadowParamCount; 

StackSrsOffset = 3; 
c64stack: array[0..14] of byte = (
$48, $81, $ec, 00, 00, 00, 00,//  sub rsp,$0 
$4c, $89, $8c, $24, ShadowStack, 00, 00, 00//  mov [rsp+$20],r9 
); 

CopySrcOffset=4; 
CopyDstOffset=4; 
c64copy: array[0..15] of byte = (
$4c, $8b, $8c, $24, 00, 00, 00, 00,//  mov r9,[rsp+0] 
$4c, $89, $8c, $24, 00, 00, 00, 00//  mov [rsp+0],r9 
); 

RegMethodOffset = 10; 
RegSelfOffset = 11; 
c64regs: array[0..28] of byte = (
$4d, $89, $c1,  // mov r9,r8 
$49, $89, $d0,  // mov r8,rdx 
$48, $89, $ca,  // mov rdx,rcx 
$48, $b9, 00, 00, 00, 00, 00, 00, 00, 00, // mov rcx, Obj 
$48, $b8, 00, 00, 00, 00, 00, 00, 00, 00 // mov rax, MethodPtr 
); 

c64jump: array[0..2] of byte = (
$48, $ff, $e0 // jump rax 
); 

CallOffset = 6; 
c64call: array[0..10] of byte = (
$48, $ff, $d0, // call rax 
$48, $81,$c4, 00, 00, 00, 00, //  add rsp,$0 
$c3// ret 
); 
var 
    i: Integer; 
    P,PP,Q: PByte; 
    lCount : integer; 
    lSize : integer; 
    lOffset : integer; 
begin 
    lCount := SizeOf(c64regs); 
    if NumArgs>=RegParamCount then 
     Inc(lCount,sizeof(c64stack)+(NumArgs-RegParamCount)*sizeof(c64copy)+sizeof(c64call)) 
    else 
     Inc(lCount,sizeof(c64jump)); 

    Q := VirtualAlloc(nil, lCount, MEM_COMMIT, PAGE_EXECUTE_READWRITE); 
    P := Q; 

    lSize := 0; 
    if NumArgs>=RegParamCount then 
    begin 
     lSize := (1+ ((NumArgs + 1 - SkipParamCount) div 2) * 2)* Size64Bit; // 16 byte stack align 

     pp := p; 
     move(c64stack,P^,SizeOf(c64stack)); 
     Inc(P,StackSrsOffset); 
     move(lSize,P^,Size32Bit); 
     p := pp; 
     Inc(P,SizeOf(c64stack)); 
     for I := 0 to NumArgs - RegParamCount -1 do 
     begin 
      pp := p; 
      move(c64copy,P^,SizeOf(c64copy)); 
      Inc(P,CopySrcOffset); 
      lOffset := lSize + (i+ShadowParamCount+1)*Size64Bit; 
      move(lOffset,P^,Size32Bit); 
      Inc(P,CopyDstOffset+Size32Bit); 
      lOffset := (i+ShadowParamCount+1)*Size64Bit; 
      move(lOffset,P^,Size32Bit); 
      p := pp; 
      Inc(P,SizeOf(c64copy)); 
     end; 
    end; 

    pp := p; 
    move(c64regs,P^,SizeOf(c64regs)); 
    Inc(P,RegSelfOffset); 
    move(Obj,P^,SizeOf(Obj)); 
    Inc(P,RegMethodOffset); 
    move(MethodPtr,P^,SizeOf(MethodPtr)); 
    p := pp; 
    Inc(P,SizeOf(c64regs)); 

    if NumArgs<RegParamCount then 
     move(c64jump,P^,SizeOf(c64jump)) 
    else 
    begin 
     move(c64call,P^,SizeOf(c64call)); 
     Inc(P,CallOffset); 
     move(lSize,P^,Size32Bit); 
    end; 
    fCodeSize := lcount; 
    fStubPointer := Q; 
{$ENDIF CPUX64} 
end; 

destructor TCallBackStub.Destroy; 
begin 
    VirtualFree(fStubPointer, fCodeSize, MEM_DECOMMIT); 
    inherited; 
end; 

function TCallBackStub.GetStubPointer: Pointer; 
begin 
    Result := fStubPointer; 
end; 
+0

PyScriper, Bạn là một quý ông và một học giả. Tôi đã cấp cho bạn quyền truy cập Commit vào mã Mustangpeak trên Google để thực hiện điều này. Tôi chỉ không có đủ kỹ năng hoặc thời gian để sửa lỗi này. Nếu bạn thực hiện VSTools biên dịch 64Bit, tôi sẽ mua cho bạn một pint! Tôi đã chuyển chủ yếu sang Mac nên sự quan tâm của tôi đối với VSTools là thấp trong những ngày này nhưng có rất nhiều người dùng yêu thích phiên bản 64 bit!Cảm ơn, Jim –

+0

Để biết ví dụ về cách sử dụng, hãy xem https://code.google.com/p/mustangpeakcommonlib/ ; liên kết này trỏ đến tệp nguồn: [link] (https://code.google.com/p/mustangpeakcommonlib/source/browse/trunk/Source/MPCommonUtilities.pas) –

3

Tôi tin rằng 99% không có giải pháp tương đương trên x64. Trên x86 mã tận dụng thuộc tính của stdcall rằng tất cả các tham số được truyền trên ngăn xếp. Mã tạo ra số stub không cần biết bất kỳ điều gì về các tham số được chuyển. Nó chỉ đơn giản là đẩy một tham số thêm, con trỏ tự, vào ngăn xếp. Tất cả các thông số khác được chuyển xuống ngăn xếp.

Trên x64, ít nhất là trên Windows, có một single calling convention. Quy ước gọi điện này sử dụng rộng rãi các thanh ghi. Khi sổ đăng ký cạn kiệt, ngăn xếp sẽ được sử dụng. Cả thanh ghi số nguyên và dấu phẩy động được sử dụng. Các quy tắc cho các tham số nào được truyền vào trong đó các thanh ghi phức tạp để nói ít nhất. Vì vậy, để chuyển đổi một phương thức thành thủ tục đứng tự do, tôi tin rằng quy trình CreateStub sẽ cần phải biết thông tin về các tham số: có bao nhiêu tham số, loại nào, v.v. Vì CreateStub không có thông tin này, đơn giản là không thể thực hiện chuyển đổi x64 của hàm này, với cùng một giao diện.

+0

Không thể có một CreateStub với một đối số thêm số tham số (nói 0 đến 4) theo giả định rằng chúng là số nguyên của con trỏ (tức là chúng vừa trong sổ đăng ký chuẩn)? Tôi sẽ giả định rằng mã assembler sẽ chỉ thêm Self vào thanh ghi thích hợp tùy thuộc vào số tham số thêm nó vào ngăn xếp nếu chúng ta hết sổ đăng ký. – PyScripter

+0

Ngoài nhận xét trước, bạn phải thay đổi thanh ghi. Nếu nói rằng bạn có hai tham số: MOV R8, RDX thì MOV RDX, RCX và cuối cùng đẩy Tự tới RCX. – PyScripter

+0

Đối với một số lượng nhỏ các thông số bạn có thể sẽ có được một cái gì đó làm việc. Một điều tôi chưa nghiên cứu là cách xử lý giá trị trả về. Nó sẽ có ý nghĩa để chuyển số tham số đến một CreateStub 64 bit. –

Các vấn đề liên quan