Re: [RFC PATCH] kbuild: pass objects instead of archives to linker

Ard Biesheuvel
2022-11-02T09:21:48Z

Hello Masahiro,

On Wed, 2 Nov 2022 at 10:13, Masahiro Yamada <masahiroy@kernel.org> wrote:
>
> This is an experimental patch, driven by the feedback from Jiri Slaby
> and Michael Matz. [1]
>
> Michael Matz says:
> "I know of no linker (outside LTO-like modes) that processes
> archives in a different order than first-to-last-member (under
> whole-archive), but that's not guaranteed anywhere. So relying on
> member-order within archives is always brittle."
>
> It is pretty easy to pass the list of objects instead of a thin archive
> because the linker supports the '@file' syntax, where command line
> arguments are read from 'file'.
>

Can you explain which problem is solved by doing this?

If we can only produce a working kernel if each object is linked in
the order it appears in the archive, I think we have bigger problems
that need solving regardless. And for the .head.text objects that need
to appear at the start of the binary image, I think the reported issue
with __head annotated C functions on x86 needs to be addressed by
getting rid of __head entirely (which seems to have been introduced
without proper justification)

> Without this patch, the linker receives
>
> --whole-archive vmlinux.a --no-whole-archive
>
> With this patch, the linker will receive
>
> @vmlinux.order
>
> Here, vmlinux.order is a text file that lists built-in objects in the
> correct link order.
>
> I am not a toolchain expert. I just want to know if this makes any
> difference from the linker perspective and from (non-upstreamed) GCC-LTO
> perspective.
>
> (I know this patch does not work for Clang LTO because I did not touch
> scripts/generate_initcall_order.pl)
>
> This patch may be unneeded because more correct patches were submitted [2]
> but I am still curious about "thin archive vs direct object list".
>
> [1]: https://lore.kernel.org/linux-kbuild/alpine.LSU.2.20.2210251210140.29399@wotan.suse.de/
> [2]: https://lore.kernel.org/all/20221101161529.1634188-1-alexandr.lobakin@intel.com/
>
> Signed-off-by: Masahiro Yamada <masahiroy@kernel.org>
> ---
>
> Makefile | 21 ++++++++++-----------
> scripts/Makefile.modpost | 5 +++--
> scripts/Makefile.vmlinux_o | 6 +++---
> scripts/clang-tools/gen_compile_commands.py | 21 ++++++++++++++++++++-
> scripts/link-vmlinux.sh | 8 ++++----
> 5 files changed, 40 insertions(+), 21 deletions(-)
>
> diff --git a/Makefile b/Makefile
> index e9e7eff906a5..511484a3dacb 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1213,19 +1213,18 @@ quiet_cmd_autoksyms_h = GEN $@
> $(autoksyms_h):
> $(call cmd,autoksyms_h)
>
> -# '$(AR) mPi' needs 'T' to workaround the bug of llvm-ar <= 14
> -quiet_cmd_ar_vmlinux.a = AR $@
> - cmd_ar_vmlinux.a = \
> - rm -f $@; \
> - $(AR) cDPrST $@ $(KBUILD_VMLINUX_OBJS); \
> - $(AR) mPiT $$($(AR) t $@ | sed -n 1p) $@ $$($(AR) t $@ | grep -F -f $(srctree)/scripts/head-object-list.txt)
> +quiet_cmd_vmlinux_order = GEN $@
> + cmd_vmlinux_order = \
> + { $(foreach m, $(KBUILD_VMLINUX_OBJS), $(AR) t $m;) :; } > $(tmp-target) ; \
> + grep -F -f $(srctree)/scripts/head-object-list.txt $(tmp-target) > $@; \
> + grep -F -f $(srctree)/scripts/head-object-list.txt $(tmp-target) -v >> $@
>
> -targets += vmlinux.a
> -vmlinux.a: $(KBUILD_VMLINUX_OBJS) scripts/head-object-list.txt autoksyms_recursive FORCE
> - $(call if_changed,ar_vmlinux.a)
> +targets += vmlinux.order
> +vmlinux.order: $(KBUILD_VMLINUX_OBJS) scripts/head-object-list.txt autoksyms_recursive FORCE
> + $(call if_changed,vmlinux_order)
>
> PHONY += vmlinux_o
> -vmlinux_o: vmlinux.a $(KBUILD_VMLINUX_LIBS)
> +vmlinux_o: vmlinux.order $(KBUILD_VMLINUX_LIBS)
> $(Q)$(MAKE) -f $(srctree)/scripts/Makefile.vmlinux_o
>
> vmlinux.o modules.builtin.modinfo modules.builtin: vmlinux_o
> @@ -2037,7 +2036,7 @@ quiet_cmd_gen_compile_commands = GEN $@
> cmd_gen_compile_commands = $(PYTHON3) $< -a $(AR) -o $@ $(filter-out $<, $(real-prereqs))
>
> $(extmod_prefix)compile_commands.json: scripts/clang-tools/gen_compile_commands.py \
> - $(if $(KBUILD_EXTMOD),, vmlinux.a $(KBUILD_VMLINUX_LIBS)) \
> + $(if $(KBUILD_EXTMOD),, vmlinux.order $(KBUILD_VMLINUX_LIBS)) \
> $(if $(CONFIG_MODULES), $(MODORDER)) FORCE
> $(call if_changed,gen_compile_commands)
>
> diff --git a/scripts/Makefile.modpost b/scripts/Makefile.modpost
> index e41dee64d429..1d6847da39bd 100644
> --- a/scripts/Makefile.modpost
> +++ b/scripts/Makefile.modpost
> @@ -70,12 +70,13 @@ quiet_cmd_vmlinux_objs = GEN $@
> for f in $(real-prereqs); do \
> case $${f} in \
> *libgcc.a) ;; \
> - *) $(AR) t $${f} ;; \
> + *.a) $(AR) t $${f} ;; \
> + *) cat $${f} ;; \
> esac \
> done > $@
>
> targets += .vmlinux.objs
> -.vmlinux.objs: vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE
> +.vmlinux.objs: vmlinux.order $(KBUILD_VMLINUX_LIBS) FORCE
> $(call if_changed,vmlinux_objs)
>
> vmlinux.o-if-present := $(wildcard vmlinux.o)
> diff --git a/scripts/Makefile.vmlinux_o b/scripts/Makefile.vmlinux_o
> index 0edfdb40364b..6eb07f2bb39f 100644
> --- a/scripts/Makefile.vmlinux_o
> +++ b/scripts/Makefile.vmlinux_o
> @@ -18,7 +18,7 @@ quiet_cmd_gen_initcalls_lds = GEN $@
> $(PERL) $(real-prereqs) > $@
>
> .tmp_initcalls.lds: $(srctree)/scripts/generate_initcall_order.pl \
> - vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE
> + vmlinux.order $(KBUILD_VMLINUX_LIBS) FORCE
> $(call if_changed,gen_initcalls_lds)
>
> targets := .tmp_initcalls.lds
> @@ -48,7 +48,7 @@ quiet_cmd_ld_vmlinux.o = LD $@
> cmd_ld_vmlinux.o = \
> $(LD) ${KBUILD_LDFLAGS} -r -o $@ \
> $(addprefix -T , $(initcalls-lds)) \
> - --whole-archive vmlinux.a --no-whole-archive \
> + @vmlinux.order \
> --start-group $(KBUILD_VMLINUX_LIBS) --end-group \
> $(cmd_objtool)
>
> @@ -57,7 +57,7 @@ define rule_ld_vmlinux.o
> $(call cmd,gen_objtooldep)
> endef
>
> -vmlinux.o: $(initcalls-lds) vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE
> +vmlinux.o: $(initcalls-lds) vmlinux.order $(KBUILD_VMLINUX_LIBS) FORCE
> $(call if_changed_rule,ld_vmlinux.o)
>
> targets += vmlinux.o
> diff --git a/scripts/clang-tools/gen_compile_commands.py b/scripts/clang-tools/gen_compile_commands.py
> index d800b2c0af97..c8ba9f084bd0 100755
> --- a/scripts/clang-tools/gen_compile_commands.py
> +++ b/scripts/clang-tools/gen_compile_commands.py
> @@ -57,7 +57,7 @@ def parse_arguments():
> parser.add_argument('-a', '--ar', type=str, default='llvm-ar', help=ar_help)
>
> paths_help = ('directories to search or files to parse '
> - '(files should be *.o, *.a, or modules.order). '
> + '(files should be *.o, *.a, or *.order). '
> 'If nothing is specified, the current directory is searched')
> parser.add_argument('paths', type=str, nargs='*', help=paths_help)
>
> @@ -124,6 +124,23 @@ def cmdfiles_for_a(archive, ar):
> yield to_cmdfile(obj)
>
>
> +def cmdfiles_for_vmlinux_order(vmlinux_order):
> + """Generate the iterator of .cmd files associated with the vmlinux.order.
> +
> + Parse the given vmlinux.order, and yield every .cmd file used to build the
> + contained modules.
> +
> + Args:
> + vmlinux_order: The vmlinux.order file to parse
> +
> + Yields:
> + The path to every .cmd file found
> + """
> + with open(vmlinux_order) as f:
> + for line in f:
> + yield to_cmdfile(line.rstrip())
> +
> +
> def cmdfiles_for_modorder(modorder):
> """Generate the iterator of .cmd files associated with the modules.order.
>
> @@ -203,6 +220,8 @@ def main():
> cmdfiles = cmdfiles_in_dir(path)
> elif path.endswith('.a'):
> cmdfiles = cmdfiles_for_a(path, ar)
> + elif path.endswith('vmlinux.order'):
> + cmdfiles = cmdfiles_for_vmlinux_order(path)
> elif path.endswith('modules.order'):
> cmdfiles = cmdfiles_for_modorder(path)
> else:
> diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
> index 918470d768e9..617ed443e377 100755
> --- a/scripts/link-vmlinux.sh
> +++ b/scripts/link-vmlinux.sh
> @@ -3,15 +3,15 @@
> #
> # link vmlinux
> #
> -# vmlinux is linked from the objects in vmlinux.a and $(KBUILD_VMLINUX_LIBS).
> -# vmlinux.a contains objects that are linked unconditionally.
> +# vmlinux is linked from the objects in vmlinux.order and $(KBUILD_VMLINUX_LIBS).
> +# vmlinux.order is a text file that contains objects that are linked unconditionally.
> # $(KBUILD_VMLINUX_LIBS) are archives which are linked conditionally
> # (not within --whole-archive), and do not require symbol indexes added.
> #
> # vmlinux
> # ^
> # |
> -# +--< vmlinux.a
> +# +--< vmlinux.order
> # |
> # +--< $(KBUILD_VMLINUX_LIBS)
> # | +--< lib/lib.a + more
> @@ -65,7 +65,7 @@ vmlinux_link()
> objs=vmlinux.o
> libs=
> else
> - objs=vmlinux.a
> + objs=@vmlinux.order
> libs="${KBUILD_VMLINUX_LIBS}"
> fi
>
> --
> 2.34.1
>