Page 1 of 1
Does crt*.o object files order really matter when linking?
Posted: Mon Sep 21, 2020 10:41 am
by xergoz
This page on wiki:
https://wiki.osdev.org/Calling_Global_Constructors states that crt*.o files must be placed in this exact order when linking: link_list = crti.o crtbegin.o $(main_objs) crtend.o crtn.o. Quote: "It is important to remember that the objects must be linked in this exact order, or you will experience strange bugs."
However, i dont really see why it makes any difference. AFAIK, only libraries order matters while linking, not object files order. Apart from that, recently i've noticed there was a mistake in my Makefile which caused those object files link in the wrong order. I've been using that kind of configuration for over a month now and i didnt notice any kind of "strange bugs" during that time period, not a single one. Though maybe its just my luck.
Can anyone enligten me on why does it matter (if it matters at all)?
Re: Does crt*.o object files order really matter when linkin
Posted: Mon Sep 21, 2020 8:08 pm
by Octocontrabass
For those objects, linking matters because the combined contents of the .init and .fini sections is one single function each. The linker is concatenating portions of machine code to create that function, so if it does it in the wrong order, the function won't execute correctly.
But you won't even notice a difference if you never call the global constructors, and you don't need to call global constructors if you don't have any. I'm going to guess you don't have any global constructors.
(That page is also quite old: it's a better idea to use .init_array/.fini_array instead of .ctors/.dtors for all CPU architectures, instead of just ARM. Unfortunately, I'm not familiar with the topic since I'm not using global constructors either.)
Re: Does crt*.o object files order really matter when linkin
Posted: Thu Sep 24, 2020 2:29 pm
by xergoz
Octocontrabass wrote:But you won't even notice a difference if you never call the global constructors, and you don't need to call global constructors if you don't have any. I'm going to guess you don't have any global constructors.
Yup, you're right. I just tried misplacing crt objects when linking, and my physical memory allocator (which depended on global objects constructors) started returning nullptr's out of sudden.
Octocontrabass wrote:(That page is also quite old: it's a better idea to use .init_array/.fini_array instead of .ctors/.dtors for all CPU architectures, instead of just ARM. Unfortunately, I'm not familiar with the topic since I'm not using global constructors either.)
BTW, what are the advantages of .init_array/.fini_array approach and why should one prefer it?
Re: Does crt*.o object files order really matter when linkin
Posted: Thu Sep 24, 2020 6:53 pm
by Octocontrabass
xergoz wrote:BTW, what are the advantages of .init_array/.fini_array approach and why should one prefer it?
I'm not all that familiar with it, but the biggest advantage I see is that you don't need to write any funky assembly for the .init_array/.fini_array sections.
Someone mentioned in another thread that it also allows you to use some linker options that don't work with the .ctors/.dtors sections.
Re: Does crt*.o object files order really matter when linkin
Posted: Fri Sep 25, 2020 1:14 am
by xergoz
Octocontrabass wrote:I'm not all that familiar with it, but the biggest advantage I see is that you don't need to write any funky assembly for the .init_array/.fini_array sections.
Someone mentioned in another thread that it also allows you to use some linker options that don't work with the .ctors/.dtors sections.
Thanks for the info! I'll check it out!
Re: Does crt*.o object files order really matter when linkin
Posted: Fri Sep 25, 2020 8:03 am
by nullplan
Octocontrabass wrote:I'm not all that familiar with it, but the biggest advantage I see is that you don't need to write any funky assembly for the .init_array/.fini_array sections. Someone mentioned in another thread that it also allows you to use some linker options that don't work with the .ctors/.dtors sections.
The problem with .init/.fini is that the order of input sections matters (for more than initializer order). That is bad because it is so brittle. The whole mechanism works by defining the start of _init in one file and the end in another, and then hoping that all other .init sections end up between them (similar for .fini). Unfortunately, linker order is higher magic, and the config files of compilers are also usually very complicated to edit and get right.
Also, there are problems with some linker options. Busybox' build system used to use --sort-sections=alignment, to avoid padding bytes, but that could end up sorting the .init sections such that the _init symbol ended up behind the corresponding ret instruction. In particular this happened with older musl version, in which the .init section in crt0.o was empty except for the symbol (thus leading to size == alignment == 0, which is less than the 1 for crtn.o's sections, so it ended up getting sorted in the order described). It's why they added a useless push and pop. There is also an obvious problem with --gc-sections, but that one persists with .init_array, and is fixed solely by linker script. --gc-sections will remove all sections from the link that are not referenced, but .init and .fini sections are referenced only indirectly. But then, the same is true for .init_array, and is fixed by adding KEEP() around those sections in the linker script.