C# static class initialization makes another victim

This past week I came across a C# code with a very surprising behavior. This simple program will better explain the observed behavior. What would you expect as output?


class Program {

    static void Main(string[] args)
    {
        A.Initialize();
        Console.WriteLine("B.Y:{0} - A.X:{1}", B.Y, A.X);
        Console.ReadKey();
    }
}
       
public static class Util {

    public static int InitMember( string message, int initValue) {

        Console.WriteLine( message );
        return initValue;
    }
    
}
public static class A {

    public static int X = Util.InitMember( "Init A.X", 5 );

    public static void Initialize() {
        Console.WriteLine( "A.Initialize()" );
        X = 10;
    }
}

public static class B {

    public static int Y = Util.InitMember( "Init B.Y", A.X );
}

If you guessed:
“B.Y: 10, A.X: 10”

Keep reading, this post is for you. 🙂

In the machine I ran this experiment I got this output: “B.Y: 5, A.X: 10”. The full output also revealed that class A and B were initialized before any of its members were accessed. How is it possible that A.Initialize() runs after B.Y? Full Output:

“Init A.X”
“Init B.Y”
“A.Initialize()”
“B.Y:5, A.X:10”

Introducing: beforefieldinit!

beforefieldinit is a class attribute that has a very interesting proposition. Its presence can be easily observed by using a simple disassemble tool (ILDASM.exe) as suggested per Scott Allen’s post.

NoConstructor_Black

Msdn simply states that this attribute: Specifies that calling static methods of the type does not force the system to initialize the type. It is pretty clear from this statement that this flag intends to improve performance, but c’mon msdn, we need a little more than that. Fortunately, a  C# in depth post came in handy. It states that a “The beforefieldinit flag has a strange effect, in that it can not only mean that a type initializer is invoked earlier than that of an equivalent type without the flag – it could even be invoked later, or not at all. … The runtime could decide to run the type initializer on loading the assembly… Or perhaps it will run it when the static method is first run…Or even wait until the field is first accessed.” . In summary, this flag gives great freedom to CLR to initialize a static class. In my test application this is exactly what I observed, static class A and static class B were initialized before my first line of code.

This non-deterministic can be avoided by adding an empty static constructor to class A and B at the cost of performance. This constructor will force CLR to remove the beforefieldinit flag. According to the C# specification:

The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • An instance of the class is created.
  • Any of the static members of the class are referenced.

Here is the code with a static constructor:


class Program {

    static void Main(string[] args)
    {
        A.Initialize();
        Console.WriteLine("B.Y:{0} - A.X:{1}", B.Y, A.X);
        Console.ReadKey();
    }
}
       
public static class Util {

    public static int InitMember( string message, int initValue) {

        Console.WriteLine( message );
        return initValue;
    }
    
}
public static class A {

    public static int X = Util.InitMember( "Init A.X", 5 );

    public static void Initialize() {
        Console.WriteLine( "A.Initialize()"; );
        X = 10;
    }
    static A(){}
}

public static class B {

    public static int Y = Util.InitMember( "Init B.Y", A.X );
    static B(){}
}

Once again ILDASM can help us observe the absence of the beforefieldinit attribute:

StaticConstructor_black

And finally, the “expected” output:

“Init A.X”
“A.Initialize()”
“Init B.Y”
“B.Y:10, A.X:10”

Keyboard Events WPF

After spending a few hours trying to get keyboard events for a specific component in my application I have finally figured it out.
I have the following Logical Tree in WPF:
         Window
                 | Grid
                       | Viewport3D

By using Snoop I was able to find out that the Window element was the only one receiving keyboard events. After this finding I decided to understand how keyboard focus works in WPF.

Keyboard Focus

“Keyboard focus refers to the element that is currently receiving keyboard input. There can be only ONE element on the whole desktop that has keyboard focus. In WPF, the element that has keyboard focus will have IsKeyboardFocused set to true. The static property FocusedElement on the Keyboard class gets the element that currently has keyboard focus.” http://msdn.microsoft.com/en-us/library/aa969768.aspx

The Implementation

In order to get the keyboard event I had to add specific code to the Window loaded event.

private void Window_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
    ViewportTest.Focusable = true;
    Keyboard.Focus(ViewportTest);
}

Straight to the Operating System main using GRUB and Bochs/KVM

My first attempt to write an operating system never got very far due to my decision to write a boot loader first. I soon realized that I’d have to spend a long time working on the boot loader before being able to work on the OS main routine. After this unsuccessful attempt, I have 2 goals:

  1. Test my OS using an emulator image so no modifications will be made to my current system. I also wanted to avoid rebooting every time I changed my kernel.
  2. Go straight to main(). I do not want to spend time writing a boot loader at this time.

As I was investigating ways to reach these goals I came across 2 very useful tutorials:

  1. http://www.omninerd.com/articles/Installing_GRUB_on_a_Hard_Disk_Image_File
  2. http://dennisferron.blogspot.com/2007/04/start-your-own-os-project-in-30-minutes.html

By combining these 2 tutorials I was able to have a bootable hard disk image ( bochs and qemu are able to load this image) and a main function that will be the starting point of my operating system.

So let’s look at the steps and some of the issues I have found.

Creating a Simple Raw Image

I have used the latest version of Ubuntu x64 to create and test these images. Create a folder called mnt in your current directory (I have kept the steps very similar to the blogs I mentioned above to avoid confusion).

Create an 8MB image file. A smaller size may cause issues with fdisk:

dd if=/dev/zero of=mnt/hdd.img bs=512 count=16065

