क्या यह पूरी तरह से एक प्रबंधित .NET भाषा में JIT संकलक (मूल कोड में) लिखना संभव है


84

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


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

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

2
"अनधिकृत कोड पर गतिशील रूप से नियंत्रण कैसे हस्तांतरित करें" जैसे शीर्षक से आपको बंद होने का कम जोखिम हो सकता है। यह बात और भी लगती है। कोड उत्पन्न करना समस्या नहीं है।
हेनक होल्टरमैन

8
सबसे सरल विचार एक फाइल में बाइट सरणी के नीचे लिखना और ओएस को इसे चलाने देना होगा। आखिरकार, आपको एक संकलक की आवश्यकता है , न कि एक दुभाषिया (जो कि संभव होगा, लेकिन अधिक जटिल)।
व्लाद

3
एक बार जब आप JIT को इच्छित कोड संकलित कर लेते हैं, तो आप कुछ मानवरहित मेमोरी (निष्पादन योग्य के रूप में चिह्नित) को आवंटित करने के लिए Win32 API का उपयोग कर सकते हैं, उस मेमोरी स्पेस में संकलित कोड को कॉपी कर सकते हैं, फिर calliसंकलित कोड को कॉल करने के लिए IL opcode का उपयोग कर सकते हैं ।
जैक पी।

जवाबों:


71

और अवधारणा के पूर्ण प्रमाण के लिए यहाँ 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

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

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

70

हाँ तुम कर सकते हो। वास्तव में, यह मेरा काम है :)

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

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

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


4
क्या आप अपने द्वारा बनाए गए कोड पर कूद नहीं सकते फ़ंक्शन पॉइंटर के माध्यम से मूल कोड पर कूदना संभव होने के बजाय: क्या फ़ंक्शन पॉइंटर के माध्यम से मूल कोड पर कूदना संभव नहीं है?
इयान बॉयड

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

@IanBoyd NoExecute बफर ओवररन और संबंधित मुद्दों से परेशानी से बचने का एक और तरीका है। यह आपके अपने कोड से सुरक्षा नहीं है, यह अवैध कोड निष्पादन को कम करने में मदद करने के लिए कुछ है।
लुआण

51

चाल होना चाहिए VirtualAlloc साथ EXECUTE_READWRITE-flag और (पी / आह्वान की जरूरत है) 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 दोनों के साथ काम करता है)।


30

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

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

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


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

मैं एक मिल AccessViolationExceptionअगर मैं अपने उदाहरण को चलाने के लिए प्रयास करें। मुझे लगता है कि यह केवल तभी काम करता है जब DEP अक्षम हो।
रासमस फैबर

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

मुझे लगता है कि लिंक मृत है, अफसोस, मैं इसे संपादित करूंगा, लेकिन मुझे मूल का स्थानांतरण नहीं मिला।
हाबिल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.