Hacker News new | past | comments | ask | show | jobs | submit login

???

If you generate a single initialization symbol that guards against multiple runs in the init section, this works just fine with dlopen.

This isn't even hard, but you need to be able to form a full dag of imports.

The only problem is that the linker slams together the .init, .ctors, or .init_array sections in some arbitrary order (usually the order that .o files are passed in on the command line, but there are no guarantees). If you topologically sorted the object and library list by import order, things would work out. But because the linker doesn't cooperate, a language that wants to avoid the initialization order fiasco needs to provide its own ordering. Or its own linker.

As far as Zig goes -- I have no clue what it does. Never touched it.

Also, as a side note -- initializers were initially designed so that there would be exactly one init function per binary or library, and then people wanted to split the initialization up, so the way the init section ends up getting constructed on most unixes is fascinatingly hacky: The linker links `crti.o`, your .o files, `crtn.o`. crti.o contains a function prologue; the init sections in your .o files contain a sequence of call functions, and crtn.o contains a function epilogue. Stitch them all together in order, and you get a single function that you can call.

This is deprecated, and now there's a table of function pointers (.init_array).




Libraries don't exist in a vacuum with a single well defined user. If I'm shipping, say, libpng.so, and I need to do a one-time initialize per process, the only feasible way to do that is with a c++ static constructor or __attribute__((constructor)). It's why those things exist. It's why linker .ctors exist. It's why rust-ctor crate exists. It's why Java land re-created that by self-scanning all open jars for classes to randomly load.

This is a very widely used capability and it is very definitely not trivial to just make an init function & define the import dag and have that work reliably. People will forget to call it & most of the time they won't have any clue what their dependency DAG even is. And that's assuming it can even form a DAG at all - cyclic dependencies are absolutely a thing, after all.


Libraries exist with a well defined compiler. The solution is at the compiler level, using information coming from the import graph that exists in languages with a module system.

And I'm not sure how you can forget to import a library and still expect to use it, assuming your language has a module system.

C and C++ can't solve this problem, of course, due to textual inclusion.


> The solution is at the compiler level, using information coming from the import graph that exists in languages with a module system.

no, it isn't because your library has to integrate with other languages when you make native stuff. I should be able to call dlopen from any native language without having to care in which language your library was implemented, just calling my OS's dlopen / LoadLibrary OS API function should be enough. If this doesn't work, then your solution is just not acceptable in many cases, so yes you have to work with its limits.


Can you explain how a compiler topologically sorting the entries in .init_array makes a library unusable? What breaks?




Consider applying for YC's Fall 2025 batch! Applications are open till Aug 4

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: