There Is Life Before Main in Rust

3 days ago (grack.com)

Author here, happy to answer any questions. I've been working on building some higher-level abstractions on link sections (specifically, link-time optimized collections like maps (1) and sorted slices (2)) and wanted to share the hard-fought knowledge from the last couple of months.

There's a decent amount of knowledge around pre-main work in Rust, but I think this is one of the first attempts to walk through mutable link sections, which open up a pretty wide world of optimization, IMO. Even without mutability, I figured there isn't nearly enough documentation on these approaches out there.

(1) https://docs.rs/scattered-collect/0.20.0/scattered_collect/m...

(2) https://docs.rs/scattered-collect/0.20.0/scattered_collect/s...

The general lesson of these things is main is not that special and it pays to understand how your program actually starts. This has little/nothing to do with Rust or other language tools. On Linux, given a static ELF program, the kernel returns to the IP given by e_entry, which can proceed to do anything. If the program is dynamic (has a .interp) then it loads the interpreter and returns to its e_entry instead. The interpreter, in turn, can do absolutely whatever.

  • The relevance to Rust is precisely that it doesn't have life before main at the language level; therefore, if you need it*, you need to use these kinds of linker hacks (which fortunately are amenable to encapsulation through macros). By contrast, if the article were about C++, the focus would be on "what happens under the hood when you use static initialization, in case you were curious" rather than "how to use these low-level mechanisms to do something not otherwise possible".

    * Which you should think very carefully before concluding is the case, as it's responsible for rather a lot of bugs in C++. I think in Rust it is mostly used for registry-pattern type stuff since the const system can't currently(?) handle that.

    • Yes. There isn't Rust language support for this.

      Order of initialization can be supported at various levels:

      - Completely random (OK if interdependence are locked out, otherwise bad)

      - Consistent, but sorted by something such as alphabetical name (meh.)

      - Manual, controlled in linker scripts (headache)

      - True dependency tree order, including diagnosing loops (seen in the Modula family).

      General comment: yes, you can, and you probably shouldn't unless you have profiling data that indicates a significant performance improvement for a critical use case.

      5 replies →

    • You don't need linker hacks to control what happens before main in Rust. You can disable the default runtime setup with `#![no_main]` in your crate root, and then manually designate a starting point via an unmangled function named appropriately for your specific platform (e.g. `_start`).

> This post is 100% human-written. Claude was used for feedback and to assist with the linker symbol diagram. Cursor was used for feedback and to ensure examples were compilable.

Love this, I hope every blog have the same disclaimer about how AI is used.

  • If Claude gave feedback then it’s not really 100% human written is it?

    • I'm pretty much hardline anti-AI and even I would say this is too far. If I read documentation or ask my wife to review something, those people did not write the final product. Perhaps it would be mentioned in a citation, like this person has.

    • Yeah! It was typed into a computer and never even put on paper. How can you say it was written at all?

      Further, can anything be "100% human writt even if it uses pen and paper? No of course not! Unless it is created by pricking a finger and put on human vellum, it's only partially human written.

      Seriously though - if you want to do stupid purity test games, at least be properly pure about it. This half-assed nonsense is just trite.

    • If you run spell check on a document is it no longer 100% human written?

I ran into this with embedded Rust: put alloc in a .init_array function, but the global allocator also uses .init_array, and there's no ordering guarantee. Took me hours to figure out why I was getting garbage from the heap before main.

everything abount Rust MUST have some AI in it nowerdays. even this article