Crate dotnetdll

Crate dotnetdll 

Source
Expand description

§dotnetdll

A Rust library for reading and writing .NET assembly metadata, implementing the ECMA-335 (CLI) standard.

§Overview

dotnetdll provides a complete toolkit for working with .NET metadata at both high and low levels. You can parse existing DLLs, inspect their contents, modify metadata, and generate new assemblies from scratch.

The library is organized into several layers, each serving different use cases:

  • resolution: High-level API for parsing and writing DLLs
  • resolved: Semantic metadata types (types, methods, IL instructions)
  • binary: Low-level ECMA-335 binary structures
  • dll: PE file parsing

Most users will work primarily with resolution and resolved.

§The Resolution Struct

resolution::Resolution is the central data structure. It represents all metadata from a DLL:

§Parsing a DLL

use dotnetdll::prelude::*;

let bytes = std::fs::read("MyLibrary.dll").unwrap();
let res = Resolution::parse(&bytes, ReadOptions::default()).unwrap();

// Access the assembly name
if let Some(assembly) = &res.assembly {
    println!("Assembly: {}", assembly.name);
}

// Iterate over all type definitions
for (type_idx, typedef) in res.enumerate_type_definitions() {
    println!("Type: {}", typedef.name);
}

§Creating a new assembly

use dotnetdll::prelude::*;

let mut res = Resolution::new(Module::new("Example.dll"));
res.assembly = Some(Assembly::new("Example"));

// Add types, methods, etc.
let my_type = res.push_type_definition(
    TypeDefinition::new(Some("MyNamespace".into()), "MyClass")
);

let bytes = res.write(WriteOptions::default()).unwrap();
std::fs::write("Example.dll", bytes).unwrap();

§Typed Indices

Instead of using raw usize indices, dotnetdll uses typed index wrappers like resolution::TypeIndex, resolution::MethodIndex, and resolution::FieldIndex that automatically index into the correct metadata table from a resolution.

use dotnetdll::prelude::*;

if let Some(type_idx) = res.type_definition_index(0) {
    let typedef = &res[type_idx];
    println!("{}", typedef.name);
}

// Enumerate with typed indices
for (type_idx, typedef) in res.enumerate_type_definitions() {
    // type_idx is a TypeIndex, not usize
    for (method_idx, method) in res.enumerate_methods(type_idx) {
        // method_idx is a MethodIndex
        println!("  {}", method.name);
    }
}

§Type System

To prevent certain simple metadata errors at compile time, dotnetdll uses a composed type hierarchy:

This design prevents method-level generic variables (M0, M1) from appearing in field types, where they would be invalid.

use dotnetdll::prelude::*;

// Fields use MemberType (only type-level generics allowed)
let field = Field::instance(
    Accessibility::Private,
    "myField",
    ctype! { string[] }  // MemberType
);

// Method parameters use MethodType (type and method generics allowed)
let method = Method::new(
    Accessibility::Public,
    msig! { string (int, bool) },  // MethodType in signature
    "MyMethod",
    None
);

§Macros

dotnetdll provides a small set of convenience macros that are documented where you will typically discover and use them:

The constructor-style macros support substitution of existing Rust values via #var (move) and @var (clone); see the ctype!/msig! docs for details.

§Working with IL

IL instructions are represented with the resolved::il::Instruction enum. Branch targets use instruction indices (not byte offsets) - the library handles offset calculation automatically.

See resolved::il for the complete instruction set and resolved::body::Method for method body construction.

§Custom Attributes

Custom attributes can be added to most metadata elements. To decode their instantiation data, you need a resolved::types::Resolver that can locate referenced types.

See resolved::attribute for details.

§Error Handling

Operations that can fail return Result<T, DLLError>. Common errors include:

  • Invalid PE format
  • Malformed metadata tables
  • Invalid signatures or IL bytecode

§Examples

The repository includes two example projects:

  • dump-dll - Parses and prints DLL contents (good for learning the API)
  • smolasm - Mini-assembler demonstrating metadata construction

Run them with:

cargo run -p dump-dll -- path/to/Some.dll
cargo run -p smolasm -- --help

§ECMA-335 Standard

This library implements the Common Language Infrastructure physical format as specified in the ECMA-335 standard. Familiarity with the standard is very helpful, but ultimately not required.

Modules§

binary
dll
prelude
Commonly used types and traits for working with dotnetdll.
resolution
High-level API for parsing and writing .NET assemblies.
resolved

Macros§

access
Construct an Accessibility value using C#-style keywords.
asm