[09/22] init: lto: ensure initcall ordering
diff mbox series

Message ID 20200624203200.78870-10-samitolvanen@google.com
State New
Headers show
Series
  • add support for Clang LTO
Related show

Commit Message

Sami Tolvanen June 24, 2020, 8:31 p.m. UTC
With LTO, the compiler doesn't necessarily obey the link order for
initcalls, and initcall variables need globally unique names to avoid
collisions at link time.

This change exports __KBUILD_MODNAME and adds the initcall_id() macro,
which uses it together with __COUNTER__ and __LINE__ to help ensure
these variables have unique names, and moves each variable to its own
section when LTO is enabled, so the correct order can be specified using
a linker script.

The generate_initcall_ordering.pl script uses nm to find initcalls from
the object files passed to the linker, and generates a linker script
that specifies the intended order. With LTO, the script is called in
link-vmlinux.sh.

Signed-off-by: Sami Tolvanen <samitolvanen@google.com>
---
 include/linux/init.h               |  52 +++++-
 scripts/Makefile.lib               |   6 +-
 scripts/generate_initcall_order.pl | 270 +++++++++++++++++++++++++++++
 scripts/link-vmlinux.sh            |  14 ++
 4 files changed, 333 insertions(+), 9 deletions(-)
 create mode 100755 scripts/generate_initcall_order.pl

Comments

kernel test robot June 25, 2020, 12:58 a.m. UTC | #1
Hi Sami,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on 26e122e97a3d0390ebec389347f64f3730fdf48f]

url:    https://github.com/0day-ci/linux/commits/Sami-Tolvanen/add-support-for-Clang-LTO/20200625-043816
base:    26e122e97a3d0390ebec389347f64f3730fdf48f
config: m68k-defconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All error/warnings (new ones prefixed by >>):

   In file included from arch/m68k/include/asm/io_mm.h:25,
                    from arch/m68k/include/asm/io.h:8,
                    from include/linux/io.h:13,
                    from include/linux/irq.h:20,
                    from include/asm-generic/hardirq.h:13,
                    from ./arch/m68k/include/generated/asm/hardirq.h:1,
                    from include/linux/hardirq.h:10,
                    from include/linux/interrupt.h:11,
                    from drivers/ide/gayle.c:13:
   arch/m68k/include/asm/raw_io.h: In function 'raw_rom_outsb':
   arch/m68k/include/asm/raw_io.h:83:7: warning: variable '__w' set but not used [-Wunused-but-set-variable]
      83 |  ({u8 __w, __v = (b);  u32 _addr = ((u32) (addr)); \
         |       ^~~
   arch/m68k/include/asm/raw_io.h:430:3: note: in expansion of macro 'rom_out_8'
     430 |   rom_out_8(port, *buf++);
         |   ^~~~~~~~~
   arch/m68k/include/asm/raw_io.h: In function 'raw_rom_outsw':
   arch/m68k/include/asm/raw_io.h:86:8: warning: variable '__w' set but not used [-Wunused-but-set-variable]
      86 |  ({u16 __w, __v = (w); u32 _addr = ((u32) (addr)); \
         |        ^~~
   arch/m68k/include/asm/raw_io.h:448:3: note: in expansion of macro 'rom_out_be16'
     448 |   rom_out_be16(port, *buf++);
         |   ^~~~~~~~~~~~
   arch/m68k/include/asm/raw_io.h: In function 'raw_rom_outsw_swapw':
   arch/m68k/include/asm/raw_io.h:90:8: warning: variable '__w' set but not used [-Wunused-but-set-variable]
      90 |  ({u16 __w, __v = (w); u32 _addr = ((u32) (addr)); \
         |        ^~~
   arch/m68k/include/asm/raw_io.h:466:3: note: in expansion of macro 'rom_out_le16'
     466 |   rom_out_le16(port, *buf++);
         |   ^~~~~~~~~~~~
   In file included from arch/m68k/include/asm/bug.h:32,
                    from include/linux/bug.h:5,
                    from include/linux/mmdebug.h:5,
                    from include/linux/mm.h:9,
                    from drivers/ide/gayle.c:12:
   include/linux/dma-mapping.h: In function 'dma_map_resource':
   arch/m68k/include/asm/page_mm.h:169:49: warning: ordered comparison of pointer with null pointer [-Wextra]
     169 | #define virt_addr_valid(kaddr) ((void *)(kaddr) >= (void *)PAGE_OFFSET && (void *)(kaddr) < high_memory)
         |                                                 ^~
   include/asm-generic/bug.h:144:27: note: in definition of macro 'WARN_ON_ONCE'
     144 |  int __ret_warn_once = !!(condition);   \
         |                           ^~~~~~~~~
   arch/m68k/include/asm/page_mm.h:170:25: note: in expansion of macro 'virt_addr_valid'
     170 | #define pfn_valid(pfn)  virt_addr_valid(pfn_to_virt(pfn))
         |                         ^~~~~~~~~~~~~~~
   include/linux/dma-mapping.h:352:19: note: in expansion of macro 'pfn_valid'
     352 |  if (WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr))))
         |                   ^~~~~~~~~
   In file included from <command-line>:
   drivers/ide/gayle.c: At top level:
>> arch/m68k/include/asm/amigayle.h:57:66: error: pasting ")" and "__279_185_amiga_gayle_ide_driver_init" does not give a valid preprocessing token
      57 | #define gayle (*(volatile struct GAYLE *)(zTwoBase+GAYLE_ADDRESS))
         |                                                                  ^
   include/linux/compiler_types.h:53:23: note: in definition of macro '___PASTE'
      53 | #define ___PASTE(a,b) a##b
         |                       ^
>> include/linux/init.h:189:2: note: in expansion of macro '__PASTE'
     189 |  __PASTE(__KBUILD_MODNAME,    \
         |  ^~~~~~~
>> <command-line>: note: in expansion of macro 'gayle'
>> include/linux/init.h:189:10: note: in expansion of macro '__KBUILD_MODNAME'
     189 |  __PASTE(__KBUILD_MODNAME,    \
         |          ^~~~~~~~~~~~~~~~
>> include/linux/init.h:236:35: note: in expansion of macro '__initcall_id'
     236 |  __unique_initcall(fn, id, __sec, __initcall_id(fn))
         |                                   ^~~~~~~~~~~~~
   include/linux/init.h:238:35: note: in expansion of macro '___define_initcall'
     238 | #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
         |                                   ^~~~~~~~~~~~~~~~~~
   include/linux/init.h:267:30: note: in expansion of macro '__define_initcall'
     267 | #define device_initcall(fn)  __define_initcall(fn, 6)
         |                              ^~~~~~~~~~~~~~~~~
>> include/linux/init.h:272:24: note: in expansion of macro 'device_initcall'
     272 | #define __initcall(fn) device_initcall(fn)
         |                        ^~~~~~~~~~~~~~~
>> include/linux/module.h:88:24: note: in expansion of macro '__initcall'
      88 | #define module_init(x) __initcall(x);
         |                        ^~~~~~~~~~
   include/linux/platform_device.h:271:1: note: in expansion of macro 'module_init'
     271 | module_init(__platform_driver##_init); \
         | ^~~~~~~~~~~
   drivers/ide/gayle.c:185:1: note: in expansion of macro 'module_platform_driver_probe'
     185 | module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe);
         | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
>> include/linux/init.h:200:10: error: pasting "__" and "(" does not give a valid preprocessing token
     200 |  __PASTE(__,      \
         |          ^~
   include/linux/compiler_types.h:53:23: note: in definition of macro '___PASTE'
      53 | #define ___PASTE(a,b) a##b
         |                       ^
   include/linux/init.h:200:2: note: in expansion of macro '__PASTE'
     200 |  __PASTE(__,      \
         |  ^~~~~~~
>> include/linux/init.h:232:3: note: in expansion of macro '__initcall_name'
     232 |   __initcall_name(initcall, __iid, id),  \
         |   ^~~~~~~~~~~~~~~
>> include/linux/init.h:236:2: note: in expansion of macro '__unique_initcall'
     236 |  __unique_initcall(fn, id, __sec, __initcall_id(fn))
         |  ^~~~~~~~~~~~~~~~~
   include/linux/init.h:238:35: note: in expansion of macro '___define_initcall'
     238 | #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
         |                                   ^~~~~~~~~~~~~~~~~~
   include/linux/init.h:267:30: note: in expansion of macro '__define_initcall'
     267 | #define device_initcall(fn)  __define_initcall(fn, 6)
         |                              ^~~~~~~~~~~~~~~~~
>> include/linux/init.h:272:24: note: in expansion of macro 'device_initcall'
     272 | #define __initcall(fn) device_initcall(fn)
         |                        ^~~~~~~~~~~~~~~
>> include/linux/module.h:88:24: note: in expansion of macro '__initcall'
      88 | #define module_init(x) __initcall(x);
         |                        ^~~~~~~~~~
   include/linux/platform_device.h:271:1: note: in expansion of macro 'module_init'
     271 | module_init(__platform_driver##_init); \
         | ^~~~~~~~~~~
   drivers/ide/gayle.c:185:1: note: in expansion of macro 'module_platform_driver_probe'
     185 | module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe);
         | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/printk.h:6,
                    from include/linux/kernel.h:15,
                    from include/asm-generic/bug.h:19,
                    from arch/m68k/include/asm/bug.h:32,
                    from include/linux/bug.h:5,
                    from include/linux/mmdebug.h:5,
                    from include/linux/mm.h:9,
                    from drivers/ide/gayle.c:12:
>> arch/m68k/include/asm/amigayle.h:57:16: error: expected declaration specifiers or '...' before '*' token
      57 | #define gayle (*(volatile struct GAYLE *)(zTwoBase+GAYLE_ADDRESS))
         |                ^
   include/linux/init.h:226:20: note: in definition of macro '____define_initcall'
     226 |  static initcall_t __name __used    \
         |                    ^~~~~~
   include/linux/compiler_types.h:54:22: note: in expansion of macro '___PASTE'
      54 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^~~~~~~~
   include/linux/init.h:198:2: note: in expansion of macro '__PASTE'
     198 |  __PASTE(__,      \
         |  ^~~~~~~
   include/linux/compiler_types.h:54:22: note: in expansion of macro '___PASTE'
      54 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^~~~~~~~
   include/linux/init.h:199:2: note: in expansion of macro '__PASTE'
     199 |  __PASTE(prefix,      \
         |  ^~~~~~~
   include/linux/compiler_types.h:54:22: note: in expansion of macro '___PASTE'
      54 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^~~~~~~~
   include/linux/init.h:200:2: note: in expansion of macro '__PASTE'
     200 |  __PASTE(__,      \
         |  ^~~~~~~
   include/linux/compiler_types.h:54:22: note: in expansion of macro '___PASTE'
      54 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^~~~~~~~
   include/linux/init.h:201:2: note: in expansion of macro '__PASTE'
     201 |  __PASTE(__iid, id))))
         |  ^~~~~~~
>> include/linux/init.h:232:3: note: in expansion of macro '__initcall_name'
     232 |   __initcall_name(initcall, __iid, id),  \
         |   ^~~~~~~~~~~~~~~
>> include/linux/init.h:236:2: note: in expansion of macro '__unique_initcall'
     236 |  __unique_initcall(fn, id, __sec, __initcall_id(fn))
         |  ^~~~~~~~~~~~~~~~~
   include/linux/compiler_types.h:54:22: note: in expansion of macro '___PASTE'
      54 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^~~~~~~~
>> include/linux/init.h:189:2: note: in expansion of macro '__PASTE'
     189 |  __PASTE(__KBUILD_MODNAME,    \
         |  ^~~~~~~
>> <command-line>: note: in expansion of macro 'gayle'
>> include/linux/init.h:189:10: note: in expansion of macro '__KBUILD_MODNAME'
     189 |  __PASTE(__KBUILD_MODNAME,    \
         |          ^~~~~~~~~~~~~~~~
>> include/linux/init.h:236:35: note: in expansion of macro '__initcall_id'
     236 |  __unique_initcall(fn, id, __sec, __initcall_id(fn))
         |                                   ^~~~~~~~~~~~~
   include/linux/init.h:238:35: note: in expansion of macro '___define_initcall'
     238 | #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
         |                                   ^~~~~~~~~~~~~~~~~~
   include/linux/init.h:267:30: note: in expansion of macro '__define_initcall'
     267 | #define device_initcall(fn)  __define_initcall(fn, 6)
         |                              ^~~~~~~~~~~~~~~~~
>> include/linux/init.h:272:24: note: in expansion of macro 'device_initcall'
     272 | #define __initcall(fn) device_initcall(fn)
         |                        ^~~~~~~~~~~~~~~
   include/linux/module.h:88:24: note: in expansion of macro '__initcall'
      88 | #define module_init(x) __initcall(x);
         |                        ^~~~~~~~~~
   include/linux/platform_device.h:271:1: note: in expansion of macro 'module_init'
     271 | module_init(__platform_driver##_init); \
         | ^~~~~~~~~~~
   drivers/ide/gayle.c:185:1: note: in expansion of macro 'module_platform_driver_probe'
     185 | module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe);
         | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from drivers/ide/gayle.c:19:
   drivers/ide/gayle.c:185:30: warning: 'amiga_gayle_ide_driver_init' defined but not used [-Wunused-function]
     185 | module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe);
         |                              ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/platform_device.h:266:19: note: in definition of macro 'module_platform_driver_probe'
     266 | static int __init __platform_driver##_init(void) \
         |                   ^~~~~~~~~~~~~~~~~

vim +200 include/linux/init.h

   170	
   171	/*
   172	 * initcalls are now grouped by functionality into separate
   173	 * subsections. Ordering inside the subsections is determined
   174	 * by link order. 
   175	 * For backwards compatibility, initcall() puts the call in 
   176	 * the device init subsection.
   177	 *
   178	 * The `id' arg to __define_initcall() is needed so that multiple initcalls
   179	 * can point at the same handler without causing duplicate-symbol build errors.
   180	 *
   181	 * Initcalls are run by placing pointers in initcall sections that the
   182	 * kernel iterates at runtime. The linker can do dead code / data elimination
   183	 * and remove that completely, so the initcall sections have to be marked
   184	 * as KEEP() in the linker script.
   185	 */
   186	
   187	/* Format: <modname>__<counter>_<line>_<fn> */
   188	#define __initcall_id(fn)					\
 > 189		__PASTE(__KBUILD_MODNAME,				\
   190		__PASTE(__,						\
   191		__PASTE(__COUNTER__,					\
   192		__PASTE(_,						\
   193		__PASTE(__LINE__,					\
   194		__PASTE(_, fn))))))
   195	
   196	/* Format: __<prefix>__<iid><id> */
   197	#define __initcall_name(prefix, __iid, id)			\
   198		__PASTE(__,						\
   199		__PASTE(prefix,						\
 > 200		__PASTE(__,						\
   201		__PASTE(__iid, id))))
   202	
   203	#ifdef CONFIG_LTO_CLANG
   204	/*
   205	 * With LTO, the compiler doesn't necessarily obey link order for
   206	 * initcalls. In order to preserve the correct order, we add each
   207	 * variable into its own section and generate a linker script (in
   208	 * scripts/link-vmlinux.sh) to specify the order of the sections.
   209	 */
   210	#define __initcall_section(__sec, __iid)			\
   211		#__sec ".init.." #__iid
   212	#else
   213	#define __initcall_section(__sec, __iid)			\
   214		#__sec ".init"
   215	#endif
   216	
   217	#ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
   218	#define ____define_initcall(fn, __name, __sec)			\
   219		__ADDRESSABLE(fn)					\
   220		asm(".section	\"" __sec "\", \"a\"		\n"	\
   221		    __stringify(__name) ":			\n"	\
   222		    ".long	" #fn " - .			\n"	\
   223		    ".previous					\n");
   224	#else
   225	#define ____define_initcall(fn, __name, __sec)			\
   226		static initcall_t __name __used 			\
   227			__attribute__((__section__(__sec))) = fn;
   228	#endif
   229	
   230	#define __unique_initcall(fn, id, __sec, __iid)			\
   231		____define_initcall(fn,					\
 > 232			__initcall_name(initcall, __iid, id),		\
   233			__initcall_section(__sec, __iid))
   234	
   235	#define ___define_initcall(fn, id, __sec)			\
 > 236		__unique_initcall(fn, id, __sec, __initcall_id(fn))
   237	
   238	#define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
   239	
   240	/*
   241	 * Early initcalls run before initializing SMP.
   242	 *
   243	 * Only for built-in code, not modules.
   244	 */
   245	#define early_initcall(fn)		__define_initcall(fn, early)
   246	
   247	/*
   248	 * A "pure" initcall has no dependencies on anything else, and purely
   249	 * initializes variables that couldn't be statically initialized.
   250	 *
   251	 * This only exists for built-in code, not for modules.
   252	 * Keep main.c:initcall_level_names[] in sync.
   253	 */
   254	#define pure_initcall(fn)		__define_initcall(fn, 0)
   255	
   256	#define core_initcall(fn)		__define_initcall(fn, 1)
   257	#define core_initcall_sync(fn)		__define_initcall(fn, 1s)
   258	#define postcore_initcall(fn)		__define_initcall(fn, 2)
   259	#define postcore_initcall_sync(fn)	__define_initcall(fn, 2s)
   260	#define arch_initcall(fn)		__define_initcall(fn, 3)
   261	#define arch_initcall_sync(fn)		__define_initcall(fn, 3s)
   262	#define subsys_initcall(fn)		__define_initcall(fn, 4)
   263	#define subsys_initcall_sync(fn)	__define_initcall(fn, 4s)
   264	#define fs_initcall(fn)			__define_initcall(fn, 5)
   265	#define fs_initcall_sync(fn)		__define_initcall(fn, 5s)
   266	#define rootfs_initcall(fn)		__define_initcall(fn, rootfs)
   267	#define device_initcall(fn)		__define_initcall(fn, 6)
   268	#define device_initcall_sync(fn)	__define_initcall(fn, 6s)
   269	#define late_initcall(fn)		__define_initcall(fn, 7)
   270	#define late_initcall_sync(fn)		__define_initcall(fn, 7s)
   271	
 > 272	#define __initcall(fn) device_initcall(fn)
   273	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot June 25, 2020, 4:19 a.m. UTC | #2
Hi Sami,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on 26e122e97a3d0390ebec389347f64f3730fdf48f]

url:    https://github.com/0day-ci/linux/commits/Sami-Tolvanen/add-support-for-Clang-LTO/20200625-043816
base:    26e122e97a3d0390ebec389347f64f3730fdf48f
config: m68k-allyesconfig (attached as .config)
compiler: m68k-linux-gcc (GCC) 9.3.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-9.3.0 make.cross ARCH=m68k 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   In file included from arch/m68k/include/asm/io_mm.h:25,
                    from arch/m68k/include/asm/io.h:8,
                    from include/linux/io.h:13,
                    from include/linux/irq.h:20,
                    from include/asm-generic/hardirq.h:13,
                    from ./arch/m68k/include/generated/asm/hardirq.h:1,
                    from include/linux/hardirq.h:10,
                    from include/linux/interrupt.h:11,
                    from drivers/ide/gayle.c:13:
   arch/m68k/include/asm/raw_io.h: In function 'raw_rom_outsb':
   arch/m68k/include/asm/raw_io.h:83:7: warning: variable '__w' set but not used [-Wunused-but-set-variable]
      83 |  ({u8 __w, __v = (b);  u32 _addr = ((u32) (addr)); \
         |       ^~~
   arch/m68k/include/asm/raw_io.h:430:3: note: in expansion of macro 'rom_out_8'
     430 |   rom_out_8(port, *buf++);
         |   ^~~~~~~~~
   arch/m68k/include/asm/raw_io.h: In function 'raw_rom_outsw':
   arch/m68k/include/asm/raw_io.h:86:8: warning: variable '__w' set but not used [-Wunused-but-set-variable]
      86 |  ({u16 __w, __v = (w); u32 _addr = ((u32) (addr)); \
         |        ^~~
   arch/m68k/include/asm/raw_io.h:448:3: note: in expansion of macro 'rom_out_be16'
     448 |   rom_out_be16(port, *buf++);
         |   ^~~~~~~~~~~~
   arch/m68k/include/asm/raw_io.h: In function 'raw_rom_outsw_swapw':
   arch/m68k/include/asm/raw_io.h:90:8: warning: variable '__w' set but not used [-Wunused-but-set-variable]
      90 |  ({u16 __w, __v = (w); u32 _addr = ((u32) (addr)); \
         |        ^~~
   arch/m68k/include/asm/raw_io.h:466:3: note: in expansion of macro 'rom_out_le16'
     466 |   rom_out_le16(port, *buf++);
         |   ^~~~~~~~~~~~
   In file included from include/asm-generic/bug.h:5,
                    from arch/m68k/include/asm/bug.h:32,
                    from include/linux/bug.h:5,
                    from include/linux/mmdebug.h:5,
                    from include/linux/mm.h:9,
                    from drivers/ide/gayle.c:12:
   include/linux/scatterlist.h: In function 'sg_set_buf':
   arch/m68k/include/asm/page_mm.h:169:49: warning: ordered comparison of pointer with null pointer [-Wextra]
     169 | #define virt_addr_valid(kaddr) ((void *)(kaddr) >= (void *)PAGE_OFFSET && (void *)(kaddr) < high_memory)
         |                                                 ^~
   include/linux/compiler.h:78:42: note: in definition of macro 'unlikely'
      78 | # define unlikely(x) __builtin_expect(!!(x), 0)
         |                                          ^
   include/linux/scatterlist.h:143:2: note: in expansion of macro 'BUG_ON'
     143 |  BUG_ON(!virt_addr_valid(buf));
         |  ^~~~~~
   include/linux/scatterlist.h:143:10: note: in expansion of macro 'virt_addr_valid'
     143 |  BUG_ON(!virt_addr_valid(buf));
         |          ^~~~~~~~~~~~~~~
   In file included from arch/m68k/include/asm/bug.h:32,
                    from include/linux/bug.h:5,
                    from include/linux/mmdebug.h:5,
                    from include/linux/mm.h:9,
                    from drivers/ide/gayle.c:12:
   include/linux/dma-mapping.h: In function 'dma_map_resource':
   arch/m68k/include/asm/page_mm.h:169:49: warning: ordered comparison of pointer with null pointer [-Wextra]
     169 | #define virt_addr_valid(kaddr) ((void *)(kaddr) >= (void *)PAGE_OFFSET && (void *)(kaddr) < high_memory)
         |                                                 ^~
   include/asm-generic/bug.h:144:27: note: in definition of macro 'WARN_ON_ONCE'
     144 |  int __ret_warn_once = !!(condition);   \
         |                           ^~~~~~~~~
   arch/m68k/include/asm/page_mm.h:170:25: note: in expansion of macro 'virt_addr_valid'
     170 | #define pfn_valid(pfn)  virt_addr_valid(pfn_to_virt(pfn))
         |                         ^~~~~~~~~~~~~~~
   include/linux/dma-mapping.h:352:19: note: in expansion of macro 'pfn_valid'
     352 |  if (WARN_ON_ONCE(pfn_valid(PHYS_PFN(phys_addr))))
         |                   ^~~~~~~~~
   In file included from <command-line>:
   drivers/ide/gayle.c: At top level:
>> arch/m68k/include/asm/amigayle.h:57:66: error: pasting ")" and "__282_185_amiga_gayle_ide_driver_init" does not give a valid preprocessing token
      57 | #define gayle (*(volatile struct GAYLE *)(zTwoBase+GAYLE_ADDRESS))
         |                                                                  ^
   include/linux/compiler_types.h:53:23: note: in definition of macro '___PASTE'
      53 | #define ___PASTE(a,b) a##b
         |                       ^
   include/linux/init.h:189:2: note: in expansion of macro '__PASTE'
     189 |  __PASTE(__KBUILD_MODNAME,    \
         |  ^~~~~~~
   <command-line>: note: in expansion of macro 'gayle'
   include/linux/init.h:189:10: note: in expansion of macro '__KBUILD_MODNAME'
     189 |  __PASTE(__KBUILD_MODNAME,    \
         |          ^~~~~~~~~~~~~~~~
   include/linux/init.h:236:35: note: in expansion of macro '__initcall_id'
     236 |  __unique_initcall(fn, id, __sec, __initcall_id(fn))
         |                                   ^~~~~~~~~~~~~
   include/linux/init.h:238:35: note: in expansion of macro '___define_initcall'
     238 | #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
         |                                   ^~~~~~~~~~~~~~~~~~
   include/linux/init.h:267:30: note: in expansion of macro '__define_initcall'
     267 | #define device_initcall(fn)  __define_initcall(fn, 6)
         |                              ^~~~~~~~~~~~~~~~~
   include/linux/init.h:272:24: note: in expansion of macro 'device_initcall'
     272 | #define __initcall(fn) device_initcall(fn)
         |                        ^~~~~~~~~~~~~~~
   include/linux/module.h:88:24: note: in expansion of macro '__initcall'
      88 | #define module_init(x) __initcall(x);
         |                        ^~~~~~~~~~
   include/linux/platform_device.h:271:1: note: in expansion of macro 'module_init'
     271 | module_init(__platform_driver##_init); \
         | ^~~~~~~~~~~
   drivers/ide/gayle.c:185:1: note: in expansion of macro 'module_platform_driver_probe'
     185 | module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe);
         | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/init.h:200:10: error: pasting "__" and "(" does not give a valid preprocessing token
     200 |  __PASTE(__,      \
         |          ^~
   include/linux/compiler_types.h:53:23: note: in definition of macro '___PASTE'
      53 | #define ___PASTE(a,b) a##b
         |                       ^
   include/linux/init.h:200:2: note: in expansion of macro '__PASTE'
     200 |  __PASTE(__,      \
         |  ^~~~~~~
   include/linux/init.h:232:3: note: in expansion of macro '__initcall_name'
     232 |   __initcall_name(initcall, __iid, id),  \
         |   ^~~~~~~~~~~~~~~
   include/linux/init.h:236:2: note: in expansion of macro '__unique_initcall'
     236 |  __unique_initcall(fn, id, __sec, __initcall_id(fn))
         |  ^~~~~~~~~~~~~~~~~
   include/linux/init.h:238:35: note: in expansion of macro '___define_initcall'
     238 | #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
         |                                   ^~~~~~~~~~~~~~~~~~
   include/linux/init.h:267:30: note: in expansion of macro '__define_initcall'
     267 | #define device_initcall(fn)  __define_initcall(fn, 6)
         |                              ^~~~~~~~~~~~~~~~~
   include/linux/init.h:272:24: note: in expansion of macro 'device_initcall'
     272 | #define __initcall(fn) device_initcall(fn)
         |                        ^~~~~~~~~~~~~~~
   include/linux/module.h:88:24: note: in expansion of macro '__initcall'
      88 | #define module_init(x) __initcall(x);
         |                        ^~~~~~~~~~
   include/linux/platform_device.h:271:1: note: in expansion of macro 'module_init'
     271 | module_init(__platform_driver##_init); \
         | ^~~~~~~~~~~
   drivers/ide/gayle.c:185:1: note: in expansion of macro 'module_platform_driver_probe'
     185 | module_platform_driver_probe(amiga_gayle_ide_driver, amiga_gayle_ide_probe);
         | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
   In file included from include/linux/printk.h:6,
                    from include/linux/kernel.h:15,
                    from include/asm-generic/bug.h:19,
                    from arch/m68k/include/asm/bug.h:32,
                    from include/linux/bug.h:5,
                    from include/linux/mmdebug.h:5,
                    from include/linux/mm.h:9,
                    from drivers/ide/gayle.c:12:
   arch/m68k/include/asm/amigayle.h:57:16: error: expected declaration specifiers or '...' before '*' token
      57 | #define gayle (*(volatile struct GAYLE *)(zTwoBase+GAYLE_ADDRESS))
         |                ^
   include/linux/init.h:226:20: note: in definition of macro '____define_initcall'
     226 |  static initcall_t __name __used    \
         |                    ^~~~~~
   include/linux/compiler_types.h:54:22: note: in expansion of macro '___PASTE'
      54 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^~~~~~~~
   include/linux/init.h:198:2: note: in expansion of macro '__PASTE'
     198 |  __PASTE(__,      \
         |  ^~~~~~~
   include/linux/compiler_types.h:54:22: note: in expansion of macro '___PASTE'
      54 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^~~~~~~~
   include/linux/init.h:199:2: note: in expansion of macro '__PASTE'
     199 |  __PASTE(prefix,      \
         |  ^~~~~~~
   include/linux/compiler_types.h:54:22: note: in expansion of macro '___PASTE'
      54 | #define __PASTE(a,b) ___PASTE(a,b)
         |                      ^~~~~~~~
   include/linux/init.h:200:2: note: in expansion of macro '__PASTE'
     200 |  __PASTE(__,      \
         |  ^~~~~~~
   include/linux/compiler_types.h:54:22: note: in expansion of macro '___PASTE'
      54 | #define __PASTE(a,b) ___PASTE(a,b)

vim +57 arch/m68k/include/asm/amigayle.h

^1da177e4c3f41 include/asm-m68k/amigayle.h Linus Torvalds 2005-04-16  53  
^1da177e4c3f41 include/asm-m68k/amigayle.h Linus Torvalds 2005-04-16  54  #define GAYLE_RESET	(0xa40000)	/* write 0x00 to start reset,
^1da177e4c3f41 include/asm-m68k/amigayle.h Linus Torvalds 2005-04-16  55                                             read 1 byte to stop reset */
^1da177e4c3f41 include/asm-m68k/amigayle.h Linus Torvalds 2005-04-16  56  
^1da177e4c3f41 include/asm-m68k/amigayle.h Linus Torvalds 2005-04-16 @57  #define gayle (*(volatile struct GAYLE *)(zTwoBase+GAYLE_ADDRESS))
^1da177e4c3f41 include/asm-m68k/amigayle.h Linus Torvalds 2005-04-16  58  #define gayle_reset (*(volatile u_char *)(zTwoBase+GAYLE_RESET))
^1da177e4c3f41 include/asm-m68k/amigayle.h Linus Torvalds 2005-04-16  59  

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org

Patch
diff mbox series

diff --git a/include/linux/init.h b/include/linux/init.h
index 212fc9e2f691..af638cd6dd52 100644
--- a/include/linux/init.h
+++ b/include/linux/init.h
@@ -184,19 +184,57 @@  extern bool initcall_debug;
  * as KEEP() in the linker script.
  */
 
+/* Format: <modname>__<counter>_<line>_<fn> */
+#define __initcall_id(fn)					\
+	__PASTE(__KBUILD_MODNAME,				\
+	__PASTE(__,						\
+	__PASTE(__COUNTER__,					\
+	__PASTE(_,						\
+	__PASTE(__LINE__,					\
+	__PASTE(_, fn))))))
+
+/* Format: __<prefix>__<iid><id> */
+#define __initcall_name(prefix, __iid, id)			\
+	__PASTE(__,						\
+	__PASTE(prefix,						\
+	__PASTE(__,						\
+	__PASTE(__iid, id))))
+
+#ifdef CONFIG_LTO_CLANG
+/*
+ * With LTO, the compiler doesn't necessarily obey link order for
+ * initcalls. In order to preserve the correct order, we add each
+ * variable into its own section and generate a linker script (in
+ * scripts/link-vmlinux.sh) to specify the order of the sections.
+ */
+#define __initcall_section(__sec, __iid)			\
+	#__sec ".init.." #__iid
+#else
+#define __initcall_section(__sec, __iid)			\
+	#__sec ".init"
+#endif
+
 #ifdef CONFIG_HAVE_ARCH_PREL32_RELOCATIONS
-#define ___define_initcall(fn, id, __sec)			\
+#define ____define_initcall(fn, __name, __sec)			\
 	__ADDRESSABLE(fn)					\
-	asm(".section	\"" #__sec ".init\", \"a\"	\n"	\
-	"__initcall_" #fn #id ":			\n"	\
+	asm(".section	\"" __sec "\", \"a\"		\n"	\
+	    __stringify(__name) ":			\n"	\
 	    ".long	" #fn " - .			\n"	\
 	    ".previous					\n");
 #else
-#define ___define_initcall(fn, id, __sec) \
-	static initcall_t __initcall_##fn##id __used \
-		__attribute__((__section__(#__sec ".init"))) = fn;
+#define ____define_initcall(fn, __name, __sec)			\
+	static initcall_t __name __used 			\
+		__attribute__((__section__(__sec))) = fn;
 #endif
 
+#define __unique_initcall(fn, id, __sec, __iid)			\
+	____define_initcall(fn,					\
+		__initcall_name(initcall, __iid, id),		\
+		__initcall_section(__sec, __iid))
+
+#define ___define_initcall(fn, id, __sec)			\
+	__unique_initcall(fn, id, __sec, __initcall_id(fn))
+
 #define __define_initcall(fn, id) ___define_initcall(fn, id, .initcall##id)
 
 /*
@@ -236,7 +274,7 @@  extern bool initcall_debug;
 #define __exitcall(fn)						\
 	static exitcall_t __exitcall_##fn __exit_call = fn
 
-#define console_initcall(fn)	___define_initcall(fn,, .con_initcall)
+#define console_initcall(fn)	___define_initcall(fn, con, .con_initcall)
 
 struct obs_kernel_param {
 	const char *str;
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 99ac59c59826..17447354b543 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -106,9 +106,11 @@  target-stem = $(basename $(patsubst $(obj)/%,%,$@))
 # These flags are needed for modversions and compiling, so we define them here
 # $(modname_flags) defines KBUILD_MODNAME as the name of the module it will
 # end up in (or would, if it gets compiled in)
-name-fix = $(call stringify,$(subst $(comma),_,$(subst -,_,$1)))
+name-fix-token = $(subst $(comma),_,$(subst -,_,$1))
+name-fix = $(call stringify,$(call name-fix-token,$1))
 basename_flags = -DKBUILD_BASENAME=$(call name-fix,$(basetarget))
-modname_flags  = -DKBUILD_MODNAME=$(call name-fix,$(modname))
+modname_flags  = -DKBUILD_MODNAME=$(call name-fix,$(modname)) \
+		 -D__KBUILD_MODNAME=$(call name-fix-token,$(modname))
 modfile_flags  = -DKBUILD_MODFILE=$(call stringify,$(modfile))
 
 orig_c_flags   = $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) \
diff --git a/scripts/generate_initcall_order.pl b/scripts/generate_initcall_order.pl
new file mode 100755
index 000000000000..fe83aec2b51e
--- /dev/null
+++ b/scripts/generate_initcall_order.pl
@@ -0,0 +1,270 @@ 
+#!/usr/bin/env perl
+# SPDX-License-Identifier: GPL-2.0
+#
+# Generates a linker script that specifies the correct initcall order.
+#
+# Copyright (C) 2019 Google LLC
+
+use strict;
+use warnings;
+use IO::Handle;
+use IO::Select;
+use POSIX ":sys_wait_h";
+
+my $nm = $ENV{'NM'} || die "$0: ERROR: NM not set?";
+my $objtree = $ENV{'objtree'} || '.';
+
+## currently active child processes
+my $jobs = {};		# child process pid -> file handle
+## results from child processes
+my $results = {};	# object index -> [ { level, secname }, ... ]
+
+## reads _NPROCESSORS_ONLN to determine the maximum number of processes to
+## start
+sub get_online_processors {
+	open(my $fh, "getconf _NPROCESSORS_ONLN 2>/dev/null |")
+		or die "$0: ERROR: failed to execute getconf: $!";
+	my $procs = <$fh>;
+	close($fh);
+
+	if (!($procs =~ /^\d+$/)) {
+		return 1;
+	}
+
+	return int($procs);
+}
+
+## writes results to the parent process
+## format: <file index> <initcall level> <base initcall section name>
+sub write_results {
+	my ($index, $initcalls) = @_;
+
+	# sort by the counter value to ensure the order of initcalls within
+	# each object file is correct
+	foreach my $counter (sort { $a <=> $b } keys(%{$initcalls})) {
+		my $level = $initcalls->{$counter}->{'level'};
+
+		# section name for the initcall function
+		my $secname = $initcalls->{$counter}->{'module'} . '__' .
+			      $counter . '_' .
+			      $initcalls->{$counter}->{'line'} . '_' .
+			      $initcalls->{$counter}->{'function'};
+
+		print "$index $level $secname\n";
+	}
+}
+
+## reads a result line from a child process and adds it to the $results array
+sub read_results{
+	my ($fh) = @_;
+
+	# each child prints out a full line w/ autoflush and exits after the
+	# last line, so even if buffered I/O blocks here, it shouldn't block
+	# very long
+	my $data = <$fh>;
+
+	if (!defined($data)) {
+		return 0;
+	}
+
+	chomp($data);
+
+	my ($index, $level, $secname) = $data =~
+		/^(\d+)\ ([^\ ]+)\ (.*)$/;
+
+	if (!defined($index) ||
+		!defined($level) ||
+		!defined($secname)) {
+		die "$0: ERROR: child process returned invalid data: $data\n";
+	}
+
+	$index = int($index);
+
+	if (!exists($results->{$index})) {
+		$results->{$index} = [];
+	}
+
+	push (@{$results->{$index}}, {
+		'level'   => $level,
+		'secname' => $secname
+	});
+
+	return 1;
+}
+
+## finds initcalls from an object file or all object files in an archive, and
+## writes results back to the parent process
+sub find_initcalls {
+	my ($index, $file) = @_;
+
+	die "$0: ERROR: file $file doesn't exist?" if (! -f $file);
+
+	open(my $fh, "\"$nm\" --defined-only \"$file\" 2>/dev/null |")
+		or die "$0: ERROR: failed to execute \"$nm\": $!";
+
+	my $initcalls = {};
+
+	while (<$fh>) {
+		chomp;
+
+		# check for the start of a new object file (if processing an
+		# archive)
+		my ($path)= $_ =~ /^(.+)\:$/;
+
+		if (defined($path)) {
+			write_results($index, $initcalls);
+			$initcalls = {};
+			next;
+		}
+
+		# look for an initcall
+		my ($module, $counter, $line, $symbol) = $_ =~
+			/[a-z]\s+__initcall__(\S*)__(\d+)_(\d+)_(.*)$/;
+
+		if (!defined($module)) {
+			$module = ''
+		}
+
+		if (!defined($counter) ||
+			!defined($line) ||
+			!defined($symbol)) {
+			next;
+		}
+
+		# parse initcall level
+		my ($function, $level) = $symbol =~
+			/^(.*)((early|rootfs|con|[0-9])s?)$/;
+
+		die "$0: ERROR: invalid initcall name $symbol in $file($path)"
+			if (!defined($function) || !defined($level));
+
+		$initcalls->{$counter} = {
+			'module'   => $module,
+			'line'     => $line,
+			'function' => $function,
+			'level'    => $level,
+		};
+	}
+
+	close($fh);
+	write_results($index, $initcalls);
+}
+
+## waits for any child process to complete, reads the results, and adds them to
+## the $results array for later processing
+sub wait_for_results {
+	my ($select) = @_;
+
+	my $pid = 0;
+	do {
+		# unblock children that may have a full write buffer
+		foreach my $fh ($select->can_read(0)) {
+			read_results($fh);
+		}
+
+		# check for children that have exited, read the remaining data
+		# from them, and clean up
+		$pid = waitpid(-1, WNOHANG);
+		if ($pid > 0) {
+			if (!exists($jobs->{$pid})) {
+				next;
+			}
+
+			my $fh = $jobs->{$pid};
+			$select->remove($fh);
+
+			while (read_results($fh)) {
+				# until eof
+			}
+
+			close($fh);
+			delete($jobs->{$pid});
+		}
+	} while ($pid > 0);
+}
+
+## forks a child to process each file passed in the command line and collects
+## the results
+sub process_files {
+	my $index = 0;
+	my $njobs = get_online_processors();
+	my $select = IO::Select->new();
+
+	while (my $file = shift(@ARGV)) {
+		# fork a child process and read it's stdout
+		my $pid = open(my $fh, '-|');
+
+		if (!defined($pid)) {
+			die "$0: ERROR: failed to fork: $!";
+		} elsif ($pid) {
+			# save the child process pid and the file handle
+			$select->add($fh);
+			$jobs->{$pid} = $fh;
+		} else {
+			# in the child process
+			STDOUT->autoflush(1);
+			find_initcalls($index, "$objtree/$file");
+			exit;
+		}
+
+		$index++;
+
+		# limit the number of children to $njobs
+		if (scalar(keys(%{$jobs})) >= $njobs) {
+			wait_for_results($select);
+		}
+	}
+
+	# wait for the remaining children to complete
+	while (scalar(keys(%{$jobs})) > 0) {
+		wait_for_results($select);
+	}
+}
+
+sub generate_initcall_lds() {
+	process_files();
+
+	my $sections = {};	# level -> [ secname, ...]
+
+	# sort results to retain link order and split to sections per
+	# initcall level
+	foreach my $index (sort { $a <=> $b } keys(%{$results})) {
+		foreach my $result (@{$results->{$index}}) {
+			my $level = $result->{'level'};
+
+			if (!exists($sections->{$level})) {
+				$sections->{$level} = [];
+			}
+
+			push(@{$sections->{$level}}, $result->{'secname'});
+		}
+	}
+
+	die "$0: ERROR: no initcalls?" if (!keys(%{$sections}));
+
+	# print out a linker script that defines the order of initcalls for
+	# each level
+	print "SECTIONS {\n";
+
+	foreach my $level (sort(keys(%{$sections}))) {
+		my $section;
+
+		if ($level eq 'con') {
+			$section = '.con_initcall.init';
+		} else {
+			$section = ".initcall${level}.init";
+		}
+
+		print "\t${section} : {\n";
+
+		foreach my $secname (@{$sections->{$level}}) {
+			print "\t\t*(${section}..${secname}) ;\n";
+		}
+
+		print "\t}\n";
+	}
+
+	print "}\n";
+}
+
+generate_initcall_lds();
diff --git a/scripts/link-vmlinux.sh b/scripts/link-vmlinux.sh
index c72f5d0238f1..42c73e24e820 100755
--- a/scripts/link-vmlinux.sh
+++ b/scripts/link-vmlinux.sh
@@ -39,6 +39,16 @@  info()
 	fi
 }
 
+# Generate a linker script to ensure correct ordering of initcalls.
+gen_initcalls()
+{
+	info GEN .tmp_initcalls.lds
+
+	${srctree}/scripts/generate_initcall_order.pl \
+		${KBUILD_VMLINUX_OBJS} ${KBUILD_VMLINUX_LIBS} \
+		> .tmp_initcalls.lds
+}
+
 # If CONFIG_LTO_CLANG is selected, collect generated symbol versions into
 # .tmp_symversions.lds
 gen_symversions()
@@ -70,6 +80,9 @@  modpost_link()
 		--end-group"
 
 	if [ -n "${CONFIG_LTO_CLANG}" ]; then
+		gen_initcalls
+		lds="-T .tmp_initcalls.lds"
+
 		if [ -n "${CONFIG_MODVERSIONS}" ]; then
 			gen_symversions
 			lds="${lds} -T .tmp_symversions.lds"
@@ -283,6 +296,7 @@  cleanup()
 {
 	rm -f .btf.*
 	rm -f .tmp_System.map
+	rm -f .tmp_initcalls.lds
 	rm -f .tmp_symversions.lds
 	rm -f .tmp_vmlinux*
 	rm -f System.map