About an inherent limitation, and a possible solution 11 Jun 2007


The linker is a static tool. It uses Mono.Cecil to analyze the assemblies, modify them, and save them back in a linked form.

That means that the linker can only know about the assemblies and the types that are statically referenced. You already see where I'm going to. Mono, just like .net, provides very interesting, and also widely used, ways to dynamically load assemblies and types. The common use case is a plug-in framework for instance, where assemblies are loaded when asked to.

As an example, let's have a look to a piece of code I've faced when working on linking MoMa:
static Socket() {
	Assembly ass;
			
	try {
		ass = Assembly.Load (Consts.AssemblyMono_Posix);
	} catch (FileNotFoundException) {
		return;
	}
				
	unixendpointtype=ass.GetType("Mono.Posix.UnixEndPoint");

	Type[] arg_types=new Type[1];
	arg_types[0]=typeof(string);
	ConstructorInfo cons=unixendpointtype.GetConstructor(arg_types);

	object[] args=new object[1];
	args[0]="nothing";

	unixendpoint=cons.Invoke(args);
}
From System.Net.Sockets.Socket in System.dll

This code is fairly simple to understand. The the Socket class constructor will be called, the assembly Mono.Posix will be loaded, and an instance of the type Mono.Posix.UnixEndPoint will be created, and its constructor will be invoked.

That's the marvel of the reflection API. But as you can see, everything here is dynamic. The assembly System.dll contains no reference to the Mono.Posix.UnixEndPoint type, and no reference to the assembly Mono.Posix.

The linker can take two different kind of input. The first one is of course an assembly. It's obvious what it does, the linker will link everything necessary for this particular assembly. The second one, is an xml file, that describes what types to save, and you can even choose in those types which fields or methods you want to link.

So, when linking your assemblies, you have to know if you're doing some dynamic loading. If yes, you have to tell the linker using a xml descriptor which dynamic types you want to save, otherwise, they may very well be not present in the output assemblies.

That's a little bit painful, and that forces you to maintain a list of such types/assemblies. To ease your pain, I wrote something that I find really useful, even if it's not completely done yet.

Guys, that was the biggest introduction ever to talk about a little piece of code. This little guy is a profiler for Mono, that is simply triggered every time the JIT will leave a method.

It's goal is to produce an xml file that the linker understand, from a running application. For instance, before linking MoMa, I used:
# mono --profile=link:moma.xml ./MoMa.exe

Then I've simply used MoMa like I would if I was simply analysing an application to submit a report, and at the end, I had an interesting moma.xml file. When opening it, I've seen:

	
	
		
			
		
		
	
	

As we can see, during the execution, we've been using the constructor of the type Mono.Posix.UnixEndPoint in the assembly Mono.Posix. That's an information the linker would not have been able to know. By using the profiler, one can prepare the linker to do its job with more data that it would have been able to gather by itself.

And that's pretty good to be able to do it without requiring me to write xml descriptors for every possible type that is dynamically loaded.

See, I can even write a few lines of C, isn't that amazing?