Code Optimization

Interesting things about software development and code optimization

Orange Pi 3 and Ubuntu

Hello fiends,


Let me share my experience with Orange Pi 3 and Ubuntu installation. It sounds like nothing special and easy but finally it is not so easy as it sounds.

Orange Pi 3 is single-board computer based on H6 Quad-core 64-bit 1.8GHZ ARM Cortex™-A53 and high-performance multi-core GPU Mali T720.

At writing time there is no good or official GPU driver for Ubuntu and this is a little bit sad if you really need it but it is still working well, ok it is just working and this is good as well :)


So lets start. We will need the single-board PC, Ubuntu build from the supplier website https://www.orangepi.org/downloadresources/ (I will use Ubuntu Desktop with EMMC linux 4.9 v1.3 image)


To be able to boot anything other than the pre-installed Android media you need to flash an image to an SD Card and boot from the SD card. To flash SD Card you need to download and use the following app balenaEtcher . Run it, select the unzipped image and click the write button.


Now it is time for booting from the SD card so just insert the SD Card into the SD card socket and turn on your orange board.

After that you will see sign-in screen and there is default password: orangepi


Next step is to extend your root file system size and add 1 GB swap file at least:

resize_rootfs.sh
git clone https://github.com/jetsonhacksnano/installSwapfile
cd installSwapfile
#modify the .sh file and put your own required size - it is 6 GB by default
./installSwapfile.sh


In my case I always had problems with locales so I had to run the following:

> export LC_ALL="C"
> sudo dpkg-reconfigure locales


Now everything is ready and if you have EMMC and want to overwrite the android os and put Ubuntu and boot from EMMC instead of SD Card then you just need to run the following script:

OrangePi_install2EMMC.sh

Seems that's all


Thank you and see you soon ;)



1vqHSTrq1GEoEF7QsL8dhmJfRMDVxhv2y



C#, .NET, x32/x64 Assembler and cross-platform code

Hello my dear friends.


Today we will look into such popular thing like cross-platform code, and such powerful thing like assembler. Of course, we will use C#.NET for this all as usually  :)

To make a cross-platform code we have to be aware of two main things:

- method call conventions 

- system API

We need different platform dependent system APIs to allocate and free executing memory and we need universal code to avoid method call convention problems.

To call system dependent APIs we need to understand what OS we are running under, here is code how we can do it:

 

private enum Platform

{

Windows,

Linux,

Mac

}

private static Platform RunningPlatform()

{

switch (Environment.OSVersion.Platform)

{

case PlatformID.Unix: // Well, there are chances MacOSX is reported as Unix instead of MacOSX. // Instead of platform check, we'll do a feature checks (Mac specific root folders) if (Directory.Exists("/Applications")

& Directory.Exists("/System")

& Directory.Exists("/Users") & Directory.Exists("/Volumes")) return Platform.Mac; else return Platform.Linux; case PlatformID.MacOSX: return Platform.Mac; default: return Platform.Windows;

}

}

Now we are going to declare PInvokes for linux and windows and implement logic to allocate and free memory with regards to OS we are running under (linux pinvokes were taken from the Mono source code):

#region Windows

[Flags]

private enum AllocationTypes : uint

{

Commit = 0x1000, Reserve = 0x2000,

Reset = 0x80000, LargePages = 0x20000000,

Physical = 0x400000, TopDown = 0x100000,

WriteWatch = 0x200000

}

[Flags]

private enum MemoryProtections : uint

{

Execute = 0x10, ExecuteRead = 0x20,

ExecuteReadWrite = 0x40, ExecuteWriteCopy = 0x80,

NoAccess = 0x01, ReadOnly = 0x02,

ReadWrite = 0x04, WriteCopy = 0x08,

GuartModifierflag = 0x100, NoCacheModifierflag = 0x200,

WriteCombineModifierflag = 0x400

}

[Flags]

private enum FreeTypes : uint

{

Decommit = 0x4000, Release = 0x8000

}

[DllImport("kernel32.dll", SetLastError = true)]

private static extern IntPtr VirtualAlloc(

IntPtr lpAddress,

UIntPtr dwSize,

AllocationTypes flAllocationType,

MemoryProtections flProtect);

 

