[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/tminka/pythonnet/master/src/runtime/platform/NativeCodePage.cs [Back]  [Original]

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

namespace Python.Runtime.Platform
{
    class NativeCodePageHelper
    {
        /// 
        /// Initialized by InitializeNativeCodePage.
        ///
        /// This points to a page of memory allocated using mmap or VirtualAlloc
        /// (depending on the system), and marked read and execute (not write).
        /// Very much on purpose, the page is *not* released on a shutdown and
        /// is instead leaked. See the TestDomainReload test case.
        ///
        /// The contents of the page are two native functions: one that returns 0,
        /// one that returns 1.
        ///
        /// If python didn't keep its gc list through a Py_Finalize we could remove
        /// this entire section.
        /// 
        internal static IntPtr NativeCodePage = IntPtr.Zero;


        /// 
        /// Structure to describe native code.
        ///
        /// Use NativeCode.Active to get the native code for the current platform.
        ///
        /// Generate the code by creating the following C code:
        /// 
        /// int Return0() { return 0; }
        /// int Return1() { return 1; }
        /// 
        /// Then compiling on the target platform, e.g. with gcc or clang:
        /// cc -c -fomit-frame-pointer -O2 foo.c
        /// And then analyzing the resulting functions with a hex editor, e.g.:
        /// objdump -disassemble foo.o
        /// 
        internal class NativeCode
        {
            /// 
            /// The code, as a string of bytes.
            /// 
            public byte[] Code { get; private set; }

            /// 
            /// Where does the "return 0" function start?
            /// 
            public int Return0 { get; private set; }

            /// 
            /// Where does the "return 1" function start?
            /// 
            public int Return1 { get; private set; }

            public static NativeCode Active
            {
                get
                {
                    switch (RuntimeInformation.ProcessArchitecture)
                    {
                        case Architecture.X86:
                            return I386;
                        case Architecture.X64:
                            return X86_64;
                        default:
                            return null;
                    }
                }
            }

            /// 
            /// Code for x86_64. See the class comment for how it was generated.
            /// 
            public static readonly NativeCode X86_64 = new NativeCode()
            {
                Return0 = 0x10,
                Return1 = 0,
                Code = new byte[]
                {
                    // First Return1:
                    0xb8, 0x01, 0x00, 0x00, 0x00, // movl $1, %eax
                    0xc3, // ret

                    // Now some padding so that Return0 can be 16-byte-aligned.
                    // I put Return1 first so there's not as much padding to type in.
                    0x66, 0x2e, 0x0f, 0x1f, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, // nop

                    // Now Return0.
                    0x31, 0xc0, // xorl %eax, %eax
                    0xc3, // ret
                }
            };

            /// 
            /// Code for X86.
            ///
            /// It's bitwise identical to X86_64, so we just point to it.
            /// 
            /// 
            public static readonly NativeCode I386 = X86_64;
        }

        /// 
        /// Platform-dependent mmap and mprotect.
        /// 
        internal interface IMemoryMapper
        {
            /// 
            /// Map at least numBytes of memory. Mark the page read-write (but not exec).
            /// 
            IntPtr MapWriteable(int numBytes);

            /// 
            /// Sets the mapped memory to be read-exec (but not write).
            /// 
            void SetReadExec(IntPtr mappedMemory, int numBytes);
        }

        class WindowsMemoryMapper : IMemoryMapper
        {
            const UInt32 MEM_COMMIT = 0x1000;
            const UInt32 MEM_RESERVE = 0x2000;
            const UInt32 PAGE_READWRITE = 0x04;
            const UInt32 PAGE_EXECUTE_READ = 0x20;

            [DllImport("kernel32.dll")]
            static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, UInt32 flAllocationType, UInt32 flProtect);

            [DllImport("kernel32.dll")]
            static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, UInt32 flNewProtect, out UInt32 lpflOldProtect);

            public IntPtr MapWriteable(int numBytes)
            {
                return VirtualAlloc(IntPtr.Zero, new IntPtr(numBytes),
                                    MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
            }

            public void SetReadExec(IntPtr mappedMemory, int numBytes)
            {
                UInt32 _;
                VirtualProtect(mappedMemory, new IntPtr(numBytes), PAGE_EXECUTE_READ, out _);
            }
        }

        class UnixMemoryMapper : IMemoryMapper
        {
            const int PROT_READ = 0x1;
            const int PROT_WRITE = 0x2;
            const int PROT_EXEC = 0x4;

            const int MAP_PRIVATE = 0x2;
            int MAP_ANONYMOUS
            {
                get
                {
                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
                    {
                        return 0x20;
                    }
                    else
                    {
                        // OSX, FreeBSD
                        return 0x1000;
                    }
                }
            }

            [DllImport("libc")]
            static extern IntPtr mmap(IntPtr addr, IntPtr len, int prot, int flags, int fd, IntPtr offset);

            [DllImport("libc")]
            static extern int mprotect(IntPtr addr, IntPtr len, int prot);

            public IntPtr MapWriteable(int numBytes)
            {
                // MAP_PRIVATE must be set on linux, even though MAP_ANON implies it.
                // It doesn't hurt on darwin, so just do it.
                return mmap(IntPtr.Zero, new IntPtr(numBytes), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, IntPtr.Zero);
            }

            public void SetReadExec(IntPtr mappedMemory, int numBytes)
            {
                mprotect(mappedMemory, new IntPtr(numBytes), PROT_READ | PROT_EXEC);
            }
        }

        internal static IMemoryMapper CreateMemoryMapper()
        {
            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
            {
                return new WindowsMemoryMapper();
            }
            else
            {
                // Linux, OSX, FreeBSD
                return new UnixMemoryMapper();
            }
        }

        /// 
        /// Initializes the native code page.
        ///
        /// Safe to call if we already initialized (this function is idempotent).
        /// 
        /// 
        internal static void InitializeNativeCodePage()
        {
            // Do nothing if we already initialized.
            if (NativeCodePage != IntPtr.Zero)
            {
                return;
            }

            // Allocate the page, write the native code into it, then set it
            // to be executable.
            IMemoryMapper mapper = CreateMemoryMapper();
            int codeLength = NativeCode.Active.Code.Length;
            NativeCodePage = mapper.MapWriteable(codeLength);
            Marshal.Copy(NativeCode.Active.Code, 0, NativeCodePage, codeLength);
            mapper.SetReadExec(NativeCodePage, codeLength);
        }
    }
}

Web Proxy Viewer  |  New URL  |  Original Page