2012-03-04 14 views
82

में एक जेआईटी कंपाइलर (मूल कोड में) लिखना संभव है। मैं एक जेआईटी कंपाइलर लिखने के विचार से टकरा रहा हूं और सोच रहा हूं कि क्या यह पूरी बात लिखने के लिए सैद्धांतिक रूप से संभव है प्रबंधित कोड में। विशेष रूप से, एक बार जब आप एक बाइट सरणी में असेंबलर उत्पन्न कर लेते हैं तो आप इसे निष्पादन शुरू करने के लिए कैसे कूदते हैं?क्या एक पूरी तरह से प्रबंधित .NET भाषा

+55

उपज कार्यान्वित में JIT को Rasmus 'दृष्टिकोण का अनुवाद है जेआईटी कंपाइलर। मुझें यह पसंद है। यह JITCEPTION है :) –

+0

मुझे विश्वास नहीं है कि - जबकि आप प्रबंधित भाषाओं में कभी-कभी असुरक्षित संदर्भ में काम कर सकते हैं, मुझे विश्वास नहीं है * आप एक सूचक से एक प्रतिनिधि को संश्लेषित कर सकते हैं - और आप कैसे कूदेंगे जेनरेट कोड के लिए? –

+0

@ डेमियन: असुरक्षित कोड आपको फ़ंक्शन पॉइंटर पर लिखने नहीं देगा? –

उत्तर

69

और अवधारणा यहां का पूरा सबूत के लिए एक पूरी तरह सक्षम एफ #

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 

कि खुशी से एक में एक JIT कम्पाइलर लेखन

Value before: FFFFFFFC 
Value after: 7FFFFFFE 
+1

वाह! उत्कृष्ट जवाब, धन्यवाद! –

+0

मेरे उत्थान के बावजूद, मैं अलग होना चाहता हूं: यह ** मनमाने ढंग से कोड निष्पादन ** है, जेआईटी नहीं - जेआईटी का अर्थ है "बस समय ** संकलन **", लेकिन मुझे इस कोड से "संकलन" पहलू नहीं दिखाई दे रहा है उदाहरण। – rwong

+4

@rwong: "संकलन" पहलू मूल प्रश्न चिंताओं के दायरे में कभी नहीं था। कार्यान्वित करने की प्रबंधित कोड क्षमता * आईएल -> देशी कोड * परिवर्तन थोड़े स्पष्ट है। –

30

असुरक्षित कोड का उपयोग करके, आप एक प्रतिनिधि को "हैक" कर सकते हैं और इसे एक मनमानी असेंबली कोड को इंगित कर सकते हैं जिसे आपने जेनरेट किया है और एक सरणी में संग्रहीत किया है। विचार यह है कि प्रतिनिधि के पास _methodPtr फ़ील्ड है, जिसे प्रतिबिंब का उपयोग करके सेट किया जा सकता है। यहां कुछ नमूना कोड है:

यह वह जगह है, ज़ाहिर है, एक गंदा हैक है कि किसी भी समय था जब .NET रनटाइम परिवर्तन पर काम करना बंद कर सकता है की।

मुझे लगता है कि, सिद्धांत रूप में, पूरी तरह से प्रबंधित सुरक्षित कोड को जेआईटी को लागू करने की अनुमति नहीं दी जा सकती है, क्योंकि यह रनटाइम पर निर्भर करता है कि किसी भी सुरक्षा धारणा को तोड़ देगा। (जब तक, जेनरेटेड असेंबली कोड मशीन-चेक करने योग्य सबूत के साथ नहीं आया है कि यह धारणाओं का उल्लंघन नहीं करता है ...)

+1

अच्छा हैक। शायद आप टूटी हुई लिंक के साथ बाद के मुद्दों से बचने के लिए कोड के कुछ हिस्सों को इस पोस्ट में कॉपी कर सकते हैं। (या बस इस पोस्ट में एक छोटा सा विवरण लिखें)। –

+0

यदि मैं आपका उदाहरण चलाने की कोशिश करता हूं तो मुझे 'AccessViolationException' मिलता है। मुझे लगता है कि यह केवल तभी काम करता है जब डीईपी अक्षम हो। –

