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.
C# with boatloads of `unsafe` is by no means an higher level language; it's pretty much the same level of C/C++ or Rust. You're just using a more sophisticated complier and a stricter language, which can be great if used correctly, but if you dereference a null pointer, it's still going to crash and burn; this also applies to unsafe Rust.
> Wasn't able to find an example for Go, but seems likely it's out there.
NERF/LinuxBoot has a lot of Go components though as I remember it the EFI application is actually the Linux kernel, with the userland (i.e. init) being written in Go.
Go has segmented stacks, which could make it awkward to use in a bare-metal environment like this. I’m sure it’s possible, but it might be quite tedious to get working.
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!
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.
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.
Same thing, I had no idea either! I was trying to figure out how to even use the native compilers on the side but never found the time to really sit down and do it. This seems like exactly what I needed to get started!
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.
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
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.
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.
"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!"
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:
"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."
I never claimed it uses some specific runtime (my specific wording was "that uses this" followed by the link to the CoreRT) as I saw it uses the ILCompiler which is, if I understand correctly, a part of CoreRT:
The rest was the quotes about how big the runtime has to be to be "minimally useful."
As the author wrote about "no runtime" example (see my other comment for the reference): "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." and about his efi example: "Similar restrictions to the no-runtime sample apply."
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.
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:
> 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.
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.
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.
Other languages:
Rust: https://medium.com/@gil0mendes/an-efi-app-a-bit-rusty-82c36b...
Wasn't able to find an example for Go, but seems likely it's out there.