guides:machine_code

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
guides:machine_code [2024-01-02 14:51] – Add section 5, Callback to AHK geekguides:machine_code [2024-01-02 16:18] (current) – Add section 6, Importing functions geek
Line 211: Line 211:
   - If beta //were// to be called, //it doesn't know where alpha is to call it//. If you look in the code, it defines the call to ''alpha'' as ''c8 00 00 00 00'' which means to enter the function at memory location ''0'', //not// memory location of ''alpha''.   - If beta //were// to be called, //it doesn't know where alpha is to call it//. If you look in the code, it defines the call to ''alpha'' as ''c8 00 00 00 00'' which means to enter the function at memory location ''0'', //not// memory location of ''alpha''.
  
-Traditional MCode tools like MCode4GCC do nothing to address these problems. Instead, you would have to compile and load each function separately, managing and passing around their pointers manually. These complicated workarounds are completely done away with in MCLhowever, which uses its built-in linker and loader to automatically identify function offsets and adjust the MCode at run-time so that references to other functions resolve correctly instead of remaining ''0''.+Traditional MCode tools like MCode4GCC do nothing to address these problems. Instead, you have to compile and load each function separately, managing and passing around function pointers manually to everywhere they are used. 
 + 
 +<runner ahk2> 
 +alpha := MCode('2,x86:uCoAAADD,x64:uCoAAADD') ; int alpha() { return 42; } 
 +beta := MCode('2,x86:/2QkBA==,x64:SP/h') ; int beta(int (*alpha)(void)) { return alpha(); } 
 +MsgBox DllCall(beta, "Ptr", alpha, "Cdecl Int"
 + 
 +MCode(mcode) { 
 +  static e := Map('1', 4, '2', 1), c := (A_PtrSize=8) ? "x64" : "x86" 
 +  if (!regexmatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", &m)) 
 +    return 
 +  if (!DllCall("crypt32\CryptStringToBinary", "str", m.3, "uint", 0, "uint", e[m.1], "ptr", 0, "uint*", &s := 0, "ptr", 0, "ptr", 0)) 
 +    return 
 +  p := DllCall("GlobalAlloc", "uint", 0, "ptr", s, "ptr"
 +  if (c="x64"
 +    DllCall("VirtualProtect", "ptr", p, "ptr", s, "uint", 0x40, "uint*", &op := 0) 
 +  if (DllCall("crypt32\CryptStringToBinary", "str", m.3, "uint", 0, "uint", e[m.1], "ptr", p, "uint*", &s, "ptr", 0, "ptr", 0)) 
 +    return p 
 +  DllCall("GlobalFree", "ptr", p) 
 +
 +</runner> 
 + 
 +This kind of workaround is not necessary when using MCL, which uses its built-in linker and loader to automatically identify function offsets and adjust the MCode at run-time so that references to other functions resolve correctly instead of remaining ''0''.
  
 For code that defines multiple functions to be called from AHK, those functions must be //exported// so that MCL can determine what should be included in the output MCode. Functions that are not exported, or used by exported code, will be trimmed from the output automatically. For code that defines multiple functions to be called from AHK, those functions must be //exported// so that MCL can determine what should be included in the output MCode. Functions that are not exported, or used by exported code, will be trimmed from the output automatically.
Line 335: Line 357:
 Although there are many things you can do with MCode, sometimes it is helpful or even necessary to callback to AutoHotkey for some actions. For example, it can be extremely valuable to call back to AutoHotkey for OutputDebug, MsgBox, or other debugging-oriented logging tools. Although there are many things you can do with MCode, sometimes it is helpful or even necessary to callback to AutoHotkey for some actions. For example, it can be extremely valuable to call back to AutoHotkey for OutputDebug, MsgBox, or other debugging-oriented logging tools.
  
-AutoHotkey provides a tool to export its own functions to be consumed as a callback from C/C++ APIs, [[https://www.autohotkey.com/docs/v2/lib/CallbackCreate.htm|CallbackCreate]], which can be used very easily with your own MCode functions as well. The simplest way to do this is to create your callback, then pass it as a parameter to the function.+AutoHotkey provides a tool to export its own functions to be consumed as a callback from C/%%C++%% APIs, [[https://www.autohotkey.com/docs/v2/lib/CallbackCreate.htm|CallbackCreate]], which can be used very easily with your own MCode functions as well. The simplest way to do this is to create your callback, then pass it as a parameter to the function.
  
 <runner ahk2> <runner ahk2>
Line 390: Line 412:
  
 It may be tempting to try to use CallbackCreate in order to wrap mathematical functions like Sqrt, however this should be avoided normally because it will absolutely destroy any performance gains that you were aiming to achieve by using MCode in the first place. Instead, native machine code implementations of sqrt and other mathematical functions should be embedded or imported. It may be tempting to try to use CallbackCreate in order to wrap mathematical functions like Sqrt, however this should be avoided normally because it will absolutely destroy any performance gains that you were aiming to achieve by using MCode in the first place. Instead, native machine code implementations of sqrt and other mathematical functions should be embedded or imported.
 +
 +==== 6. Importing Functions ====
 +
 +Writing C or %%C++%% code in an MCode environment, without any access to the standard libraries, can be extremely limiting. These limits can be eased significantly by importing key functions from DLLs that ship with Windows, or from third-party DLLs that you have on hand.
 +
 +The basic strategy is this:
 +
 +  - Load the DLL using AutoHotkey
 +  - Fetch the function pointer using AutoHotkey
 +  - Pass that function pointer using the same strategy we did for CallbackCreate
 +  - Call that function from your MCode
 +
 +For example, we can import ''sqrt'' from ''msvcrt.dll'' in order to do some mathematical calculations:
 +
 +<runner ahk2>
 +#Requires AutoHotkey v2.0
 +
 +if !(hDll := DllCall("GetModuleHandle", "Str", "msvcrt", "Ptr"))
 + throw OSError(,, "Failed to find DLL msvcrt")
 +if !(pFunction := DllCall("GetProcAddress", "Ptr", hDll, "AStr", "sqrt", "Ptr"))
 + throw Error(,, "Failed to find function sprintf from DLL msvcrt")
 +
 +;double hypotenuse(double (*sqrt)(double), double a, double b) {
 +; return sqrt(a * a + b * b);
 +;}
 +lib := MCode("2,x86:8g8QRCQI8g8QTCQQi0QkBPIPWcDyD1nJ8g9YwfIPEUQkBP/g,x64:8g9ZyfIPWdJmDyjB8g9Ywkj/4Q==")
 +
 +MsgBox DllCall(lib, "Ptr", pFunction, "Double", 3.0, "Double", 4.0, "Cdecl Double")
 +
 +MCode(mcode) {
 +  static e := Map('1', 4, '2', 1), c := (A_PtrSize=8) ? "x64" : "x86"
 +  if (!regexmatch(mcode, "^([0-9]+),(" c ":|.*?," c ":)([^,]+)", &m))
 +    return
 +  if (!DllCall("crypt32\CryptStringToBinary", "str", m.3, "uint", 0, "uint", e[m.1], "ptr", 0, "uint*", &s := 0, "ptr", 0, "ptr", 0))
 +    return
 +  p := DllCall("GlobalAlloc", "uint", 0, "ptr", s, "ptr")
 +  if (c="x64")
 +    DllCall("VirtualProtect", "ptr", p, "ptr", s, "uint", 0x40, "uint*", &op := 0)
 +  if (DllCall("crypt32\CryptStringToBinary", "str", m.3, "uint", 0, "uint", e[m.1], "ptr", p, "uint*", &s, "ptr", 0, "ptr", 0))
 +    return p
 +  DllCall("GlobalFree", "ptr", p)
 +}
 +</runner>
 +
 +When working with MCL, importing functions like this can be handled automatically by the ''MCL_IMPORT'' macro. Any standalone loader generated by MCL that has an import like this will automatically include any AHK-side import loading code that would otherwise have to be written manually. Imports done this way are automatically added to the global scope, so they do not have to be passed as a parameter to DllCall.
 +
 +<runner ahk2>
 +#Requires AutoHotkey v2.0
 +#include <MCL>
 +
 +lib := MCL.FromC("
 +(
 +#include <mcl.h>
 +MCL_IMPORT(double, msvcrt, sqrt, (double));
 +
 +double hypotenuse(double a, double b) {
 + return sqrt(a * a + b * b);
 +}
 +)")
 +
 +MsgBox DllCall(lib, "Double", 3.0, "Double", 4.0, "Cdecl Double")
 +</runner>
 +
 +MCL can also be used to import functions from third-party DLLs, such as lua54.dll. For more information about that, please refer to the [[libraries:machine_code:mcl|library page for MCL]].
 ===== Compatibility Notes ===== ===== Compatibility Notes =====