Saturday, February 26, 2022

[SOLVED] Section mismatch in reference from a function to another function during Linux kernel build after adding #pragma GCC optimize("O0")

Issue

In Linux 5.4.21 source code, I put

#pragma GCC push_options
#pragma GCC optimize ("O0")

and

#pragma GCC pop_options

around the function static int __init gic_init_bases in the file /drivers/irqchip/irq-gic-v3.c.

When I build it, I get this warning message (section mismatch). I later found it is actually the #pragma GCC optimize ("O0") line that is causing it.

  CALL    scripts/atomic/check-atomics.sh
  CALL    scripts/checksyscalls.sh
  CHK     include/generated/compile.h
  CC      arch/arm64/kernel/irq.o
  CC      arch/arm64/kernel/setup.o
  CC      drivers/irqchip/irq-gic-v3.o
  AS      arch/arm64/kernel/head.o
  AR      arch/arm64/kernel/built-in.a
  AR      arch/arm64/built-in.a
  AR      drivers/irqchip/built-in.a
  AR      drivers/built-in.a
  GEN     .version
  CHK     include/generated/compile.h
  UPD     include/generated/compile.h
  CC      init/version.o
  AR      init/built-in.a
  LD      vmlinux.o
  MODPOST vmlinux.o
WARNING: vmlinux.o(.text+0x227cc0): Section mismatch in reference from the function gic_smp_init() to the function .init.text:set_smp_cross_call()
The function gic_smp_init() references
the function __init set_smp_cross_call().
This is often because gic_smp_init lacks a __init 
annotation or the annotation of set_smp_cross_call is wrong.

  MODINFO modules.builtin.modinfo
  LD      .tmp_vmlinux1
  KSYM    .tmp_kallsyms1.o
  LD      .tmp_vmlinux2
  KSYM    .tmp_kallsyms2.o
  LD      vmlinux
  SORTEX  vmlinux
  SYSMAP  System.map
  OBJCOPY arch/arm64/boot/Image

Function call chain is like this: gic_init_bases -> gic_smp_init() -> set_smp_cross_call (currently CONFIG_SMP=y). The message seems to say set_smp_cross_call is annotated with __init (meaning it is placed in .init.text), but gic_smp_init is not.

Without the #pragma debug setting, there was not warning of this kind. I'm not sure if I can just add add __init to gic_smp_init() (or remove _init from set_smp_cross_call). What is the correct method to fix it?

If I add __init to gic_smp_init, this warning goes away, but I think this will make the function be removed after initialization (which is ok maybe?).


Solution

TL;DR: gic_smp_init() should be annotated with __init.


Update: this was fixed in kernel v5.8, here's the relevant commit.

Looks to me like you found a bug: gic_smp_init() is only called by __init gic_init_bases(), so it has no real reason to not be annotated with __init too. The function set_smp_cross_call() is annotated with __init, so callers should also be annotated with __init.

If you compile with optimizations (without the pragma) this inconsistency disappears because the compiler simply inlines the entire body of gic_smp_init() into gic_init_bases(): the call chain is then just gic_init_bases -> set_smp_cross_call and everything is fine since they are both annotated __init. However, when you disable optimizations (#pragma GCC optimize ("O0")) the compiler no longer inlines the call to gic_smp_init(), which stays on its own as an actual function, and the inconsistency reveals itself.

The authors of the module probably missed the __init annotation of set_smp_cross_call(), or the annotation was added later and nobody noticed it also needed to be "propagated" in that driver code.

There also seems to be an inconsistency between the annotations for set_smp_cross_call(): in the C files (/arch/{arm,arm64}/kernel/smp.c) it is annotated with __init, while in the header files (/arch/{arm,arm64}/include/asm/smp.h) it is not. The latter instances should probably be annotated too as the doc-comment for the __init macro suggests:

 * If the function has a prototype somewhere, you can also add
 * __init between closing brace of the prototype and semicolon:
 *
 * extern int initialize_foobar_device(int, int, int) __init;


Answered By - Marco Bonelli
Answer Checked By - Willingham (WPSolving Volunteer)