[DllImport("kernel32")]

[return: MarshalAs(UnmanagedType.Bool)]

private static extern bool VirtualFree(

IntPtr lpAddress,

uint dwSize,

FreeTypes flFreeType);

#endregion

#region Unix

[AttributeUsage(

AttributeTargets.Class |

AttributeTargets.Delegate |

AttributeTargets.Enum |

AttributeTargets.Field |

AttributeTargets.Struct)]

private class MapAttribute : Attribute

{

private string nativeType;

private string suppressFlags;

public MapAttribute()

{

}

public MapAttribute(string nativeType)

{

this.nativeType = nativeType;

}

public string NativeType

{

get { return nativeType; }

}

public string SuppressFlags

{

get { return suppressFlags; }

set { suppressFlags = value; }

}

}

private const string MPH = "MonoPosixHelper";

private const string LIBC = "msvcrt";

[Map]

[Flags]

private enum MmapProts : int

{

PROT_READ = 0x1, // Page can be read.

PROT_WRITE = 0x2, // Page can be written.

PROT_EXEC = 0x4, // Page can be executed.

PROT_NONE = 0x0, // Page can not be accessed.

PROT_GROWSDOWN = 0x01000000, // Extend change to start of

// growsdown vma (mprotect only).

PROT_GROWSUP = 0x02000000, // Extend change to start of

// growsup vma (mprotect only).

}

[Map]

[Flags]

private enum MmapFlags : int

{

MAP_SHARED = 0x01, // Share changes.

MAP_PRIVATE = 0x02, // Changes are private.

MAP_TYPE = 0x0f, // Mask for type of mapping.

MAP_FIXED = 0x10, // Interpret addr exactly.

MAP_FILE = 0,

MAP_ANONYMOUS = 0x20, // Don't use a file.

MAP_ANON = MAP_ANONYMOUS,

// These are Linux-specific.

MAP_GROWSDOWN = 0x00100, // Stack-like segment.

MAP_DENYWRITE = 0x00800, // ETXTBSY

MAP_EXECUTABLE = 0x01000, // Mark it as an executable.

MAP_LOCKED = 0x02000, // Lock the mapping.

MAP_NORESERVE = 0x04000, // Don't check for reservations.

MAP_POPULATE = 0x08000, // Populate (prefault) pagetables.

MAP_NONBLOCK = 0x10000, // Do not block on IO.

}

[DllImport(MPH, SetLastError = true,

EntryPoint = "Mono_Posix_Syscall_mmap")]

private static extern IntPtr mmap(IntPtr start, ulong length,

MmapProts prot, MmapFlags flags, int fd, long offset);

[DllImport(MPH, SetLastError = true,

EntryPoint = "Mono_Posix_Syscall_munmap")]

public static extern int munmap(IntPtr start, ulong length);

[DllImport(MPH, SetLastError = true,

EntryPoint = "Mono_Posix_Syscall_mprotect")]

private static extern int mprotect(IntPtr start, ulong len, MmapProts prot);


[DllImport(MPH, CallingConvention = CallingConvention.Cdecl,

SetLastError = true, EntryPoint = "Mono_Posix_Stdlib_malloc")] private static extern IntPtr malloc(ulong size);

[DllImport(LIBC, CallingConvention = CallingConvention.Cdecl)] public static extern void free(IntPtr ptr);

#endregion


[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]

public unsafe delegate void asmFunc();

public static IntPtr VirtualAlloc(uint size)

{

IntPtr ptr = IntPtr.Zero;

if (RunningPlatform() == Platform.Windows)

{

ptr = VirtualAlloc(

IntPtr.Zero,

new UIntPtr(size),

AllocationTypes.Commit | AllocationTypes.Reserve,

MemoryProtections.ExecuteReadWrite);

}

else

{

Console.WriteLine("Linux memory allocation...");

ptr = mmap(IntPtr.Zero, 4096, MmapProts.PROT_EXEC | MmapProts.PROT_READ | MmapProts.PROT_WRITE, MmapFlags.MAP_ANONYMOUS | MmapFlags.MAP_PRIVATE, 0, 0);

Console.WriteLine("memory ptr: " + ptr.ToInt64());

}

return ptr;

}


