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.
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.
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.
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.
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.
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.
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.
If you merely get feedback from a human, are they now a co-author?
2 replies →
It was written on a computer with a keyboard, so clearly it's 0% human written
1 reply →
Most human editors are not given credit, why would it be any different for an LLM?
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?
Editors (as in, the human kind) are not co-writers either.
I thought it was about prehistoric Rust before the main branch was created :)
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