Explore .NET Assemblies

Assembly Binding Revealed

Jeff Niblack

October 30, 2009

18 Min Read
ITPro Today logo

asp:cover story

Languages: VB

Technologies: Assemblies | MSIL | MSILDisassembler

 

Explore.NET Assemblies

AssemblyBinding Revealed

 

By JeffNiblack

 

Thisarticle digs down into .NET assemblies, metadata, the assembly manifest, andmore. Having a thorough understanding of these concepts will help you keep yoursanity when deploying .NET-based applications.

 

Changing Times

Anassembly is a logical .dll or .exe with a few extras to help the .NET run timeunderstand how to use and work with it. Among other things, these extrasinclude:

  • Version,name, culture, and any security requirements.

  • Otherfiles, if any, that make up the assembly.

  • Importedand exported types, methods, events, properties, etc.

 

Ingeneral, this information is the metadata that describes the assembly, and isalways physically included with the assembly that it describes. Create a simpleprogram by entering the following in your favorite code editor, and save it ashw.vb:

 

Public Module Hello

  Sub Main()

    System.Console.WriteLine("HelloWorld!")

  End Sub

End Module

 

Thencompile it by entering the following at a command prompt:

 

vbc hw.vb

 

Thissimple application exposes a single type, Hello, that provides a singlemethod, Main. Main uses the Console.WriteLine method ofthe System namespace to write out "Hello World!" to the command linewhen the application is executed.

 

As theassembly is compiled, the compiler emits information about the assembly, andstores it in a collection of tables. These tables are stored in a raw formatthat's not the easiest to read or understand. Fortunately, you can use the MSILDisassembler tool, Ildasm.exe, that ships with the .NET Framework to view thisinformation in an easy-to-understand format. To examine the assembly that theVB .NET compiler creates, enter:

 

ildasm /advhw.exe

 

