third_party.pigweed.src/pw_toolchain/arm_gcc.gni
Paul Mathieu 0b955ca0d3 arm_gcc: help ld link all these libs together
There is some confusion as to what the following flags do:

--start-group/--end-group
--[no-]whole-archive

Before explaining them further, the following facts should be
highlighted:

Fact: the linker loads *all* input *.o objects
Fact: a static library is an archive of object files
Fact: the linker will try to resolve symbols across *all* currently
	loaded object
Fact: if a symbol cannot be resolved among currently loaded object, the
	linker will consider static libraries in the order provided and
	load the first object that provides that symbol
Fact: the linker only considers static libraries once and in the order
	provided

--start-group/--end-group pairs tell the linker to re-attempt
considering the libraries contained within the pair, and *in the order
provded* for as long as new unresolved symbols can be resolved that way.

--whole-archive/--no-whole-archive pairs tell the linker to load all
objects from the libraries contained within the pair, making it
equivalent to manually extracting all the object files out of these
libraries and providing them on the command line to the linker

In well-formed cases, none of these flags are necessary. An acyclic
chain of depencies can be established within these libraries by making
them into source_set() targets that depend on each other. In this case,
gn will output the correct order for these libraries.

There are cases where this is not possible. This is typically the case
when vendor-provided libraries have circular dependencies among them.
In these cases, either --start-group/--end-group or
--whole-archive/--no-whole-archive pairs need to be used.

--whole-archive/--no-whole-archive is safer because it allows the proper
resolution of weak/strong symbols.
Example:
libfoo.a:
  foo.o:
    w foo

libbar.a:
  bar.o:
    T foo

main.o:
  U foo

$ gcc main.o libfoo.a libbar.a

will result in the weak symbol defined in libfoo.a to be used.

$ gcc main.o --Wl,--whole-archive libfoo.a libbar.a -wl,--no-whole-archive

will result in the strong symbol defined in libbar.a to be used.

In light of all of the above, we propose always using
--whole-archive/--no-whole-archive and relying on --gc-sections to trim
the resulting executable to a reasonable size.

Change-Id: I355200961b6df6e797f3035f7945eb1a7f9e30b7
2019-12-18 16:43:36 +00:00

201 lines
6.6 KiB
Plaintext

# Copyright 2019 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
# Generates an arm-eabi-none gcc toolchain for a specific target.
#
# Args:
# toolchain_cflags: Additional C/C++ compiler flags for the target.
# toolchain_ldflags: Additional linker flags for the target.
template("arm_gcc_toolchain") {
_cflags_list = [
# Colorize output. Ninja's GCC invocation disables color by default.
"-fdiagnostics-color",
# Disable obnoxious ABI warning.
#
# GCC 7.1 adds an over-zealous ABI warning with little useful information
# on how to resolve the issue. The warning you get is:
#
# note: parameter passing for argument of type '...' changed in GCC 7.1
#
# There is no other information, and searching for the error is needed to
# understand what is happening. For upstream Pigweed, we compile from
# source so this is irrelevant; so disable it.
#
# See: https://gcc.gnu.org/gcc-7/changes.html (search for "psabi").
# https://gcc.gnu.org/ml/gcc/2017-05/msg00073.html
"-Wno-psabi",
]
if (defined(invoker.toolchain_cflags)) {
_cflags_list += invoker.toolchain_cflags
}
_toolchain_cflags = string_join(" ", _cflags_list)
_toolchain_ldflags = ""
if (defined(invoker.toolchain_ldflags)) {
_toolchain_ldflags += string_join(" ", invoker.toolchain_ldflags)
}
# TODO(frolv): This assumes that the ARM gcc toolchain is in the PATH.
# It should be updated to point to the prebuilt path within the source tree
# once that is added.
_tool_name_root = "arm-none-eabi-"
_ar = _tool_name_root + "ar"
_cc = _tool_name_root + "gcc"
_cxx = _tool_name_root + "g++"
toolchain(target_name) {
tool("asm") {
depfile = "{{output}}.d"
command = string_join(" ",
[
_cc,
"-MMD -MF $depfile", # Write out dependencies.
_toolchain_cflags,
"{{defines}}",
"{{include_dirs}}",
"{{asmflags}}",
"-c {{source}}",
"-o {{output}}",
])
depsformat = "gcc"
description = "as {{output}}"
outputs = [
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
]
}
tool("cc") {
depfile = "{{output}}.d"
command = string_join(" ",
[
_cc,
"-MMD -MF $depfile", # Write out dependencies.
_toolchain_cflags,
"{{defines}}",
"{{include_dirs}}",
"{{cflags}}",
"{{cflags_c}}",
"-c {{source}}",
"-o {{output}}",
])
depsformat = "gcc"
description = "cc {{output}}"
outputs = [
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
]
}
tool("cxx") {
depfile = "{{output}}.d"
command = string_join(" ",
[
_cc,
"-MMD -MF $depfile", # Write out dependencies.
_toolchain_cflags,
"{{defines}}",
"{{include_dirs}}",
"{{cflags}}",
"{{cflags_cc}}",
"-c {{source}}",
"-o {{output}}",
])
depsformat = "gcc"
description = "c++ {{output}}"
outputs = [
"{{source_out_dir}}/{{target_output_name}}.{{source_name_part}}.o",
]
}
tool("alink") {
command = "rm -f {{output}} && $_ar rcs {{output}} {{inputs}}"
description = "ar {{target_output_name}}{{output_extension}}"
outputs = [
"{{target_out_dir}}/{{target_output_name}}{{output_extension}}",
]
default_output_extension = ".a"
}
lib_switch = "-l"
lib_dir_switch = "-L"
_link_outfile = "{{output_dir}}/{{target_output_name}}{{output_extension}}"
_link_mapfile = "{{output_dir}}/{{target_output_name}}.map"
_link_command = string_join(
" ",
[
_cxx,
"{{ldflags}}",
# Delete unreferenced sections. Helpful with -ffunction-sections.
"-Wl,--gc-sections",
# Output a map file that shows symbols and their location.
"-Wl,-Map=$_link_mapfile",
_toolchain_cflags,
_toolchain_ldflags,
"{{inputs}}",
# Load all object files from all libraries to resolve symbols.
# Short of living in the ideal world where all dependency graphs
# among static libs are acyclic and all developers diligently
# express such graphs in terms that GN understands, this is the
# safest option.
# Make sure you use this with --gc-sections, otherwise the
# resulting binary will contain every symbol defined in every
# input file and every static library. That could be quite a lot.
"-Wl,--whole-archive",
"{{libs}}",
"-Wl,--no-whole-archive",
"-o $_link_outfile",
])
tool("link") {
command = _link_command
description = "ld $_link_outfile"
outputs = [
_link_outfile,
]
default_output_dir = "{{target_out_dir}}"
default_output_extension = ".elf"
}
tool("solink") {
command = _link_command + " -shared"
description = "ld -shared $_link_outfile"
outputs = [
_link_outfile,
]
default_output_dir = "{{target_out_dir}}"
default_output_extension = ".so"
}
tool("stamp") {
command = "touch {{output}}"
description = "stamp {{output}}"
}
tool("copy") {
command = "cp -af {{source}} {{output}}"
description = "cp {{source}} {{output}}"
}
}
}