programing

관리되는 .NET 언어로 JIT 컴파일러 (네이티브 코드로)를 작성할 수 있습니까?

nasanasas 2020. 9. 23. 07:41
반응형

관리되는 .NET 언어로 JIT 컴파일러 (네이티브 코드로)를 작성할 수 있습니까?


나는 JIT 컴파일러를 작성하는 아이디어를 가지고 놀면서 관리 코드에서 모든 것을 이론적으로 작성할 수 있는지 궁금합니다. 특히 어셈블러를 바이트 배열로 생성 한 후 실행을 시작하려면 어떻게해야합니까?


그리고 완전한 개념 증명을 위해 여기 에 Rasmus의 JIT 접근 방식을 F #으로 완벽하게 변환 할 수 있습니다 .

open System
open System.Runtime.InteropServices

type AllocationType =
    | COMMIT=0x1000u

type MemoryProtection =
    | EXECUTE_READWRITE=0x40u

type FreeType =
    | DECOMMIT = 0x4000u

[<DllImport("kernel32.dll", SetLastError=true)>]
extern IntPtr VirtualAlloc(IntPtr lpAddress, UIntPtr dwSize, AllocationType flAllocationType, MemoryProtection flProtect);

[<DllImport("kernel32.dll", SetLastError=true)>]
extern bool VirtualFree(IntPtr lpAddress, UIntPtr dwSize, FreeType freeType);

let JITcode: byte[] = [|0x55uy;0x8Buy;0xECuy;0x8Buy;0x45uy;0x08uy;0xD1uy;0xC8uy;0x5Duy;0xC3uy|]

[<UnmanagedFunctionPointer(CallingConvention.Cdecl)>] 
type Ret1ArgDelegate = delegate of (uint32) -> uint32

[<EntryPointAttribute>]
let main (args: string[]) =
    let executableMemory = VirtualAlloc(IntPtr.Zero, UIntPtr(uint32(JITcode.Length)), AllocationType.COMMIT, MemoryProtection.EXECUTE_READWRITE)
    Marshal.Copy(JITcode, 0, executableMemory, JITcode.Length)
    let jitedFun = Marshal.GetDelegateForFunctionPointer(executableMemory, typeof<Ret1ArgDelegate>) :?> Ret1ArgDelegate
    let mutable test = 0xFFFFFFFCu
    printfn "Value before: %X" test
    test <- jitedFun.Invoke test
    printfn "Value after: %X" test
    VirtualFree(executableMemory, UIntPtr.Zero, FreeType.DECOMMIT) |> ignore
    0

행복하게 양보를 실행하는

Value before: FFFFFFFC
Value after: 7FFFFFFE

그래 넌 할수있어. 사실, 그것은 내 직업입니다 :)

저는 GPU.NET을 완전히 F #으로 작성했습니다 (단위 테스트 모듈로)-실제로 .NET CLR과 마찬가지로 런타임에 IL을 분해하고 JIT합니다. 사용하려는 기본 가속 장치에 대해 네이티브 코드를 내 보냅니다. 현재 우리는 Nvidia GPU 만 지원하지만 최소한의 작업으로 리 타겟팅 할 수 있도록 시스템을 설계 했으므로 향후 다른 플랫폼을 지원할 가능성이 높습니다.

성능에 관해서는 F #이 있습니다. 최적화 된 모드 (꼬리 호출 포함)에서 컴파일 할 때 JIT 컴파일러 자체는 아마도 CLR (C ++, IIRC로 작성 됨) 내의 컴파일러만큼 빠릅니다.

실행을 위해, jitted 코드를 실행하기 위해 하드웨어 드라이버에 제어를 전달할 수 있다는 이점이 있습니다. 그러나 .NET은 관리되지 않는 / 네이티브 코드에 대한 함수 포인터를 지원하기 때문에 CPU에서 수행하기가 더 어렵지 않을 것입니다 (일반적으로 .NET에서 제공하는 안전 / 보안을 잃을 수 있음).


트릭이 있어야 할 VirtualAlloc을EXECUTE_READWRITE-flag와 (P / 호출 필요) Marshal.GetDelegateForFunctionPointer는 .

다음은 회전 정수 예제의 수정 된 버전입니다 (여기에는 안전하지 않은 코드가 필요하지 않습니다).

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint Ret1ArgDelegate(uint arg1);

public static void Main(string[] args){
    // Bitwise rotate input and return it.
    // The rest is just to handle CDECL calling convention.
    byte[] asmBytes = new byte[]
    {        
      0x55,             // push ebp
      0x8B, 0xEC,       // mov ebp, esp 
      0x8B, 0x45, 0x08, // mov eax, [ebp+8]
      0xD1, 0xC8,       // ror eax, 1
      0x5D,             // pop ebp 
      0xC3              // ret
    };

    // Allocate memory with EXECUTE_READWRITE permissions
    IntPtr executableMemory = 
        VirtualAlloc(
            IntPtr.Zero, 
            (UIntPtr) asmBytes.Length,    
            AllocationType.COMMIT,
            MemoryProtection.EXECUTE_READWRITE
        );

    // Copy the machine code into the allocated memory
    Marshal.Copy(asmBytes, 0, executableMemory, asmBytes.Length);

    // Create a delegate to the machine code.
    Ret1ArgDelegate del = 
        (Ret1ArgDelegate) Marshal.GetDelegateForFunctionPointer(
            executableMemory, 
            typeof(Ret1ArgDelegate)
        );

    // Call it
    uint n = (uint)0xFFFFFFFC;
    n = del(n);
    Console.WriteLine("{0:x}", n);

    // Free the memory
    VirtualFree(executableMemory, UIntPtr.Zero, FreeType.DECOMMIT);
 }

전체 예제 (이제 X86 및 X64 모두에서 작동).


Using unsafe code, you can "hack" a delegate and make it point to an arbitrary assembly code that you generated and stored in an array. The idea is that delegate has a _methodPtr field, which can be set using Reflection. Here is some sample code:

This is, of course, a dirty hack that may stop working at any time when the .NET runtime changes.

I guess that, in principle, fully managed safe code cannot be allowed to implement JIT, because that would break any security assumptions that the runtime relies on. (Unless, the generated assembly code came with a machine-checkable proof that it does not violate the assumptions...)

참고URL : https://stackoverflow.com/questions/9557293/is-it-possible-to-write-a-jit-compiler-to-native-code-entirely-in-a-managed-n

반응형