Connect a loopback device to the image. A loopback device is a block device that can be used as a disk but really points to an ordinary file somewhere. In summary, the image file can be handled as a “device”:

sudo losetup /dev/loop1 mnt/hdd.img

Create a primary Linux partition on the image using fdisk:

sudo fdisk /dev/loop1

press n
press p
choose 1
press w

At this point we need to put a filesystem on the partition we just created. We cannot use our current loopback device since it points to the beginning of the image. We’ll need another loopback device pointing to the beginning of the partition.
So first let’s find out the offset in sectors for the beginning of the partition. The offset is in sectors so multiply that by 512:
sudo fdisk -ul /dev/loop1

Point a different loopback device at this location. After that, format the partition:

sudo losetup -o 32256 /dev/loop2 /dev/loop1

mkfs /dev/loop2

Creating your Ready to Boot Kernel

We will create a Multiboot-Complient kernel using very few steps. First we need to copy boot.S, kernel.c, and multiboot.h files from the
GRUB source code. Save those to a different folder. I created a folder named kernel .
http://cvs.savannah.gnu.org/viewcvs/grub/grub/docs/#dirlist

Open kernel.c and, at the end of the main function add few printf’s:

void
cmain (unsigned long magic, unsigned long addr)
{
  ...
	printf (" size = 0x%x, base_addr = 0x%x%x,"
		" length = 0x%x%x, type = 0x%x\n",
		(unsigned) mmap->size,
		(unsigned) mmap->base_addr_high,
		(unsigned) mmap->base_addr_low,
		(unsigned) mmap->length_high,
		(unsigned) mmap->length_low,
		(unsigned) mmap->type);
    }
cls();

	printf("\nWelcome to My Own OS Main\n");
}

Since I am using a 64 bit operating systems I had to install gcc 32-bit package and add the -m32 option. Compile everything using the following command:

cd kernel
gcc boot.S kernel.c -o MyOsKernel -ffreestanding -nostdlib -nostartfiles -fno-stack-protector -m32

The MyOsKernel file will be generated.

Installing GRUB

Mount the previously created image so we can explore it:

mount /dev/loop2 mnt/hdd/

Create a folder for GRUB and copy stage1 and stage2 files. Also copy your kernel to the mounted image:

mkdir -p mnt/hdd/boot/grub
cp -r /usr/lib/grub/x86_64-pc/stage1 /usr/lib/grub/x86_64-pc/stage1 /boot/grub/menu.lst mnt/hdd/boot/grub
cp -r MyOsKernel mnt/hdd/

Edit menu.lst to put your kernel information:

default 0
timeout 10
title=MyOwnOS
root (hd0,0)
kernel /MyOsKernel

Now lets, install GRUB. Start a GRUB command prompt:

grub –device-map=/dev/null

Enter the following to install grub:

device (hd0) mnt/hdd.img
root (hd0,0)
setup (hd0)

Using the image

We have to follow a similar process of creating a loopback device and mounting the image we just created:

sudo losetup -o 32256 /dev/loop3 mnt/hdd.img
mount /dev/loop3 mnt/hdd
chown -R username:username mnt/hdd

If you do not have any loopback device available you may delete some using losetup -d.
Boot with KVM. Do not forget to specify the emulator memory otherwise you may run into an out of memory error:

kvm mnt/hdd.img -m 512

You should see your kernel as one of the options in the GRUB menu. For some reason I was unable to boot by selecting the kernel (I am still invertigating this issue). So I entered GRUB comman-line by pressing ‘c’ in the boot menu and issued the following commands:

grub> root (hd0,0)
grub> kernel /MyOsKernel
grub> boot

Welcome to main!!!

I also booted using windows bochs. I created the following bochssrc.bxrc file:

###############################################################
# bochsrc.txt file for DLX Linux disk image.
###############################################################
# how much memory the emulated machine will have
megs: 512
# filename of ROM images
romimage: file=../BIOS-bochs-latest
vgaromimage: file=../VGABIOS-lgpl-latest
# what disk images will be used
floppya: 1_44=floppya.img, status=inserted
floppyb: 1_44=floppyb.img, status=inserted
# hard disk
ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14
ata0-master: type=disk, path="hdd.img", cylinders=306, heads=4, spt=17
# choose the boot disk.
boot: c
# default config interface is textconfig.
#config_interface: textconfig
#config_interface: wx
#display_library: x
# other choices: win32 sdl wx carbon amigaos beos macintosh nogui rfb term svga
# where do we send log messages?
log: bochsout.txt
# disable the mouse, since DLX is text only
mouse: enabled=0
# enable key mapping, using US layout as default.
#
# NOTE: In Bochs 1.4, keyboard mapping is only 100% implemented on X windows.
# However, the key mapping tables are used in the paste function, so
# in the DLX Linux example I'm enabling keyboard_mapping so that paste
# will work. Cut&Paste is currently implemented on win32 and X windows only.
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-us.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-fr.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-de.map
#keyboard_mapping: enabled=1, map=$BXSHARE/keymaps/x11-pc-es.map

And booted the image issuing the command:

bochs -q -f bochsrc.bxrc

My next post will explain how to develop some of the basic components of an operating system.

Writing your own Operating System … Is there an easy start?

YES, I have tried to write my own operating system. Yes, I did think I could start by writing my own boot loader and quickly reach the point where my main() function was the only thing that mattered. Yes, I have read  many articles attempting to teach/enumerate all necessary steps to write a simple but functional OS. Needless to say I have been disappointed many times and unable to answer a simple question … How do I get started? Is there an easy way? I am determined to find out and hopefully answer YES, there is an easier way!