Welcome Guest ( Log In | Register )

Outline · [ Standard ] · Linear+

C# Michal: I write C# which run without .NET runtime

views
     
TSTullamarine
post Mar 20 2023, 10:12 PM, updated 3y ago

Getting Started
**
Validating
163 posts

Joined: Apr 2020
Source: https://github.com/MichalStrehovsky

QUOTE
I'm Michal, I live in Slovakia, and I work remotely at the .NET Runtime team at Microsoft.


He has a top repo 'zerosharp' (Demo of the potential of C# for systems programming with the .NET native ahead-of-time compilation technology.)

QUOTE
no-runtime is a rather pointless sample that demonstrates how to write code in C# that is directly runnable without a runtime. C# has value types and you can p/invoke into an unmanaged memory allocator, so you can do things with this, but you're so severily limited it's rather pointless. But Hello world ends up being about 8 kB native EXE with no dependencies, so that's rather cool.


https://github.com/MichalStrehovsky/zerosharp

CODE
using System;
using System.Runtime;
using System.Runtime.InteropServices;

#region A couple very basic things
namespace System
{
   public class Object
   {
#pragma warning disable 169
       // The layout of object is a contract with the compiler.
       private IntPtr m_pMethodTable;
#pragma warning restore 169
   }
   public struct Void { }

   // The layout of primitive types is special cased because it would be recursive.
   // These really don't need any fields to work.
   public struct Boolean { }
   public struct Char { }
   public struct SByte { }
   public struct Byte { }
   public struct Int16 { }
   public struct UInt16 { }
   public struct Int32 { }
   public struct UInt32 { }
   public struct Int64 { }
   public struct UInt64 { }
   public struct IntPtr { }
   public struct UIntPtr { }
   public struct Single { }
   public struct Double { }

   public abstract class ValueType { }
   public abstract class Enum : ValueType { }

   public struct Nullable<T> where T : struct { }
   
   public sealed class String { public readonly int Length; }
   public abstract class Array { }
   public abstract class Delegate { }
   public abstract class MulticastDelegate : Delegate { }

   public struct RuntimeTypeHandle { }
   public struct RuntimeMethodHandle { }
   public struct RuntimeFieldHandle { }

   public class Attribute { }

   public enum AttributeTargets { }

   public sealed class AttributeUsageAttribute : Attribute
   {
       public AttributeUsageAttribute(AttributeTargets validOn) { }
       public bool AllowMultiple { get; set; }
       public bool Inherited { get; set; }
   }

   public class AppContext
   {
       public static void SetData(string s, object o) { }
   }

   namespace Runtime.CompilerServices
   {
       public class RuntimeHelpers
       {
           public static unsafe int OffsetToStringData => sizeof(IntPtr) + sizeof(int);
       }
   }
}
namespace System.Runtime.InteropServices
{
   public sealed class DllImportAttribute : Attribute
   {
       public DllImportAttribute(string dllName) { }
   }
}
#endregion

#region Things needed by ILC
namespace System
{
   namespace Runtime
   {
       internal sealed class RuntimeExportAttribute : Attribute
       {
           public RuntimeExportAttribute(string entry) { }
       }
   }

   class Array<T> : Array { }
}

namespace Internal.Runtime.CompilerHelpers
{
   // A class that the compiler looks for that has helpers to initialize the
   // process. The compiler can gracefully handle the helpers not being present,
   // but the class itself being absent is unhandled. Let's add an empty class.
   class StartupCodeHelpers
   {
       // A couple symbols the generated code will need we park them in this class
       // for no particular reason. These aid in transitioning to/from managed code.
       // Since we don't have a GC, the transition is a no-op.
       [RuntimeExport("RhpReversePInvoke")]
       static void RhpReversePInvoke(IntPtr frame) { }
       [RuntimeExport("RhpReversePInvokeReturn")]
       static void RhpReversePInvokeReturn(IntPtr frame) { }
       [RuntimeExport("RhpPInvoke")]
       static void RhpPInvoke(IntPtr frame) { }
       [RuntimeExport("RhpPInvokeReturn")]
       static void RhpPInvokeReturn(IntPtr frame) { }

       [RuntimeExport("RhpFallbackFailFast")]
       static void RhpFallbackFailFast() { while (true); }
   }
}
#endregion

unsafe class Program
{
   [DllImport("libc")]
   static extern int printf(byte* fmt);

   [DllImport("kernel32")]
   static extern IntPtr GetStdHandle(int nStdHandle);

   [DllImport("kernel32")]
   static extern IntPtr WriteConsoleW(IntPtr hConsole, void* lpBuffer, int charsToWrite, out int charsWritten, void* reserved);

#if !WINDOWS
   // Export this as "main" so that we can link with the C runtime library properly.
   // If the C runtime library is not initialized we can't even printf.
   // This is not needed on Windows because we don't call the C runtime.
   [RuntimeExport("main")]
#endif
   static int Main()
   {
       string hello = "Hello world!\n";
       fixed (char* pHello = hello)
       {
#if WINDOWS
           WriteConsoleW(GetStdHandle(-11), pHello, hello.Length, out int _, null);
#else
           // Once C# has support for UTF-8 string literals, this can be simplified.
           // https://github.com/dotnet/csharplang/issues/2911
           // Since we don't have that, convert from UTF-16 to ASCII.
           byte* pHelloASCII = stackalloc byte[hello.Length + 1];
           for (int i = 0; i < hello.Length; i++)
               pHelloASCII[i] = (byte)pHello[i];

           printf(pHelloASCII);
#endif
       }

       return 42;
   }
}

jibpek
post Mar 20 2023, 10:23 PM

Enthusiast
*****
Junior Member
711 posts

Joined: Jul 2012
Want small "Hello, World"? Do this.


C:\>debug
-A
1165:0100 MOV AH, 9
1165:0102 MOV DX, 108
1165:0105 INT 21
1165:0107 RET
1165:0108 DB 'hello, world', D, A, '$'
1165:0117
-G
hello, world

Program terminated normally
-N HELLO.COM
-R CX
CX 0000
:17
-W
Writing 00017 bytes
-Q

C:\>HELLO
hello, world

C:\>
TSTullamarine
post Mar 20 2023, 10:29 PM

Getting Started
**
Validating
163 posts

Joined: Apr 2020
QUOTE(jibpek @ Mar 20 2023, 10:23 PM)
Want small "Hello, World"? Do this.
C:\>debug
-A
1165:0100 MOV AH, 9
1165:0102 MOV DX, 108
1165:0105 INT 21
1165:0107 RET
1165:0108 DB 'hello, world', D, A, '$'
1165:0117
-G
hello, world

Program terminated normally
-N HELLO.COM
-R CX
CX 0000
:17
-W
Writing 00017 bytes
-Q

C:\>HELLO
hello, world

C:\>
*
Thanks for sharing, 17 bytes program, but that is for DOS..... On Windows, it is different, program size might be bigger. bruce.gif
hoilok
post Mar 20 2023, 10:30 PM

Regular
******
Senior Member
1,165 posts

Joined: Apr 2007



Could be potential
AbbyCom
post Mar 20 2023, 11:06 PM

Casual
***
Junior Member
457 posts

Joined: Mar 2020
QUOTE(jibpek @ Mar 20 2023, 10:23 PM)
Want small "Hello, World"? Do this.

» Click to show Spoiler - click again to hide... «

*
Sudah hampir 25 tahun x sentuh assembly
jibpek
post Mar 20 2023, 11:17 PM

Enthusiast
*****
Junior Member
711 posts

Joined: Jul 2012
QUOTE(Tullamarine @ Mar 20 2023, 10:29 PM)
Thanks for sharing, 17 bytes program, but that is for DOS..... On Windows, it is different, program size might be bigger.  bruce.gif
*
But yours is also a console apps. And you are importing DLL.

If that were allowed, then this is even better (using Win32.dll)


class Test {
[DllImport("user32.dll", CharSet = CharSet.Unicode)]
public static extern int MessageBox(
IntPtr hWnd,
String text,
String caption,
uint type
);

public static void Main() {
int Result = MessageBox(IntPtr.Zero, "Hello World",
"Hello World", 0);
}
}
TSTullamarine
post Mar 20 2023, 11:27 PM

Getting Started
**
Validating
163 posts

Joined: Apr 2020
QUOTE(jibpek @ Mar 20 2023, 11:17 PM)
But yours is also a console apps. And you are importing DLL.

If that were allowed, then this is even better (using Win32.dll)/
class Test {
  [DllImport("user32.dll", CharSet = CharSet.Unicode)]
  public static extern int MessageBox(
    IntPtr hWnd,
    String text,
    String caption,
    uint type
  );

  public static void Main() {
    int Result = MessageBox(IntPtr.Zero, "Hello World",
      "Hello World", 0);
  }
}
*
Acknowledged, this MessageBox GUI app is certainly better than console app.

But I am not that superior, the code posted in #1 is not mine, I shared a GitHub repo by Microsoft's employee with coders on here.
If you look at the details at post #1, the sample is runnable without .NET runtime, the EXE is standalone.
The example code you posted still need .NET Framework or .NET (depending which project you choose) despite P/Invoke.

Hopefully no misunderstanding here. I myself am amazed with Michal Strehovsky which understand .NET runtime inside out.

This post has been edited by Tullamarine: Mar 20 2023, 11:28 PM
TSTullamarine
post Mar 20 2023, 11:37 PM

Getting Started
**
Validating
163 posts

Joined: Apr 2020
QUOTE(AbbyCom @ Mar 20 2023, 11:06 PM)
Sudah hampir 25 tahun x sentuh assembly
*
Boleh belajar lagi, rasanya kami umur sebaya, saya pun tak sentuh assembly lebih kurang 20 tahun sehingga 2020, barulah belajar semula 32-bit dan 64-bit assembly, sebab dulu kan 16-bit, program DOS tak sesuai lagi untuk zaman sekarang.


jibpek
post Mar 20 2023, 11:47 PM

Enthusiast
*****
Junior Member
711 posts

Joined: Jul 2012
QUOTE(Tullamarine @ Mar 20 2023, 11:27 PM)
Acknowledged, this MessageBox GUI app is certainly better than console app.

But I am not that superior, the code posted in #1 is not mine, I shared a GitHub repo by Microsoft's employee with coders on here.
If you look at the details at post #1, the sample is runnable without .NET runtime, the EXE is standalone.
The example code you posted still need .NET Framework or .NET (depending which project you choose) despite P/Invoke.

Hopefully no misunderstanding here. I myself am amazed with Michal Strehovsky which understand .NET runtime inside out.
*
You can't run C# without the dotnet framework runtime or mono installed.

The above code requires only

using System;
using System.Runtime.InteropServices;

flashang
post Mar 22 2023, 06:50 PM

Casual
***
Junior Member
355 posts

Joined: Aug 2021


QUOTE(jibpek @ Mar 20 2023, 10:23 PM)
Want small "Hello, World"? Do this.
C:\>debug
-A
1165:0100 MOV AH, 9
1165:0102 MOV DX, 108
1165:0105 INT 21
1165:0107 RET
1165:0108 DB 'hello, world', D, A, '$'
1165:0117
-G
hello, world

Program terminated normally
-N HELLO.COM
-R CX
CX 0000
:17
-W
Writing 00017 bytes
-Q

C:\>HELLO
hello, world

C:\>
*
INT 21H only for dos.

sad.gif


jibpek
post Mar 22 2023, 07:26 PM

Enthusiast
*****
Junior Member
711 posts

Joined: Jul 2012
QUOTE(flashang @ Mar 22 2023, 06:50 PM)
INT 21H only for dos.

sad.gif
*
Yes, and I have hard time accepting using soft interrupt as function call in MS DOS and BIOS.

Anyway, if no function call is allowed, then the only solution is to fill up the screen memory buffer ourselves.

Gone the day when we can bootstrap into the boot sector and directly debug using the built in debugger.
flashang
post Mar 22 2023, 08:56 PM

Casual
***
Junior Member
355 posts

Joined: Aug 2021


QUOTE(jibpek @ Mar 22 2023, 07:26 PM)
Yes, and I have hard time accepting using soft interrupt as function call in MS DOS and BIOS.

Anyway, if no function call is allowed, then the only solution is to fill up the screen memory buffer ourselves.

Gone the day when we can bootstrap into the boot sector and directly debug using the built in debugger.
*
now is very lazy to do on mid-low level language.

cross platform :

<html> hello, world </html>

rclxs0.gif


jibpek
post Mar 22 2023, 10:19 PM

Enthusiast
*****
Junior Member
711 posts

Joined: Jul 2012
QUOTE(flashang @ Mar 22 2023, 08:56 PM)
now is very lazy to do on mid-low level language.

cross platform :

<html> hello, world </html>

rclxs0.gif
*
You have forgot the <body> tag.

If Html is allowed, then a plain text file will work on any browser, and considered cross platform as well.

 

Change to:
| Lo-Fi Version
0.0198sec    1.08    5 queries    GZIP Disabled
Time is now: 24th December 2025 - 01:08 PM