<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Masahiro Morino</title>
    <description>The latest articles on DEV Community by Masahiro Morino (@masamori).</description>
    <link>https://dev.to/masamori</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1272991%2F920d4607-51ca-4b23-bbb2-f0fcfbad05a8.JPG</url>
      <title>DEV Community: Masahiro Morino</title>
      <link>https://dev.to/masamori</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/masamori"/>
    <language>en</language>
    <item>
      <title>What Happens Before Rust Calls Your main Function?</title>
      <dc:creator>Masahiro Morino</dc:creator>
      <pubDate>Sun, 18 Oct 2026 09:00:31 +0000</pubDate>
      <link>https://dev.to/masamori/what-happens-before-rust-calls-your-main-function-1175</link>
      <guid>https://dev.to/masamori/what-happens-before-rust-calls-your-main-function-1175</guid>
      <description>&lt;p&gt;When we write a Rust program, &lt;code&gt;fn main()&lt;/code&gt; is where our code begins.&lt;/p&gt;

&lt;p&gt;Does the CPU jump straight into that function? Who puts the executable into memory, and where does execution actually begin?&lt;/p&gt;

&lt;p&gt;Let's inspect a small Rust program and follow what runs before the &lt;code&gt;main&lt;/code&gt; we wrote. Looking inside the executable, it turns out there is more than one &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This experiment uses an Apple Silicon Mac and an ordinary executable that uses the standard library, &lt;code&gt;std&lt;/code&gt;. My environment is rustc 1.96.0 on macOS 26.6.2, with tools from Xcode Command Line Tools. Entry mechanisms, internal function names, and addresses can change across operating systems and Rust versions.&lt;/p&gt;

&lt;h2&gt;
  
  
  An executable needs some preparation before it can run
&lt;/h2&gt;

&lt;p&gt;An executable on disk contains machine code and data. When the CPU executes instructions, though, it uses addresses in the process's memory.&lt;/p&gt;

&lt;p&gt;At startup, the OS reads information from the executable and maps its code and data into the process's virtual address space. This does not mean copying the entire file into memory before anything can run. The system can establish mappings and load individual pages as they are needed.&lt;/p&gt;

&lt;p&gt;For this macOS executable, the dynamic linker, &lt;code&gt;dyld&lt;/code&gt;, also runs. It makes shared libraries available, performs the required reference resolution and initialization, and passes control to the executable's entry point. See the &lt;a href="https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h" rel="noopener noreferrer"&gt;Mach-O format definitions&lt;/a&gt; and &lt;a href="https://github.com/apple-oss-distributions/dyld/blob/main/dyld/dyldMain.cpp" rel="noopener noreferrer"&gt;dyld startup implementation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The dynamic linker works when the program starts. The build-time linker combines object files into an executable; the dynamic linker loads the shared libraries that executable uses and connects references to functions and other symbols.&lt;/p&gt;

&lt;p&gt;Once that preparation is done, do we finally reach our own &lt;code&gt;main&lt;/code&gt;?&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmzu5wohhagp9c92a937x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmzu5wohhagp9c92a937x.png" alt="The OS and dyld prepare the program, the compiler-generated main calls Rust startup code, and that code reaches the user's main" width="800" height="1429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: The path we will follow in this executable. Helper functions and details such as library initialization are omitted. The dyld and Rust startup routines are themselves code already running on the CPU.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Build an executable with an ordinary main
&lt;/h2&gt;

&lt;p&gt;No special attributes or &lt;code&gt;unsafe&lt;/code&gt; are needed. Save this as &lt;code&gt;main.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Hello from Rust main"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compile and run it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc main.rs &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nv"&gt;debuginfo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;--emit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;link&lt;/span&gt;,llvm-ir
./main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Hello from Rust main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--emit=link,llvm-ir&lt;/code&gt; produces both the executable, &lt;code&gt;main&lt;/code&gt;, and the LLVM IR, &lt;code&gt;main.ll&lt;/code&gt;. I disable optimization for our code to make the calls easier to observe, and include debug information so a debugger can follow functions and source lines. This does not disable optimization in the already-built standard library. See &lt;a href="https://doc.rust-lang.org/rustc/command-line-arguments.html#--emit-specifies-the-types-of-output-files-to-generate" rel="noopener noreferrer"&gt;rustc output options&lt;/a&gt; and &lt;a href="https://doc.rust-lang.org/rustc/codegen-options/index.html" rel="noopener noreferrer"&gt;code generation options&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two main functions?
&lt;/h2&gt;

&lt;p&gt;Disassemble the executable. &lt;code&gt;--demangle&lt;/code&gt; makes Rust's encoded symbol names easier to read.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcrun llvm-objdump &lt;span class="nt"&gt;--disassemble&lt;/span&gt; &lt;span class="nt"&gt;--demangle&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is long. For now, here are just two function headings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;00000001000009f4 &amp;lt;main::main::h2dd6e7c25b480450&amp;gt;:
0000000100000a2c &amp;lt;_main&amp;gt;:
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait. Two &lt;code&gt;main&lt;/code&gt;s? And at different addresses?&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;main::main&lt;/code&gt;, the first &lt;code&gt;main&lt;/code&gt; is our crate's name. The second is the function we defined. A hash is appended to distinguish symbols. Inside this function, I found the code that prints our message.&lt;/p&gt;

&lt;p&gt;The other function, &lt;code&gt;_main&lt;/code&gt;, is an entry wrapper generated by the compiler for this target. In Mach-O symbol output, its name has a leading underscore.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;fn main()&lt;/code&gt; in our source and the externally called entry function are separate functions. We did not write the entry wrapper ourselves.&lt;/p&gt;

&lt;h2&gt;
  
  
  Which one does the executable identify as its entry point?
&lt;/h2&gt;

&lt;p&gt;Names alone do not establish which function is the entry point. Let's inspect the executable's metadata too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;otool &lt;span class="nt"&gt;-l&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mach-O has load commands that describe information needed for memory mapping and startup. These are relevant excerpts from my output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  segname __TEXT
   vmaddr 0x0000000100000000
  fileoff 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;          cmd LC_LOAD_DYLINKER
      cmdsize 32
         name /usr/lib/dyld (offset 12)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;       cmd LC_MAIN
   cmdsize 24
  entryoff 2604
 stacksize 0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;LC_LOAD_DYLINKER&lt;/code&gt; identifies the dynamic linker, while &lt;code&gt;LC_MAIN&lt;/code&gt; records the entry location. Here, &lt;code&gt;entryoff&lt;/code&gt; gives the entry's byte offset in &lt;code&gt;__TEXT&lt;/code&gt;. See Mach-O's &lt;a href="https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/loader.h" rel="noopener noreferrer"&gt;entry_point_command&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;2604&lt;/code&gt; in hexadecimal is &lt;code&gt;0xa2c&lt;/code&gt;. In this executable, &lt;code&gt;__TEXT&lt;/code&gt; starts at file offset zero and has a preferred virtual address of &lt;code&gt;0x100000000&lt;/code&gt;, giving:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0x100000000 + 0xa2c = 0x100000a2c
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That matches &lt;code&gt;_main&lt;/code&gt;, not the &lt;code&gt;0x1000009f4&lt;/code&gt; address of our &lt;code&gt;main::main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;These are addresses in the layout recorded in the file. ASLR can shift the actual load addresses at runtime. Also, &lt;code&gt;LC_MAIN&lt;/code&gt; does not mean "the first instruction the CPU executes at startup." It identifies this executable's entry point, reached after preparation by dyld and other startup code.&lt;/p&gt;

&lt;h2&gt;
  
  
  The entry wrapper does not call our main directly
&lt;/h2&gt;

