xenos wrote: ↑Thu Dec 19, 2024 2:36 am
Personally, I see nothing wrong with using autotools - I use them in my OS project and I am quite satisfied with them. They give me a nice way to configure the build for different target architectures.
Well, I do the same thing with a simple shell script that writes a Ninja file. But don't let me discourage you; if you find some value in that project, then I'm happy for you.
xenos wrote: ↑Thu Dec 19, 2024 2:36 am
Regarding the crti / crtn, or init / fini question, my personal recommendation is to not have any global constructors at all in a kernel.
Oh, right. In a kernel the question is often when and in what order to run the initializers. Especially with the mixed non-paged/paged environment many newbies use, it becomes important not to execute too much code too early. I solve that problem by putting the bootstrapper (that sets up the paging) into a separate binary, to really put a huge wall between the two environments.
Linux has different initializer levels, for example, precisely because of initializer order. Each init function can only depend on everything on lower init levels being initialized. So if the memory allocator is initialized at level 3, then only init functions of level 4 and up can allocate memory. This works, but requires lots of manual maintenance.
In userspace, you often have the same issue of initializer ordering. In C++, the compiler will sort the initializer array correctly for every explicated dependency. Problem is dynamic/implicit dependencies, but those are bad style, anyway. And in dynamic linking, you get the whole thing again with the dependency tree and the explicit requirement to initialize each module only once all dependencies were initialized (which you can do with a depth-first sweep of the dep tree, but take care not to initialize any module more than once!)
Some more about the _init thing: Biggest problem is that I cannot find any documentation as to what the compiler expects of the prelude and coda parts of those functions, or alternatively, what code the compiler is constrained to produce as .init or .fini snippet. On i386, I have seen empty preludes and codas before (where the coda was only "ret"), but that violates the ABI because of stack alignment (you have to push 3 words to the stack before calling another function).
On other architectures, like PowerPC, it is impossible to call other functions without first spilling the link register to stack, so the prelude should do that and allocate 16 bytes of stack (the minimum stack frame). Or does the compiler generate code to do that? No clue, and no docs. And I don't want to keep copying stuff from musl or glibc.