+1

लेकिन अगर मैं EXECUTE_READWRITE ध्वज के साथ स्मृति आवंटित करता हूं और _methodPtr फ़ील्ड में इसका उपयोग करता हूं तो यह ठीक काम करता है। रोटर-कोड के माध्यम से देखकर, यह मूल रूप से मार्शल। गेटडिलेगेटफोरफंक्शन पॉइंटर() करता है, सिवाय इसके कि यह स्टैक और हैंडलिंग सुरक्षा स्थापित करने के लिए कोड के चारों ओर कुछ अतिरिक्त थंक्स जोड़ता है। –

69

हाँ, आप कर सकते हैं। असल में, यह मेरा काम है :)

मैंने पूरी तरह से एफ # (मॉड्यूलो हमारे यूनिट परीक्षण) में GPU.NET लिखा है - यह वास्तव में .NET CLR की तरह रन-टाइम पर डिस्सेबल और जेआईटीएस आईएल लिखा है। हम जो भी अंतर्निहित त्वरण उपकरण का उपयोग करना चाहते हैं उसके लिए हम देशी कोड उत्सर्जित करते हैं; वर्तमान में हम केवल एनवीडिया जीपीयू का समर्थन करते हैं, लेकिन मैंने अपने सिस्टम को कम से कम काम के साथ पुनः लक्षित करने के लिए डिज़ाइन किया है, इसलिए संभवतः हम भविष्य में अन्य प्लेटफॉर्म का समर्थन करेंगे।

प्रदर्शन के लिए, मेरे पास एफ # धन्यवाद है - ऑप्टिमाइज्ड मोड (टेलकॉल के साथ) में संकलित होने पर, हमारे जेआईटी कंपाइलर शायद सीएलआर (जो सी ++, आईआईआरसी में लिखा गया है) के भीतर कंपाइलर जितना तेज हो।

निष्पादन के लिए, हमारे पास जेट कोड चलाने के लिए हार्डवेयर ड्राइवरों को नियंत्रण पास करने में सक्षम होने का लाभ है; हालांकि, सीपीयू पर ऐसा करना मुश्किल नहीं होगा क्योंकि .NET फ़ंक्शन पॉइंटर्स को अप्रबंधित/मूल कोड के लिए समर्थन करता है (हालांकि आप सामान्य रूप से .NET द्वारा प्रदान की गई कोई सुरक्षा/सुरक्षा खो देंगे)।

+4

NoExecute का पूरा बिंदु नहीं है कि आप उस कोड पर कूद नहीं सकते जिसे आपने स्वयं बनाया है? एक फ़ंक्शन पॉइंटर के माध्यम से मूल कोड पर कूदने के बजाय: क्या यह ** सूचक नहीं है ** फ़ंक्शन पॉइंटर के माध्यम से देशी कोड पर कूदना संभव है? –

+0

एक बहुत ही अच्छी परियोजना की तरह लगता है। –

+0

बहुत बढ़िया प्रोजेक्ट, हालांकि मुझे लगता है कि अगर आप इसे गैर-लाभकारी अनुप्रयोगों के लिए मुफ्त बनाते हैं तो आपको बहुत अधिक जोखिम मिलेगा। आप "उत्साही" स्तर से चंप-परिवर्तन खो देंगे, लेकिन इसका उपयोग करने वाले लोगों से बढ़े हुए एक्सपोजर के लिए यह उचित होगा * (मुझे पता है कि मैं निश्चित रूप से करूंगा;)) *! –

50

चाल VirtualAllocEXECUTE_READWRITE -flag (पी/Invoke की आवश्यकता है) और 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); 
} 

Full example (अब दोनों x86 और x64 के साथ काम करता है):

यहाँ घुमाने पूर्णांक उदाहरण का एक संशोधित संस्करण (ध्यान दें कि असुरक्षित कोड यहाँ की जरूरत है) है।

+1

शानदार, धन्यवाद! –

संबंधित मुद्दे