&lt;p&gt;Run the same disassembly command and extract the section starting with &lt;code&gt;&amp;lt;_main&amp;gt;:&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcrun llvm-objdump &lt;span class="nt"&gt;--disassemble&lt;/span&gt; &lt;span class="nt"&gt;--demangle&lt;/span&gt; main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0000000100000a2c &amp;lt;_main&amp;gt;:
100000a2c: a9bf7bfd    stp  x29, x30, [sp, #-0x10]!
100000a30: 910003fd    mov  x29, sp
100000a34: aa0103e2    mov  x2, x1
100000a38: aa0003e8    mov  x8, x0
100000a3c: 93407d01    sxtw x1, w8
100000a40: 90000000    adrp x0, 0x100000000 &amp;lt;dyld_stub_binder+0x100000000&amp;gt;
100000a44: 9127d000    add  x0, x0, #0x9f4
100000a48: 52800003    mov  w3, #0x0
100000a4c: 97ffff8f    bl   0x100000888 &amp;lt;std::rt::lang_start::hf66e9cda02e72e37&amp;gt;
100000a50: a8c17bfd    ldp  x29, x30, [sp], #0x10
100000a54: d65f03c0    ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each instruction line contains its address, the machine-code encoding, and a readable representation of the instruction. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100000a34:  aa0103e2    mov x2, x1
Address     Encoding    Instruction and operands
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;mov x2, x1&lt;/code&gt; copies the value in register &lt;code&gt;x1&lt;/code&gt; into &lt;code&gt;x2&lt;/code&gt;. Registers are locations inside the CPU that hold values and addresses temporarily.&lt;/p&gt;

&lt;p&gt;We do not need to decode every instruction. Start with &lt;code&gt;bl&lt;/code&gt;, the instruction that calls a function. Under the calling convention used here, the first argument is passed in register &lt;code&gt;x0&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The call goes to &lt;code&gt;std::rt::lang_start&lt;/code&gt;, rather than our &lt;code&gt;main&lt;/code&gt;. Before that, &lt;code&gt;adrp&lt;/code&gt; and &lt;code&gt;add&lt;/code&gt; construct &lt;code&gt;0x1000009f4&lt;/code&gt; in &lt;code&gt;x0&lt;/code&gt;. That was the address of our &lt;code&gt;main::main&lt;/code&gt;. Focus on the address calculation here, rather than the symbolic annotation printed beside &lt;code&gt;adrp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The wrapper passes the location of our &lt;code&gt;main&lt;/code&gt; as an argument to the standard library's startup routine. In effect: "Start this Rust program using this function."&lt;/p&gt;

&lt;p&gt;We can check the same relationship in LLVM IR. Here is the entry wrapper from &lt;code&gt;main.ll&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="k"&gt;define&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="vg"&gt;@main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="nv"&gt;%0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ptr&lt;/span&gt; &lt;span class="nv"&gt;%1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;unnamed_addr&lt;/span&gt; &lt;span class="vg"&gt;#4&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="nl"&gt;top:&lt;/span&gt;
  &lt;span class="nv"&gt;%2&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;sext&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="nv"&gt;%0&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="kt"&gt;i64&lt;/span&gt;
&lt;span class="c1"&gt;; call std::rt::lang_start&lt;/span&gt;
  &lt;span class="nv"&gt;%3&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;call&lt;/span&gt; &lt;span class="kt"&gt;i64&lt;/span&gt; &lt;span class="vg"&gt;@_ZN3std2rt10lang_start17hf66e9cda02e72e37E&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;ptr&lt;/span&gt; &lt;span class="vg"&gt;@_ZN4main4main17h2dd6e7c25b480450E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;i64&lt;/span&gt; &lt;span class="nv"&gt;%2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ptr&lt;/span&gt; &lt;span class="nv"&gt;%1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;i8&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nv"&gt;%4&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;trunc&lt;/span&gt; &lt;span class="kt"&gt;i64&lt;/span&gt; &lt;span class="nv"&gt;%3&lt;/span&gt; &lt;span class="k"&gt;to&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;
  &lt;span class="k"&gt;ret&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="nv"&gt;%4&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the parentheses on the line calling &lt;code&gt;lang_start&lt;/code&gt;, the first argument is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="kt"&gt;ptr&lt;/span&gt; &lt;span class="vg"&gt;@_ZN4main4main17h2dd6e7c25b480450E&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;ptr&lt;/code&gt; is the pointer type, and &lt;code&gt;@_ZN4main4main…&lt;/code&gt; refers to our &lt;code&gt;main::main&lt;/code&gt; function. This argument is a pointer to that function.&lt;/p&gt;

&lt;p&gt;Replacing the long names with readable descriptions gives the following argument order. This is explanatory notation, not literal LLVM IR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lang_start(pointer to our main, argc, argv, setting)
           ↑ first argument
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The remaining arguments are the command-line argument count, &lt;code&gt;argc&lt;/code&gt;; &lt;code&gt;argv&lt;/code&gt;, which points to the argument strings; and a setting related to signal handling.&lt;/p&gt;

&lt;p&gt;Our own &lt;code&gt;main&lt;/code&gt; has no declared parameters. The entry wrapper still handles information passed in from the operating system.&lt;/p&gt;

&lt;h2&gt;
  
  
  What does lang_start prepare?
&lt;/h2&gt;

&lt;p&gt;The entry wrapper passed our &lt;code&gt;main&lt;/code&gt; to &lt;code&gt;std::rt::lang_start&lt;/code&gt; instead of calling it directly. What does &lt;code&gt;lang_start&lt;/code&gt; do before calling our function? Let's look at the standard library implementation.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;library/std/src/rt.rs&lt;/code&gt; shipped with my Rust toolchain, &lt;code&gt;lang_start&lt;/code&gt; is defined as follows. Its attributes are omitted.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;lang_start&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;process&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Termination&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;isize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sigpipe&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;isize&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;lang_start_internal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;backtrace&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;__rust_begin_short_backtrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.report&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_i32&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;sigpipe&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first parameter, &lt;code&gt;main: fn() -&amp;gt; T&lt;/code&gt;, is the function pointer received from the entry wrapper. &lt;code&gt;lang_start&lt;/code&gt; wraps the work of calling that function and converting its result into an exit code in a closure, then passes that closure to &lt;code&gt;lang_start_internal&lt;/code&gt;. The &lt;code&gt;&amp;amp;move || ...&lt;/code&gt; part creates the closure and passes a reference to it.&lt;/p&gt;

&lt;p&gt;Next, here is an excerpt from the body of &lt;code&gt;lang_start_internal&lt;/code&gt;. Comments mark omitted code; this excerpt is not independently compilable.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Excerpt from the body of lang_start_internal&lt;/span&gt;
&lt;span class="nn"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;catch_unwind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;argc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sigpipe&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ret_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;catch_unwind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or_else&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Panic handling omitted&lt;/span&gt;
        &lt;span class="mi"&gt;101&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ret_code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ret_code&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;isize&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;cleanup&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Coordination of concurrent thread exits omitted&lt;/span&gt;

    &lt;span class="n"&gt;ret_code&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nf"&gt;.unwrap_or_else&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;handle_rt_panic&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It first calls &lt;code&gt;init&lt;/code&gt;, then runs the supplied closure through &lt;code&gt;panic::catch_unwind(main)&lt;/code&gt;. That leads to our own &lt;code&gt;main&lt;/code&gt;. Afterward, it calls &lt;code&gt;cleanup&lt;/code&gt; and returns the exit code, &lt;code&gt;ret_code&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Leaving the detailed error handling aside, the sequence is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lang_start_internal
    ↓
Initialize Rust runtime state
    ↓
Invoke the supplied closure, reaching our main
    ↓
Handle the return value as an exit code
    ↓
Clean up stdout and other runtime state
    ↓
Return the exit code to the entry wrapper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialization records the main thread's ID and proceeds to platform-specific preparation. In the Unix implementation used here, that includes checking the standard I/O file descriptors, configuring &lt;code&gt;SIGPIPE&lt;/code&gt; behavior, and preparing stack overflow handling. On macOS, it also sets the main thread's name. See &lt;a href="https://doc.rust-lang.org/src/std/rt.rs.html" rel="noopener noreferrer"&gt;std startup&lt;/a&gt; and &lt;a href="https://doc.rust-lang.org/src/std/sys/pal/unix/mod.rs.html" rel="noopener noreferrer"&gt;Unix initialization&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;SIGPIPE&lt;/code&gt; is a signal relevant to situations such as writing to a pipe whose reader has gone away. Startup handles settings we rarely think about when using &lt;code&gt;println!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Rust does not prepare everything from scratch, though. The Apple-specific command-line argument implementation uses information already prepared by dyld. Rust's preparation builds on work done by the OS and dynamic linker. See the &lt;a href="https://doc.rust-lang.org/src/std/sys/args/unix.rs.html" rel="noopener noreferrer"&gt;Apple argument implementation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;When &lt;code&gt;main&lt;/code&gt; returns normally, execution proceeds to cleanup. The call is also wrapped in &lt;code&gt;catch_unwind&lt;/code&gt; to handle panic unwinding. With a configuration that aborts on panic, however, the process terminates without unwinding.&lt;/p&gt;

&lt;p&gt;Asking whether Rust "has a runtime" can be ambiguous. What we can see here is that this ordinary executable using &lt;code&gt;std&lt;/code&gt; contains startup routines implemented in Rust.&lt;/p&gt;

&lt;h2&gt;
  
  
  Stop inside main and look at the path we took
&lt;/h2&gt;

&lt;p&gt;We can also check this while the program runs. Use LLDB to place a breakpoint in our own &lt;code&gt;main&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lldb ./main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside LLDB, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;breakpoint set -n main::main
run
bt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;bt&lt;/code&gt; walks the current call stack: the current function, its caller, that caller's caller, and so on. My program stopped at the &lt;code&gt;println!&lt;/code&gt; line. See the &lt;a href="https://lldb.llvm.org/use/tutorial.html" rel="noopener noreferrer"&gt;LLDB tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the relevant frames, with long arguments, hashes, and line numbers removed. The frame numbers are unchanged.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;frame #0:  main::main
frame #1:  core::ops::function::FnOnce::call_once
frame #2:  std::sys::backtrace::__rust_begin_short_backtrace
frame #3:  std::rt::lang_start::{closure}
           (frames #4 through #11 omitted)
frame #12: std::rt::lang_start_internal
frame #13: std::rt::lang_start
frame #14: main
frame #15: dyld`start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The current location is at the top, so read from bottom to top to follow the calls that brought us here.&lt;/p&gt;

&lt;p&gt;From dyld, through the entry &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;lang_start&lt;/code&gt;, and &lt;code&gt;lang_start_internal&lt;/code&gt;, we finally reach our own &lt;code&gt;main::main&lt;/code&gt;. Along the way are a closure and helper functions used to invoke it.&lt;/p&gt;

&lt;p&gt;Now "code runs before our main" is something we can see in an actual call stack.&lt;/p&gt;

&lt;h2&gt;
  
  
  Returning from main is not the final step either
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;main&lt;/code&gt; has no explicit return type, so it returns &lt;code&gt;()&lt;/code&gt;. &lt;code&gt;lang_start&lt;/code&gt; uses the &lt;code&gt;Termination&lt;/code&gt; trait's &lt;code&gt;report&lt;/code&gt; method to convert that result into an exit status. For &lt;code&gt;()&lt;/code&gt;, the status indicates success. See &lt;a href="https://doc.rust-lang.org/std/process/trait.Termination.html" rel="noopener noreferrer"&gt;Termination&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The startup code does more than invoke our function: it translates a Rust return value into a result for the program as a whole. After standard library cleanup, the entry wrapper returns an exit code, leading to the system's process termination handling.&lt;/p&gt;

&lt;p&gt;There are other ways to terminate, such as &lt;code&gt;process::exit&lt;/code&gt; and aborting, which do not return through this entire path. We followed the normal completion of this small program.&lt;/p&gt;

&lt;p&gt;Even for a three-line program, the OS and dyld prepare it, and Rust's standard library performs initialization, before the &lt;code&gt;main&lt;/code&gt; we wrote is called.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Why Rust Object Files Still Need a Linker: Symbols and Relocations</title>
      <dc:creator>Masahiro Morino</dc:creator>
      <pubDate>Sat, 10 Oct 2026 10:59:01 +0000</pubDate>
      <link>https://dev.to/masamori/why-rust-object-files-still-need-a-linker-symbols-and-relocations-5729</link>
      <guid>https://dev.to/masamori/why-rust-object-files-still-need-a-linker-symbols-and-relocations-5729</guid>
      <description>&lt;p&gt;When you compile Rust code, object files are produced along the way. They have the extension &lt;code&gt;.o&lt;/code&gt; and contain machine code and data. The linker combines them to build an executable or another output.&lt;/p&gt;

&lt;p&gt;But something bothers me here. If the machine code is already there, shouldn't the CPU be able to execute it? Why is linking still necessary?&lt;/p&gt;

&lt;p&gt;Let's compile a pair of small functions separately and look at a state where &lt;strong&gt;the machine code exists, but the destination of a function call has not been determined yet&lt;/strong&gt;. Symbols and relocations will help us read that state.&lt;/p&gt;

&lt;p&gt;I tried this on an Apple Silicon Mac. The object format is Mach-O, and the instructions are ARM64. On Linux or Windows, the format and inspection commands differ. The addresses and instruction output below were obtained on October 10, 2026.&lt;/p&gt;

&lt;h2&gt;
  
  
  Can we generate machine code without the callee's implementation?
&lt;/h2&gt;

&lt;p&gt;In everyday Rust, we define functions with &lt;code&gt;fn&lt;/code&gt; and use Rust's usual mechanisms to call functions in other crates. We do not need to write &lt;code&gt;unsafe&lt;/code&gt; or &lt;code&gt;extern "C"&lt;/code&gt; just to use the linker.&lt;/p&gt;

&lt;p&gt;For this experiment, I want to build the caller and callee as separate &lt;code&gt;.o&lt;/code&gt; files and &lt;strong&gt;compare the state without the callee's implementation with the state after linking&lt;/strong&gt;. That calls for a slightly unusual setup. &lt;code&gt;extern "C"&lt;/code&gt; gives both sides the same calling convention, and &lt;code&gt;#[unsafe(no_mangle)]&lt;/code&gt; makes the symbol names easy to follow. The &lt;code&gt;"C"&lt;/code&gt; specifies a calling convention; it does not mean we are using C source code. Both implementations are written in Rust.&lt;/p&gt;

&lt;p&gt;With an external function declared this way, however, the compiler alone cannot verify that the declaration matches the actual definition. That is why &lt;code&gt;unsafe&lt;/code&gt; appears in this declaration and call. It is part of the setup chosen to observe the linker, rather than a requirement for ordinary Rust function calls.&lt;/p&gt;

&lt;p&gt;First, let's write a function that simply calls &lt;code&gt;answer&lt;/code&gt;. Save this as &lt;code&gt;caller.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#![no_std]&lt;/span&gt;

&lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[unsafe(no_mangle)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;call_answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have the body of &lt;code&gt;call_answer&lt;/code&gt;, but not the body of &lt;code&gt;answer&lt;/code&gt;. All we have for &lt;code&gt;answer&lt;/code&gt; is a declaration saying that it takes no arguments and returns a &lt;code&gt;u32&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Since this looks a little different from everyday Rust, let's go through the settings used for the experiment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;unsafe extern "C"&lt;/code&gt; block declares an external function. We specify its type and calling convention, but this declaration alone does not let the compiler verify that they match the actual definition.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;extern "C"&lt;/code&gt; selects the C ABI calling convention. Here, it gives the caller and the definition the same convention. We will write the implementation in Rust later.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#[unsafe(no_mangle)]&lt;/code&gt; disables the usual symbol name mangling so that we can follow the function names easily. It is an unsafe attribute because the names can collide with other symbols.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;#![no_std]&lt;/code&gt; prevents this small library from automatically using the standard library, &lt;code&gt;std&lt;/code&gt;. The &lt;code&gt;main&lt;/code&gt; function we write later will use &lt;code&gt;std&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For more about these settings, see the Rust Reference sections on &lt;a href="https://doc.rust-lang.org/reference/items/external-blocks.html" rel="noopener noreferrer"&gt;External blocks&lt;/a&gt;, &lt;a href="https://doc.rust-lang.org/reference/abi.html#the-no_mangle-attribute" rel="noopener noreferrer"&gt;The no_mangle attribute&lt;/a&gt;, and &lt;a href="https://doc.rust-lang.org/reference/names/preludes.html#the-no_std-attribute" rel="noopener noreferrer"&gt;The no_std attribute&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The following command produces an object file before linking:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc caller.rs &lt;span class="nt"&gt;--crate-type&lt;/span&gt; lib &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nv"&gt;panic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;abort &lt;span class="nt"&gt;--emit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;obj &lt;span class="nt"&gt;-o&lt;/span&gt; caller.o
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--emit=obj&lt;/code&gt; requests a native object file. &lt;code&gt;--crate-type lib&lt;/code&gt; tells rustc to compile code without a &lt;code&gt;main&lt;/code&gt; function as a library. Here, we are taking the &lt;code&gt;.o&lt;/code&gt; requested by &lt;code&gt;--emit=obj&lt;/code&gt;, rather than an &lt;code&gt;.rlib&lt;/code&gt;. We also specify &lt;code&gt;-C panic=abort&lt;/code&gt;, so unwinding on panic is outside this experiment. &lt;a href="https://doc.rust-lang.org/rustc/command-line-arguments.html#--emit-specifies-the-types-of-output-files-to-generate" rel="noopener noreferrer"&gt;Command-line Arguments: --emit&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even without the body of &lt;code&gt;answer&lt;/code&gt;, &lt;code&gt;caller.o&lt;/code&gt; was produced.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;file caller.o
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;caller.o: Mach-O 64-bit object arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So this part works. But what happened to the call to &lt;code&gt;answer&lt;/code&gt;?&lt;/p&gt;

&lt;h2&gt;
  
  
  Object files contain more than machine code
&lt;/h2&gt;

&lt;p&gt;An object file is more than a sequence of machine instructions. Alongside code and data, it carries information needed to connect them later.&lt;/p&gt;

&lt;p&gt;We will focus on three things:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Contents&lt;/th&gt;
&lt;th&gt;What we will examine&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Code section&lt;/td&gt;
&lt;td&gt;What the machine code for &lt;code&gt;call_answer&lt;/code&gt; looks like&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Symbol table&lt;/td&gt;
&lt;td&gt;Which names this file defines, and which it needs from elsewhere&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Relocation information&lt;/td&gt;
&lt;td&gt;Where references must be adjusted, and which targets they refer to&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A section is a region that holds a particular kind of content, such as code or data. In my &lt;code&gt;caller.o&lt;/code&gt;, the instructions were in the &lt;code&gt;__text&lt;/code&gt; section of &lt;code&gt;__TEXT&lt;/code&gt;. Here, &lt;code&gt;text&lt;/code&gt; means code, rather than written text.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ooy40wctojwh59ocqth.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8ooy40wctojwh59ocqth.png" alt="caller.o contains the instructions for call_answer, a symbol table with a definition of call_answer and an undefined reference to answer, and relocation information for the instruction at offset 8" width="800" height="192"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: The three kinds of information we examine in caller.o. This diagram does not show their actual positions or sizes in the file. Headers and other information are omitted.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Mach-O also has headers and load commands that let tools locate this information. We will read the parts relevant to the function call rather than decode the entire file format. On macOS, &lt;code&gt;nm&lt;/code&gt; inspects the symbol table, and &lt;code&gt;otool&lt;/code&gt; inspects sections and other contents. &lt;a href="https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/building_files.html" rel="noopener noreferrer"&gt;Building Mach-O Files — Apple&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The symbol table records the missing implementation
&lt;/h2&gt;

&lt;p&gt;First, let's use &lt;code&gt;nm&lt;/code&gt; to see the externally visible symbols:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nm &lt;span class="nt"&gt;-g&lt;/span&gt; caller.o
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                 U _answer
0000000000000000 T _call_answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;strong&gt;symbol&lt;/strong&gt; is information used to refer to a function or another entity by name. In this Mach-O output, the source names &lt;code&gt;answer&lt;/code&gt; and &lt;code&gt;call_answer&lt;/code&gt; appear with a leading underscore.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;T&lt;/code&gt; denotes a definition in the code section; &lt;code&gt;U&lt;/code&gt; denotes a symbol that is undefined in this file. In other words, &lt;code&gt;call_answer&lt;/code&gt; is here, while the definition of &lt;code&gt;answer&lt;/code&gt; must come from elsewhere. &lt;a href="https://llvm.org/docs/CommandGuide/llvm-nm.html#description" rel="noopener noreferrer"&gt;llvm-nm: symbol types&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At this stage, &lt;code&gt;U&lt;/code&gt; is not a failure. &lt;strong&gt;The object file records the intention to call something defined elsewhere.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;0000000000000000&lt;/code&gt; to the left of &lt;code&gt;_call_answer&lt;/code&gt; does not mean the function will execute at address zero, either. In this &lt;code&gt;.o&lt;/code&gt;, it is at the start of the code section. Its placement in the final executable is still to be determined.&lt;/p&gt;

&lt;p&gt;So the name is still there. What about the machine code?&lt;/p&gt;

&lt;h2&gt;
  
  
  There is a call instruction. But where does it go?
&lt;/h2&gt;

&lt;p&gt;Let's disassemble the code with &lt;code&gt;otool&lt;/code&gt;. Disassembly turns machine code bytes back into a human-readable instruction listing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;otool &lt;span class="nt"&gt;-tvV&lt;/span&gt; caller.o
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;caller.o:
(__TEXT,__text) section
_call_answer:
0000000000000000 stp x29, x30, [sp, #-0x10]!
0000000000000004 mov x29, sp
0000000000000008 bl  0x8
000000000000000c ldp x29, x30, [sp], #0x10
0000000000000010 ret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The instruction to focus on is &lt;code&gt;bl&lt;/code&gt; at &lt;code&gt;0000000000000008&lt;/code&gt;. ARM64 uses this instruction for function calls, and it also preserves information needed to return. Around it, we can see register saves and restores associated with the call. &lt;a href="https://developer.arm.com/documentation/ddi0602/2021-12/Base-Instructions/BL--Branch-with-Link-" rel="noopener noreferrer"&gt;Arm: BL — Branch with Link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;0000000000000008&lt;/code&gt; on the left is the address where the &lt;code&gt;bl&lt;/code&gt; instruction sits. The &lt;code&gt;bl 0x8&lt;/code&gt; on the right shows its branch destination as &lt;code&gt;0x8&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Wait. The instruction is at &lt;code&gt;0x8&lt;/code&gt;, and its destination is also &lt;code&gt;0x8&lt;/code&gt;? Read on its own, this looks like an instruction that branches to itself.&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;bl&lt;/code&gt; instruction encodes the distance from the instruction to its destination. That distance is called a relative displacement. In this output before linking, it is still a placeholder value of zero. So &lt;code&gt;0x8 + 0 = 0x8&lt;/code&gt;, and the disassembler shows a branch to the same location. During linking, the linker writes the correct distance to &lt;code&gt;answer&lt;/code&gt; into the instruction.&lt;/p&gt;

&lt;p&gt;What we actually want to call is &lt;code&gt;answer&lt;/code&gt;. To see that intention, we need to read the instruction together with the relocation information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Relocation information says: adjust this reference to answer
&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;otool -rv&lt;/code&gt; to display the relocations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;otool &lt;span class="nt"&gt;-rv&lt;/span&gt; caller.o
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;caller.o:
Relocation information (__TEXT,__text) 1 entries
address  pcrel length extern type    scattered symbolnum/value
00000008 True  long   True   BR26    False     _answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's match this row to the instruction we just saw:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Field&lt;/th&gt;
&lt;th&gt;Meaning in this example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;address: 00000008&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Apply the relocation 8 bytes from the start of the &lt;code&gt;__text&lt;/code&gt; section&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;pcrel: True&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Treat the reference as relative to the instruction's position&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;type: BR26&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A relocation that adjusts the displacement of an ARM64 branch instruction&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_answer&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The symbol used to determine the target&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Here, &lt;code&gt;address&lt;/code&gt; does not mean byte 8 from the start of the entire file. It is an offset within the code section. The file also contains headers and other information, so section offsets and file offsets must be read separately.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;BR26&lt;/code&gt; corresponds to Apple's &lt;code&gt;ARM64_RELOC_BRANCH26&lt;/code&gt;, which handles the 26-bit displacement in &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;BL&lt;/code&gt; instructions. Different instruction types need different adjustments, so a relocation needs both the referenced symbol and a relocation type. &lt;a href="https://github.com/apple-oss-distributions/xnu/blob/main/EXTERNAL_HEADERS/mach-o/arm64/reloc.h" rel="noopener noreferrer"&gt;Apple: arm64 relocation types&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;symbol table&lt;/strong&gt; tells us which names exist. The &lt;strong&gt;relocation information&lt;/strong&gt; tells us where to adjust references to those names.&lt;/p&gt;

&lt;p&gt;That lets us produce the call instruction and the instructions for completing it later, even before we know where &lt;code&gt;answer&lt;/code&gt; will be placed.&lt;/p&gt;

&lt;p&gt;The word “relocation” might sound as though we are simply moving a function somewhere else. What we are examining here is information for adjusting a reference encoded in an instruction to match the final placement.&lt;/p&gt;

&lt;h2&gt;
  
  
  Put the body of answer in another .o
&lt;/h2&gt;

&lt;p&gt;Now let's write the callee. Save this as &lt;code&gt;provider.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#![no_std]&lt;/span&gt;

&lt;span class="nd"&gt;#[unsafe(no_mangle)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mi"&gt;42&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compile this into a separate object file as well:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc provider.rs &lt;span class="nt"&gt;--crate-type&lt;/span&gt; lib &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nv"&gt;panic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;abort &lt;span class="nt"&gt;--emit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;obj &lt;span class="nt"&gt;-o&lt;/span&gt; provider.o

nm &lt;span class="nt"&gt;-g&lt;/span&gt; provider.o
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;0000000000000000 T _answer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, &lt;code&gt;_answer&lt;/code&gt; has a &lt;code&gt;T&lt;/code&gt;. The definition that &lt;code&gt;caller.o&lt;/code&gt; needs now exists in &lt;code&gt;provider.o&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;But this address is also zero. &lt;code&gt;call_answer&lt;/code&gt; is at zero, and &lt;code&gt;answer&lt;/code&gt; is at zero. Are they in the same place?&lt;/p&gt;

&lt;p&gt;No: they are at the starts of their own sections in separate object files. During linking, they are placed in the output, and their references are connected using that placement.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4f7lscg3kr7pgc9ty4k0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4f7lscg3kr7pgc9ty4k0.png" alt="The linker matches caller.o's undefined reference and relocation at offset 8 to the definition of answer in provider.o, then adjusts the call instruction using the output layout" width="800" height="736"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2: A conceptual view of resolving the reference to answer. Other linker inputs, such as main and the standard library, are omitted.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Matching a name's reference to its definition is &lt;strong&gt;symbol resolution&lt;/strong&gt;. Adjusting the necessary references according to the layout is the relocation processing we are looking at here.&lt;/p&gt;

&lt;p&gt;Putting the files next to each other does not connect them. We need to establish which code calls which destination, and where that destination is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Leave out the implementation, and linking fails
&lt;/h2&gt;

&lt;p&gt;Let's add an entry point so we can run the program. Save this as &lt;code&gt;main.rs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;call_answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;call_answer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This &lt;code&gt;main&lt;/code&gt; uses &lt;code&gt;std&lt;/code&gt;. First, deliberately add only &lt;code&gt;caller.o&lt;/code&gt; to the build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc main.rs &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nv"&gt;panic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;abort &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; link-arg&lt;span class="o"&gt;=&lt;/span&gt;caller.o &lt;span class="nt"&gt;-o&lt;/span&gt; demo-missing
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This time, linking failed. Here is the actual error, with only the long linker command and the note about its arguments omitted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: linking with `cc` failed: exit status: 1
  |
  … (linker command and argument note omitted)
  = note: Undefined symbols for architecture arm64:
            "_answer", referenced from:
                _call_answer in caller.o
          ld: symbol(s) not found for architecture arm64
          clang: error: linker command failed with exit code 1 (use -v to see invocation)

error: aborting due to 1 previous error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Undefined symbols&lt;/code&gt; means that a symbol's definition could not be found. The missing name is &lt;code&gt;_answer&lt;/code&gt;, and the error identifies &lt;code&gt;_call_answer&lt;/code&gt; in &lt;code&gt;caller.o&lt;/code&gt; as the code referencing it.&lt;/p&gt;

&lt;p&gt;We have &lt;code&gt;call_answer&lt;/code&gt;, but cannot find the &lt;code&gt;answer&lt;/code&gt; it calls. A reference that could remain unresolved when producing the &lt;code&gt;.o&lt;/code&gt; cannot be resolved when building this executable, so linking fails.&lt;/p&gt;

&lt;p&gt;Now let's also provide &lt;code&gt;provider.o&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc main.rs &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nv"&gt;panic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;abort &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; link-arg&lt;span class="o"&gt;=&lt;/span&gt;caller.o &lt;span class="nt"&gt;-C&lt;/span&gt; link-arg&lt;span class="o"&gt;=&lt;/span&gt;provider.o &lt;span class="nt"&gt;-o&lt;/span&gt; demo

./demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;42
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It works.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;-C link-arg&lt;/code&gt; adds an argument to the linker invocation. Here, we use it to add the two &lt;code&gt;.o&lt;/code&gt; files. rustc also supplies inputs needed for &lt;code&gt;main&lt;/code&gt;, the standard library, and so on. These two files are not the executable's only ingredients. &lt;a href="https://doc.rust-lang.org/rustc/codegen-options/#link-arg" rel="noopener noreferrer"&gt;Codegen Options: link-arg&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The instruction really changed after linking
&lt;/h2&gt;

&lt;p&gt;Finally, let's compare the machine code before and after linking. I used the LLVM disassembler available on my machine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xcrun llvm-objdump &lt;span class="nt"&gt;--disassemble&lt;/span&gt; caller.o
xcrun llvm-objdump &lt;span class="nt"&gt;--disassemble&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below are just the lines containing the call to &lt;code&gt;answer&lt;/code&gt;. From left to right, they show the address, the machine instruction's value, and its human-readable form. Tabs and column spacing have been normalized.&lt;/p&gt;

&lt;p&gt;Before linking, in &lt;code&gt;caller.o&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;8: 94000000  bl 0x8 &amp;lt;ltmp0+0x8&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After linking, in &lt;code&gt;demo&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;100030a0c: 94000003  bl 0x100030a18 &amp;lt;_answer&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;94000000&lt;/code&gt; became &lt;code&gt;94000003&lt;/code&gt;. This is more than a function name being added to the display: &lt;strong&gt;the value encoded in the call instruction itself changed&lt;/strong&gt;. &lt;a href="https://llvm.org/docs/CommandGuide/llvm-objdump.html" rel="noopener noreferrer"&gt;llvm-objdump&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In my output, &lt;code&gt;call_answer&lt;/code&gt; was placed at &lt;code&gt;0x100030a04&lt;/code&gt;, and &lt;code&gt;answer&lt;/code&gt; at &lt;code&gt;0x100030a18&lt;/code&gt;. Given that layout, the call instruction at &lt;code&gt;0x100030a0c&lt;/code&gt; now branches to &lt;code&gt;answer&lt;/code&gt;. We can see the relocation applied to the actual machine code.&lt;/p&gt;

&lt;p&gt;The “3” encoded in the instruction does not mean 3 bytes. The 26-bit displacement in &lt;code&gt;bl&lt;/code&gt; (imm26) is a signed value in units of 4 bytes. Here, it means &lt;code&gt;3 × 4 = 12&lt;/code&gt; bytes ahead: &lt;code&gt;0x100030a0c + 12 = 0x100030a18&lt;/code&gt;, the address of &lt;code&gt;answer&lt;/code&gt;. &lt;a href="https://documentation-service.arm.com/static/68da52dfbd7cab51328c0622" rel="noopener noreferrer"&gt;Arm instruction specification&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The addresses shown here are virtual addresses recorded in the executable. They are distinct both from byte offsets within the file and from addresses necessarily used unchanged on every run. ASLR can shift the layout at load time, and references to shared libraries also involve runtime mechanisms. Here, we have focused on a call between two functions included in the same executable. &lt;a href="https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/executing_files.html" rel="noopener noreferrer"&gt;Executing Mach-O Files — Apple&lt;/a&gt;, &lt;a href="https://support.apple.com/guide/security/glossary-sec93292bfa6/web" rel="noopener noreferrer"&gt;Apple Platform Security: ASLR&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why do we still need a linker when the machine code exists?
&lt;/h2&gt;

&lt;p&gt;Our &lt;code&gt;caller.o&lt;/code&gt; contained the machine code for &lt;code&gt;call_answer&lt;/code&gt;. But &lt;code&gt;answer&lt;/code&gt; remained an undefined symbol, accompanied by relocation information for adjusting the call instruction to reference it.&lt;/p&gt;

&lt;p&gt;The compiler producing &lt;code&gt;caller.o&lt;/code&gt; can generate an instruction saying “call &lt;code&gt;answer&lt;/code&gt; here.” At that point, however, we have not supplied &lt;code&gt;provider.o&lt;/code&gt;. We do not know where the body of &lt;code&gt;answer&lt;/code&gt; will sit in the final executable, so we cannot determine the distance to it yet.&lt;/p&gt;

&lt;p&gt;Instead, we leave behind the target's name and the location of the instruction that needs adjustment. Once the necessary inputs are available, the linker does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Match references to definitions.&lt;/strong&gt; It connects the &lt;code&gt;answer&lt;/code&gt; requested by &lt;code&gt;caller.o&lt;/code&gt; to the definition in &lt;code&gt;provider.o&lt;/code&gt;. When we left out the implementation, resolution failed and produced an error.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Determine the layout of code and data.&lt;/strong&gt; It places sections and other contents from separate &lt;code&gt;.o&lt;/code&gt; files into the output executable. Positions local to each object file become part of the final layout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Adjust references to match that layout.&lt;/strong&gt; It reads the relocation information and, in our example, writes the displacement to &lt;code&gt;answer&lt;/code&gt; into the &lt;code&gt;bl&lt;/code&gt; instruction. This is why &lt;code&gt;94000000&lt;/code&gt; became &lt;code&gt;94000003&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Simply concatenating the files would leave the zero in the call instruction unchanged. Even if we included the function's body, the call would not be connected unless we adjusted the reference to reach it. Producing machine code and combining that code into a working program are separate jobs. &lt;a href="https://developer.apple.com/library/archive/documentation/DeveloperTools/Conceptual/MachOTopics/1-Articles/building_files.html" rel="noopener noreferrer"&gt;Building Mach-O Files — Apple&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then why not compile everything together from the start? With a small example, that is tempting. But real programs use the standard library and other libraries alongside our own code. Compiling pieces separately and combining them later allows existing machine code to be reused. There is a qualification in Rust: generic functions, for example, may be monomorphized for concrete types and have machine code generated in the consuming crate. Not everything can be supplied solely as precompiled machine code. When producing the final executable, references between the generated pieces still need to be resolved. &lt;a href="https://rustc-dev-guide.rust-lang.org/backend/monomorph.html" rel="noopener noreferrer"&gt;Monomorphization&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;extern "C"&lt;/code&gt; in this experiment made that boundary easy to observe. Even without this syntax in everyday Rust, separately generated code still has to be connected. rustc can divide a single crate into multiple Codegen Units; with the LLVM backend, the object files produced from those units are linked. This normally happens as part of a build through rustc or Cargo, so we rarely need to operate the linker directly. &lt;a href="https://rustc-dev-guide.rust-lang.org/backend/codegen.html" rel="noopener noreferrer"&gt;Code generation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There was still work left between “the machine code exists” and “we have something executable.” The undefined symbol and relocation information we saw were how that work was handed to the next stage.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>What Code Does Rust Pass to LLVM? Generics and Codegen Units</title>
      <dc:creator>Masahiro Morino</dc:creator>
      <pubDate>Sat, 03 Oct 2026 05:05:26 +0000</pubDate>
      <link>https://dev.to/masamori/what-code-does-rust-pass-to-llvm-generics-and-codegen-units-ll6</link>
      <guid>https://dev.to/masamori/what-code-does-rust-pass-to-llvm-generics-and-codegen-units-ll6</guid>
      <description>&lt;p&gt;In the previous article, I generated MIR from Rust code and tried reading it. An &lt;code&gt;if&lt;/code&gt; expression became a set of blocks, and we could see where values went and which block execution would enter next.&lt;/p&gt;

&lt;p&gt;But being able to read MIR doesn't mean we've reached machine code yet. LLVM apparently comes into the picture further along. What does rustc actually pass to it, and how?&lt;/p&gt;

&lt;p&gt;For example, if we call one generic function with both &lt;code&gt;u32&lt;/code&gt; and &lt;code&gt;f64&lt;/code&gt;, does LLVM receive one function? Or two? Even if both use &lt;code&gt;x + x&lt;/code&gt;, integer addition and floating-point addition involve different operations, don't they?&lt;/p&gt;

&lt;p&gt;As a follow-up to &lt;a href="https://dev.to/masamori/reading-rusts-mir-following-control-flow-and-values-36jm"&gt;Reading Rust's MIR&lt;/a&gt;, this article explores &lt;strong&gt;which code rustc passes to LLVM, and how it groups that code&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The key ideas are monomorphization of generics and partitioning into Codegen Units (CGUs). We'll start with collecting the code we need, then look at dividing it into units, and finally at converting it to LLVM IR. I started out investigating linkers, and somehow I still haven't reached one.&lt;/p&gt;

&lt;p&gt;This article is based on the Rust Compiler Development Guide as consulted on October 1, 2026. I rechecked the examples with Rust 1.96.0, the same version used in the previous article, and used that version's implementation when looking at CGU partitioning. Compiler internals aren't fixed by the Rust language specification.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting a feel for the path after MIR
&lt;/h2&gt;

&lt;p&gt;rustc normally uses LLVM as its code-generation backend. On the Rust side, rustc handles syntax, types, borrowing, and related processing, then lowers the program to an intermediate representation called LLVM IR. LLVM receives that representation and performs optimization and machine-code generation for the target environment. Here, we'll follow the path that uses the LLVM backend. &lt;a href="https://rustc-dev-guide.rust-lang.org/backend/codegen.html" rel="noopener noreferrer"&gt;Code generation — Rust Compiler Development Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once MIR is ready, the part of the process we're interested in looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5jhf6o2ldtpjic5cesa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fn5jhf6o2ldtpjic5cesa.png" alt="MIR passes through collection of code-generation items, CGU partitioning, and lowering to LLVM IR before LLVM generates object files." width="795" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: A conceptual view from MIR to object files. Details such as optimization across units through LTO are omitted.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let's pause here. MIR, LLVM IR, CGU. With these names lined up, they all start to look like similar intermediate representations.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;What it represents&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;MIR&lt;/td&gt;
&lt;td&gt;A representation of Rust operations for analysis and transformation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLVM IR&lt;/td&gt;
&lt;td&gt;A representation of code that rustc passes to LLVM&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CGU&lt;/td&gt;
&lt;td&gt;A grouping that determines which code undergoes code generation together&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;MIR and LLVM IR are formats for representing code; a CGU is a unit that groups code-generation items.&lt;/strong&gt; One concerns how code is represented, and the other concerns which code is processed together. They appear in sequence in the diagram, but they're describing different things.&lt;/p&gt;

&lt;p&gt;rustc first collects the functions and other items it needs, then decides which CGU to put them in. It lowers MIR to LLVM IR for each grouping and passes the result to LLVM. Let's look at what the “collection” and “partitioning” in the middle of the diagram are for.&lt;/p&gt;

&lt;h2&gt;
  
  
  Collecting the concrete types that are needed
&lt;/h2&gt;

&lt;p&gt;First comes collection: identifying the items needed for code generation.&lt;/p&gt;

&lt;p&gt;In a generic function, a type parameter such as &lt;code&gt;T&lt;/code&gt; doesn't simply become a machine-code type as written in the source. Rust generates code for the combinations of concrete types it needs. This is &lt;strong&gt;monomorphization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, this &lt;code&gt;twice&lt;/code&gt; function adds a value to itself:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;twice&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Add&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Copy&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;twice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3_u32&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;twice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1.5_f64&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;Add&amp;lt;Output = T&amp;gt;&lt;/code&gt; requires the addition's result to have type &lt;code&gt;T&lt;/code&gt; as well. &lt;code&gt;Copy&lt;/code&gt; lets us use the same &lt;code&gt;x&lt;/code&gt; on both sides of the addition. This example uses the function in two ways: with &lt;code&gt;T = u32&lt;/code&gt; and with &lt;code&gt;T = f64&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here, I'll call a function with concrete type arguments, such as &lt;code&gt;twice::&amp;lt;u32&amp;gt;&lt;/code&gt;, an “instance.” We're talking about a function to generate code for, rather than a value or object stored in a variable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmfe65k0884kjaub9ih81.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmfe65k0884kjaub9ih81.png" alt="One definition of twice is called with u32 and f64. The two instances are collected and, after placement in CGUs, lowered to LLVM IR." width="800" height="822"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 2: A conceptual view focused on twice. Other code-generation items, such as main and the addition implementations, are omitted. The two columns do not imply that the instances go into separate CGUs.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;It helps to distinguish &lt;strong&gt;collecting the required items&lt;/strong&gt; from &lt;strong&gt;generating code for their concrete types&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The monomorphization collector first identifies the instances needed for code generation, such as &lt;code&gt;twice::&amp;lt;u32&amp;gt;&lt;/code&gt; and &lt;code&gt;twice::&amp;lt;f64&amp;gt;&lt;/code&gt;. This includes ordinary functions such as &lt;code&gt;main&lt;/code&gt; and statics, too. The items are then placed in CGUs, and their type parameters are treated as concrete types during lowering from MIR to LLVM IR. &lt;a href="https://rustc-dev-guide.rust-lang.org/backend/monomorph.html" rel="noopener noreferrer"&gt;Monomorphization&lt;/a&gt;, &lt;a href="https://rustc-dev-guide.rust-lang.org/backend/lowering-mir.html" rel="noopener noreferrer"&gt;Lowering MIR to a Codegen IR&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I emitted LLVM IR for this code locally, I found two definitions of &lt;code&gt;twice&lt;/code&gt;: one taking &lt;code&gt;i32&lt;/code&gt; and another taking &lt;code&gt;double&lt;/code&gt;. The commands and environment are listed later, under “Generating LLVM IR locally.”&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Use in Rust&lt;/th&gt;
&lt;th&gt;Argument and return types in this LLVM IR output&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;twice::&amp;lt;u32&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Both &lt;code&gt;i32&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;twice::&amp;lt;f64&amp;gt;&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Both &lt;code&gt;double&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Wait, &lt;code&gt;i32&lt;/code&gt; for a Rust &lt;code&gt;u32&lt;/code&gt;? In LLVM IR, &lt;code&gt;i32&lt;/code&gt; means a 32-bit integer type. It doesn't carry the same “signed” meaning as Rust's &lt;code&gt;i32&lt;/code&gt;. Signedness is distinguished by the operations or their specifications, such as the kind of comparison being performed. Similar-looking names don't always mean the same thing. &lt;a href="https://llvm.org/docs/LangRef.html#integer-type" rel="noopener noreferrer"&gt;LLVM Language Reference: Integer Type&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Counting “one function definition” in source code is therefore different from counting “how many concrete function instances are needed” during code generation. This is where writing one function with &lt;code&gt;T&lt;/code&gt; connects to performing operations on particular types.&lt;/p&gt;

&lt;p&gt;That doesn't mean two functions always survive in the final binary. This example doesn't use the results, so optimization may remove the calls altogether. With inlining and other transformations in the picture, we need to distinguish the collected items from the machine code that eventually remains.&lt;/p&gt;

&lt;h2&gt;
  
  
  CGUs group the code passed to LLVM
&lt;/h2&gt;

&lt;p&gt;Once the required code-generation items have been collected, rustc partitions them into &lt;strong&gt;Codegen Units&lt;/strong&gt; (CGUs). With the LLVM backend, each CGU gets an LLVM module: a grouping of LLVM IR. An LLVM module doesn't correspond one-to-one with a Rust &lt;code&gt;mod&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Here's a conceptual example with two CGUs:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz8e4sto4uxws39kuclwi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fz8e4sto4uxws39kuclwi.png" alt="Collected items are placed in CGU A and CGU B. Each becomes an LLVM module and then an object file, which proceeds to linking." width="800" height="839"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 3: A conceptual view of processing two CGUs. LLVM's work in the two columns can proceed in parallel. LTO and duplication of code for inlining are omitted.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Dividing the work LLVM can handle allows compilation to use multiple cores. &lt;a href="https://rustc-dev-guide.rust-lang.org/backend/codegen.html" rel="noopener noreferrer"&gt;Code generation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another consideration is incremental compilation. It reuses results from an earlier compilation where changes haven't affected them. For code-generation artifacts, rustc decides whether reuse is possible at the CGU level. If it can reuse a result, it can skip repeating code generation and optimization for that part. Ideally, we wouldn't regenerate every bit of code on every build. That also influences how CGUs are partitioned. &lt;a href="https://github.com/rust-lang/rust/blob/1.96.0/compiler/rustc_monomorphize/src/partitioning.rs" rel="noopener noreferrer"&gt;CGU partitioning implementation and design notes, Rust 1.96.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So should we just split everything into smaller and smaller units?&lt;/p&gt;

&lt;p&gt;There are tradeoffs. When LLVM optimizes within one module, it can consider the functions and other items inside that module together. Partitioning affects both parallelism and the granularity of reuse, as well as opportunities for optimization across functions.&lt;/p&gt;

&lt;p&gt;For example, inlining puts the body of a called function into its caller, which requires that body to be available. As we divide the work, we also have to consider how much code can be optimized together. A question about compilation speed has turned into a question about the speed of the generated program.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;CGU partitioning&lt;/th&gt;
&lt;th&gt;Potential benefit during compilation&lt;/th&gt;
&lt;th&gt;What to watch for in the generated code&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;More CGUs&lt;/td&gt;
&lt;td&gt;May allow more parallel LLVM work and finer-grained reuse&lt;/td&gt;
&lt;td&gt;Partitioning can affect optimization opportunities and may reduce runtime performance&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Fewer CGUs&lt;/td&gt;
&lt;td&gt;Allows optimization over a wider scope within one module&lt;/td&gt;
&lt;td&gt;Compilation may be slower, and better runtime performance isn't guaranteed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;rustc's &lt;code&gt;-C codegen-units=N&lt;/code&gt; sets an upper limit on the number of CGUs. The official documentation likewise explains that increasing the number may speed up compilation while producing slower code. &lt;a href="https://doc.rust-lang.org/rustc/codegen-options/#codegen-units" rel="noopener noreferrer"&gt;Codegen Options: codegen-units&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Optimization can also cross CGU boundaries. LTO (Link-Time Optimization) allows optimization across module boundaries. To understand runtime performance, we need to consider optimization levels and LTO settings alongside the CGU count. &lt;a href="https://doc.rust-lang.org/rustc/codegen-options/#lto" rel="noopener noreferrer"&gt;Codegen Options: lto&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  CGU boundaries don't simply follow source files
&lt;/h2&gt;

&lt;p&gt;Looking at the diagrams, you might wonder whether rustc creates one CGU for each Rust source file. The relationship isn't that simple.&lt;/p&gt;

&lt;p&gt;During incremental compilation, Rust 1.96.0's partitioning process separates non-generic code associated with a module from instances of generic functions. The generic instances can change as the combinations of types used change, even when the function bodies themselves haven't been edited.&lt;/p&gt;

&lt;p&gt;For example, we could leave the definition of &lt;code&gt;twice&lt;/code&gt; alone but add a use of &lt;code&gt;twice::&amp;lt;u64&amp;gt;&lt;/code&gt; elsewhere. That would require an instance for the new type. Separating these changes from a module's non-generic code helps preserve parts that can be reused.&lt;/p&gt;

&lt;p&gt;This is only part of the partitioning policy, though. The implementation also merges units to stay within the CGU count limit. We can't conclude that every module always produces exactly two final CGUs. &lt;a href="https://github.com/rust-lang/rust/blob/1.96.0/compiler/rustc_monomorphize/src/partitioning.rs" rel="noopener noreferrer"&gt;CGU partitioning implementation, Rust 1.96.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;CGUs are groupings chosen with code generation and reuse in mind, so their organization can differ from how we organize the source.&lt;/p&gt;

&lt;h2&gt;
  
  
  What gets translated from MIR to LLVM IR?
&lt;/h2&gt;

&lt;p&gt;Finally, let's look at the step toward LLVM IR in Figure 1.&lt;/p&gt;

&lt;p&gt;The developer guide introduces &lt;code&gt;rustc_codegen_ssa::mir::codegen_mir&lt;/code&gt; as the entry point for lowering MIR. The processing of MIR's elements is divided into modules such as these:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Module&lt;/th&gt;
&lt;th&gt;What it handles&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;block&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Basic blocks and terminators, including branches and function calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;statement&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Statements such as assignments&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;operand&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Values used in computations and calls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;place&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;References to locations where values are stored&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;rvalue&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Computations and other expressions producing a value to assign&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the previous article, we used &lt;code&gt;choose&lt;/code&gt; to look at branches and their merge in MIR:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;choose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this function, LLVM IR needs to express which block execution enters depending on the condition, what goes into the return value, and where the function returns.&lt;/p&gt;

&lt;p&gt;Let's emit &lt;code&gt;choose.ll&lt;/code&gt; by adding &lt;code&gt;--emit=llvm-ir&lt;/code&gt; to the same example. Omitting the function header with its name and arguments, the braces enclosing the function, and the comments beside the block labels, the body looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight llvm"&gt;&lt;code&gt;&lt;span class="nl"&gt;start:&lt;/span&gt;
  &lt;span class="nv"&gt;%_0&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;alloca&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt; &lt;span class="p"&gt;x&lt;/span&gt; &lt;span class="kt"&gt;i8&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="k"&gt;align&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;i1&lt;/span&gt; &lt;span class="nv"&gt;%flag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%bb1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%bb2&lt;/span&gt;

&lt;span class="nl"&gt;bb2:&lt;/span&gt;
  &lt;span class="k"&gt;store&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ptr&lt;/span&gt; &lt;span class="nv"&gt;%_0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;align&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%bb3&lt;/span&gt;

&lt;span class="nl"&gt;bb1:&lt;/span&gt;
  &lt;span class="k"&gt;store&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="nv"&gt;%x&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ptr&lt;/span&gt; &lt;span class="nv"&gt;%_0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;align&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="k"&gt;br&lt;/span&gt; &lt;span class="kt"&gt;label&lt;/span&gt; &lt;span class="nv"&gt;%bb3&lt;/span&gt;

&lt;span class="nl"&gt;bb3:&lt;/span&gt;
  &lt;span class="nv"&gt;%0&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;load&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;ptr&lt;/span&gt; &lt;span class="nv"&gt;%_0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;align&lt;/span&gt; &lt;span class="m"&gt;4&lt;/span&gt;
  &lt;span class="k"&gt;ret&lt;/span&gt; &lt;span class="kt"&gt;i32&lt;/span&gt; &lt;span class="nv"&gt;%0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are more symbols now, but the branch from the previous article is still recognizable. Let's start with &lt;code&gt;br&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt;, and &lt;code&gt;ret&lt;/code&gt;.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;LLVM IR line&lt;/th&gt;
&lt;th&gt;What it does in this example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;br i1 %flag, label %bb1, label %bb2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enters &lt;code&gt;bb1&lt;/code&gt; if &lt;code&gt;%flag&lt;/code&gt; is true, or &lt;code&gt;bb2&lt;/code&gt; if false&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;store i32 %x, ptr %_0, align 4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Writes &lt;code&gt;%x&lt;/code&gt; to the memory pointed to by &lt;code&gt;%_0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;br label %bb3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Enters &lt;code&gt;bb3&lt;/code&gt; unconditionally&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;%0 = load i32, ptr %_0, align 4&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reads a value from that memory and names it &lt;code&gt;%0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ret i32 %0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Returns the value that was read&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;alloca&lt;/code&gt; allocates space for the return value. The two branches write to that space, and &lt;code&gt;bb3&lt;/code&gt;, where the paths meet, reads it and returns. The output lists &lt;code&gt;bb2&lt;/code&gt; first, but the destinations of the &lt;code&gt;br&lt;/code&gt; instructions determine execution order. We don't execute every block from top to bottom. &lt;a href="https://llvm.org/docs/LangRef.html#br-instruction" rel="noopener noreferrer"&gt;LLVM Language Reference&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, &lt;code&gt;%_0&lt;/code&gt; here is a value pointing to memory, while &lt;code&gt;%0&lt;/code&gt; is the integer read from it. They look almost identical, but that underscore makes them different names. Including the &lt;code&gt;_0&lt;/code&gt; we saw in MIR, it seems safer to follow which instruction creates a value and where it's used, rather than assigning a role based only on its name.&lt;/p&gt;

&lt;p&gt;MIR elements don't always correspond one-to-one with LLVM IR elements, either. Function calls may need to handle unwinding after a panic, depending on the settings and the called function. Lowering calls or &lt;code&gt;assert&lt;/code&gt; operations can produce multiple LLVM basic blocks from a single MIR basic block.&lt;/p&gt;

&lt;p&gt;We saw memory writes and reads in this example, but those aren't guaranteed to remain. Before lowering, rustc also analyzes whether values can be represented directly in SSA form. SSA gives each value name a single definition. Where that form is suitable, rustc can avoid some of the work of building a memory-based representation first and asking LLVM to simplify it afterward.&lt;/p&gt;

&lt;p&gt;Lowering from MIR to LLVM IR therefore involves preserving the meaning of Rust operations while building a form LLVM can work with. It takes more than mechanically replacing the notation. &lt;a href="https://rustc-dev-guide.rust-lang.org/backend/lowering-mir.html" rel="noopener noreferrer"&gt;Lowering MIR to a Codegen IR — Rust Compiler Development Guide&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating LLVM IR locally
&lt;/h2&gt;

&lt;p&gt;Save the &lt;code&gt;twice&lt;/code&gt; example as &lt;code&gt;twice.rs&lt;/code&gt; and the &lt;code&gt;choose&lt;/code&gt; example as &lt;code&gt;choose.rs&lt;/code&gt;. These commands produce the output used here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc twice.rs &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--emit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mir,llvm-ir

rustc choose.rs &lt;span class="nt"&gt;--crate-type&lt;/span&gt; lib &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--emit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mir,llvm-ir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each command produces a &lt;code&gt;.mir&lt;/code&gt; file and a &lt;code&gt;.ll&lt;/code&gt; file. &lt;code&gt;choose&lt;/code&gt; has no &lt;code&gt;main&lt;/code&gt;, so it uses &lt;code&gt;--crate-type lib&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The environment was &lt;code&gt;rustc 1.96.0 (ac68faa20 2026-05-25)&lt;/code&gt;, targeting &lt;code&gt;aarch64-apple-darwin&lt;/code&gt;, with LLVM 22.1.2. With optimization kept low under these conditions, we could follow the branches and memory operations. Changing the optimization settings or target environment may change the output's form. I haven't compared compilation time or runtime performance here.&lt;/p&gt;

&lt;h2&gt;
  
  
  LLVM produces object files, and the process continues
&lt;/h2&gt;

&lt;p&gt;Once the LLVM IR for each CGU is ready, LLVM proceeds with optimization and machine-code generation, producing object files. The two columns in Figure 3 show how this work can proceed in separate units.&lt;/p&gt;

&lt;p&gt;After that, the necessary objects and other inputs are linked to produce the output artifact. The kind of LTO in use also affects when optimization happens, but I'll leave the details of linking for another topic. &lt;a href="https://rustc-dev-guide.rust-lang.org/backend/codegen.html" rel="noopener noreferrer"&gt;Code generation — Rust Compiler Development Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Returning to the original question, “Which code is passed to LLVM, and in what units?”, the connection is becoming clearer: rustc collects the necessary function instances and other items, places them in CGUs, and lowers MIR to LLVM IR for each unit.&lt;/p&gt;

&lt;p&gt;Following one &lt;code&gt;if&lt;/code&gt; led from handling values to dividing work and deciding how much of a previous compilation could be reused. That division can even affect how fast the generated program runs. There's more going on inside a compiler than carrying out computations. Every topic seems to lead to another thing I want to investigate.&lt;/p&gt;

&lt;p&gt;Hmm. Have I finally made it as far as the linker's doorstep...?&lt;/p&gt;

&lt;p&gt;For the basics of reading MIR blocks and locals, see &lt;a href="https://dev.to/masamori/reading-rusts-mir-following-control-flow-and-values-36jm"&gt;Reading Rust's MIR: Following Control Flow and Values&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>Reading Rust's MIR: Following Control Flow and Values</title>
      <dc:creator>Masahiro Morino</dc:creator>
      <pubDate>Tue, 29 Sep 2026 12:43:23 +0000</pubDate>
      <link>https://dev.to/masamori/reading-rusts-mir-following-control-flow-and-values-36jm</link>
      <guid>https://dev.to/masamori/reading-rusts-mir-following-control-flow-and-values-36jm</guid>
      <description>&lt;p&gt;I started looking into linkers, but a linker's job isn't specific to Rust. Before getting there, I thought it would be more interesting to see how rustc takes our Rust code toward code generation.&lt;/p&gt;

&lt;p&gt;One of the representations we meet along the way is MIR. Apparently, it's used for both borrow checking and code generation. But what exactly is an “intermediate representation,” anyway?&lt;/p&gt;

&lt;p&gt;In this article, I'll use a few small examples to explore &lt;strong&gt;how to read Rust operations in the compiler's MIR&lt;/strong&gt;. We'll start with an &lt;code&gt;if&lt;/code&gt; expression to get a feel for control flow, look closely at a single assignment to see how values and places are represented, and then move on to a call to &lt;code&gt;Vec::push&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;This article is based on the Rust Compiler Development Guide as consulted on September 29, 2026. The examples were checked with Rust 1.96.0. MIR's structure and textual output can change with the compiler version and optimization settings.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  What is MIR for?
&lt;/h2&gt;

&lt;p&gt;MIR stands for &lt;strong&gt;Mid-level Intermediate Representation&lt;/strong&gt;. An intermediate representation is a way for a compiler to represent code while processing a program.&lt;/p&gt;

&lt;p&gt;The Rust we write has constructs such as &lt;code&gt;if&lt;/code&gt;, &lt;code&gt;match&lt;/code&gt;, and nested expressions that make programs convenient to write and read. The compiler needs to answer questions like “Where is this value created, and where is it used?” or “After taking this branch, what happens next?” MIR organizes the program into a form that makes types, operations on values, and control flow explicit. That gives the compiler something it can analyze. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html" rel="noopener noreferrer"&gt;The MIR — Rust Compiler Development Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For example, the expression &lt;code&gt;if flag { x } else { 0 }&lt;/code&gt;, which we'll look at shortly, becomes a condition check, operations that set the return value, and a block where the paths meet. Complex expressions are broken down into smaller computations and temporary values. A more uniform representation lets the compiler follow values and control flow without having to handle every variation of the source syntax separately. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/construction.html" rel="noopener noreferrer"&gt;MIR construction — Rust Compiler Development Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One major use of MIR is &lt;strong&gt;borrow checking&lt;/strong&gt;. Remember borrowing? The compiler examines how values are used and borrowed on MIR, and also uses MIR for subsequent optimization and code generation. That doesn't mean one unchanged MIR body survives all the way through compilation. It passes through analysis and transformation stages on its way to the form used for code generation. &lt;a href="https://rustc-dev-guide.rust-lang.org/overview.html#mir-lowering" rel="noopener noreferrer"&gt;Overview of the compiler&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's pause and put these representations side by side.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Representation&lt;/th&gt;
&lt;th&gt;Its role in the path we're looking at&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Rust source code&lt;/td&gt;
&lt;td&gt;Where we write the program using expressions and other language constructs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MIR&lt;/td&gt;
&lt;td&gt;Makes Rust types, operations on values, and branches explicit for borrow checking, optimization, and code generation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;LLVM IR&lt;/td&gt;
&lt;td&gt;Carries the program from rustc into LLVM for further optimization and machine-code generation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are other compiler representations before MIR, but I'm leaving those out here. I'd like to explore that part of the compiler sometime, too. For now, let's focus on &lt;strong&gt;MIR as a representation for analyzing Rust programs&lt;/strong&gt; and look inside it. &lt;a href="https://rustc-dev-guide.rust-lang.org/overview.html" rel="noopener noreferrer"&gt;Overview of the compiler&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  In MIR, control flow becomes blocks
&lt;/h2&gt;

&lt;p&gt;One characteristic of MIR is that control flow takes the form of a &lt;strong&gt;control-flow graph&lt;/strong&gt;, or CFG. Operations are grouped into basic blocks, connected by branches and jumps. Each block contains statements, such as assignments, and ends with a terminator, such as a branch, a function call, or a return. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html" rel="noopener noreferrer"&gt;The MIR — Rust Compiler Development Guide&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here's a small function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;choose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;flag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;flag&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns &lt;code&gt;x&lt;/code&gt; when &lt;code&gt;flag&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt;, and &lt;code&gt;0&lt;/code&gt; when it's &lt;code&gt;false&lt;/code&gt;. Compiling this function to MIR with Rust 1.96.0 gives us four basic blocks:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ups83w7ok35knjqd6ma.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F6ups83w7ok35knjqd6ma.png" alt="bb0 checks flag. The true branch enters bb1 and sets the return value to x; the false branch enters bb2 and sets it to 0. Both paths join at bb3, which returns." width="800" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Figure 1: The block structure of the generated MIR. The diagram uses flag, x, and return value in place of the internal local names.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;bb1&lt;/code&gt;, setting the return value to &lt;code&gt;x&lt;/code&gt; is a statement, and jumping to &lt;code&gt;bb3&lt;/code&gt; is the terminator. &lt;code&gt;bb0&lt;/code&gt; has no statements at all, just the conditional-branch terminator. So a block doesn't have to do some computation before it branches.&lt;/p&gt;

&lt;p&gt;A basic block is a unit that &lt;strong&gt;executes its statements in order and ends with a terminator&lt;/strong&gt;. A terminator doesn't necessarily choose between multiple destinations. In this example, &lt;code&gt;goto&lt;/code&gt; has one successor, &lt;code&gt;switchInt&lt;/code&gt; selects a successor based on a value, and &lt;code&gt;return&lt;/code&gt; has no successor block within this function. Thinking of a terminator as the block's exit helps explain why these operations belong to the same category.&lt;/p&gt;

&lt;h2&gt;
  
  
  Matching the MIR output to the diagram
&lt;/h2&gt;

&lt;p&gt;In the output, arguments and the return value are represented by names such as &lt;code&gt;_1&lt;/code&gt;, &lt;code&gt;_2&lt;/code&gt;, and &lt;code&gt;_0&lt;/code&gt;. Here, &lt;code&gt;_1&lt;/code&gt; is &lt;code&gt;flag&lt;/code&gt;, &lt;code&gt;_2&lt;/code&gt; is &lt;code&gt;x&lt;/code&gt;, and &lt;code&gt;_0&lt;/code&gt; holds the return value. If we omit the declarations and keep just the blocks, the correspondence with the diagram becomes clear:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bb0: {
    switchInt(copy _1) -&amp;gt; [0: bb2, otherwise: bb1];
}

bb1: {
    _0 = copy _2;
    goto -&amp;gt; bb3;
}

bb2: {
    _0 = const 0_u32;
    goto -&amp;gt; bb3;
}

bb3: {
    return;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What was one &lt;code&gt;if&lt;/code&gt; expression in the source has become a conditional branch, assignments, and a block where the paths meet. Making control flow explicit gives the compiler a way to analyze each path.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading one line of MIR: Places and values
&lt;/h2&gt;

&lt;p&gt;Now that we know how the blocks connect, let's look more closely at the assignment in &lt;code&gt;bb1&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;_0 = copy _2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It simply puts &lt;code&gt;x&lt;/code&gt; into the return value, but the pieces have different roles in MIR. The key terms are &lt;strong&gt;Local, Place, Operand, and Rvalue&lt;/strong&gt;. We're starting to collect quite a few terms now.&lt;/p&gt;

&lt;h3&gt;
  
  
  A Local is storage; a Place identifies a location
&lt;/h3&gt;

&lt;p&gt;A &lt;strong&gt;Local&lt;/strong&gt; is a storage location for something like a function argument, a local variable, or a temporary value. MIR identifies locals by indices, written as &lt;code&gt;_1&lt;/code&gt;, for example. These are the locals in &lt;code&gt;choose&lt;/code&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Local&lt;/th&gt;
&lt;th&gt;Role in this function&lt;/th&gt;
&lt;th&gt;Type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_0&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The special location for the return value&lt;/td&gt;
&lt;td&gt;&lt;code&gt;u32&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The argument &lt;code&gt;flag&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;bool&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;_2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;The argument &lt;code&gt;x&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;code&gt;u32&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Storage here is a compiler-level concept. It doesn't mean that every Local necessarily ends up with its own stack slot in the final machine code. And the same-looking local names can have different roles in different functions, as we'll see below.&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;Place&lt;/strong&gt; is an expression identifying a location to read from or write to. It can refer to an entire Local, a field within it, or a location reached through a reference. For example, if &lt;code&gt;_1&lt;/code&gt; were a tuple, &lt;code&gt;_1&lt;/code&gt; would identify the whole tuple and &lt;code&gt;_1.0&lt;/code&gt; its first field. Yes, this gets a little confusing.&lt;/p&gt;

&lt;p&gt;A Local is a unit of storage; a Place specifies a particular location. In &lt;code&gt;_0 = copy _2;&lt;/code&gt;, both the destination &lt;code&gt;_0&lt;/code&gt; and the source &lt;code&gt;_2&lt;/code&gt; are Places referring to entire Locals. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html#key-mir-vocabulary" rel="noopener noreferrer"&gt;Key MIR vocabulary&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  An Operand supplies a value; an Rvalue produces the value to assign
&lt;/h3&gt;

&lt;p&gt;Identifying a location doesn't yet say how we'll use the value stored there. That's where an &lt;strong&gt;Operand&lt;/strong&gt; comes in. Three forms we'll encounter are:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operand&lt;/th&gt;
&lt;th&gt;Meaning&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;copy _2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Copy the value from the specified Place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;move _3&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Move the value from the specified Place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;const 0_u32&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Use a constant value&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;An &lt;strong&gt;Rvalue&lt;/strong&gt;, meanwhile, is an expression that produces the value to assign. It might add values, create a reference to a Place, or simply use an Operand's value. The R comes from the &lt;strong&gt;right-hand side&lt;/strong&gt; of an assignment.&lt;/p&gt;

&lt;p&gt;Here's how the pieces fit together. This is an explanatory tree, not literal rustc output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Statement: assignment  _0 = copy _2;
├─ Destination Place: _0
└─ Rvalue: use an Operand's value directly
   └─ Operand: copy _2
      └─ Source Place: _2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The printed &lt;code&gt;copy _2&lt;/code&gt; can be a little puzzling: we're looking at an Rvalue when we consider the whole right-hand side, and an Operand when we focus on its input. &lt;strong&gt;The Rvalue in this example wraps one Operand and produces that Operand's value unchanged.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The assignment &lt;code&gt;_0 = const 0_u32;&lt;/code&gt; in &lt;code&gt;bb2&lt;/code&gt; has the same shape. The input changes from a value stored in a Local to a constant, but we're still assigning an Operand's value to the return-value Place. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html#key-mir-vocabulary" rel="noopener noreferrer"&gt;Assignments, Rvalues, and Operands in MIR&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Vec::push a terminator?
&lt;/h2&gt;

&lt;p&gt;Let's use those terms to read another example, also found in the official guide:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;vec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;vec&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Compiling it to MIR with Rust 1.96.0 produces this block for the first &lt;code&gt;push&lt;/code&gt;. I explicitly used &lt;code&gt;-C panic=unwind&lt;/code&gt; to inspect the unwinding path:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;bb1: {
    _3 = &amp;amp;mut _1;
    _2 = Vec::&amp;lt;i32&amp;gt;::push(move _3, const 1_i32) -&amp;gt; [return: bb2, unwind: bb5];
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here, &lt;code&gt;_1&lt;/code&gt; is the Local for &lt;code&gt;vec&lt;/code&gt;. &lt;code&gt;_3&lt;/code&gt; is a temporary Local of type &lt;code&gt;&amp;amp;mut Vec&amp;lt;i32&amp;gt;&lt;/code&gt;, and &lt;code&gt;_2&lt;/code&gt; receives the &lt;code&gt;()&lt;/code&gt; returned by &lt;code&gt;push&lt;/code&gt;. Local indices belong to their individual functions, so these &lt;code&gt;_1&lt;/code&gt; and &lt;code&gt;_2&lt;/code&gt; are different from the ones in &lt;code&gt;choose&lt;/code&gt;. Watch out for that!&lt;/p&gt;

&lt;p&gt;The first line, &lt;code&gt;_3 = &amp;amp;mut _1;&lt;/code&gt;, is an assignment statement. On the left, &lt;code&gt;_3&lt;/code&gt; is the destination Place. On the right, &lt;code&gt;&amp;amp;mut _1&lt;/code&gt; is an Rvalue that creates a mutable reference to the Place &lt;code&gt;_1&lt;/code&gt;. Our earlier Rvalue simply used an Operand; this one creates a reference value from a Place.&lt;/p&gt;

&lt;p&gt;The next line passes the Operands &lt;code&gt;move _3&lt;/code&gt; and &lt;code&gt;const 1_i32&lt;/code&gt; to &lt;code&gt;push&lt;/code&gt;. What's being moved is the mutable reference stored in &lt;code&gt;_3&lt;/code&gt;, not the &lt;code&gt;Vec&lt;/code&gt; itself.&lt;/p&gt;

&lt;p&gt;And that whole call is a terminator. Despite its &lt;code&gt;_2 = ...&lt;/code&gt; appearance, it isn't an assignment statement. Look at the destinations at the end:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Destination&lt;/th&gt;
&lt;th&gt;What happens in this example&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;return: bb2&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If the call returns normally, continue to the block containing the next &lt;code&gt;push&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unwind: bb5&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;If the call unwinds, enter the cleanup block that drops &lt;code&gt;vec&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;return: bb2&lt;/code&gt; label does not mean that &lt;code&gt;main&lt;/code&gt; finishes. It tells us &lt;strong&gt;where execution resumes in this function after the called function returns&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In Rust source, &lt;code&gt;vec.push(1);&lt;/code&gt; is one line. In MIR, it is split into preparing the reference argument and a call that explicitly includes the paths execution can take afterward. Treating a function call as a terminator lets us follow both normal execution and unwinding cleanup through the connections between blocks. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html#key-mir-vocabulary" rel="noopener noreferrer"&gt;The guide's Vec example&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Generating MIR locally
&lt;/h2&gt;

&lt;p&gt;For the first example, I saved the function in &lt;code&gt;choose.rs&lt;/code&gt; and ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc choose.rs &lt;span class="nt"&gt;--crate-type&lt;/span&gt; lib &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;--emit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This writes the MIR to &lt;code&gt;choose.mir&lt;/code&gt;. The environment was &lt;code&gt;rustc 1.96.0 (ac68faa20 2026-05-25)&lt;/code&gt;, targeting &lt;code&gt;aarch64-apple-darwin&lt;/code&gt;, with LLVM 22.1.2. The output shown here comes from those conditions. Different optimization settings, for example, won't necessarily produce the same block structure.&lt;/p&gt;

&lt;p&gt;For the &lt;code&gt;Vec&lt;/code&gt; example, I saved the code in &lt;code&gt;vec_push.rs&lt;/code&gt; and ran:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rustc vec_push.rs &lt;span class="nt"&gt;--edition&lt;/span&gt; 2024 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-C&lt;/span&gt; opt-level&lt;span class="o"&gt;=&lt;/span&gt;0 &lt;span class="nt"&gt;-C&lt;/span&gt; &lt;span class="nv"&gt;panic&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;unwind &lt;span class="nt"&gt;--emit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even with &lt;code&gt;-C opt-level=0&lt;/code&gt;, the emitted MIR isn't necessarily the state before all transformations. The &lt;code&gt;StorageLive&lt;/code&gt; statements shown in the official guide don't appear in this &lt;code&gt;Vec&lt;/code&gt; output. When comparing dumps, we need to pay attention to both the Rust version and the stage of MIR we're looking at. Setting the MIR optimization level with &lt;code&gt;-Z mir-opt-level=0&lt;/code&gt; requires nightly; this article uses output from stable. &lt;a href="https://rustc-dev-guide.rust-lang.org/mir/index.html#key-mir-vocabulary" rel="noopener noreferrer"&gt;MIR output and optimization&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Reading control flow and values separately
&lt;/h2&gt;

&lt;p&gt;In &lt;code&gt;choose&lt;/code&gt;, a single &lt;code&gt;if&lt;/code&gt; expression became a block that checks the condition, two blocks that set the return value, and a block that returns. Separating storage locations such as &lt;code&gt;_0&lt;/code&gt; from units of control flow such as &lt;code&gt;bb0&lt;/code&gt; makes the output easier to follow.&lt;/p&gt;

&lt;p&gt;Inside each block, Local and Place tell us which location we're dealing with; Operand and Rvalue tell us how values are used and produced. In the &lt;code&gt;Vec::push&lt;/code&gt; example, we also saw that a call is itself a terminator, with normal-return and unwinding paths.&lt;/p&gt;

&lt;p&gt;MIR represents Rust operations in a form the compiler can analyze. Follow control flow through the connections between blocks, then distinguish places from values within each line. With those two ideas in mind, it becomes easier to find the original Rust operations in what initially looks like a wall of symbols.&lt;/p&gt;

&lt;p&gt;So, when we move from MIR toward machine code, what happens to generic type arguments? And how is the code divided into units that can be compiled in parallel? In &lt;a href="https://dev.to/masamori/what-code-does-rust-pass-to-llvm-generics-and-codegen-units-ll6"&gt;What Code Does Rust Pass to LLVM? Generics and Codegen Units&lt;/a&gt;, I follow the collection of the required code and its partitioning into Codegen Units.&lt;/p&gt;

&lt;p&gt;There's a lot to wrap my head around. But that's what makes it interesting.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>computerscience</category>
    </item>
  </channel>
</rss>
