Dear OSdevers,
I am currently restructuring my project to use autotools (autoconf/automake) and need help in what to do with crti.o and crtn.o.
I am totally new to the autotools. I started yesterday with reading and just got my project to a point where it is compilable and runs on emulators.
I just can't figure out how to include object files in the Makefile.am. They also need to be at the correct spot in the command line...
Or alternatively, can somebody point me to ressources, how not to need crti.o and crtn.o, but instead get a array of ctor functions to execute?
Best regards
Sebi
gnu autotools and crti.o/crtn.o
-
- Member
- Posts: 186
- Joined: Tue Aug 26, 2008 11:24 am
- GitHub: https://github.com/sebihepp
Re: gnu autotools and crti.o/crtn.o
What in three devils' name possessed you to do that? I have literally never heard anything good about autotools. It's like the SAP of build tools: Everyone hates it but uses it anyway.
Those don't have anything to do with the build system. In GCC, you can set their paths in the spec file, and in clang you have to wrap the linker to replace those files. Alternatively, you can not bother with _init and _fini, since the .init_array and .fini_array mechanisms are far simpler and easier to support. As far as I know, GCC and clang emit those by default now. Examine your files. However, it might be necessary to create those object files (with those names) from empty assembler files just to satisfy the linker command lines that GCC is emitting.
The exact definition of the init and fini arrays is in the general ELF ABI: https://refspecs.linuxfoundation.org/el ... #init_fini
In static linking, you do not have those dynamic tags to guide you, but you have the linker symbols __init_array_start, __init_array_end, __preinit_array_start, __preinit_array_end, and __fini_array_start and __fini_array_end. You execute all the entries of the preinit-array in forward order first, then the entries of the init-array, also in forward order. At the end of the program, you execute __fini_array in reverse order. I have code that does that in the exit() function
Carpe diem!
- xenos
- Member
- Posts: 1121
- Joined: Thu Aug 11, 2005 11:00 pm
- Libera.chat IRC: xenos1984
- Location: Tartu, Estonia
- Contact:
Re: gnu autotools and crti.o/crtn.o
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.
Regarding the crti / crtn, or init / fini question, my personal recommendation is to not have any global constructors at all in a kernel. In my kernel, all global objects / global data structures are either statically initialized (so that their initial values are stored in .data) or default initialized to some zero values, and explicitly initialized at runtime (by something like "placement new"). I made this design choice because it allows me to control when and in which order these objects are initialized, which is helpful if they depend on each other (for example, initializing the kernel heap after the physical memory manager). With global constructors, the compiler places them in some order, which might not be the one desired by the programmer, and for some objects it might be more convenient to initialize some after the start of the kernel main function (such as the physical memory manager, after reading the memory map). In this approach, there is no init / fini code.
Regarding the crti / crtn, or init / fini question, my personal recommendation is to not have any global constructors at all in a kernel. In my kernel, all global objects / global data structures are either statically initialized (so that their initial values are stored in .data) or default initialized to some zero values, and explicitly initialized at runtime (by something like "placement new"). I made this design choice because it allows me to control when and in which order these objects are initialized, which is helpful if they depend on each other (for example, initializing the kernel heap after the physical memory manager). With global constructors, the compiler places them in some order, which might not be the one desired by the programmer, and for some objects it might be more convenient to initialize some after the start of the kernel main function (such as the physical memory manager, after reading the memory map). In this approach, there is no init / fini code.
-
- Member
- Posts: 186
- Joined: Tue Aug 26, 2008 11:24 am
- GitHub: https://github.com/sebihepp
Re: gnu autotools and crti.o/crtn.o
Thank you very much - to both of you.
I have read about .init_array and use that now. Works like a charm.
While you are right about better not using global constructors, @xenos, I still want to implement it to be on the safe side - maybe g++ puts other stuff there too in the future, not only global constructors. But I use static members/methods in classes too, if they are needed from the start. But better safe than sorry. And it was nice learning about it.
@nullplan Maybe this is because I work for SAP.
I have read about .init_array and use that now. Works like a charm.
While you are right about better not using global constructors, @xenos, I still want to implement it to be on the safe side - maybe g++ puts other stuff there too in the future, not only global constructors. But I use static members/methods in classes too, if they are needed from the start. But better safe than sorry. And it was nice learning about it.
@nullplan Maybe this is because I work for SAP.
Re: gnu autotools and crti.o/crtn.o
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.
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.
Carpe diem!
- xenos
- Member
- Posts: 1121
- Joined: Thu Aug 11, 2005 11:00 pm
- Libera.chat IRC: xenos1984
- Location: Tartu, Estonia
- Contact:
Re: gnu autotools and crti.o/crtn.o
My approach is a bit of the opposite: I rather avoid using unknown code which g++ puts there and do all of the kernel initialization using code I have written myself (plus static data generated by the compiler). So in some sense it's also a "better safe than sorry", but I am more worried that the compiler generates something which may break the initialization, given that the content of these sections is rather poorly documented.sebihepp wrote: ↑Thu Dec 19, 2024 4:13 am While you are right about better not using global constructors, @xenos, I still want to implement it to be on the safe side - maybe g++ puts other stuff there too in the future, not only global constructors. But I use static members/methods in classes too, if they are needed from the start. But better safe than sorry. And it was nice learning about it.
Of course, there are many ways to achieve this. I guess your approach then requires that the user compiling the code has ninja installed? What like about autotools is that I get a configure script which can be distributed, and which then generates the makefiles. So then the user only needs GNU make. Well, of course, that's also a dependency, like ninja, so in the end there is not much of a difference.