gcclinker

Understanding GCC linker command (-Wl and --start-group)


Linking objects ST's CubeIDE issues a command like this:

arm-none-eabi-g++ -mcpu=cortex-m33 --specs=nosys.specs -Wl,-Map=mymap.map -Wl,--gc-sections -static --specs=nano.specs -mfpu=fpv5-sp-d16 -mfloat-abi=hard -mthumb -Wl,--start-group -lc -lm -lstdc++ -lsupc++ -Wl,--end-group -Tmyldscript.ld @myobjects.txt -o myelf.elf

I am trying to understand why they use -Wl several times and --start-group / --end-group. Is there a valid reason for that? Could this command be simplified?


Solution

  • -Wl

    -Wl is used multiple times because it is needed every time GCC has to be told that it should pass one or more linker options straight through to the linker, ld, rather than trying to obey the option(s) itself (and likely giving a an unknown option error).

    -Wl,<linker-option>[,<linker-option>...]
    

    is a GCC option that means pass <linker-option> [<linker-option>...] to the linker. See the GCC Manual: 3.15 Options for Linking.

    --start-group/--end-group

    Used as in:

    --start-group -liba [-libb...] --end-group 
    

    are paired linker options. By default the linker will consider a static library as a candidate for resolving undefined symbol references that the linkage has so far accrued only when the static library is first input and never again (unless the library is input again). The --start-group/--end-group options tell the linker to suspend its default policy for the enclosed sequence of static libraries -liba [-libb...] and instead to consider and reconsider them cyclically, resolving undefined references and accruing new ones, until no new ones accrue. See the GNU ld manual: 2.1 Command-line Options

    --start-group/--end-group are necessary when there are cyclic dependencies amongst the enclosed static libraries -liba [-libb...], meaning that it would not be possible for the linker to resolve all the undefined references that arise from their linkage if each library was considered only once when first seen. Such cyclic dependencies are unusual and suggestive of ill-considered library design.

    --start-group/--end-group are useful, though not necessary, when there are not any cyclic dependencies amongst -liba [-libb...] but the person or build system composing the linkage commandline does not know that and does not know a successful order in which to input the libraries under the default policy.

    If --start-group/--end-group are used to "brute force" a successful linkage of disordered libraries that could have been input in dependency order with the linker's default policy then some inefficiency is incurred: the linker must revisit libraries that it would not need to revisit if they were input in the right order.

    In general it impossible to tell just by looking at a linkage commandline whether the use of --start-group/--end-group is necessary, because it is impossible to tell just by looking whether the enclosed -liba [-libb...] contain cyclic dependencies. "High level" build systems, such as CMake, Meson, and others, may impose their own --start-group/--end-group default policy on sequences of libraries in a linkage in order to brute-force the successful linkage of static libraries that the build-system does not want to insist on being put in dependency order by the user.

    --start-group -liba [-libb...] --end-group have no effect on enclosed libraries that are shared, not static libraries, but a build-system may enclose them regardless because this will be harmless even if all the libraries turn out to be shared libraries. In your case, all of the library options:

    -lc -lm -lstdc++ -lsupc++
    

    must be resolved to static libraries because the option -static has been specified beforehand.

    Could this command be simplified?

    The fragment:

    -Wl,--start-group -lc -lm -lstdc++ -lsupc++ -Wl,--end-group
    

    could be rewritten as:

    -Wl,--start-group,-lc,-lm,-lstdc++,-lsupc++,--end-group
    

    using one less -Wl. That is because the option -l lib is an option for both GCC and for the linker, and has the same meaning for both. (It is in fact a linker option used so commonly that GCC will pass it to the linker without needing a -Wl. Other such linker options are -L dir and -static). It is conventional however to apply -Wl only to linker options that GCC does not recognise itself.

    The use of --start-group/--end-group in your linkage is a brute-forcing use. Without it, the libraries could be linked in the dependency order:

    lstdc++ -lsupc++ -lm -lc