4 ms·
If we're in the realm of "non-standard linker tricks"... Compilers will concatenate sections of the same name. You can use this trick to produce a concatenatio
by haberman 7y ago
If we're in the realm of "non-standard linker tricks"...
Compilers will concatenate sections of the same name. You can use this trick to produce a concatenation of arrays across several files:
$ cat t1.c
__attribute__((section("some_array"))) int a[] = {1, 2, 3};
$ cat t2.c
__attribute__((section("some_array"))) int b[] = {4, 5, 6};
$ cat t.c
#include <stdio.h>
extern const int __start_some_array;
extern const int __stop_some_array;
int main() {
const int* ptr = &__start_some_array;
const int n = &__stop_some_array - ptr;
for (int i = 0; i < n; i++) {
printf("some_array[%d] = %d\n", i, ptr[i]);
}
return 0;
}
$ gcc -std=c99 -o t t.c t1.c t2.c && ./t
some_array[0] = 1
some_array[1] = 2
some_array[2] = 3
some_array[3] = 4
some_array[4] = 5
some_array[5] = 6
This is the mechanism that linkers use "under the hood" to get a list of C++ object initializers that need to run pre-main().
It's unfortunate that there is no standard way of getting at this functionality in portable C, or to get it in C++ without actually running code pre-main(). Sometimes you really want a linker-initialized list things (of some sort) that have been linked in, without actually running code pre-main() (which has all kind of issues).
I would love to see a thing like this standardized in both C and C++. C++ compilers need it under the hood anyway.
- sheidavanesa 7y agonice
- haneefmubarak 7y agoHow exactly is the ordering of items across the section decided? Is it relatively random or is it based on the order that the object files are passed to the linker?
- monocasa 7y agoIt's unspecified, and I've seen it change over time as the linker changes it's data structures.
- RealityVoid 7y agoThis is true, to my knowledge, I would NOT relie on the order to be always consistent. But I think you can enforce the order in the linker file, i.e. load the arrays one after the other, and then the order should keep constant. But you still have several places that you need to keep synchronized and, really, most people don't look in the linker file at all.
- monocasa 7y agoYou're right, you can manually specify the ordering of object files in the linker file. Though, like you say, this is rarely done because of how brittle it is. I was referring more to the way shown above, where I've seen a linker change the list of object files from a sorted list of filenames, to a hash table of filenames, which obviously changed the ordering with which it iterated across all object files.
- loeg 7y agoYou can sort the array during program initialization, if you care about the order.
- monocasa 7y agoIt's probably already in link order. If you have to know the names of the symbols in code to be able to sort them, it sort of defeats the purpose of the scheme in the first place.
- loeg 7y agoYou might want to sort them by some other criteria. For example, the FreeBSD kernel's use of this construct embeds a well-defined priority[1], and the array is sorted by this priority value at boot time.[2] [1]: https://github.com/freebsd/freebsd/blob/master/sys/sys/kernel.h#L76-L175 https://github.com/freebsd/freebsd/blob/master/sys/sys/kerne... [2]: https://github.com/freebsd/freebsd/blob/master/sys/kern/init_main.c#L217-L265 https://github.com/freebsd/freebsd/blob/master/sys/kern/init...
- stefan_ 7y agoAssume it's random. See also the static initialization order ‘fiasco’: https://isocpp.org/wiki/faq/ctors#static-init-order https://isocpp.org/wiki/faq/ctors#static-init-order
- qppo 7y agoCan you give an example of a use case where this is needed?
- codys 7y agoOne place it's used is to avoid central "registration" for multiple "things". For example, consider a program with many fairly separate "subcommands". Using this mechanism allows adding a new subcommand simply by linking in a new source file (without changing a shared source file to list the new command).
- berti 7y agoI use this idea in embedded work, where the code is split into "modules". The same firmware source is shared amongst various differing pieces of hardware, each of which utilise a different subset of modules.
- clort 7y agoThis is used for constructor & destructor functions in C code. The function pointers get put in an array in a certain section which is collated then the startup and exit code calls them. Ordering is not guaranteed.
- saagarjha 7y agoNote that the ordering that the constructors and destructors will be run once the binary has been constructed is guaranteed by your C runtime.
- jhallenworld 7y agoI like to use this technique for CLI command and help tables. It eliminates having a single giant table in a file, instead you have a C macro which makes the system aware of your command right next to your command definition. int my_command(..) { ... } COMMAND(my_command, "command_name", "example command") HELP("use --foo argument for this...") HELP("use --bar argument for that...") Also used this all the time back when I programmed assembly language. It's definitely a problem that this is not standardized. For example, TI has their own C compiler for their ARM CPUs, not everybody uses gcc..
- loeg 7y agoThe BSDs define some macros in sys/linker_set.h to make this slightly easier. The mechanism is used somewhat widely in both kernel and userspace in FreeBSD.
- ndesaulniers 7y agoWhat a weird coincidence seeing a comment about mergeable sections today... A patch I helped review that's related to this in LLVM just landed today: https://reviews.llvm.org/D72194 https://reviews.llvm.org/D72194. (LLVM previously had support, but would perform bad merges in specific and rare edge cases; that patch fixes those cases). Also, note that you need a custom linker script to define those symbols (__start_some_array, __stop_some_array). The Linux kernel does this, as noted in: https://nickdesaulniers.github.io/blog/2020/04/06/off-by-two/ https://nickdesaulniers.github.io/blog/2020/04/06/off-by-two...
- haberman 7y ago> A patch I helped review that's related to this in LLVM just landed today Cool. :) > Also, note that you need a custom linker script to define those symbols The code I posted above compiles and runs without any custom linker script.
- ndesaulniers 7y ago> The code I posted above compiles and runs without any custom linker script. What?! Ok, I've seen the Linux kernel's custom linker script define symbols for custom ELF section boundaries, but TIL that you can get these automagically. I'm curious to see if LLD implements this logic, too.