Includingthe /adv flag turns on a few advanced features of Ildasm, includingMetaInfo (you'll use this feature later). Although there's a tremendous amountof information provided by Ildasm, I'll focus on the more important aspects. Toview the manifest, as shown in FIGURE 1, double-click Manifest.

 


FIGURE 1: Manifest forhw.exe.

 

Thefirst block listed is .assembly extern mscorlib. This means that hw.exe referencesan external assembly named mscorlib. mscorlib.dll is an assembly that'sautomatically added to every .NET assembly, and includes the base types, someof the System namespaces, and a few other things. (You can use Ildasm todisassemble mscorlib.dll and learn more about its features.) The .publickeytokenvalue indicates that mscorlib is a pubic assembly, and the value is the keyused to verify its authenticity. The .ver value is the version ofmscorlib that was used during compilation. With future releases of .NET,mscorlib.dll will be updated. Knowing which version of mscorlib was used tocompile your assembly could help in isolating problems associated with changesto mscorlib.

 

Thesecond block is specific to VB .NET assemblies. Once again, hw.exe isreferencing an external assembly named Microsoft.VisualBasic. This assemblyreference is comparable to using the VB run-time environment in previousversions of VB. Comparing the.vervalue with the current version of the Microsoft.VisualBasic assembly being usedis helpful when isolating changes in future releases.

 

The thirdblock describes the hw assembly. Notice that the extern keyword isn'tused because this block defines the assembly that's included in this file.Version information wasn't included for this simple application, so the compiler automatically set the versionto 0:0:0:0. The .hash algorithm value is used by theCommon Language Runtime (CLR) to determine if the assembly was tampered withafter it was compiled. When the CLR runs hw.exe, it generates a hashvalue based on the current file. If this run-time hash value is different from the hash value stored in the assembly, the CLR will generate a run-timeerror.

 

Followingthe third block are two values: .module and MVID. .module is the actual name of the filethat was output by the compiler. Even if the file is physically renamed, say tohw.junk, the manifest will still list hw.exe as the.module. Comparing the compiled name and the current physical nameis one method to ensure that your application is using the correct file.

 

MVID is another important value to usewhen verifying assemblies. Each time an assembly is compiled, a GUID isgenerated to uniquely identify the compilation. This GUID is stored as the MVIDfor the assembly. Even when the assembly code and version information do notchange from one compile to another, MVIDis always updated with a new GUID.Comparing MVID values of twoassembly files that are supposed to be the same will verify if the files arefrom different compilations or exact copies.

 

Theremaining information in the manifest, address locations, and offsets for wherethe auxiliary manifest and metadata tables begin is beyond the scope of thecurrent discussion.

 

Viewing Metadata

Anotheruseful feature of Ildasm is the View| MetaInfo | Show!menu option available from the main Ildasm window. MetaInfo is useful inproviding a more detailed view of the metadata associated with an assembly. Asshown in FIGURE 2, the ScopeName andMVID values provide the name of theassembly and the compile-time GUIDthat's associated with the assembly, respectively.

 


FIGURE 2: MetaInfo forhw.exe.

 

Theremaining MetaInfo output lists detailed information from various manifest andmetadata tables. The first significant entry is TypeDef #1, as shown in FIGURE3. The TypeDef table contains one entry for each type defined in the assembly.For this simple application, there's only one type: Hello. As indicated,Hello is a public type - and because custom types are based on System.Object,Hello extends the System.Object namespace.

 


FIGURE 3: TypeDef #1for hw.exe.

 

Withineach TypeDef, any methods associated with the type are listed. The Hellotype provides one method: Main. The [ENTRYPOINT] attribute indicatesthat Main is the entry point for our type. .NET assemblies are permittedto have one and only one entry point. Generally, this is Main, WinMain, or DllMain. As indicated by the Flags value, Main is a publicmethod with a return type of Void, shown by the ReturnType value.Main also doesn't require anyarguments, as indicated by the Noarguments declaration. The remaining information is valuable for the runtime, but not too interesting for the current discussion.

 

TheTypeRef table contains an entry for each type referenced in our assembly, asshown in FIGURE 4. The first one with a token of 0x01000001 is the System.Objecttype that was referenced in TypeDef #1. Along with the TypeRefName value, the ResolutionScopeis provided. The ResolutionScope isa token value that is used to define where the System.Object type reference is resolved. Generally, this points toan entry in the AssemblyRef, TypeRef, or ModuleRef tables. For TypeRef #1(token 0x01000001), it's resolved by the AssemblyRef #1 entry (token0x23000001), which is mscorlib,as indicated by the name value shown in FIGURE 5.

 


FIGURE 4: TypeRef tablefor hw.exe.

 


FIGURE 5: AssemblyReftable for hw.exe.

 

TypeRef#2, also resolved by mscorlib, is the reference to the System.Console type. The application uses the WriteLinemethod, which is listed in the associated MemberRef table, provided by the System.Console type.

 

TypeRef's3 and 4 are always included with VB .NET assemblies. TypeRef #3 is the typereference for Microsoft.VisualBasic.CompilerServices.StandardModuleAttributeand has a ResolutionScope of0x23000002. If you look at the AssemblyRef table, you'll see that 0x23000002 isthe Microsoft.VisualBasic assembly.

 

Lookingbeyond the TypeRef and AssemblyRef tables, the last table used by this assemblyis UserString. This table lists the user-defined strings used by the assembly.

 

In theend, this simple Hello World VB .NET application includes:

  • Onedefined type (Hello) with one method (Main)

  • Fourexternal types, provided by two external assemblies (mscorlib andMicrosoft.VisualBasic)

  • Oneuser-defined string (Hello World!)

 

Multi-file Assemblies

Up tothis point, a simple, single file assembly has been used as our guinea pig.However, assemblies can be made up of one or more files. Although multiplefiles are used, the assembly is still versioned, deployed, managed, andreferenced as a single unit. Supporting multi-file assemblies might sound likea bad idea on the surface because of the increased chance of deployment issues(i.e., not including all files when installing an assembly). However, bydecoupling the logical representation from the physical implementation, you canhave increased control over how your application loads resources. For example,because .NET uses a "lazy" approach to loading assemblies, when a type isreferenced, the file that resolves that reference is loaded. The other files inan assembly remain unloaded until a type they provide is referenced. In theInternet space, this allows you to build an assembly made up of small modulesthat are only downloaded when referenced. If a user never requires a type, theassociated file is never downloaded.

 

Thereare several deployment options when creating a multi-file assembly. The firstone includes a single .exe file that references a module (.mod) file. First,create a file named hwmod1.vb, and enter the code shown in FIGURE 6.

 

Option Explicit

Option Strict

 

Public Class Test

  Public Sub Hello(strLang As String)

    If strLang = "SP" Then

      System.Console.WriteLine("Hola")

    Else

      System.Console.WriteLine("Hello")

    End If

  End Sub

End Class

FIGURE 6: Sourcefor hwmod1.mod.

 

Thiscode provides a Test class with one method, Hello, that accepts astring argument that determines what language to use when saying Hello. If "SP" is provided, Hello returns "Hola" (Spanish for Hello). If any otherstring is provided, Hello returns "Hello".Compile this as a module by entering:

 

vbc/out:hwmod1.mod /t:module hwmod1.vb

 

Next, create a file named hwexe1.vb, and enter the code shown inFIGURE 7.

 

Option Explicit

Option Strict

 

Public Module Hello

  Sub Main()

    Dim t As New Test()

    t.Hello("SP")

    t.Hello("EN")

  End Sub

End Module

FIGURE 7:Source for hwexe1.exe.

 

Thiscode exposes a single type, Hello, which supports a single method, Main. Within Main, the Test class is referenced, and the Spanish andEnglish versions of Hello are returned. If compiled like this:

 

vbc /out:hwexe1.exe /t:exe hwexe1.vb

 

thecompiler can't resolve the reference to the Test type, so a referenceneeds to be added to the hwmod1.mod module to the assembly. Enter the followingto compile the hwexe1.vb code:

 

vbc/out:hwexe1.exe /t:exe /addmodule:hwmod1.mod hwexe1.vb

 

Noticethat the /addmodule option allows the assembly to resolve the referenceto the module, but the module still remains a separate file from the assembly,and must be included during deployment. Now that the assembly and itsassociated module are compiled, use Ildasm again to look into the details:

 

ildasm /advhwexe1.exe

 

Thenopen the MetaInfo window. The MetaInfo describing hwexe1.exe is very similar tothe hw.exe MetaInfo viewed previously; however, there are a few newdefinitions. TypeRef #2, shown in FIGURE 8, is similar to the previous TypeRefentries, but in this case, the name is Test and there's a ResolutionScope of 0x1a000001. Noticethat Test also has two MemberRef entries; the default constructor(.ctor) and Hello. Test is resolved from the ModuleRef table, using the0x1a000001 token, to point to hwmod1.mod.

 


FIGURE 8: TypeRef #2for hwexe1.exe.

 

FIGURE 9shows the File and ExportedType tables where ModuleRef #1 is resolved to File#1 with a token of 0x26000001. The ExportedType table entry lists Testas the exported type that's implemented by token 0x26000001 (File #1 orhwmod1.mod).

 


FIGURE 9: File andExportedType tables for hwexe1.exe.

 

Nowenter:

 

ildasm /advhwmod1.mod

 

Thenopen the MetaInfo window for the module. While the MetaInfo output lookssimilar to the previous examples, there are two important things to note.First, no entry point is defined; second, there's no Assembly table entry forthis module because it was compiled as a module rather than an .exe or .dllfile. These are the two major differences in the metadata that distinguish amodule from an .exe or .dll file.

 

Nowcreate a multi-file assembly, and reference a namespace that's implemented inan external .dll file. First, create a file named hwmod2.vb, andenter the code shown in FIGURE 10.

 

Option Explicit

Option Strict

 

Namespace HW

  Public Class Test

    Public Sub Hello(strLang As String)

      If strLang = "SP" Then

        System.Console.WriteLine("Hola")

      Else

        System.Console.WriteLine("Hello")

      End If

    End Sub

  End Class

End Namespace

FIGURE 10:Source for hwmod2.dll.

 

Thiscode is exactly the same as in hwmod1.vb, except that now there's a definednamespace, HW, which encapsulates the Test class. Compile this asa library by entering:

 

vbc/out:hwmod2.dll /t:library hwmod2.vb

 

Next, create a file named hwexe2.vb,and enter the code shown in FIGURE 11.

 

Option Explicit

Option Strict

 

Imports HW

 

Public Module Hello

  Sub Main()

     Dimt As New HW.Test()

    t.Hello("SP")

    t.Hello("EN")

  End Sub

End Module

FIGURE 11:Source for hwexe2.exe.

 

Thiscode is exactly the same as in hwexe1.vb, except that now the HWnamespace is used to resolve the reference to the Test type. With the additionof an external reference, the following reference (/r) directive needs to beincluded when compiling hwexe2:

 

vbc/out:hwexe2.exe /t:exe /r:hwmod2.dll hwexe2.vb

 

With the.dll and .exe compiled, look at hwexe2.exe using Ildasm by entering:

 

ildasm /advhwexe2.exe

 

Thenopen the MetaInfo window. Once again the MetaInfo information is similar toprevious examples. Looking at TypeRef #2 in FIGURE 12, you can see that HW.Test is referenced with a ResolutionScopeof 0x23000003. This token is resolved in the AssemblyRef table where hwmod2 islisted as an external assembly.

 


FIGURE 12: TypeRef #2for hwexe2.exe.

 

Nowenter:

 

ildasm /advhwmod2.dll

 

Thenopen the MetaInfo window for the library. As was the case for the modulecreated earlier, there's no entry point defined. However, there's an Assemblytable entry for this .dll. The table in FIGURE 13 summarizes the major metadatadifferences.

 

Type

Entry Point

Assembly Table Entry

Module (.mod)

No

No

Library (.dll)

No

Yes

Program (.exe)

Yes - one and only one!

Yes

FIGURE 13: Majorassembly metadata differences.

 

So far,the MSIL Disassembler tool has been used to walk through the metadata emittedby the compiler. You can use this information to further understand howthird-party assemblies, as well as your own, are composed, and how the variousfiles, types, methods, etc. are related. This provides invaluable informationwhen developing with and deploying assemblies. This view, however, is static.

 

Run-time Assembly Binding

Anothertool to determine the run-time binding of an assembly is the assembly manager(Fusion.dll). Within the .NET Framework, it is loaded each time an assembly isloaded. The assembly manager is responsible for binding the various externalreferences with the called assembly. To view this binding process, the AssemblyBinding Log Viewer (FusLogVw.exe) tool is shipped with the .NET Framework. Thistool provides insight into how an assembly was composed at run time. It'sinvaluable when determining why assembly loads fail, or to verify the exactassemblies being referenced at run time.

 

Bydefault, FusLogVw only logs assembly bind failures. Generally, these arerelated to version mismatches or assemblies deployed in the wrong location.Typically, these failures show up as TypeLoadExceptions in yourapplication; however, you can have FusLogVw log successful assembly binds aswell. This is a great learning tool to see how the run time handles assemblybinding. It's also the only way to verify if an assembly is being loaded from aparticular location. To log all assembly binds, you will need to add a new DWORD registry value at HKLMSoftwareMicrosoftFusionForceLog.Setting ForceLog to 1 will log all assembly binds. Setting the value to0 will log only assembly bind failures - the default behavior.

 

Add the ForceLogregistry value, and set its value to 1. Thenrun hwexe2.exe, the previous sample application. To view the assembly bindinglog, enter:

 

fuslogvw

 

FIGURE14 shows the viewer (you might have more assembly bind information than shown).FusLogVw has three locations in which it will look for log files:

  • Default looks in per-user directories ondisk in the wininet cache. All application types are logged in this locationexcept for ASP.NET applications.

  • ASP.NET looks in a per-application cachedirectory, and only contains ASP.NET application logs.

  • Custom is controlled by the HKLMSoftwareMicrosoftFusionLogPathvalue. By default, this value doesn't exist. However, you can set LogPathto a valid directory name where all logs will be stored. In general, it's bestto not set this value because the run time automatically manages and cleans outthe Default location. Whereas, if you use the Custom location, you're responsible for managing and cleaning up thedirectory.

 


FIGURE 14: AssemblyBinding Log Viewer.

 

As shownin FIGURE 14, hwexe2.exe requires three different assembly binds: mscorlib,Microsoft.VisualBasic, and hwmod2. Select the log entry for the hwexe2.exe andmscorlib bind, and click the ViewLog button, or simplydouble-click the mscorlib log entry. FIGURE 15 shows the log for this binding.As noted in the log, this is a successful bind. The first entry of the pre-bindstate information area shows the name, version, culture, and PublicKeyTokenvalue (i.e., the assembly identification) that is requested for the bind. Allthe possible bind paths and configuration values associated with them are alsolisted. In the case of mscorlib, these values are generally Null, andthe run time is allowed to load the mscorlib available in the cache (GlobalAssembly Cache [GAC]). The last line in the log verifies that mscorlib isloaded from the GAC.

 


FIGURE 15: Assemblybinding log for hwexe2.exe and mscorlib.dll.

 

The bindlog for hwexe2.exe and Microsoft.VisualBasic is very similar to mscorlib. Generally,the run time is allowed to manage the location of the Microsoft.VisualBasicassembly, and it's typically found in the GAC.

 

Selectand view the bind log for hwexe2.exe and hwmod2, as shown in FIGURE 16. Asindicated, the operation is successful. Likewise, as in the previous example,the pre-bind state information provides the specifics on the expected identityof the target assembly. In addition, the bind paths are all set to Nullbecause custom configuration options were not provided for this assembly. Thelast section of the log shows the process, or probing, followed to load thehwmod2 assembly. An attempt to locate the default configuration file for theapplication, hwexe2.exe.config, failed. Therefore, Fusion.dll determined thereis no custom configuration policy associated with binding to hwmod2. Since thiswasn't present, the default location for hwmod2 is tried first. This is thesame directory as the application with .dll appended to the assembly name. Asshown in the log, hwmod2.dll is located successfully. The next step is forFusion.dll to begin the initialization process for hwmod2.dll, as indicated bythe last entry in the log.

 


FIGURE 16: Successfulassembly binding log for hwexe2.exe and hwmod2.dll.

 

Whathappens when an assembly bind fails? To illustrate this, rename hwmod2.dll tohwmod2.junk so that when hwexe2.exe runs, it won't be able to locate hwmod2.Then run hwexe2.exe once again. When the bind failure occurs, the run timepresents the Just-In Time Debugging dialog box. Just select No to close the dialog box. Next, refresh the FusLogVw window. Thefirst thing you'll notice is that the mscorlib and hwmod2 log entries haveupdated Date/Time values, while the Microsoft.VisualBasic log entrystill has the Date/Time from the previous run. This is because whenhwexe2.exe is executed, the Microsoft.VisualBasic assembly wasn't loaded beforethe hwmod2 bind failed. Therefore, there's no need to bind withMicrosoft.VisualBasic.

 

Selectand view the bind log for hwexe2.exe and hwmod2 (see FIGURE 17). As indicated,the bind operation failed because it could not locate the file. Focusing on thelast section of the log, notice that after the attempt to locate the customconfiguration policy file failed, the probing process begins by looking forhwmod2.dll in the same directory as the application. The next locationattempted is hwmod2/hwmod2.dll off of the application's root directory. Thethird and forth attempts to locate hwmod2 search for hwmod2.exe in theapplication's root, followed by the hwmod2 directory off of the application'sroot directory. Note that even though the hwmod2directory doesn't exist, the bind process still attempts to locate thereferenced assembly in that location. After all four attempts fail, the probingprocess is aborted. If a custom configuration policy file (i.e.,hwexe2.exe.config) is included with the application, the custom bindinglocations provided would also have been probed. With all binding locationsexhausted, an attempt to use the Windows Installer to locate hwmod2 isperformed.

 


FIGURE 17: Failedassembly binding log for hwexe2.exe and hwmod2.dll.

 

Fromthis simple example, you can see how the Assembly Binding Log Viewer tool canbe helpful when determining what assemblies are being loaded, and the probingprocess used to locate the actual file to resolve the bind request.

 

Conclusion

The MSILDisassembler tool was used in this article to dig into the manifest andmetadata details of an assembly to determine how it was constructed at compiletime. The Assembly Binding Log Viewer was also used to determine how anassembly was constructed at run time. Having both of these tools in yourdeveloper handbag and knowing when and how to use them will save countlesshours when deploying and debugging your .NET applications.

 

To learnmore about the assembly manifest and metadata, along with lots of other usefulinformation about assemblies, refer to the Common Language Infrastructure (CLI)and the IL Assembly Language Programmer's Reference documents. Usually, thesedocuments are found on your .NET Framework install drive at C:ProgramFilesMicrosoft Visual Studio .NETFrameworkSDKTool DevelopersGuidedocsStartDocs.htm. These documents, along with the tools used in thisarticle, ship with the .NET Framework, and provide detailed explanations of allaspects of assemblies.

 

The files referenced in this article are available for download.

 

JeffNiblack, chief information officer for Interactive Gaming & Wagering (http://www.interactive-gaming.com),has over 18 years experience in the IT field. Jeff has dealt with variousaspects of application, network, and database design and development. Inaddition, Jeff has written articles for numerous online and print publications,and regularly speaks at technical conferences, including TechEd, MicrosoftASP.NET Connections, and ASP.NET and XML Web Services Solutions Conference, ona variety of .NET topics. You can reach Jeff at mailto:[email protected].

 

Tell us what you think! Please send any comments about thisarticle to [email protected] include the article title and author.

 

 

 

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like