M
MaskRay
maskray.me · Personal blog · on HN since 2018
MaskRay, an LLVM and linker developer, writes technical posts on linkers, ELF, thread-local storage and compiler toolchain performance.
What HN saysHN commenters link it for linker and dynamic linking details, and one notes a renamed post caused a changed link.
43HN people shared it
17linked it in comments
134stories submitted
1.6Kstory points
3 months agolast shared
2018people per year2026
Most-shared posts
- 1Long branches in compilers, assemblers, and linkers5 people · 6 points · first shared 2026 · discussion on HN
- 2Evolution of the ELF object file format4 people · 207 points · first shared 2024 · 51 comments on HN
- 3Exploring Object File Formats4 people · 119 points · first shared 2024 · 19 comments on HN
- 4All about thread-local storage4 people · 55 points · first shared 2021 · 10 comments on HN
- 5Why Isn't Ld.lld Faster4 people · 12 points · first shared 2021 · discussion on HN
- 6Better performance for dynamic linking with -fno-semantic-interposition4 people · 9 points · first shared 2021 · discussion on HN
- 7Precompiled Headers4 people · 6 points · first shared 2023 · discussion on HN
- 8Default -Bsymbolic-global-functions for shared object performance4 people · 2 points · first shared 2021 · discussion on HN
- 9When can glibc be built with Clang?3 people · 158 points · first shared 2021 · 28 comments on HN
- 10C++ Dynamic Initialization: .init, .ctors, and .init_array3 people · 49 points · first shared 2021 · 14 comments on HN
- 11MMU-less systems and FDPIC3 people · 40 points · first shared 2024 · 4 comments on HN
- 12Relocation Generation in Assemblers3 people · 12 points · first shared 2025 · discussion on HN
- 13A compact relocation format for ELF3 people · 5 points · first shared 2024 · discussion on HN
- 14Reflections on LLVM's switch to GitHub pull requests3 people · 4 points · first shared 2023 · discussion on HN
- 15Compiling C++ with the Clang API2 people · 105 points · first shared 2025 · 20 comments on HN
What HN people wrote when they linked it
MaskRay · 2026-01-24
Amazing! I read you article in 2012 when the link was https://homepages.cwi.nl/~tromp/maze.html
I was learning Haskell and Ocaml and wrote my own article in Chinese then https://maskray.me/blog/2012-11-02-perfect-maze-generation
Now I should fix the link.
mananaysiempre · 2025-05-14
I’m also a bit weirded out by the lack of mentions of dominators (in TFA or in the article[1] it links to). Once you have the dominator tree, though, you’re still going to need to answer LCA queries on it (in this problem and in an SSA-based compiler both), so there seems to be no way around this part.
The algorithm in the article does O(1) queries with O(V+E) preprocessing (assuming linear-preprocessing LCA, which, yeah). What’s the best algorithm for dominator trees? People usually talk about Lengauer–Tarjan[2], which is linear in practice (linear except for UNION-FIND), and not the linear one by Georgiadis[3,4]. Unfortunately, I’m not a compiler person.
[1] https://doi.org/10.1016/j.ipl.2010.02.014
[2] https://maskray.me/blog/2020-12-11-dominator-tree
[3] https://www.cs.princeton.edu/research/techreps/43
[4] https://dl.acm.org/doi/abs/10.1137/070693217
AdelaideSimone · 2025-06-28
A lot of vendors use non-lazy binding for security reasons, and some platforms don't support anything other than RTLD_NOW (e.g., Android).
Anyway, while what you said is theoretically half-true, a fairly large number of libraries are not designed/encapsulated well. This means almost all of their symbols are exported dynamically, so, the idea that there are only "few public exported symbols" is unfortunately false.
However, something almost no one ever mentions is that ELF was actually designed to allow dynamic libraries to be fairly performant. It isn't something I would recommend, as it breaks many assumptions on Unices, (while you don't get the benefits of LTO) you can achieve code generation almost equivalent to static linking by using something like "-fno-semantic-interposition -Wl,-Bsymbolic,-z,now". MaskRay has a good explanation on it: https://maskray.me/blog/2021-05-16-elf-interposition-and-bsymbolic
saidinesh5 · 2026-02-15
Let's say you need to use a function like:
int add(int a, int b){
// Long logic and then this
return a+b;
}
Let's say this is your main.c.
#include "add.h"
int main(void) {
return add(5,6);
}
The preprocessor just copies the contents of add.h into your main.c whenever you're trying to compile main.c. (let's ignore the concept of precompiled headers for now).
What you can instead do is just put the add function declaration in add.h that just tells the compiler that add function takes two integers and returns an integer.
int add(int a, int b);
You can then put the add function definition in add.c , compile that to an add.o and link it to your main.o at link time to get your final binary - without having to recompile add.o every time you change your main.c.
Precompiled headers: https://maskray.me/blog/2023-07-16-precompiled-headers
netule · 2026-01-27
It looks like the author has renamed their blog post, and thus the link has changed: https://maskray.me/blog/2026-01-25-long-branches-in-compilers-assemble…
loeg · 2025-12-29
It is: https://maskray.me/blog/2022-01-23-compressed-debug-sections
But the compression ratio isn't magical (approx. 1:0.25, for both zlib and zstd in the examples given). You'd probably still want to set aside debuginfo in separate files.
jcalvinowens · 2025-12-11
While I certainly empathize with the author of that article and respect the work they put into debugging it, this is a better explanation of that whole thing: https://maskray.me/blog/2022-08-21-glibc-and-dt-gnu-hash
kbolino · 2025-09-02
Thanks! I was able to find some additional info on no-MMU Linux [1], [2], [3]. It seems position-independent executables are the norm on regular (MMU) Linux now anyway (and probably have been for a long time). I took a look under the covers of uClibc and it seems like malloc just delegates most of its work to mmap, at least for the malloc-simple implementation [4]. That implies to me that different processes' heaps can be interleaved (without overlapping), but the kernel manages the allocations.
[1]: https://maskray.me/blog/2024-02-20-mmu-less-systems-and-fdpic
[2]: https://popovicu.com/posts/789-kb-linux-without-mmu-riscv/
[3]: https://www.kernel.org/doc/Documentation/nommu-mmap.txt
[4]: https://github.com/kraj/uClibc/blob/ca1c74d67dd115d059a875150e10b8560a…
atq2119 · 2025-02-17
Take a look at https://maskray.me/blog/2021-02-14-all-about-thread-local-storage
I think what you want is to force the "initial exec" model using an attribute to get the more efficient code.
IIRC this (or something equivalent) is what libGL does because basically every OpenGL function call needs to read the thread-local variable holding the current GL context.
The downside is that dlopen()ing your library may fail.
tliltocatl · 2025-01-23
Having fixed instruction length doesn't make the need to load large constants magically disappear. These just get split between multiple instructions. If anything, RISC-V might be worse. See also https://maskray.me/blog/2021-03-14-the-dark-side-of-riscv-linker-relax…
xvilka · 2024-01-16
Always quality content in that blog. We used MaskRay's article[1] on stack unwinding to improve our debuginfo (DWARF) support[2] in the past. If someone wants to have a more hands-on approach to executable file formats, e.g., XCOFF or GOFF, they can check Rizin's ideas for new formats[3] to support.
[1] https://maskray.me/blog/2020-11-08-stack-unwinding
[2] https://rizin.re/posts/gsoc-2023-dwarf/
[3] https://github.com/rizinorg/ideas/issues?q=is%3Aissue+is%3Aopen+label%…
wyldfire · 2023-09-09
Interesting blog post by Fangrui Song "ELF interposition and -Bsymbolic" [1] - talks about the real world cost of this feature that's imposed by ELF. On one hand it enables incredible extensibility, OTOH codegen is obligated to be pessimistic about resolving symbols.
[1]https://maskray.me/blog/2021-05-16-elf-interposition-and-bsymbolic
housel · 2022-02-07
https://maskray.me/blog/ has plenty of solid information on the LLVM and GCC/Binutils toolchains.
sitkack · 2022-01-31
You can do somethings like this using the .init and .fini sections in a dynamically loaded shared lib.
https://maskray.me/blog/2021-11-07-init-ctors-init-array
pm215 · 2022-01-01
I put my question to a friend of mine who works on linkers, and his take was that for a single threaded linker like ld.bfd the copy-bytes part would probably dominate, but that for a multithreaded linker like lld that part trivially parallelizes and so the slow part tends to be elsewhere. He also pointed me at a recent blogpost by the lld maintainer on this topic: https://maskray.me/blog/2021-12-19-why-isnt-ld.lld-faster which I should go and read...
wz1000 · 2018-05-19
Xmonad has something called grid select, which works kind of like a combination of pie menus and a grid, while also being keyboard navigable(I use vim keybindings hjkl).
http://maskray.me/static/xmonad-grid-select.jpg
https://hackage.haskell.org/package/xmonad-contrib-0.13/docs/XMonad-Ac…