
EFI Boot Application in C# - pjmlp
https://github.com/MichalStrehovsky/zerosharp/blob/master/efi-no-runtime/README.md
======
mmastrac
This is pretty wild. I hadn't realized that there were easy ways to work with
EFI in higher-level languages than C.

Other languages:

Rust: [https://medium.com/@gil0mendes/an-efi-app-a-bit-
rusty-82c36b...](https://medium.com/@gil0mendes/an-efi-app-a-bit-
rusty-82c36b745f49)

Wasn't able to find an example for Go, but seems likely it's out there.

~~~
int_19h
C# is higher-level in aggregate, but if you look at individual features, it
lets you go as low as C in most respects - raw pointers with arithmetic,
unions, stack allocations, you name it. It seems that this EFI project uses it
at that level, and higher-level features that would require e.g. GC aren't
supported.

~~~
cptskippy
This is one of the benefits of C# that makes it approachable to beginners.

~~~
ygra
I wouldn't say pointers and unsafe code are features that makes it
approachable to beginners.

------
daeken
I genuinely had no idea that you could produce bare object files with ilc.
This is so exciting to me, as compiler work has ended up being the majority of
the work that goes into pure-managed OSes. Guess I know what I'm going to do
when I actually have some free time, down the road!

~~~
userbinator
EFI is actually itself already much of an OS (it's certainly bigger than DOS,
for example), and the executables are PE format just like Windows application
binaries, so this isn't quite as low-level as it may sound at first.

~~~
bpye
EFI just means you don't have to deal with the arcane x86 boot process. If you
want to do anything kernel-like then you are still pretty much on your own,
you're still gonna need a memory manager, process scheduler, etc.

------
dlahoda
I have started pure pointers algorithms and data structures library half year
ago. Got hit by lack of generics of pointers and unmanaged generics. These
seems fixed in preview of f# and c#. There are many other improvements to have
non GC runtime going and considerrd in C# and F# code and other movements into
Rust like features. So it may be reasonable to write low level code in c#
within 2 versions I guess. F# even better as it has templates(c++ like
generics) and other non oop compositional stuff. Given this tendency I stopped
to try to learn Rust.

~~~
platz
Pretty sure F# allocates like nobody's business

~~~
algorithmsRcool
By default yes, but that's the price of using immutable objects and
datastructures.

However, you can cut pretty much all of those allocations with a bit of care
and still retain some F# niceties.

I recently wrote a very low allocation text file parser that could ingest log
files at over 200MB/s single threaded. All in F# using structs and Span the
same way I would in C#.

Except I get the safety, terse syntax and rigor of options, records and
matches

~~~
platz
That's pretty cool. I wish some of the foundational f# libs and webserver were
written that way. They probably use more idiomatic code which means
application code pays those costs too.

~~~
algorithmsRcool
For years, a lot of pain was caused by F# using classes for critical types
such as Option, Tuple and Choice. Additionally most records should have been
structs by default.

They have started fixing this by introducing value tuple, value option and
supporting C# performance primitives like Span. But they have a way to go
still.

~~~
platz
Ah, that summary is helpful, thanks

------
acqq
If I understand correctly, that uses this:

[https://github.com/dotnet/corert](https://github.com/dotnet/corert)

Written about here:

[https://mattwarren.org/2018/06/07/CoreRT-.NET-Runtime-for-
AO...](https://mattwarren.org/2018/06/07/CoreRT-.NET-Runtime-for-AOT/)

"One of the first things people asked about CoreRT is “what is the size of a
‘Hello World’ app” and the answer is ~3.93 MB (if you compile in Release
mode), but there is work being done to reduce this."

..

"So Test.CoreLib really is a minimal runtime!! But the difference in size is
dramatic, it shrinks down to 0.49 MB compared to 3.93 MB for the fully-
featured runtime!"

~~~
MarkSweep
What is new compared to this post is even Runtime.Base is optional. You can
get Hello World down to 5k by implementing a even more stripped down core
library:

[https://github.com/MichalStrehovsky/zerosharp/blob/master/no...](https://github.com/MichalStrehovsky/zerosharp/blob/master/no-
runtime/zerosharp.cs)

~~~
acqq
Thanks. One level above your link is I think the best explanation:

[https://github.com/MichalStrehovsky/zerosharp](https://github.com/MichalStrehovsky/zerosharp)

" _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 4-5 kB native EXE, so that's rather cool.

 _with-runtime_ is something that can be actually useful. This includes the
full managed and unmanaged runtime - GC, exception handling, and interface
dispatch all work. Test.CoreLib used as the class library here is the same
Test.CoreLib that you can find in the CoreRT repo. Don't look for things like
Object.ToString() because being compatible with .NET is not the point. This
sample comes down to about 400 kB, most of which is the C runtime library.

 _efi-no-runtime_ is an EFI boot application that lets you run C# on bare
metal, without an OS. Similar restrictions to the no-runtime sample apply.
Making a version of this sample with a runtime would require some porting work
on the runtime side."

------
lwansbrough
Importantly there's a limitation, which is that the CoreRT runtime doesn't
support EFI directly so you won't be able to use most of what you're familiar
with when it comes to .NET. But AOT compilation for .NET is maturing and can
be used today for other exciting targets like WebAssembly. Perhaps EFI one day
in the future.

------
userbinator
I hope this is just a "for fun"/"to see if it can be done" type of thing,
because otherwise it is more than a little disturbing when the source code in
a high-level language takes _more_ lines of code than doing the same thing in
pure Asm:

[https://gist.github.com/yackx/a52010a05a430496ae11](https://gist.github.com/yackx/a52010a05a430496ae11)

[https://github.com/MichalStrehovsky/zerosharp/blob/master/ef...](https://github.com/MichalStrehovsky/zerosharp/blob/master/efi-
no-runtime/zerosharp.cs)

(The Asm above was for good old BIOS, here's the EFI version:
[http://x86asm.net/articles/uefi-programming-first-
steps/hell...](http://x86asm.net/articles/uefi-programming-first-
steps/hello.asm) )

~~~
naasking
> otherwise it is more than a little disturbing when the source code in a
> high-level language takes more lines of code than doing the same thing in
> pure Asm

The vast majority of the code is just defining the minimal set of standard C#
data types needed to compile any C# program.

~~~
molticrystal
There is an analog in the efi asm example in the include file [1] with its
structures and defines which have a few types but is mostly concerned with
defining the same structures as in the C# program.

Something that is very unique and interesting is the usage of the
System.Runtime & System.Runtime.InteropService , I'm guessing that some of the
contorting needed to get things working in the code lays with those functions.
The other half would be the ilc and the linker having the EFI subsystem as an
option.

[1] [http://x86asm.net/articles/uefi-programming-first-
steps/efi....](http://x86asm.net/articles/uefi-programming-first-
steps/efi.inc)

~~~
naasking
Runtime and InteropServices are needed for .NET's P/Invoke FFI. It's used to
declare the layout of structs, foreign pointers and marshalling code. You can
see this being used starting at line 127, where it appears to define EFI
handles, tables and headers.

