2013-04-22 15 views
6

Pracuję nad projektem cpp. Projekt musi zostać zmigrowany do wersji 64-bitowej. Zawiera jakiś kod zespołu Inline, którego nie można skompilować na x64. Jest to funkcja, która zawiera kod montaż:Konwertuj kod wbudowanego zestawu do C++

void ExternalFunctionCall::callFunction(ArgType resultType, void* resultBuffer) 
{ 
#if defined(_NT_) || defined(__OS2__) 

    // I386 

    // just copy the args buffer to the stack (it's already layed out correctly) 
    int* begin = m_argsBegin; 
    int* ptr = m_argsEnd; 
    int arr[1000], i=0; 
    while (ptr > begin) { 
     int val = *(--ptr); 

     __asm push val 
    } 

    void* functionAddress = m_functionAddress; 

    // call the function & handle the return value. use __stdcall calling convention 
    switch (resultType) { 
    case voidType: 
     __asm { 
      call functionAddress 
     } 
     break; 
    case pointerType: 
    case int32Type: 
     __asm { 
      call functionAddress 
      mov ebx, resultBuffer 
      mov dword ptr [ebx],eax 
     } 
     break; 
    case floatType: 
     __asm { 
      call functionAddress 
      mov ebx, resultBuffer 
      fstp dword ptr [ebx] 
     } 
     break; 
    case doubleType: 
     __asm { 
      call functionAddress 
      mov ebx, resultBuffer 
      fstp qword ptr [ebx] 
     } 
     break; 
    } 

użyłem stos, tablicę do migracji to „asm pchnięcie val” ale nie działa. Mimo, że nie wyrzuci żadnego błędu kompilacji, ale logika nie zadziałała.

Tak, chcę zapytać, co mogę użyć w C++ zamiast "__asm ​​push val". Każda pomoc zostanie doceniona.

+1

Z pewnością nie jest to jedyna instrukcja montażu w programie? Jak korzystać z wartości pchanej? W tej chwili nie ma wystarczającej ilości informacji, aby ci pomóc. –

+1

Będziesz musiał pokazać więcej kodu, abyśmy wiedzieli, co robimy z wartością pchaną na stos. –

+2

Standardowy C++ nie zawiera wbudowanego języka asemblera lub jakichkolwiek operacji służących do bezpośredniego modyfikowania stosu procesora. Masz większą szansę na przydatną poradę, jeśli wyjaśnisz, co robi twoja funkcja ze stosem? Jeśli przygotowuje wartości dla wywołania funkcji lub czegoś, może istnieć inny sposób dostarczenia argumentów. Prawdopodobnie jest to bardzo specyficzne dla twojego systemu operacyjnego i/lub kompilatora, którego nie podano. –

Odpowiedz

0

Jest kilka rzeczy, które musisz rozwiązać. (Sprawdziłem twój kod na innym pytaniu). Jak zrozumiałem, ten kod to opakowanie do wywoływania ładnej funkcji abstrakcyjnej zlokalizowanej pod określonym adresem, która oczekuje pewnej ilości danych w stosie i może zwracać różne rzeczy w ArgType.

Jeśli chcesz ominąć zwykły C, musisz zdefiniować kilka prototypów funkcji (w oparciu o wartość zwracaną), które będą potem używane na przełączniku, ale wtedy musisz rozwiązać inny problem, który jest trudniejszy.

Problem z portowaniem takich rzeczy w C polega na tym, że nie znasz z góry liczby argumentów (wielkości danych), które musisz wepchnąć do stosu, więc będziesz miał problemy z definicją prototypu.

Powiedzmy, że func (char c) zdecydowanie przesunie bajt w stosie 1 (ale także nie zawsze będzie poprawny z powodu wyrównania danych). W twoim przypadku musisz pomyśleć o rozwiązaniu, które będzie miało zestaw parametrów o jednakowej wielkości do rozmiaru danych, które musisz znaleźć na stosie. Na pierwszy rzut oka nie można czegoś od razu zrobić.

UPD. możesz to zrobić za pomocą func (char [] param); ale ma również problemy, które wyjaśniono w odpowiedzi powyżej.

+0

Wiesz, to nie jest taka zła odpowiedź, ale ostatni akapit, szczególnie "musisz myśleć o rozwiązaniu", niszczy go dla mnie. PO nie prosi o pomoc, jeśli coś wymyśli. Najwyraźniej bardzo się starali rozwiązać samodzielnie. – StoryTeller

+0

Przepraszam, że cię rozczarowałem .. Może być "trzeba pomyśleć" rzeczywiście było trochę trudne, ale mam pretekst, że nie jestem native speakerem języka angielskiego .. Mam na myśli to, że naprawdę nie znam dokładnego rozwiązania dla ostatniego kroku, a to, co napisałem, było raczej rodzajem rozchwytywanego myślenia niż gotowej odpowiedzi. Inną rzeczą jest to, że z tego, co przeczytałem w pytaniu OP (właściwie obaj), uważam, że "oczywiście próbowałem bardzo ciężko do rozwiązania" ma zastosowanie. – evilruff

13

Problem ogólnie nie da się rozwiązać; to dlatego, że komentarz,

// call the function & handle the return value. use __stdcall calling convention 

wskazuje zależność 32bit konwencji wywołań.

W 32bit x86, stdcall oznacza, że ​​wszystko argumenty są przekazywane na stosie, w odwrotnej kolejności (czyli ostatnia arg jest popychany pierwszy. Oznacza to, że jeśli arg[0] jest addr następnie arg[1], bez względu na to, że to typ, jest pod adresem addr + sizeof(arg[0])). To jest powód, dla którego poniższy kod w twojej próbce:

// just copy the args buffer to the stack (it's already layed out correctly) 
int* begin = m_argsBegin; 
int* ptr = m_argsEnd; 
int arr[1000], i=0; 
while (ptr > begin) { 
    int val = *(--ptr); 

    __asm push val 
} 

faktycznie może działać - ponieważ po prostu nie ma znaczenia, co dokładnie tam jest, jakie są typy argumentów; wszystko, co istotne, to fakt, że każdy z nich znajduje się w znanym miejscu w pamięci i jest znany jako kolejny w pamięci. Jeśli wiesz, że argument N jest pod adresem addr, możesz wiedzieć, że argument N+1 ma wartość addr + sizeof(arg[N]).

To właśnie komentarz mówi: „to już rozplanowane poprawnie” - niestety, to jest nie prawda w trybie 64-bitowym. Dlatego kod nie może być "przeniesiony"; nie ma odpowiednika portu do.

Konwencje wywoływania, które są przynajmniej częściowo oparte na rejestrze - tak jak w przypadku Win64 na x64 (64bit x86), zachowują się inaczej.W tym przypadku zależy to od tego, jakie typy argumentów wywołują wywoływane funkcje (w systemie Windows można przekazać cztery argumenty typu całkowitego w rejestrach ogólnego przeznaczenia oraz niektóre arguły typu float w rejestrach XMM). Dlatego musisz wiedzieć, więcej o podpisie (prototyp) funkcji, którą wywołujesz, niż po prostu "wymaga ona argumentów", aby móc poprawnie marszować argumenty z opakowania typu "anycall", jak wyżej. W trybie 64-bitowym, dla każdej funkcji, którą chcesz wywołać za pomocą otoki, musisz wiedzieć nie tylko o liczbie argumentów w ogóle, ale także o tym, ile jest w rejestrach ogólnego zastosowania, ile w XMM regach i jak wiele na stosie.

"Wywołanie funkcji za pośrednictwem wskaźnika i skopiowanie wartości zwracanej do znanej lokalizacji" jest przenośne i może być wyrażone zwykłym C/C++. Ale część, która pobiera argumenty do tego, jak powyżej, nie niekoniecznie Port w żaden bezpośredni sposób między 32bit stdcall oraz wszelkich 64bit x86 wywołującego konwencji znam (ani Win64/x64 ani UN * X x86_64 konwencje pozwalają do przewidywania zarówno lokalizacji, jak i wykorzystania pamięci całkowitej dla wszystkich argumentów dla funkcji podanej tylko liczbą i typami argumentów, ale nie ich kolejności).

Co dokładnie trzeba zrobić o wiele więcej zależy na dzwoniący/użytkowników powyższej class ExternalFunctionCall niż na małym małej próbie montażu inline masz pokazane. Niezbędne jest w szczególności ustalenie, w jaki sposób zainicjowano członków i jakie są ich adresy. Czy możesz podać kilka dodatkowych informacji o tym, jak wygląda klasa (wszystkie zmienne składowe/funkcje), a także przykłady dotyczące jej rzeczywistego wykorzystania?

+0

Dzięki, Frank, chciałbym zapytać jeszcze jedną rzecz. Czy nie możemy oddzielić tego kodu asm w pliku asm/s? Oddzielny plik ASM można skompilować na x64 poprzez MASM64.exe. Jeśli to możliwe, proszę powiedz mi, jak możemy to zrobić? Przeprowadziliśmy migrację wszystkich innych kodów asm do cpp, pozostaje tylko "__asm ​​push val".Możesz zobaczyć kod pełnej funkcji powyżej. – vsoni

+0

@ user2118116: jak już wspomniano, jest _more_ do zrobienia niż po prostu "zastąpienie' __asm ​​push val' z pewną 64-bitność ". To "więcej" jest _ na zewnątrz funkcji _ - to _bot_ w całej klasie, a także, i tam właśnie twoja największa część pracy jest, w rzeczywistych miejscach, w których instancje tej klasy są tworzone/używane. Nie ma łatwego wyjścia, przepraszam. Twoje pytanie dotyczące przeniesienia tego na 64-bitowe jest trochę jak powiedzenie "mój kod x86 używa' lcall' w wbudowanym zestawie i muszę go przenieść na ARM, więc chcę znać instrukcję ARM, która wykonuje 'lcall'". –

+0

@FrankH. Pełny ACK na ten temat. Zasugerowałem OP w duplikacie tego: 「Najlepszą rzeczą, jaką możesz zrobić, to przepisanie całej funkcji za pomocą [libffi] (http://sourceware.org/libffi/) lub [dyncall] (http: // dyncall. org /) zamiast 」 – mirabilos

0

Zdaję sobie sprawę, że jest to nieco stare pytanie, ale natknąłem się na: xbyak.

Być może tego, czego szukasz?

+0

Hi Nix, Chcę zastąpić kod asm z tego kontekstu: int * begin = m_argsBegin; int * ptr = m_argsEnd; int arr [1000], i = 0; podczas gdy (ptr> początek) { int val = * (- ptr); __asm ​​push val } Zasadniczo chcę zamienić wyrażenie "__asm ​​push val" na kod Cpp. Dzięki – vsoni

+0

Co to są m_argsBegin i m_argsEnd? –

+0

@ Nix - oba są int * Dzięki – vsoni