public static void VirtualFree(IntPtr ptr, uint size)

{

if (RunningPlatform() == Platform.Windows)

{

VirtualFree(ptr, size, FreeTypes.Release);

}

else

{

Console.WriteLine("Free memory ptr: " + ptr.ToInt64());

int r = munmap(ptr, size);

Console.WriteLine("memory free status: " + r);

}

}


Ok, we have methods to allocate and free memory and now, we need some predefined assembly code and template to avoid any calling convention problems. To do it we will declare our methods and delegates as parameter-less and will pass parameters as declared bytes:

 

byte[] codeArray = new byte[]

{

0xE8, // call next code after data

0x00,

0x00,

0x00,

0x00,

//

//data will go here

//


0x5B, // pop e/rbx - now e/rbx looks into data address

0xFF, // inc dword [e/rbx]

0x03,

(byte)(IntPtr.Size > 4 ? 0x48 : 0x90),

0xFF, // inc e/rbx

0xC3,

(byte)(IntPtr.Size > 4 ? 0x48 : 0x90),

0xFF, // inc e/rbx

0xC3,

(byte)(IntPtr.Size > 4 ? 0x48 : 0x90),

0xFF, // inc e/rbx

0xC3,

(byte)(IntPtr.Size > 4 ? 0x48 : 0x90),

0xFF, // inc e/rbx

0xC3,

(byte)(IntPtr.Size > 4 ? 0x48 : 0x90), // dec d/qword [rbx]

0xFF,

0x0B,


0xC3 // retn - return from your method

};

byte[] dataArray = new byte[]

{

0xFF, //parameter Int32

0x00,

0x00,

0x00,

0x00, //parameter Int64

0x00,

0x00,

0x00,

0x00,

0x00,

0x00,

0x00

};


Console.WriteLine("Ptr size: " + IntPtr.Size);

//allocate memory for our asm method

IntPtr pp = Native.VirtualAlloc((uint)(codeArray.Length + dataArray.Length));

unsafe

{

IntPtr p = pp;

int n = 0;

byte* bptr = (byte*)p;

Marshal.Copy(codeArray, 0, p, 4);

p += 1;

//write offset to the next code line

Marshal.WriteInt32(p, dataArray.Length);

p += 4;

//copy data

Marshal.Copy(dataArray, 0, p, dataArray.Length);

p += dataArray.Length;

//copy rest of the code

Marshal.Copy(codeArray, 5, p, codeArray.Length - 5);

bptr[n] = bptr[n];

n = 0;

}

Native.asmFunc asmFunc = (Native.asmFunc)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(pp, typeof(Native.asmFunc));

Console.WriteLine("in param int32 = " + BitConverter.ToInt32(dataArray, 0));

Console.WriteLine("in param int64 = " + BitConverter.ToInt64(dataArray, sizeof(Int32)));

Console.WriteLine("call asm method...");

asmFunc();

Console.WriteLine("exit asm method");

As our methods are parameter-less a system will not use stack and we will not have to care about stack. To get address of our first parameter we will use well-known technic, such as call addr and pop reg, that moves to our next processor instruction and pops back an address, this address will be the address of our first parameter.

To pass back any parameters from our assembly method we will use the same address to put them before return back.

//copy params back to array

Marshal.Copy(pp + 5, dataArray, 0, dataArray.Length);

Console.WriteLine("out param int32 = " + BitConverter.ToInt32(dataArray, 0));

Console.WriteLine("out param int64 = " + BitConverter.ToInt64(dataArray, sizeof(Int32)));

//free allocated memory

Native.VirtualFree(pp, (uint)(codeArray.Length + dataArray.Length));

GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced);

Console.WriteLine("any key to exit");

Console.ReadLine();


Now run it and test it. Remember that running this code, or any assembly code, under visual studio may cause error and exception, so always test your code out of any debugger.


I did test this code under Windows 10 64 bit and compiling in x32 and x64 modes, and Linux Ubuntu 14.04 64 bit mode.


Comments are welcome.


Thank you all,

See you later :)


1vqHSTrq1GEoEF7QsL8dhmJfRMDVxhv2y