2017-02-01 5 views
0

Я пытаюсь создать свой собственный JIT, и до сих пор мне удалось запустить очень простой код сборки (в машинный код), но возникли проблемы с выяснением способа вызова функций таким образом. В Visual Studio я могу видеть функции в окне разборки.Функции вызова из сборки x86_64

Другой связанный с этим вопрос - как я могу назвать Win32 MessageBox() в машинный код?

Следующий вопрос: каким образом я могу вызвать внешние функции DLL/LIB таким образом?

Также есть ли какие-либо книги или учебники, которые могли бы научить меня дальше в этой теме? Я попытался найти его, но получить результаты, такие как .NET, JVM и LLVM, которые, я думаю, не совсем то, что я ищу.

Вот упрощенная версия кода, что я работаю на:

#include <iostream> 
#include <Windows.h> 

int main(int argc, char* argv[]) 
{ 
    // b8 03 00 00 00 83 c0 02 c3 
    unsigned char code[] = { 
     0xb8,     // mov eax, 3 
     0x03, 0x00, 0x00, 0x00, // 3 (32 bit) 
     0x83,     // add eax, 2 // 0x83 = add, 
     0xc0,     // ModR/M with immediate 8 bit value 
     0x02,     // 2 (8 bit) 
     0xc3     // ret 
    }; 

    void* mem = VirtualAlloc(0, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE); 

    memcpy(mem, code, sizeof(code)); 

    DWORD old; 
    VirtualProtect(mem, sizeof(mem), PAGE_EXECUTE_READ, &old); 

    int(*func)() = reinterpret_cast<int(*)()>(mem); 

    printf("Number is %d\n", func()); 

    VirtualFree(mem, 0, MEM_RELEASE); 

    return 0; 
} 

Можно ли иметь ассемблерный код JIT для вызова функции C++?

Перед этим проектом я сделал интерпретатор байт-кода на C++, но я не был доволен скоростью при сравнении его с эквивалентной тестовой программой на C#. C# был примерно в 25 раз быстрее. Поэтому я наткнулся на что-то под названием JIT, чтобы сделать его быстрее. Поэтому я надеюсь, что вы все увидите, где я беру этот проект JIT. И, возможно, если возможно, это поможет графическому интерфейсу.

+0

Конечно, это возможно. Газиллион примеры, возможно, посмотрите на [мир привет] (http://stackoverflow.com/a/1032422/547981) для начала. Сборка его вручную не будет забавой. Обратите внимание, что вызовы функций вряд ли будут вашим узким местом. – Jester

+1

Обычно я пишу образец программы в C/C++, а затем код сборки для компилятора, чтобы получить имена уровня сборки и последовательность вызовов. В случае с Visual Studio 2015, printf теперь является частью файла include, что означает, что он эффективно интегрируется с кодом C/C++. Один из способов борьбы с этим - создать проект, содержащий файл C для printf, и файл сборки для остальной части проекта. Может существовать возможность импортировать конкретные библиотеки, которые по-прежнему включают старый стиль printf. – rcgldr

+0

Ну, IIRC, можно согнуть clang, чтобы скомпилировать источник C в память, а затем JIT его LLVM в машинный код и запустить его, поэтому, изучая источники LLVM, вы, вероятно, получите ответы ... через несколько лет ... Это также непонятно для меня, почему вас беспокоит интерпретируемая скорость языка, просто записывайте части производительности на C++ и сборку, JIT, как правило, вряд ли будет в порядке в лучшем случае и будет хуже в любом правильно настроенном случае производительности. Для неэффективных критических частей 25x тоже не имеет значения. Хотя это может быть хорошим упражнением, чтобы вы поняли, насколько классный C++. :) – Ped7g

ответ

0

Возможно, вы найдете несколько руководств по написанию компилятора/компоновщика. Это может помочь с реализацией/вызовом динамических библиотек.

Я не уверен, что вы подразумеваете под вызовами функций C++. В любом случае я написал следующую демонстрационную программу, которую вы можете посмотреть и посмотреть, помогает ли она вообще.

#include <Windows.h> 
#include <iostream> 


using namespace std; 

__int64 sub(__int64 a, __int64 b) 
{ 
    return a - b; 
} 

int main(int argc, char **argv) 
{ 
    char code[] = 
    { 
     0x48, 0x89, 0xC8,   // mov rax, rcx 
     0xC3,      // ret 

     0x48, 0x83, 0xEC, 0x20,  // sub rsp, 0x20 
     0xFF, 0xD0,     // call rax 
     0x48, 0x83, 0xC4, 0x20,  // add rsp, 0x20 
     0xC3      // ret 
    }; 


    char *mem = static_cast<char *>(VirtualAlloc(0, sizeof(code), MEM_COMMIT, PAGE_EXECUTE_READWRITE)); 

    MoveMemory(mem, code, sizeof(code)); 

    auto setFunc = reinterpret_cast<void *(*)(void *)>(mem); 
    auto callFunc = reinterpret_cast<__int64 (*)(__int64, __int64)>(mem + 4); 

    setFunc(sub); 
    __int64 r = callFunc(0, 1); 
    cout << "r = " << r << endl; 

    VirtualFree(mem, 0, MEM_RELEASE); 


    cin.ignore(); 
    return 0; 
} 
+0

Как вы предотвращаете C++ от загрязнения 'rax' между' setFunc (sub); 'и' __int64 r = callFunc (0, 1); '? ИМО это не стабильный пример (хотя он может работать с достаточно низким уровнем оптимизации и достаточно удачей). – Ped7g

+0

@ Ped7g Моя точка зрения заключается не в том, что вы можете просто использовать код как есть. Если вы можете напрямую вызвать функцию, почему бы вам сделать это так или иначе. Я хотел сказать, что вы можете получить адрес функции таким образом, а затем вы можете делать все, что хотите, например, настраивая таблицу переходов. – MegaStupidMonkeys

+0

@MegaStupidMonkeys Это то, что меня действительно интересовало. Спасибо! –

Смежные вопросы