Kbuild updates for v6.12

- Support cross-compiling linux-headers Debian package and kernel-devel
    RPM package
 
  - Add support for the linux-debug Pacman package
 
  - Improve module rebuilding speed by factoring out the common code to
    scripts/module-common.c
 
  - Separate device tree build rules into scripts/Makefile.dtbs
 
  - Add a new script to generate modules.builtin.ranges, which is useful
    for tracing tools to find symbols in built-in modules
 
  - Refactor Kconfig and misc tools
 
  - Update Kbuild and Kconfig documentation
 -----BEGIN PGP SIGNATURE-----
 
 iQJJBAABCgAzFiEEbmPs18K1szRHjPqEPYsBB53g2wYFAmby2+QVHG1hc2FoaXJv
 eUBrZXJuZWwub3JnAAoJED2LAQed4NsGpQ0QALWMgox3OdceNiBT8QieqRFfwKFv
 5jxtsZt+MbTdWNMEfgc4Cq2i5ZAqpYGZh32RwTiZJogBvYEIoO7M4Md9VwoEe/BC
 q8VZ6FhUy7358IX/FCukfB0dYvkziRalBRDrE4iFmMMdhBvZ9nrvMxllqFCMllLj
 DTrBTTiMus3qiiczr4tb5QwaIR6C+yqiEBF++ftLmWvo9dn8YNNUnI65fGjyQM/w
 0wMPwsB3Y2HdnRpLUS6T18gZbjoXsAk4+WX0TpdBfTs3d7AdbzlSMtc0BslEm6Tb
 JjIK6SbJCM3kNC7O0/gsUenOaSBxSbKjjg33gQxn/eNoi0nRt+qnBMMreYiTd95G
 Hq86QcNfKQtWAagKRTppMkYEDqMU2RKH7BmJOsfQyeG9cGpAAu+0HsQv3f/h5QP1
 MlA8o+NP5oQn6RbrhZz1Pqm24+OMxiXaBhmo8XbZ+MXzi/CBR54Eo4ip/FSHzXII
 EGEAQL7t7YU7xu8qMIE6ZQMH7BJsjJNee0vrNiYZa4xHLYyHi6mJl8K6LlHQ3nEx
 WOsPX9MLITtSJwcvIio/0sEnuR7pjcShGfqhbHO5tiOYznsbcSvu3+18HPGCpFRt
 vYFkNIRc298k7++A+Zp2wwdD2TS+SSilrAImmJXMhf0M+Nyg2vnlfAo8t0QSkFlh
 1g9dJuy+8jYRjHXP
 =g4t/
 -----END PGP SIGNATURE-----

Merge tag 'kbuild-v6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild

Pull Kbuild updates from Masahiro Yamada:

 - Support cross-compiling linux-headers Debian package and kernel-devel
   RPM package

 - Add support for the linux-debug Pacman package

 - Improve module rebuilding speed by factoring out the common code to
   scripts/module-common.c

 - Separate device tree build rules into scripts/Makefile.dtbs

 - Add a new script to generate modules.builtin.ranges, which is useful
   for tracing tools to find symbols in built-in modules

 - Refactor Kconfig and misc tools

 - Update Kbuild and Kconfig documentation

* tag 'kbuild-v6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild: (51 commits)
  kbuild: doc: replace "gcc" in external module description
  kbuild: doc: describe the -C option precisely for external module builds
  kbuild: doc: remove the description about shipped files
  kbuild: doc: drop section numbering, use references in modules.rst
  kbuild: doc: throw out the local table of contents in modules.rst
  kbuild: doc: remove outdated description of the limitation on -I usage
  kbuild: doc: remove description about grepping CONFIG options
  kbuild: doc: update the description about Kbuild/Makefile split
  kbuild: remove unnecessary export of RUST_LIB_SRC
  kbuild: remove append operation on cmd_ld_ko_o
  kconfig: cache expression values
  kconfig: use hash table to reuse expressions
  kconfig: refactor expr_eliminate_dups()
  kconfig: add comments to expression transformations
  kconfig: change some expr_*() functions to bool
  scripts: move hash function from scripts/kconfig/ to scripts/include/
  kallsyms: change overflow variable to bool type
  kallsyms: squash output_address()
  kbuild: add install target for modules.builtin.ranges
  scripts: add verifier script for builtin module range data
  ...
This commit is contained in:
Linus Torvalds 2024-09-24 13:02:06 -07:00
commit 68e5c7d4ce
56 changed files with 1867 additions and 909 deletions

3
.gitignore vendored
View File

@ -47,7 +47,6 @@
*.so.dbg
*.su
*.symtypes
*.symversions
*.tab.[ch]
*.tar
*.xz
@ -71,6 +70,7 @@ modules.order
/Module.markers
/modules.builtin
/modules.builtin.modinfo
/modules.builtin.ranges
/modules.nsdeps
#
@ -143,7 +143,6 @@ GTAGS
# id-utils files
ID
*.orig
*~
\#*#

View File

@ -180,6 +180,7 @@ modpost
modules-only.symvers
modules.builtin
modules.builtin.modinfo
modules.builtin.ranges
modules.nsdeps
modules.order
modversions.h*

View File

@ -22,6 +22,11 @@ modules.builtin.modinfo
This file contains modinfo from all modules that are built into the kernel.
Unlike modinfo of a separate module, all fields are prefixed with module name.
modules.builtin.ranges
----------------------
This file contains address offset ranges (per ELF section) for all modules
that are built into the kernel. Together with System.map, it can be used
to associate module names with symbols.
Environment variables
=====================
@ -129,6 +134,11 @@ KBUILD_OUTPUT
-------------
Specify the output directory when building the kernel.
This variable can also be used to point to the kernel output directory when
building external modules against a pre-built kernel in a separate build
directory. Please note that this does NOT specify the output directory for the
external modules themselves.
The output directory can also be specified using "O=...".
Setting "O=..." takes precedence over KBUILD_OUTPUT.

View File

@ -70,7 +70,11 @@ applicable everywhere (see syntax).
Every menu entry can have at most one prompt, which is used to display
to the user. Optionally dependencies only for this prompt can be added
with "if".
with "if". If a prompt is not present, the config option is a non-visible
symbol, meaning its value cannot be directly changed by the user (such as
altering the value in ``.config``) and the option will not appear in any
config menus. Its value can only be set via "default" and "select" (see
below).
- default value: "default" <expr> ["if" <expr>]

View File

@ -1665,6 +1665,5 @@ Credits
TODO
====
- Describe how kbuild supports shipped files with _shipped.
- Generating offset header files.
- Add more variables to chapters 7 or 9?

View File

@ -4,41 +4,12 @@ Building External Modules
This document describes how to build an out-of-tree kernel module.
.. Table of Contents
=== 1 Introduction
=== 2 How to Build External Modules
--- 2.1 Command Syntax
--- 2.2 Options
--- 2.3 Targets
--- 2.4 Building Separate Files
=== 3. Creating a Kbuild File for an External Module
--- 3.1 Shared Makefile
--- 3.2 Separate Kbuild file and Makefile
--- 3.3 Binary Blobs
--- 3.4 Building Multiple Modules
=== 4. Include Files
--- 4.1 Kernel Includes
--- 4.2 Single Subdirectory
--- 4.3 Several Subdirectories
=== 5. Module Installation
--- 5.1 INSTALL_MOD_PATH
--- 5.2 INSTALL_MOD_DIR
=== 6. Module Versioning
--- 6.1 Symbols From the Kernel (vmlinux + modules)
--- 6.2 Symbols and External Modules
--- 6.3 Symbols From Another External Module
=== 7. Tips & Tricks
--- 7.1 Testing for CONFIG_FOO_BAR
1. Introduction
===============
Introduction
============
"kbuild" is the build system used by the Linux kernel. Modules must use
kbuild to stay compatible with changes in the build infrastructure and
to pick up the right flags to "gcc." Functionality for building modules
to pick up the right flags to the compiler. Functionality for building modules
both in-tree and out-of-tree is provided. The method for building
either is similar, and all modules are initially developed and built
out-of-tree.
@ -48,11 +19,11 @@ in building out-of-tree (or "external") modules. The author of an
external module should supply a makefile that hides most of the
complexity, so one only has to type "make" to build the module. This is
easily accomplished, and a complete example will be presented in
section 3.
section `Creating a Kbuild File for an External Module`_.
2. How to Build External Modules
================================
How to Build External Modules
=============================
To build external modules, you must have a prebuilt kernel available
that contains the configuration and header files used in the build.
@ -69,12 +40,12 @@ NOTE: "modules_prepare" will not build Module.symvers even if
CONFIG_MODVERSIONS is set; therefore, a full kernel build needs to be
executed to make module versioning work.
2.1 Command Syntax
==================
Command Syntax
--------------
The command to build an external module is::
$ make -C <path_to_kernel_src> M=$PWD
$ make -C <path_to_kernel_dir> M=$PWD
The kbuild system knows that an external module is being built
due to the "M=<dir>" option given in the command.
@ -88,15 +59,18 @@ executed to make module versioning work.
$ make -C /lib/modules/`uname -r`/build M=$PWD modules_install
2.2 Options
===========
Options
-------
($KDIR refers to the path of the kernel source directory.)
($KDIR refers to the path of the kernel source directory, or the path
of the kernel output directory if the kernel was built in a separate
build directory.)
make -C $KDIR M=$PWD
-C $KDIR
The directory where the kernel source is located.
The directory that contains the kernel and relevant build
artifacts used for building an external module.
"make" will actually change to the specified directory
when executing and will change back when finished.
@ -106,8 +80,8 @@ executed to make module versioning work.
directory where the external module (kbuild file) is
located.
2.3 Targets
===========
Targets
-------
When building an external module, only a subset of the "make"
targets are available.
@ -129,7 +103,8 @@ executed to make module versioning work.
modules_install
Install the external module(s). The default location is
/lib/modules/<kernel_release>/updates/, but a prefix may
be added with INSTALL_MOD_PATH (discussed in section 5).
be added with INSTALL_MOD_PATH (discussed in section
`Module Installation`_).
clean
Remove all generated files in the module directory only.
@ -137,8 +112,8 @@ executed to make module versioning work.
help
List the available targets for external modules.
2.4 Building Separate Files
===========================
Building Separate Files
-----------------------
It is possible to build single files that are part of a module.
This works equally well for the kernel, a module, and even for
@ -152,8 +127,8 @@ executed to make module versioning work.
make -C $KDIR M=$PWD ./
3. Creating a Kbuild File for an External Module
================================================
Creating a Kbuild File for an External Module
=============================================
In the last section we saw the command to build a module for the
running kernel. The module is not actually built, however, because a
@ -180,10 +155,9 @@ module 8123.ko, which is built from the following files::
8123_if.c
8123_if.h
8123_pci.c
8123_bin.o_shipped <= Binary blob
3.1 Shared Makefile
-------------------
Shared Makefile
---------------
An external module always includes a wrapper makefile that
supports building the module using "make" with no arguments.
@ -198,7 +172,7 @@ module 8123.ko, which is built from the following files::
ifneq ($(KERNELRELEASE),)
# kbuild part of makefile
obj-m := 8123.o
8123-y := 8123_if.o 8123_pci.o 8123_bin.o
8123-y := 8123_if.o 8123_pci.o
else
# normal makefile
@ -207,10 +181,6 @@ module 8123.ko, which is built from the following files::
default:
$(MAKE) -C $(KDIR) M=$$PWD
# Module specific targets
genbin:
echo "X" > 8123_bin.o_shipped
endif
The check for KERNELRELEASE is used to separate the two parts
@ -221,19 +191,18 @@ module 8123.ko, which is built from the following files::
line; the second pass is by the kbuild system, which is
initiated by the parameterized "make" in the default target.
3.2 Separate Kbuild File and Makefile
-------------------------------------
Separate Kbuild File and Makefile
---------------------------------
In newer versions of the kernel, kbuild will first look for a
file named "Kbuild," and only if that is not found, will it
then look for a makefile. Utilizing a "Kbuild" file allows us
to split up the makefile from example 1 into two files:
Kbuild will first look for a file named "Kbuild", and if it is not
found, it will then look for "Makefile". Utilizing a "Kbuild" file
allows us to split up the "Makefile" from example 1 into two files:
Example 2::
--> filename: Kbuild
obj-m := 8123.o
8123-y := 8123_if.o 8123_pci.o 8123_bin.o
8123-y := 8123_if.o 8123_pci.o
--> filename: Makefile
KDIR ?= /lib/modules/`uname -r`/build
@ -241,68 +210,13 @@ module 8123.ko, which is built from the following files::
default:
$(MAKE) -C $(KDIR) M=$$PWD
# Module specific targets
genbin:
echo "X" > 8123_bin.o_shipped
The split in example 2 is questionable due to the simplicity of
each file; however, some external modules use makefiles
consisting of several hundred lines, and here it really pays
off to separate the kbuild part from the rest.
The next example shows a backward compatible version.
Example 3::
--> filename: Kbuild
obj-m := 8123.o
8123-y := 8123_if.o 8123_pci.o 8123_bin.o
--> filename: Makefile
ifneq ($(KERNELRELEASE),)
# kbuild part of makefile
include Kbuild
else
# normal makefile
KDIR ?= /lib/modules/`uname -r`/build
default:
$(MAKE) -C $(KDIR) M=$$PWD
# Module specific targets
genbin:
echo "X" > 8123_bin.o_shipped
endif
Here the "Kbuild" file is included from the makefile. This
allows an older version of kbuild, which only knows of
makefiles, to be used when the "make" and kbuild parts are
split into separate files.
3.3 Binary Blobs
----------------
Some external modules need to include an object file as a blob.
kbuild has support for this, but requires the blob file to be
named <filename>_shipped. When the kbuild rules kick in, a copy
of <filename>_shipped is created with _shipped stripped off,
giving us <filename>. This shortened filename can be used in
the assignment to the module.
Throughout this section, 8123_bin.o_shipped has been used to
build the kernel module 8123.ko; it has been included as
8123_bin.o::
8123-y := 8123_if.o 8123_pci.o 8123_bin.o
Although there is no distinction between the ordinary source
files and the binary file, kbuild will pick up different rules
when creating the object file for the module.
3.4 Building Multiple Modules
=============================
Building Multiple Modules
-------------------------
kbuild supports building multiple modules with a single build
file. For example, if you wanted to build two modules, foo.ko
@ -315,8 +229,8 @@ module 8123.ko, which is built from the following files::
It is that simple!
4. Include Files
================
Include Files
=============
Within the kernel, header files are kept in standard locations
according to the following rule:
@ -334,19 +248,19 @@ according to the following rule:
include/scsi; and architecture specific headers are located
under arch/$(SRCARCH)/include/.
4.1 Kernel Includes
-------------------
Kernel Includes
---------------
To include a header file located under include/linux/, simply
use::
#include <linux/module.h>
kbuild will add options to "gcc" so the relevant directories
kbuild will add options to the compiler so the relevant directories
are searched.
4.2 Single Subdirectory
-----------------------
Single Subdirectory
-------------------
External modules tend to place header files in a separate
include/ directory where their source is located, although this
@ -360,15 +274,11 @@ according to the following rule:
--> filename: Kbuild
obj-m := 8123.o
ccflags-y := -Iinclude
8123-y := 8123_if.o 8123_pci.o 8123_bin.o
ccflags-y := -I $(src)/include
8123-y := 8123_if.o 8123_pci.o
Note that in the assignment there is no space between -I and
the path. This is a limitation of kbuild: there must be no
space present.
4.3 Several Subdirectories
--------------------------
Several Subdirectories
----------------------
kbuild can handle files that are spread over several directories.
Consider the following example::
@ -407,8 +317,8 @@ according to the following rule:
file is located.
5. Module Installation
======================
Module Installation
===================
Modules which are included in the kernel are installed in the
directory:
@ -419,8 +329,8 @@ And external modules are installed in:
/lib/modules/$(KERNELRELEASE)/updates/
5.1 INSTALL_MOD_PATH
--------------------
INSTALL_MOD_PATH
----------------
Above are the default directories but as always some level of
customization is possible. A prefix can be added to the
@ -434,8 +344,8 @@ And external modules are installed in:
calling "make." This has effect when installing both in-tree
and out-of-tree modules.
5.2 INSTALL_MOD_DIR
-------------------
INSTALL_MOD_DIR
---------------
External modules are by default installed to a directory under
/lib/modules/$(KERNELRELEASE)/updates/, but you may wish to
@ -448,8 +358,8 @@ And external modules are installed in:
=> Install dir: /lib/modules/$(KERNELRELEASE)/gandalf/
6. Module Versioning
====================
Module Versioning
=================
Module versioning is enabled by the CONFIG_MODVERSIONS tag, and is used
as a simple ABI consistency check. A CRC value of the full prototype
@ -461,8 +371,8 @@ module.
Module.symvers contains a list of all exported symbols from a kernel
build.
6.1 Symbols From the Kernel (vmlinux + modules)
-----------------------------------------------
Symbols From the Kernel (vmlinux + modules)
-------------------------------------------
During a kernel build, a file named Module.symvers will be
generated. Module.symvers contains all exported symbols from
@ -486,8 +396,8 @@ build.
1) It lists all exported symbols from vmlinux and all modules.
2) It lists the CRC if CONFIG_MODVERSIONS is enabled.
6.2 Symbols and External Modules
--------------------------------
Symbols and External Modules
----------------------------
When building an external module, the build system needs access
to the symbols from the kernel to check if all external symbols
@ -496,8 +406,8 @@ build.
tree. During the MODPOST step, a new Module.symvers file will be
written containing all exported symbols from that external module.
6.3 Symbols From Another External Module
----------------------------------------
Symbols From Another External Module
------------------------------------
Sometimes, an external module uses exported symbols from
another external module. Kbuild needs to have full knowledge of
@ -537,11 +447,11 @@ build.
initialization of its symbol tables.
7. Tips & Tricks
================
Tips & Tricks
=============
7.1 Testing for CONFIG_FOO_BAR
------------------------------
Testing for CONFIG_FOO_BAR
--------------------------
Modules often need to check for certain `CONFIG_` options to
decide if a specific feature is included in the module. In
@ -553,9 +463,3 @@ build.
ext2-y := balloc.o bitmap.o dir.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
External modules have traditionally used "grep" to check for
specific `CONFIG_` settings directly in .config. This usage is
broken. As introduced before, external modules should use
kbuild for building and can therefore use the same methods as
in-tree modules when testing for `CONFIG_` definitions.

View File

@ -64,6 +64,7 @@ GNU tar 1.28 tar --version
gtags (optional) 6.6.5 gtags --version
mkimage (optional) 2017.01 mkimage --version
Python (optional) 3.5.x python3 --version
GNU AWK (optional) 5.1.0 gawk --version
====================== =============== ========================================
.. [#f1] Sphinx is needed only to build the Kernel documentation
@ -192,6 +193,12 @@ platforms. The tool is available via the ``u-boot-tools`` package or can be
built from the U-Boot source code. See the instructions at
https://docs.u-boot.org/en/latest/build/tools.html#building-tools-for-linux
GNU AWK
-------
GNU AWK is needed if you want kernel builds to generate address range data for
builtin modules (CONFIG_BUILTIN_MODULE_RANGES).
System utilities
****************

View File

@ -579,10 +579,6 @@ else
RUSTC_OR_CLIPPY = $(RUSTC)
endif
ifdef RUST_LIB_SRC
export RUST_LIB_SRC
endif
# Allows the usage of unstable features in stable compilers.
export RUSTC_BOOTSTRAP := 1
@ -1483,6 +1479,7 @@ endif # CONFIG_MODULES
# Directories & files removed with 'make clean'
CLEAN_FILES += vmlinux.symvers modules-only.symvers \
modules.builtin modules.builtin.modinfo modules.nsdeps \
modules.builtin.ranges vmlinux.o.map \
compile_commands.json rust/test \
rust-project.json .vmlinux.objs .vmlinux.export.c
@ -1947,7 +1944,7 @@ clean: $(clean-dirs)
-o -name '*.c.[012]*.*' \
-o -name '*.ll' \
-o -name '*.gcno' \
-o -name '*.*.symversions' \) -type f -print \
\) -type f -print \
-o -name '.tmp_*' -print \
| xargs rm -rf

View File

@ -554,7 +554,7 @@ config ARC_BUILTIN_DTB_NAME
string "Built in DTB"
help
Set the name of the DTB to embed in the vmlinux binary
Leaving it blank selects the minimal "skeleton" dtb
Leaving it blank selects the "nsim_700" dtb.
endmenu # "ARC Architecture Configuration"

View File

@ -1,6 +1,2 @@
CONFIG_NOHIGHMEM=y
# CONFIG_HIGHMEM4G is not set
# CONFIG_HIGHMEM64G is not set
# CONFIG_UNWINDER_ORC is not set
CONFIG_UNWINDER_GUESS=y
# CONFIG_UNWINDER_FRAME_POINTER is not set

View File

@ -34,7 +34,7 @@
/*
* __dtb_empty_root_begin[] and __dtb_empty_root_end[] magically created by
* cmd_dt_S_dtb in scripts/Makefile.lib
* cmd_wrap_S_dtb in scripts/Makefile.dtbs
*/
extern uint8_t __dtb_empty_root_begin[];
extern uint8_t __dtb_empty_root_end[];

View File

@ -1861,7 +1861,7 @@ static int __init unittest_data_add(void)
struct device_node *unittest_data_node = NULL, *np;
/*
* __dtbo_testcases_begin[] and __dtbo_testcases_end[] are magically
* created by cmd_dt_S_dtbo in scripts/Makefile.lib
* created by cmd_wrap_S_dtbo in scripts/Makefile.dtbs
*/
extern uint8_t __dtbo_testcases_begin[];
extern uint8_t __dtbo_testcases_end[];
@ -3525,7 +3525,7 @@ static void __init of_unittest_lifecycle(void)
/*
* __dtbo_##overlay_name##_begin[] and __dtbo_##overlay_name##_end[] are
* created by cmd_dt_S_dtbo in scripts/Makefile.lib
* created by cmd_wrap_S_dtbo in scripts/Makefile.dtbs
*/
#define OVERLAY_INFO_EXTERN(overlay_name) \

View File

@ -1,10 +1,4 @@
# CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE is not set
CONFIG_CC_OPTIMIZE_FOR_SIZE=y
# CONFIG_KERNEL_GZIP is not set
# CONFIG_KERNEL_BZIP2 is not set
# CONFIG_KERNEL_LZMA is not set
CONFIG_KERNEL_XZ=y
# CONFIG_KERNEL_LZO is not set
# CONFIG_KERNEL_LZ4 is not set
CONFIG_SLUB=y
CONFIG_SLUB_TINY=y

View File

@ -573,6 +573,21 @@ config VMLINUX_MAP
pieces of code get eliminated with
CONFIG_LD_DEAD_CODE_DATA_ELIMINATION.
config BUILTIN_MODULE_RANGES
bool "Generate address range information for builtin modules"
depends on !LTO
depends on VMLINUX_MAP
help
When modules are built into the kernel, there will be no module name
associated with its symbols in /proc/kallsyms. Tracers may want to
identify symbols by module name and symbol name regardless of whether
the module is configured as loadable or not.
This option generates modules.builtin.ranges in the build tree with
offset ranges (per ELF section) for the module(s) they belong to.
It also records an anchor symbol to determine the load address of the
section.
config DEBUG_FORCE_WEAK_PER_CPU
bool "Force weak per-cpu definitions"
depends on DEBUG_KERNEL

View File

@ -41,20 +41,6 @@ include $(srctree)/scripts/Makefile.compiler
include $(kbuild-file)
include $(srctree)/scripts/Makefile.lib
# Do not include hostprogs rules unless needed.
# $(sort ...) is used here to remove duplicated words and excessive spaces.
hostprogs := $(sort $(hostprogs))
ifneq ($(hostprogs),)
include $(srctree)/scripts/Makefile.host
endif
# Do not include userprogs rules unless needed.
# $(sort ...) is used here to remove duplicated words and excessive spaces.
userprogs := $(sort $(userprogs))
ifneq ($(userprogs),)
include $(srctree)/scripts/Makefile.userprogs
endif
ifndef obj
$(warning kbuild: Makefile.build is included improperly)
endif
@ -71,7 +57,6 @@ endif
# subdir-builtin and subdir-modorder may contain duplications. Use $(sort ...)
subdir-builtin := $(sort $(filter %/built-in.a, $(real-obj-y)))
subdir-modorder := $(sort $(filter %/modules.order, $(obj-m)))
subdir-dtbslist := $(sort $(filter %/dtbs-list, $(dtb-y)))
targets-for-builtin := $(extra-y)
@ -363,7 +348,7 @@ $(obj)/%.o: $(obj)/%.S FORCE
targets += $(filter-out $(subdir-builtin), $(real-obj-y))
targets += $(filter-out $(subdir-modorder), $(real-obj-m))
targets += $(real-dtb-y) $(lib-y) $(always-y)
targets += $(lib-y) $(always-y)
# Linker scripts preprocessor (.lds.S -> .lds)
# ---------------------------------------------------------------------------
@ -389,7 +374,6 @@ $(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
# To build objects in subdirs, we need to descend into the directories
$(subdir-builtin): $(obj)/%/built-in.a: $(obj)/% ;
$(subdir-modorder): $(obj)/%/modules.order: $(obj)/% ;
$(subdir-dtbslist): $(obj)/%/dtbs-list: $(obj)/% ;
#
# Rule to compile a set of .o files into one .a file (without symbol table)
@ -405,12 +389,8 @@ quiet_cmd_ar_builtin = AR $@
$(obj)/built-in.a: $(real-obj-y) FORCE
$(call if_changed,ar_builtin)
#
# Rule to create modules.order and dtbs-list
#
# This is a list of build artifacts (module or dtb) from the current Makefile
# and its sub-directories. The timestamp should be updated when any of the
# member files.
# This is a list of build artifacts from the current Makefile and its
# sub-directories. The timestamp should be updated when any of the member files.
cmd_gen_order = { $(foreach m, $(real-prereqs), \
$(if $(filter %/$(notdir $@), $m), cat $m, echo $m);) :; } \
@ -419,9 +399,6 @@ cmd_gen_order = { $(foreach m, $(real-prereqs), \
$(obj)/modules.order: $(obj-m) FORCE
$(call if_changed,gen_order)
$(obj)/dtbs-list: $(dtb-y) FORCE
$(call if_changed,gen_order)
#
# Rule to compile a set of .o files into one .a file (with symbol table)
#
@ -450,15 +427,26 @@ intermediate_targets = $(foreach sfx, $(2), \
$(patsubst %$(strip $(1)),%$(sfx), \
$(filter %$(strip $(1)), $(targets))))
# %.asn1.o <- %.asn1.[ch] <- %.asn1
# %.dtb.o <- %.dtb.S <- %.dtb <- %.dts
# %.dtbo.o <- %.dtbo.S <- %.dtbo <- %.dtso
# %.lex.o <- %.lex.c <- %.l
# %.tab.o <- %.tab.[ch] <- %.y
targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \
$(call intermediate_targets, .dtb.o, .dtb.S .dtb) \
$(call intermediate_targets, .dtbo.o, .dtbo.S .dtbo) \
$(call intermediate_targets, .lex.o, .lex.c) \
$(call intermediate_targets, .tab.o, .tab.c .tab.h)
targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h)
# Include additional build rules when necessary
# ---------------------------------------------------------------------------
# $(sort ...) is used here to remove duplicated words and excessive spaces.
hostprogs := $(sort $(hostprogs))
ifneq ($(hostprogs),)
include $(srctree)/scripts/Makefile.host
endif
# $(sort ...) is used here to remove duplicated words and excessive spaces.
userprogs := $(sort $(userprogs))
ifneq ($(userprogs),)
include $(srctree)/scripts/Makefile.userprogs
endif
ifneq ($(need-dtbslist)$(dtb-y)$(dtb-)$(filter %.dtb %.dtb.o %.dtbo.o,$(targets)),)
include $(srctree)/scripts/Makefile.dtbs
endif
# Build
# ---------------------------------------------------------------------------

142
scripts/Makefile.dtbs Normal file
View File

@ -0,0 +1,142 @@
# SPDX-License-Identifier: GPL-2.0-only
# If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built
dtb-$(CONFIG_OF_ALL_DTBS) += $(dtb-)
# Composite DTB (i.e. DTB constructed by overlay)
multi-dtb-y := $(call multi-search, $(dtb-y), .dtb, -dtbs)
# Primitive DTB compiled from *.dts
real-dtb-y := $(call real-search, $(dtb-y), .dtb, -dtbs)
# Base DTB that overlay is applied onto
base-dtb-y := $(filter %.dtb, $(call real-search, $(multi-dtb-y), .dtb, -dtbs))
dtb-y := $(addprefix $(obj)/, $(dtb-y))
multi-dtb-y := $(addprefix $(obj)/, $(multi-dtb-y))
real-dtb-y := $(addprefix $(obj)/, $(real-dtb-y))
always-y += $(dtb-y)
targets += $(real-dtb-y)
# dtbs-list
# ---------------------------------------------------------------------------
ifdef need-dtbslist
subdir-dtbslist := $(addsuffix /dtbs-list, $(subdir-ym))
dtb-y += $(subdir-dtbslist)
always-y += $(obj)/dtbs-list
endif
$(subdir-dtbslist): $(obj)/%/dtbs-list: $(obj)/% ;
$(obj)/dtbs-list: $(dtb-y) FORCE
$(call if_changed,gen_order)
# Assembly file to wrap dtb(o)
# ---------------------------------------------------------------------------
# Generate an assembly file to wrap the output of the device tree compiler
quiet_cmd_wrap_S_dtb = WRAP $@
cmd_wrap_S_dtb = { \
symbase=__$(patsubst .%,%,$(suffix $<))_$(subst -,_,$(notdir $*)); \
echo '\#include <asm-generic/vmlinux.lds.h>'; \
echo '.section .dtb.init.rodata,"a"'; \
echo '.balign STRUCT_ALIGNMENT'; \
echo ".global $${symbase}_begin"; \
echo "$${symbase}_begin:"; \
echo '.incbin "$<" '; \
echo ".global $${symbase}_end"; \
echo "$${symbase}_end:"; \
echo '.balign STRUCT_ALIGNMENT'; \
} > $@
$(obj)/%.dtb.S: $(obj)/%.dtb FORCE
$(call if_changed,wrap_S_dtb)
$(obj)/%.dtbo.S: $(obj)/%.dtbo FORCE
$(call if_changed,wrap_S_dtb)
# Schema check
# ---------------------------------------------------------------------------
ifneq ($(CHECK_DTBS),)
DT_CHECKER ?= dt-validate
DT_CHECKER_FLAGS ?= $(if $(DT_SCHEMA_FILES),-l $(DT_SCHEMA_FILES),-m)
DT_BINDING_DIR := Documentation/devicetree/bindings
DT_TMP_SCHEMA := $(objtree)/$(DT_BINDING_DIR)/processed-schema.json
dtb-check-enabled = $(if $(filter %.dtb, $@),y)
endif
quiet_dtb_check_tag = $(if $(dtb-check-enabled),[C], )
cmd_dtb_check = $(if $(dtb-check-enabled),; $(DT_CHECKER) $(DT_CHECKER_FLAGS) -u $(srctree)/$(DT_BINDING_DIR) -p $(DT_TMP_SCHEMA) $@ || true)
# Overlay
# ---------------------------------------------------------------------------
# NOTE:
# Do not replace $(filter %.dtb %.dtbo, $^) with $(real-prereqs). When a single
# DTB is turned into a multi-blob DTB, $^ will contain header file dependencies
# recorded in the .*.cmd file.
quiet_cmd_fdtoverlay = OVL $(quiet_dtb_check_tag) $@
cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^) $(cmd_dtb_check)
$(multi-dtb-y): $(DT_TMP_SCHEMA) FORCE
$(call if_changed,fdtoverlay)
$(call multi_depend, $(multi-dtb-y), .dtb, -dtbs)
# DTC
# ---------------------------------------------------------------------------
DTC ?= $(objtree)/scripts/dtc/dtc
DTC_FLAGS += -Wno-unique_unit_address
# Disable noisy checks by default
ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
DTC_FLAGS += -Wno-unit_address_vs_reg \
-Wno-avoid_unnecessary_addr_size \
-Wno-alias_paths \
-Wno-graph_child_address \
-Wno-simple_bus_reg
else
DTC_FLAGS += -Wunique_unit_address_if_enabled
endif
ifneq ($(findstring 2,$(KBUILD_EXTRA_WARN)),)
DTC_FLAGS += -Wnode_name_chars_strict \
-Wproperty_name_chars_strict \
-Wunique_unit_address
endif
DTC_FLAGS += $(DTC_FLAGS_$(target-stem))
# Set -@ if the target is a base DTB that overlay is applied onto
DTC_FLAGS += $(if $(filter $(patsubst $(obj)/%,%,$@), $(base-dtb-y)), -@)
DTC_INCLUDE := $(srctree)/scripts/dtc/include-prefixes
dtc_cpp_flags = -Wp,-MMD,$(depfile).pre.tmp -nostdinc -I $(DTC_INCLUDE) -undef -D__DTS__
dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
quiet_cmd_dtc = DTC $(quiet_dtb_check_tag) $@
cmd_dtc = \
$(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
$(DTC) -o $@ -b 0 $(addprefix -i,$(dir $<) $(DTC_INCLUDE)) \
$(DTC_FLAGS) -d $(depfile).dtc.tmp $(dtc-tmp) ; \
cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) \
$(cmd_dtb_check)
$(obj)/%.dtb: $(obj)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE
$(call if_changed_dep,dtc)
$(obj)/%.dtbo: $(src)/%.dtso $(DTC) FORCE
$(call if_changed_dep,dtc)
# targets
# ---------------------------------------------------------------------------
targets += $(always-y)
# %.dtb.o <- %.dtb.S <- %.dtb <- %.dts
# %.dtbo.o <- %.dtbo.S <- %.dtbo <- %.dtso
targets += $(call intermediate_targets, .dtb.o, .dtb.S .dtb) \
$(call intermediate_targets, .dtbo.o, .dtbo.S .dtbo)

View File

@ -160,3 +160,8 @@ $(host-rust): $(obj)/%: $(src)/%.rs FORCE
targets += $(host-csingle) $(host-cmulti) $(host-cobjs) \
$(host-cxxmulti) $(host-cxxobjs) $(host-rust)
# %.lex.o <- %.lex.c <- %.l
# %.tab.o <- %.tab.[ch] <- %.y
targets += $(call intermediate_targets, .lex.o, .lex.c) \
$(call intermediate_targets, .tab.o, .tab.c .tab.h)

View File

@ -45,11 +45,6 @@ else
obj-y := $(filter-out %/, $(obj-y))
endif
ifdef need-dtbslist
dtb-y += $(addsuffix /dtbs-list, $(subdir-ym))
always-y += dtbs-list
endif
# Expand $(foo-objs) $(foo-y) etc. by replacing their individuals
suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s))))
# List composite targets that are constructed by combining other targets
@ -80,19 +75,6 @@ always-y += $(hostprogs-always-y) $(hostprogs-always-m)
userprogs += $(userprogs-always-y) $(userprogs-always-m)
always-y += $(userprogs-always-y) $(userprogs-always-m)
# DTB
# If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built
dtb-$(CONFIG_OF_ALL_DTBS) += $(dtb-)
# Composite DTB (i.e. DTB constructed by overlay)
multi-dtb-y := $(call multi-search, $(dtb-y), .dtb, -dtbs)
# Primitive DTB compiled from *.dts
real-dtb-y := $(call real-search, $(dtb-y), .dtb, -dtbs)
# Base DTB that overlay is applied onto
base-dtb-y := $(filter %.dtb, $(call real-search, $(multi-dtb-y), .dtb, -dtbs))
always-y += $(dtb-y)
# Add subdir path
ifneq ($(obj),.)
@ -104,9 +86,6 @@ lib-y := $(addprefix $(obj)/,$(lib-y))
real-obj-y := $(addprefix $(obj)/,$(real-obj-y))
real-obj-m := $(addprefix $(obj)/,$(real-obj-m))
multi-obj-m := $(addprefix $(obj)/, $(multi-obj-m))
dtb-y := $(addprefix $(obj)/, $(dtb-y))
multi-dtb-y := $(addprefix $(obj)/, $(multi-dtb-y))
real-dtb-y := $(addprefix $(obj)/, $(real-dtb-y))
subdir-ym := $(addprefix $(obj)/,$(subdir-ym))
endif
@ -238,7 +217,7 @@ modkern_rustflags = \
modkern_aflags = $(if $(part-of-module), \
$(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE), \
$(KBUILD_AFLAGS_KERNEL) $(AFLAGS_KERNEL))
$(KBUILD_AFLAGS_KERNEL) $(AFLAGS_KERNEL) $(modfile_flags))
c_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
-include $(srctree)/include/linux/compiler_types.h \
@ -248,19 +227,13 @@ c_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
rust_flags = $(_rust_flags) $(modkern_rustflags) @$(objtree)/include/generated/rustc_cfg
a_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(_a_flags) $(modkern_aflags)
$(_a_flags) $(modkern_aflags) $(modname_flags)
cpp_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(_cpp_flags)
ld_flags = $(KBUILD_LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F))
DTC_INCLUDE := $(srctree)/scripts/dtc/include-prefixes
dtc_cpp_flags = -Wp,-MMD,$(depfile).pre.tmp -nostdinc \
$(addprefix -I,$(DTC_INCLUDE)) \
-undef -D__DTS__
ifdef CONFIG_OBJTOOL
objtool := $(objtree)/tools/objtool/objtool
@ -350,94 +323,6 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
quiet_cmd_gzip = GZIP $@
cmd_gzip = cat $(real-prereqs) | $(KGZIP) -n -f -9 > $@
# DTC
# ---------------------------------------------------------------------------
DTC ?= $(objtree)/scripts/dtc/dtc
DTC_FLAGS += \
-Wno-unique_unit_address
# Disable noisy checks by default
ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
DTC_FLAGS += -Wno-unit_address_vs_reg \
-Wno-avoid_unnecessary_addr_size \
-Wno-alias_paths \
-Wno-graph_child_address \
-Wno-simple_bus_reg
else
DTC_FLAGS += \
-Wunique_unit_address_if_enabled
endif
ifneq ($(findstring 2,$(KBUILD_EXTRA_WARN)),)
DTC_FLAGS += -Wnode_name_chars_strict \
-Wproperty_name_chars_strict \
-Wunique_unit_address
endif
DTC_FLAGS += $(DTC_FLAGS_$(target-stem))
# Set -@ if the target is a base DTB that overlay is applied onto
DTC_FLAGS += $(if $(filter $(patsubst $(obj)/%,%,$@), $(base-dtb-y)), -@)
# Generate an assembly file to wrap the output of the device tree compiler
quiet_cmd_wrap_S_dtb = WRAP $@
cmd_wrap_S_dtb = { \
symbase=__$(patsubst .%,%,$(suffix $<))_$(subst -,_,$(notdir $*)); \
echo '\#include <asm-generic/vmlinux.lds.h>'; \
echo '.section .dtb.init.rodata,"a"'; \
echo '.balign STRUCT_ALIGNMENT'; \
echo ".global $${symbase}_begin"; \
echo "$${symbase}_begin:"; \
echo '.incbin "$<" '; \
echo ".global $${symbase}_end"; \
echo "$${symbase}_end:"; \
echo '.balign STRUCT_ALIGNMENT'; \
} > $@
$(obj)/%.dtb.S: $(obj)/%.dtb FORCE
$(call if_changed,wrap_S_dtb)
$(obj)/%.dtbo.S: $(obj)/%.dtbo FORCE
$(call if_changed,wrap_S_dtb)
quiet_dtb_check_tag = $(if $(dtb-check-enabled),[C], )
cmd_dtb_check = $(if $(dtb-check-enabled),; $(DT_CHECKER) $(DT_CHECKER_FLAGS) -u $(srctree)/$(DT_BINDING_DIR) -p $(DT_TMP_SCHEMA) $@ || true)
quiet_cmd_dtc = DTC $(quiet_dtb_check_tag) $@
cmd_dtc = $(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
$(DTC) -o $@ -b 0 \
$(addprefix -i,$(dir $<) $(DTC_INCLUDE)) $(DTC_FLAGS) \
-d $(depfile).dtc.tmp $(dtc-tmp) ; \
cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) \
$(cmd_dtb_check)
# NOTE:
# Do not replace $(filter %.dtb %.dtbo, $^) with $(real-prereqs). When a single
# DTB is turned into a multi-blob DTB, $^ will contain header file dependencies
# recorded in the .*.cmd file.
quiet_cmd_fdtoverlay = OVL $(quiet_dtb_check_tag) $@
cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^) $(cmd_dtb_check)
$(multi-dtb-y): FORCE
$(call if_changed,fdtoverlay)
$(call multi_depend, $(multi-dtb-y), .dtb, -dtbs)
ifneq ($(CHECK_DTBS),)
DT_CHECKER ?= dt-validate
DT_CHECKER_FLAGS ?= $(if $(DT_SCHEMA_FILES),-l $(DT_SCHEMA_FILES),-m)
DT_BINDING_DIR := Documentation/devicetree/bindings
DT_TMP_SCHEMA := $(objtree)/$(DT_BINDING_DIR)/processed-schema.json
dtb-check-enabled = $(if $(filter %.dtb, $@),y)
endif
$(obj)/%.dtb: $(obj)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE
$(call if_changed_dep,dtc)
$(obj)/%.dtbo: $(src)/%.dtso $(DTC) FORCE
$(call if_changed_dep,dtc)
dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
# Bzip2
# ---------------------------------------------------------------------------

View File

@ -30,8 +30,11 @@ quiet_cmd_cc_o_c = CC [M] $@
%.mod.o: %.mod.c FORCE
$(call if_changed_dep,cc_o_c)
$(extmod_prefix).module-common.o: $(srctree)/scripts/module-common.c FORCE
$(call if_changed_dep,cc_o_c)
quiet_cmd_ld_ko_o = LD [M] $@
cmd_ld_ko_o += \
cmd_ld_ko_o = \
$(LD) -r $(KBUILD_LDFLAGS) \
$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
-T scripts/module.lds -o $@ $(filter %.o, $^)
@ -54,13 +57,13 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \
printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
# Re-generate module BTFs if either module's .ko or vmlinux changed
%.ko: %.o %.mod.o scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),vmlinux) FORCE
%.ko: %.o %.mod.o $(extmod_prefix).module-common.o scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),vmlinux) FORCE
+$(call if_changed_except,ld_ko_o,vmlinux)
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+$(if $(newer-prereqs),$(call cmd,btf_ko))
endif
targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o)
targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) $(extmod_prefix).module-common.o
# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------

View File

@ -30,10 +30,12 @@ $(MODLIB)/modules.order: modules.order FORCE
quiet_cmd_install_modorder = INSTALL $@
cmd_install_modorder = sed 's:^\(.*\)\.o$$:kernel/\1.ko:' $< > $@
# Install modules.builtin(.modinfo) even when CONFIG_MODULES is disabled.
# Install modules.builtin(.modinfo,.ranges) even when CONFIG_MODULES is disabled.
install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo)
$(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo): $(MODLIB)/%: % FORCE
install-$(CONFIG_BUILTIN_MODULE_RANGES) += $(MODLIB)/modules.builtin.ranges
$(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo modules.builtin.ranges): $(MODLIB)/%: % FORCE
$(call cmd,install)
endif
@ -146,7 +148,7 @@ quiet_cmd_gzip = GZIP $@
quiet_cmd_xz = XZ $@
cmd_xz = $(XZ) --check=crc32 --lzma2=dict=1MiB -f $<
quiet_cmd_zstd = ZSTD $@
cmd_zstd = $(ZSTD) -T0 --rm -f -q $<
cmd_zstd = $(ZSTD) --rm -f -q $<
$(dst)/%.ko.gz: $(dst)/%.ko FORCE
$(call cmd,gzip)

View File

@ -147,8 +147,7 @@ snap-pkg:
PHONY += pacman-pkg
pacman-pkg:
@ln -srf $(srctree)/scripts/package/PKGBUILD $(objtree)/PKGBUILD
+objtree="$(realpath $(objtree))" \
BUILDDIR="$(realpath $(objtree))/pacman" \
+BUILDDIR="$(realpath $(objtree))/pacman" \
CARCH="$(UTS_MACHINE)" \
KBUILD_MAKEFLAGS="$(MAKEFLAGS)" \
KBUILD_REVISION="$(shell $(srctree)/scripts/build-version)" \

View File

@ -33,6 +33,24 @@ targets += vmlinux
vmlinux: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE
+$(call if_changed_dep,link_vmlinux)
# module.builtin.ranges
# ---------------------------------------------------------------------------
ifdef CONFIG_BUILTIN_MODULE_RANGES
__default: modules.builtin.ranges
quiet_cmd_modules_builtin_ranges = GEN $@
cmd_modules_builtin_ranges = gawk -f $(real-prereqs) > $@
targets += modules.builtin.ranges
modules.builtin.ranges: $(srctree)/scripts/generate_builtin_ranges.awk \
modules.builtin vmlinux.map vmlinux.o.map FORCE
$(call if_changed,modules_builtin_ranges)
vmlinux.map: vmlinux
@:
endif
# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------

View File

@ -45,9 +45,12 @@ objtool-args = $(vmlinux-objtool-args-y) --link
# Link of vmlinux.o used for section mismatch analysis
# ---------------------------------------------------------------------------
vmlinux-o-ld-args-$(CONFIG_BUILTIN_MODULE_RANGES) += -Map=$@.map
quiet_cmd_ld_vmlinux.o = LD $@
cmd_ld_vmlinux.o = \
$(LD) ${KBUILD_LDFLAGS} -r -o $@ \
$(vmlinux-o-ld-args-y) \
$(addprefix -T , $(initcalls-lds)) \
--whole-archive vmlinux.a --no-whole-archive \
--start-group $(KBUILD_VMLINUX_LIBS) --end-group \

View File

@ -99,6 +99,8 @@
#include <stdio.h>
#include <ctype.h>
#include <xalloc.h>
static void usage(void)
{
fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
@ -131,12 +133,9 @@ static unsigned int strhash(const char *str, unsigned int sz)
static void add_to_hashtable(const char *name, int len, unsigned int hash,
struct item *hashtab[])
{
struct item *aux = malloc(sizeof(*aux) + len);
struct item *aux;
if (!aux) {
perror("fixdep:malloc");
exit(1);
}
aux = xmalloc(sizeof(*aux) + len);
memcpy(aux->name, name, len);
aux->len = len;
aux->hash = hash;
@ -228,11 +227,7 @@ static void *read_file(const char *filename)
perror(filename);
exit(2);
}
buf = malloc(st.st_size + 1);
if (!buf) {
perror("fixdep: malloc");
exit(2);
}
buf = xmalloc(st.st_size + 1);
if (read(fd, buf, st.st_size) != st.st_size) {
perror("fixdep: read");
exit(2);

View File

@ -0,0 +1,508 @@
#!/usr/bin/gawk -f
# SPDX-License-Identifier: GPL-2.0
# generate_builtin_ranges.awk: Generate address range data for builtin modules
# Written by Kris Van Hees <kris.van.hees@oracle.com>
#
# Usage: generate_builtin_ranges.awk modules.builtin vmlinux.map \
# vmlinux.o.map > modules.builtin.ranges
#
# Return the module name(s) (if any) associated with the given object.
#
# If we have seen this object before, return information from the cache.
# Otherwise, retrieve it from the corresponding .cmd file.
#
function get_module_info(fn, mod, obj, s) {
if (fn in omod)
return omod[fn];
if (match(fn, /\/[^/]+$/) == 0)
return "";
obj = fn;
mod = "";
fn = substr(fn, 1, RSTART) "." substr(fn, RSTART + 1) ".cmd";
if (getline s <fn == 1) {
if (match(s, /DKBUILD_MODFILE=['"]+[^'"]+/) > 0) {
mod = substr(s, RSTART + 16, RLENGTH - 16);
gsub(/['"]/, "", mod);
} else if (match(s, /RUST_MODFILE=[^ ]+/) > 0)
mod = substr(s, RSTART + 13, RLENGTH - 13);
}
close(fn);
# A single module (common case) also reflects objects that are not part
# of a module. Some of those objects have names that are also a module
# name (e.g. core). We check the associated module file name, and if
# they do not match, the object is not part of a module.
if (mod !~ / /) {
if (!(mod in mods))
mod = "";
}
gsub(/([^/ ]*\/)+/, "", mod);
gsub(/-/, "_", mod);
# At this point, mod is a single (valid) module name, or a list of
# module names (that do not need validation).
omod[obj] = mod;
return mod;
}
# Update the ranges entry for the given module 'mod' in section 'osect'.
#
# We use a modified absolute start address (soff + base) as index because we
# may need to insert an anchor record later that must be at the start of the
# section data, and the first module may very well start at the same address.
# So, we use (addr << 1) + 1 to allow a possible anchor record to be placed at
# (addr << 1). This is safe because the index is only used to sort the entries
# before writing them out.
#
function update_entry(osect, mod, soff, eoff, sect, idx) {
sect = sect_in[osect];
idx = sprintf("%016x", (soff + sect_base[osect]) * 2 + 1);
entries[idx] = sprintf("%s %08x-%08x %s", sect, soff, eoff, mod);
count[sect]++;
}
# (1) Build a lookup map of built-in module names.
#
# The first file argument is used as input (modules.builtin).
#
# Lines will be like:
# kernel/crypto/lzo-rle.ko
# and we record the object name "crypto/lzo-rle".
#
ARGIND == 1 {
sub(/kernel\//, ""); # strip off "kernel/" prefix
sub(/\.ko$/, ""); # strip off .ko suffix
mods[$1] = 1;
next;
}
# (2) Collect address information for each section.
#
# The second file argument is used as input (vmlinux.map).
#
# We collect the base address of the section in order to convert all addresses
# in the section into offset values.
#
# We collect the address of the anchor (or first symbol in the section if there
# is no explicit anchor) to allow users of the range data to calculate address
# ranges based on the actual load address of the section in the running kernel.
#
# We collect the start address of any sub-section (section included in the top
# level section being processed). This is needed when the final linking was
# done using vmlinux.a because then the list of objects contained in each
# section is to be obtained from vmlinux.o.map. The offset of the sub-section
# is recorded here, to be used as an addend when processing vmlinux.o.map
# later.
#
# Both GNU ld and LLVM lld linker map format are supported by converting LLVM
# lld linker map records into equivalent GNU ld linker map records.
#
# The first record of the vmlinux.map file provides enough information to know
# which format we are dealing with.
#
ARGIND == 2 && FNR == 1 && NF == 7 && $1 == "VMA" && $7 == "Symbol" {
map_is_lld = 1;
if (dbg)
printf "NOTE: %s uses LLVM lld linker map format\n", FILENAME >"/dev/stderr";
next;
}
# (LLD) Convert a section record fronm lld format to ld format.
#
# lld: ffffffff82c00000 2c00000 2493c0 8192 .data
# ->
# ld: .data 0xffffffff82c00000 0x2493c0 load address 0x0000000002c00000
#
ARGIND == 2 && map_is_lld && NF == 5 && /[0-9] [^ ]+$/ {
$0 = $5 " 0x"$1 " 0x"$3 " load address 0x"$2;
}
# (LLD) Convert an anchor record from lld format to ld format.
#
# lld: ffffffff81000000 1000000 0 1 _text = .
# ->
# ld: 0xffffffff81000000 _text = .
#
ARGIND == 2 && map_is_lld && !anchor && NF == 7 && raw_addr == "0x"$1 && $6 == "=" && $7 == "." {
$0 = " 0x"$1 " " $5 " = .";
}
# (LLD) Convert an object record from lld format to ld format.
#
# lld: 11480 11480 1f07 16 vmlinux.a(arch/x86/events/amd/uncore.o):(.text)
# ->
# ld: .text 0x0000000000011480 0x1f07 arch/x86/events/amd/uncore.o
#
ARGIND == 2 && map_is_lld && NF == 5 && $5 ~ /:\(/ {
gsub(/\)/, "");
sub(/ vmlinux\.a\(/, " ");
sub(/:\(/, " ");
$0 = " "$6 " 0x"$1 " 0x"$3 " " $5;
}
# (LLD) Convert a symbol record from lld format to ld format.
#
# We only care about these while processing a section for which no anchor has
# been determined yet.
#
# lld: ffffffff82a859a4 2a859a4 0 1 btf_ksym_iter_id
# ->
# ld: 0xffffffff82a859a4 btf_ksym_iter_id
#
ARGIND == 2 && map_is_lld && sect && !anchor && NF == 5 && $5 ~ /^[_A-Za-z][_A-Za-z0-9]*$/ {
$0 = " 0x"$1 " " $5;
}
# (LLD) We do not need any other ldd linker map records.
#
ARGIND == 2 && map_is_lld && /^[0-9a-f]{16} / {
next;
}
# (LD) Section records with just the section name at the start of the line
# need to have the next line pulled in to determine whether it is a
# loadable section. If it is, the next line will contains a hex value
# as first and second items.
#
ARGIND == 2 && !map_is_lld && NF == 1 && /^[^ ]/ {
s = $0;
getline;
if ($1 !~ /^0x/ || $2 !~ /^0x/)
next;
$0 = s " " $0;
}
# (LD) Object records with just the section name denote records with a long
# section name for which the remainder of the record can be found on the
# next line.
#
# (This is also needed for vmlinux.o.map, when used.)
#
ARGIND >= 2 && !map_is_lld && NF == 1 && /^ [^ \*]/ {
s = $0;
getline;
$0 = s " " $0;
}
# Beginning a new section - done with the previous one (if any).
#
ARGIND == 2 && /^[^ ]/ {
sect = 0;
}
# Process a loadable section (we only care about .-sections).
#
# Record the section name and its base address.
# We also record the raw (non-stripped) address of the section because it can
# be used to identify an anchor record.
#
# Note:
# Since some AWK implementations cannot handle large integers, we strip off the
# first 4 hex digits from the address. This is safe because the kernel space
# is not large enough for addresses to extend into those digits. The portion
# to strip off is stored in addr_prefix as a regexp, so further clauses can
# perform a simple substitution to do the address stripping.
#
ARGIND == 2 && /^\./ {
# Explicitly ignore a few sections that are not relevant here.
if ($1 ~ /^\.orc_/ || $1 ~ /_sites$/ || $1 ~ /\.percpu/)
next;
# Sections with a 0-address can be ignored as well.
if ($2 ~ /^0x0+$/)
next;
raw_addr = $2;
addr_prefix = "^" substr($2, 1, 6);
base = $2;
sub(addr_prefix, "0x", base);
base = strtonum(base);
sect = $1;
anchor = 0;
sect_base[sect] = base;
sect_size[sect] = strtonum($3);
if (dbg)
printf "[%s] BASE %016x\n", sect, base >"/dev/stderr";
next;
}
# If we are not in a section we care about, we ignore the record.
#
ARGIND == 2 && !sect {
next;
}
# Record the first anchor symbol for the current section.
#
# An anchor record for the section bears the same raw address as the section
# record.
#
ARGIND == 2 && !anchor && NF == 4 && raw_addr == $1 && $3 == "=" && $4 == "." {
anchor = sprintf("%s %08x-%08x = %s", sect, 0, 0, $2);
sect_anchor[sect] = anchor;
if (dbg)
printf "[%s] ANCHOR %016x = %s (.)\n", sect, 0, $2 >"/dev/stderr";
next;
}
# If no anchor record was found for the current section, use the first symbol
# in the section as anchor.
#
ARGIND == 2 && !anchor && NF == 2 && $1 ~ /^0x/ && $2 !~ /^0x/ {
addr = $1;
sub(addr_prefix, "0x", addr);
addr = strtonum(addr) - base;
anchor = sprintf("%s %08x-%08x = %s", sect, addr, addr, $2);
sect_anchor[sect] = anchor;
if (dbg)
printf "[%s] ANCHOR %016x = %s\n", sect, addr, $2 >"/dev/stderr";
next;
}
# The first occurrence of a section name in an object record establishes the
# addend (often 0) for that section. This information is needed to handle
# sections that get combined in the final linking of vmlinux (e.g. .head.text
# getting included at the start of .text).
#
# If the section does not have a base yet, use the base of the encapsulating
# section.
#
ARGIND == 2 && sect && NF == 4 && /^ [^ \*]/ && !($1 in sect_addend) {
if (!($1 in sect_base)) {
sect_base[$1] = base;
if (dbg)
printf "[%s] BASE %016x\n", $1, base >"/dev/stderr";
}
addr = $2;
sub(addr_prefix, "0x", addr);
addr = strtonum(addr);
sect_addend[$1] = addr - sect_base[$1];
sect_in[$1] = sect;
if (dbg)
printf "[%s] ADDEND %016x - %016x = %016x\n", $1, addr, base, sect_addend[$1] >"/dev/stderr";
# If the object is vmlinux.o then we will need vmlinux.o.map to get the
# actual offsets of objects.
if ($4 == "vmlinux.o")
need_o_map = 1;
}
# (3) Collect offset ranges (relative to the section base address) for built-in
# modules.
#
# If the final link was done using the actual objects, vmlinux.map contains all
# the information we need (see section (3a)).
# If linking was done using vmlinux.a as intermediary, we will need to process
# vmlinux.o.map (see section (3b)).
# (3a) Determine offset range info using vmlinux.map.
#
# Since we are already processing vmlinux.map, the top level section that is
# being processed is already known. If we do not have a base address for it,
# we do not need to process records for it.
#
# Given the object name, we determine the module(s) (if any) that the current
# object is associated with.
#
# If we were already processing objects for a (list of) module(s):
# - If the current object belongs to the same module(s), update the range data
# to include the current object.
# - Otherwise, ensure that the end offset of the range is valid.
#
# If the current object does not belong to a built-in module, ignore it.
#
# If it does, we add a new built-in module offset range record.
#
ARGIND == 2 && !need_o_map && /^ [^ ]/ && NF == 4 && $3 != "0x0" {
if (!(sect in sect_base))
next;
# Turn the address into an offset from the section base.
soff = $2;
sub(addr_prefix, "0x", soff);
soff = strtonum(soff) - sect_base[sect];
eoff = soff + strtonum($3);
# Determine which (if any) built-in modules the object belongs to.
mod = get_module_info($4);
# If we are processing a built-in module:
# - If the current object is within the same module, we update its
# entry by extending the range and move on
# - Otherwise:
# + If we are still processing within the same main section, we
# validate the end offset against the start offset of the
# current object (e.g. .rodata.str1.[18] objects are often
# listed with an incorrect size in the linker map)
# + Otherwise, we validate the end offset against the section
# size
if (mod_name) {
if (mod == mod_name) {
mod_eoff = eoff;
update_entry(mod_sect, mod_name, mod_soff, eoff);
next;
} else if (sect == sect_in[mod_sect]) {
if (mod_eoff > soff)
update_entry(mod_sect, mod_name, mod_soff, soff);
} else {
v = sect_size[sect_in[mod_sect]];
if (mod_eoff > v)
update_entry(mod_sect, mod_name, mod_soff, v);
}
}
mod_name = mod;
# If we encountered an object that is not part of a built-in module, we
# do not need to record any data.
if (!mod)
next;
# At this point, we encountered the start of a new built-in module.
mod_name = mod;
mod_soff = soff;
mod_eoff = eoff;
mod_sect = $1;
update_entry($1, mod, soff, mod_eoff);
next;
}
# If we do not need to parse the vmlinux.o.map file, we are done.
#
ARGIND == 3 && !need_o_map {
if (dbg)
printf "Note: %s is not needed.\n", FILENAME >"/dev/stderr";
exit;
}
# (3) Collect offset ranges (relative to the section base address) for built-in
# modules.
#
# (LLD) Convert an object record from lld format to ld format.
#
ARGIND == 3 && map_is_lld && NF == 5 && $5 ~ /:\(/ {
gsub(/\)/, "");
sub(/:\(/, " ");
sect = $6;
if (!(sect in sect_addend))
next;
sub(/ vmlinux\.a\(/, " ");
$0 = " "sect " 0x"$1 " 0x"$3 " " $5;
}
# (3b) Determine offset range info using vmlinux.o.map.
#
# If we do not know an addend for the object's section, we are interested in
# anything within that section.
#
# Determine the top-level section that the object's section was included in
# during the final link. This is the section name offset range data will be
# associated with for this object.
#
# The remainder of the processing of the current object record follows the
# procedure outlined in (3a).
#
ARGIND == 3 && /^ [^ ]/ && NF == 4 && $3 != "0x0" {
osect = $1;
if (!(osect in sect_addend))
next;
# We need to work with the main section.
sect = sect_in[osect];
# Turn the address into an offset from the section base.
soff = $2;
sub(addr_prefix, "0x", soff);
soff = strtonum(soff) + sect_addend[osect];
eoff = soff + strtonum($3);
# Determine which (if any) built-in modules the object belongs to.
mod = get_module_info($4);
# If we are processing a built-in module:
# - If the current object is within the same module, we update its
# entry by extending the range and move on
# - Otherwise:
# + If we are still processing within the same main section, we
# validate the end offset against the start offset of the
# current object (e.g. .rodata.str1.[18] objects are often
# listed with an incorrect size in the linker map)
# + Otherwise, we validate the end offset against the section
# size
if (mod_name) {
if (mod == mod_name) {
mod_eoff = eoff;
update_entry(mod_sect, mod_name, mod_soff, eoff);
next;
} else if (sect == sect_in[mod_sect]) {
if (mod_eoff > soff)
update_entry(mod_sect, mod_name, mod_soff, soff);
} else {
v = sect_size[sect_in[mod_sect]];
if (mod_eoff > v)
update_entry(mod_sect, mod_name, mod_soff, v);
}
}
mod_name = mod;
# If we encountered an object that is not part of a built-in module, we
# do not need to record any data.
if (!mod)
next;
# At this point, we encountered the start of a new built-in module.
mod_name = mod;
mod_soff = soff;
mod_eoff = eoff;
mod_sect = osect;
update_entry(osect, mod, soff, mod_eoff);
next;
}
# (4) Generate the output.
#
# Anchor records are added for each section that contains offset range data
# records. They are added at an adjusted section base address (base << 1) to
# ensure they come first in the second records (see update_entry() above for
# more information).
#
# All entries are sorted by (adjusted) address to ensure that the output can be
# parsed in strict ascending address order.
#
END {
for (sect in count) {
if (sect in sect_anchor) {
idx = sprintf("%016x", sect_base[sect] * 2);
entries[idx] = sect_anchor[sect];
}
}
n = asorti(entries, indices);
for (i = 1; i <= n; i++)
print entries[indices[i]];
}

28
scripts/include/hash.h Normal file
View File

@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef HASH_H
#define HASH_H
static inline unsigned int hash_str(const char *s)
{
/* fnv32 hash */
unsigned int hash = 2166136261U;
for (; *s; s++)
hash = (hash ^ *s) * 0x01000193;
return hash;
}
/* simplified version of functions from include/linux/hash.h */
#define GOLDEN_RATIO_32 0x61C88647
static inline unsigned int hash_32(unsigned int val)
{
return 0x61C88647 * val;
}
static inline unsigned int hash_ptr(const void *ptr)
{
return hash_32((unsigned int)(unsigned long)ptr);
}
#endif /* HASH_H */

View File

@ -15,6 +15,23 @@
#define hash_head(table, key) (&(table)[(key) % HASH_SIZE(table)])
static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
{
unsigned int i;
for (i = 0; i < sz; i++)
INIT_HLIST_HEAD(&ht[i]);
}
/**
* hash_init - initialize a hash table
* @table: hashtable to be initialized
*
* This has to be a macro since HASH_SIZE() will not work on pointers since
* it calculates the size during preprocessing.
*/
#define hash_init(table) __hash_init(table, HASH_SIZE(table))
/**
* hash_add - add an object to a hashtable
* @table: hashtable to add to
@ -24,6 +41,15 @@
#define hash_add(table, node, key) \
hlist_add_head(node, hash_head(table, key))
/**
* hash_del - remove an object from a hashtable
* @node: &struct hlist_node of the object to remove
*/
static inline void hash_del(struct hlist_node *node)
{
hlist_del_init(node);
}
/**
* hash_for_each - iterate over a hashtable
* @table: hashtable to iterate
@ -34,6 +60,18 @@
for (int _bkt = 0; _bkt < HASH_SIZE(table); _bkt++) \
hlist_for_each_entry(obj, &table[_bkt], member)
/**
* hash_for_each_safe - iterate over a hashtable safe against removal of
* hash entry
* @table: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @tmp: a &struct hlist_node used for temporary storage
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each_safe(table, obj, tmp, member) \
for (int _bkt = 0; _bkt < HASH_SIZE(table); _bkt++) \
hlist_for_each_entry_safe(obj, tmp, &table[_bkt], member)
/**
* hash_for_each_possible - iterate over all possible objects hashing to the
* same bucket
@ -45,4 +83,16 @@
#define hash_for_each_possible(table, obj, member, key) \
hlist_for_each_entry(obj, hash_head(table, key), member)
/**
* hash_for_each_possible_safe - iterate over all possible objects hashing to the
* same bucket safe against removals
* @table: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @tmp: a &struct hlist_node used for temporary storage
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible_safe(table, obj, tmp, member, key) \
hlist_for_each_entry_safe(obj, tmp, hash_head(table, key), member)
#endif /* HASHTABLE_H */

View File

@ -268,6 +268,63 @@ static inline int list_empty(const struct list_head *head)
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
/**
* hlist_unhashed - Has node been removed from list and reinitialized?
* @h: Node to be checked
*
* Not that not all removal functions will leave a node in unhashed
* state. For example, hlist_nulls_del_init_rcu() does leave the
* node in unhashed state, but hlist_nulls_del() does not.
*/
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}
/**
* hlist_del - Delete the specified hlist_node from its list
* @n: Node to delete.
*
* Note that this function leaves the node in hashed state. Use
* hlist_del_init() or similar instead to unhash @n.
*/
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
/**
* hlist_del_init - Delete the specified hlist_node from its list and initialize
* @n: Node to delete.
*
* Note that this function leaves the node in unhashed state.
*/
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
/**
* hlist_add_head - add a new entry at the beginning of the hlist
@ -306,4 +363,16 @@ static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: a &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, typeof(*pos), member))
#endif /* LIST_H */

53
scripts/include/xalloc.h Normal file
View File

@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef XALLOC_H
#define XALLOC_H
#include <stdlib.h>
#include <string.h>
static inline void *xmalloc(size_t size)
{
void *p = malloc(size);
if (!p)
exit(1);
return p;
}
static inline void *xcalloc(size_t nmemb, size_t size)
{
void *p = calloc(nmemb, size);
if (!p)
exit(1);
return p;
}
static inline void *xrealloc(void *p, size_t size)
{
p = realloc(p, size);
if (!p)
exit(1);
return p;
}
static inline char *xstrdup(const char *s)
{
char *p = strdup(s);
if (!p)
exit(1);
return p;
}
static inline char *xstrndup(const char *s, size_t n)
{
char *p = strndup(s, n);
if (!p)
exit(1);
return p;
}
#endif /* XALLOC_H */

View File

@ -27,6 +27,8 @@
#include <ctype.h>
#include <limits.h>
#include <xalloc.h>
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#define KSYM_NAME_LEN 512
@ -168,12 +170,7 @@ static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len)
* compressed together */
len++;
sym = malloc(sizeof(*sym) + len + 1);
if (!sym) {
fprintf(stderr, "kallsyms failure: "
"unable to allocate required amount of memory\n");
exit(EXIT_FAILURE);
}
sym = xmalloc(sizeof(*sym) + len + 1);
sym->addr = addr;
sym->len = len;
sym->sym[0] = type;
@ -278,12 +275,7 @@ static void read_map(const char *in)
if (table_cnt >= table_size) {
table_size += 10000;
table = realloc(table, sizeof(*table) * table_size);
if (!table) {
fprintf(stderr, "out of memory\n");
fclose(fp);
exit (1);
}
table = xrealloc(table, sizeof(*table) * table_size);
}
table[table_cnt++] = sym;
@ -300,15 +292,6 @@ static void output_label(const char *label)
printf("%s:\n", label);
}
/* Provide proper symbols relocatability by their '_text' relativeness. */
static void output_address(unsigned long long addr)
{
if (_text <= addr)
printf("\tPTR\t_text + %#llx\n", addr - _text);
else
printf("\tPTR\t_text - %#llx\n", _text - addr);
}
/* uncompress a compressed symbol. When this function is called, the best table
* might still be compressed itself, so the function needs to be recursive */
static int expand_symbol(const unsigned char *data, int len, char *result)
@ -391,12 +374,7 @@ static void write_src(void)
/* table of offset markers, that give the offset in the compressed stream
* every 256 symbols */
markers_cnt = (table_cnt + 255) / 256;
markers = malloc(sizeof(*markers) * markers_cnt);
if (!markers) {
fprintf(stderr, "kallsyms failure: "
"unable to allocate required memory\n");
exit(EXIT_FAILURE);
}
markers = xmalloc(sizeof(*markers) * markers_cnt);
output_label("kallsyms_names");
off = 0;
@ -477,17 +455,17 @@ static void write_src(void)
*/
long long offset;
int overflow;
bool overflow;
if (!absolute_percpu) {
offset = table[i]->addr - relative_base;
overflow = (offset < 0 || offset > UINT_MAX);
overflow = offset < 0 || offset > UINT_MAX;
} else if (symbol_absolute(table[i])) {
offset = table[i]->addr;
overflow = (offset < 0 || offset > INT_MAX);
overflow = offset < 0 || offset > INT_MAX;
} else {
offset = relative_base - table[i]->addr - 1;
overflow = (offset < INT_MIN || offset >= 0);
overflow = offset < INT_MIN || offset >= 0;
}
if (overflow) {
fprintf(stderr, "kallsyms failure: "
@ -501,7 +479,11 @@ static void write_src(void)
printf("\n");
output_label("kallsyms_relative_base");
output_address(relative_base);
/* Provide proper symbols relocatability by their '_text' relativeness. */
if (_text <= relative_base)
printf("\tPTR\t_text + %#llx\n", relative_base - _text);
else
printf("\tPTR\t_text - %#llx\n", _text - relative_base);
printf("\n");
sort_symbols_by_name();

View File

@ -18,6 +18,7 @@
#include <time.h>
#include <unistd.h>
#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
@ -395,6 +396,8 @@ int conf_read_simple(const char *name, int def)
}
}
expr_invalidate_all();
while (getline_stripped(&line, &line_asize, in) != -1) {
struct menu *choice;

View File

@ -9,44 +9,68 @@
#include <stdlib.h>
#include <string.h>
#include <hash.h>
#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
#define DEBUG_EXPR 0
HASHTABLE_DEFINE(expr_hashtable, EXPR_HASHSIZE);
static struct expr *expr_eliminate_yn(struct expr *e);
/**
* expr_lookup - return the expression with the given type and sub-nodes
* This looks up an expression with the specified type and sub-nodes. If such
* an expression is found in the hash table, it is returned. Otherwise, a new
* expression node is allocated and added to the hash table.
* @type: expression type
* @l: left node
* @r: right node
* return: expression
*/
static struct expr *expr_lookup(enum expr_type type, void *l, void *r)
{
struct expr *e;
int hash;
hash = hash_32((unsigned int)type ^ hash_ptr(l) ^ hash_ptr(r));
hash_for_each_possible(expr_hashtable, e, node, hash) {
if (e->type == type && e->left._initdata == l &&
e->right._initdata == r)
return e;
}
e = xmalloc(sizeof(*e));
e->type = type;
e->left._initdata = l;
e->right._initdata = r;
hash_add(expr_hashtable, &e->node, hash);
return e;
}
struct expr *expr_alloc_symbol(struct symbol *sym)
{
struct expr *e = xcalloc(1, sizeof(*e));
e->type = E_SYMBOL;
e->left.sym = sym;
return e;
return expr_lookup(E_SYMBOL, sym, NULL);
}
struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
{
struct expr *e = xcalloc(1, sizeof(*e));
e->type = type;
e->left.expr = ce;
return e;
return expr_lookup(type, ce, NULL);
}
struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
{
struct expr *e = xcalloc(1, sizeof(*e));
e->type = type;
e->left.expr = e1;
e->right.expr = e2;
return e;
return expr_lookup(type, e1, e2);
}
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
{
struct expr *e = xcalloc(1, sizeof(*e));
e->type = type;
e->left.sym = s1;
e->right.sym = s2;
return e;
return expr_lookup(type, s1, s2);
}
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
@ -63,76 +87,6 @@ struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
}
struct expr *expr_copy(const struct expr *org)
{
struct expr *e;
if (!org)
return NULL;
e = xmalloc(sizeof(*org));
memcpy(e, org, sizeof(*org));
switch (org->type) {
case E_SYMBOL:
e->left = org->left;
break;
case E_NOT:
e->left.expr = expr_copy(org->left.expr);
break;
case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL:
e->left.sym = org->left.sym;
e->right.sym = org->right.sym;
break;
case E_AND:
case E_OR:
e->left.expr = expr_copy(org->left.expr);
e->right.expr = expr_copy(org->right.expr);
break;
default:
fprintf(stderr, "can't copy type %d\n", e->type);
free(e);
e = NULL;
break;
}
return e;
}
void expr_free(struct expr *e)
{
if (!e)
return;
switch (e->type) {
case E_SYMBOL:
break;
case E_NOT:
expr_free(e->left.expr);
break;
case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL:
break;
case E_OR:
case E_AND:
expr_free(e->left.expr);
expr_free(e->right.expr);
break;
default:
fprintf(stderr, "how to free type %d?\n", e->type);
break;
}
free(e);
}
static int trans_count;
/*
@ -145,16 +99,24 @@ static int trans_count;
*/
static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
{
struct expr *l, *r;
/* Recurse down to leaves */
if ((*ep1)->type == type) {
__expr_eliminate_eq(type, &(*ep1)->left.expr, ep2);
__expr_eliminate_eq(type, &(*ep1)->right.expr, ep2);
l = (*ep1)->left.expr;
r = (*ep1)->right.expr;
__expr_eliminate_eq(type, &l, ep2);
__expr_eliminate_eq(type, &r, ep2);
*ep1 = expr_alloc_two(type, l, r);
return;
}
if ((*ep2)->type == type) {
__expr_eliminate_eq(type, ep1, &(*ep2)->left.expr);
__expr_eliminate_eq(type, ep1, &(*ep2)->right.expr);
l = (*ep2)->left.expr;
r = (*ep2)->right.expr;
__expr_eliminate_eq(type, ep1, &l);
__expr_eliminate_eq(type, ep1, &r);
*ep2 = expr_alloc_two(type, l, r);
return;
}
@ -170,7 +132,6 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e
/* *ep1 and *ep2 are equal leaves. Prepare them for elimination. */
trans_count++;
expr_free(*ep1); expr_free(*ep2);
switch (type) {
case E_OR:
*ep1 = expr_alloc_symbol(&symbol_no);
@ -242,9 +203,10 @@ void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
* equals some operand in the other (operands do not need to appear in the same
* order), recursively.
*/
int expr_eq(struct expr *e1, struct expr *e2)
bool expr_eq(struct expr *e1, struct expr *e2)
{
int res, old_count;
int old_count;
bool res;
/*
* A NULL expr is taken to be yes, but there's also a different way to
@ -254,7 +216,7 @@ int expr_eq(struct expr *e1, struct expr *e2)
return expr_is_yes(e1) && expr_is_yes(e2);
if (e1->type != e2->type)
return 0;
return false;
switch (e1->type) {
case E_EQUAL:
case E_GEQ:
@ -269,14 +231,10 @@ int expr_eq(struct expr *e1, struct expr *e2)
return expr_eq(e1->left.expr, e2->left.expr);
case E_AND:
case E_OR:
e1 = expr_copy(e1);
e2 = expr_copy(e2);
old_count = trans_count;
expr_eliminate_eq(&e1, &e2);
res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
e1->left.sym == e2->left.sym);
expr_free(e1);
expr_free(e2);
trans_count = old_count;
return res;
case E_RANGE:
@ -291,11 +249,11 @@ int expr_eq(struct expr *e1, struct expr *e2)
printf(" ?\n");
}
return 0;
return false;
}
/*
* Recursively performs the following simplifications in-place (as well as the
* Recursively performs the following simplifications (as well as the
* corresponding simplifications with swapped operands):
*
* expr && n -> n
@ -307,79 +265,39 @@ int expr_eq(struct expr *e1, struct expr *e2)
*/
static struct expr *expr_eliminate_yn(struct expr *e)
{
struct expr *tmp;
struct expr *l, *r;
if (e) switch (e->type) {
case E_AND:
e->left.expr = expr_eliminate_yn(e->left.expr);
e->right.expr = expr_eliminate_yn(e->right.expr);
if (e->left.expr->type == E_SYMBOL) {
if (e->left.expr->left.sym == &symbol_no) {
expr_free(e->left.expr);
expr_free(e->right.expr);
e->type = E_SYMBOL;
e->left.sym = &symbol_no;
e->right.expr = NULL;
return e;
} else if (e->left.expr->left.sym == &symbol_yes) {
free(e->left.expr);
tmp = e->right.expr;
*e = *(e->right.expr);
free(tmp);
return e;
}
}
if (e->right.expr->type == E_SYMBOL) {
if (e->right.expr->left.sym == &symbol_no) {
expr_free(e->left.expr);
expr_free(e->right.expr);
e->type = E_SYMBOL;
e->left.sym = &symbol_no;
e->right.expr = NULL;
return e;
} else if (e->right.expr->left.sym == &symbol_yes) {
free(e->right.expr);
tmp = e->left.expr;
*e = *(e->left.expr);
free(tmp);
return e;
l = expr_eliminate_yn(e->left.expr);
r = expr_eliminate_yn(e->right.expr);
if (l->type == E_SYMBOL) {
if (l->left.sym == &symbol_no)
return l;
else if (l->left.sym == &symbol_yes)
return r;
}
if (r->type == E_SYMBOL) {
if (r->left.sym == &symbol_no)
return r;
else if (r->left.sym == &symbol_yes)
return l;
}
break;
case E_OR:
e->left.expr = expr_eliminate_yn(e->left.expr);
e->right.expr = expr_eliminate_yn(e->right.expr);
if (e->left.expr->type == E_SYMBOL) {
if (e->left.expr->left.sym == &symbol_no) {
free(e->left.expr);
tmp = e->right.expr;
*e = *(e->right.expr);
free(tmp);
return e;
} else if (e->left.expr->left.sym == &symbol_yes) {
expr_free(e->left.expr);
expr_free(e->right.expr);
e->type = E_SYMBOL;
e->left.sym = &symbol_yes;
e->right.expr = NULL;
return e;
}
}
if (e->right.expr->type == E_SYMBOL) {
if (e->right.expr->left.sym == &symbol_no) {
free(e->right.expr);
tmp = e->left.expr;
*e = *(e->left.expr);
free(tmp);
return e;
} else if (e->right.expr->left.sym == &symbol_yes) {
expr_free(e->left.expr);
expr_free(e->right.expr);
e->type = E_SYMBOL;
e->left.sym = &symbol_yes;
e->right.expr = NULL;
return e;
l = expr_eliminate_yn(e->left.expr);
r = expr_eliminate_yn(e->right.expr);
if (l->type == E_SYMBOL) {
if (l->left.sym == &symbol_no)
return r;
else if (l->left.sym == &symbol_yes)
return l;
}
if (r->type == E_SYMBOL) {
if (r->left.sym == &symbol_no)
return l;
else if (r->left.sym == &symbol_yes)
return r;
}
break;
default:
@ -397,7 +315,7 @@ static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
struct symbol *sym1, *sym2;
if (expr_eq(e1, e2))
return expr_copy(e1);
return e1;
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
return NULL;
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
@ -440,6 +358,7 @@ static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
}
}
if (sym1->type == S_BOOLEAN) {
// a || !a -> y
if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
(e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
return expr_alloc_symbol(&symbol_yes);
@ -461,7 +380,7 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
struct symbol *sym1, *sym2;
if (expr_eq(e1, e2))
return expr_copy(e1);
return e1;
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
return NULL;
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
@ -558,38 +477,33 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
*/
static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
{
struct expr *tmp;
struct expr *tmp, *l, *r;
/* Recurse down to leaves */
if ((*ep1)->type == type) {
expr_eliminate_dups1(type, &(*ep1)->left.expr, ep2);
expr_eliminate_dups1(type, &(*ep1)->right.expr, ep2);
l = (*ep1)->left.expr;
r = (*ep1)->right.expr;
expr_eliminate_dups1(type, &l, ep2);
expr_eliminate_dups1(type, &r, ep2);
*ep1 = expr_alloc_two(type, l, r);
return;
}
if ((*ep2)->type == type) {
expr_eliminate_dups1(type, ep1, &(*ep2)->left.expr);
expr_eliminate_dups1(type, ep1, &(*ep2)->right.expr);
l = (*ep2)->left.expr;
r = (*ep2)->right.expr;
expr_eliminate_dups1(type, ep1, &l);
expr_eliminate_dups1(type, ep1, &r);
*ep2 = expr_alloc_two(type, l, r);
return;
}
/* *ep1 and *ep2 are leaves. Compare and process them. */
if (*ep1 == *ep2)
return;
switch ((*ep1)->type) {
case E_OR: case E_AND:
expr_eliminate_dups1((*ep1)->type, ep1, ep1);
default:
;
}
switch (type) {
case E_OR:
tmp = expr_join_or(*ep1, *ep2);
if (tmp) {
expr_free(*ep1); expr_free(*ep2);
*ep1 = expr_alloc_symbol(&symbol_no);
*ep2 = tmp;
trans_count++;
@ -598,7 +512,6 @@ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct
case E_AND:
tmp = expr_join_and(*ep1, *ep2);
if (tmp) {
expr_free(*ep1); expr_free(*ep2);
*ep1 = expr_alloc_symbol(&symbol_yes);
*ep2 = tmp;
trans_count++;
@ -628,10 +541,15 @@ struct expr *expr_eliminate_dups(struct expr *e)
oldcount = trans_count;
do {
struct expr *l, *r;
trans_count = 0;
switch (e->type) {
case E_OR: case E_AND:
expr_eliminate_dups1(e->type, &e, &e);
l = expr_eliminate_dups(e->left.expr);
r = expr_eliminate_dups(e->right.expr);
expr_eliminate_dups1(e->type, &l, &r);
e = expr_alloc_two(e->type, l, r);
default:
;
}
@ -645,12 +563,34 @@ struct expr *expr_eliminate_dups(struct expr *e)
* Performs various simplifications involving logical operators and
* comparisons.
*
* For bool type:
* A=n -> !A
* A=m -> n
* A=y -> A
* A!=n -> A
* A!=m -> y
* A!=y -> !A
*
* For any type:
* !!A -> A
* !(A=B) -> A!=B
* !(A!=B) -> A=B
* !(A<=B) -> A>B
* !(A>=B) -> A<B
* !(A<B) -> A>=B
* !(A>B) -> A<=B
* !(A || B) -> !A && !B
* !(A && B) -> !A || !B
*
* For constant:
* !y -> n
* !m -> m
* !n -> y
*
* Allocates and returns a new expression.
*/
struct expr *expr_transform(struct expr *e)
{
struct expr *tmp;
if (!e)
return NULL;
switch (e->type) {
@ -663,8 +603,9 @@ struct expr *expr_transform(struct expr *e)
case E_SYMBOL:
break;
default:
e->left.expr = expr_transform(e->left.expr);
e->right.expr = expr_transform(e->right.expr);
e = expr_alloc_two(e->type,
expr_transform(e->left.expr),
expr_transform(e->right.expr));
}
switch (e->type) {
@ -672,21 +613,19 @@ struct expr *expr_transform(struct expr *e)
if (e->left.sym->type != S_BOOLEAN)
break;
if (e->right.sym == &symbol_no) {
e->type = E_NOT;
e->left.expr = expr_alloc_symbol(e->left.sym);
e->right.sym = NULL;
// A=n -> !A
e = expr_alloc_one(E_NOT, expr_alloc_symbol(e->left.sym));
break;
}
if (e->right.sym == &symbol_mod) {
// A=m -> n
printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
e->type = E_SYMBOL;
e->left.sym = &symbol_no;
e->right.sym = NULL;
e = expr_alloc_symbol(&symbol_no);
break;
}
if (e->right.sym == &symbol_yes) {
e->type = E_SYMBOL;
e->right.sym = NULL;
// A=y -> A
e = expr_alloc_symbol(e->left.sym);
break;
}
break;
@ -694,104 +633,71 @@ struct expr *expr_transform(struct expr *e)
if (e->left.sym->type != S_BOOLEAN)
break;
if (e->right.sym == &symbol_no) {
e->type = E_SYMBOL;
e->right.sym = NULL;
// A!=n -> A
e = expr_alloc_symbol(e->left.sym);
break;
}
if (e->right.sym == &symbol_mod) {
// A!=m -> y
printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
e->type = E_SYMBOL;
e->left.sym = &symbol_yes;
e->right.sym = NULL;
e = expr_alloc_symbol(&symbol_yes);
break;
}
if (e->right.sym == &symbol_yes) {
e->type = E_NOT;
e->left.expr = expr_alloc_symbol(e->left.sym);
e->right.sym = NULL;
// A!=y -> !A
e = expr_alloc_one(E_NOT, e->left.expr);
break;
}
break;
case E_NOT:
switch (e->left.expr->type) {
case E_NOT:
// !!a -> a
tmp = e->left.expr->left.expr;
free(e->left.expr);
free(e);
e = tmp;
e = expr_transform(e);
// !!A -> A
e = e->left.expr->left.expr;
break;
case E_EQUAL:
case E_UNEQUAL:
// !a='x' -> a!='x'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
// !(A=B) -> A!=B
e = expr_alloc_comp(e->left.expr->type == E_EQUAL ? E_UNEQUAL : E_EQUAL,
e->left.expr->left.sym,
e->left.expr->right.sym);
break;
case E_LEQ:
case E_GEQ:
// !a<='x' -> a>'x'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = e->type == E_LEQ ? E_GTH : E_LTH;
// !(A<=B) -> A>B
e = expr_alloc_comp(e->left.expr->type == E_LEQ ? E_GTH : E_LTH,
e->left.expr->left.sym,
e->left.expr->right.sym);
break;
case E_LTH:
case E_GTH:
// !a<'x' -> a>='x'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = e->type == E_LTH ? E_GEQ : E_LEQ;
// !(A<B) -> A>=B
e = expr_alloc_comp(e->left.expr->type == E_LTH ? E_GEQ : E_LEQ,
e->left.expr->left.sym,
e->left.expr->right.sym);
break;
case E_OR:
// !(a || b) -> !a && !b
tmp = e->left.expr;
e->type = E_AND;
e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
tmp->type = E_NOT;
tmp->right.expr = NULL;
// !(A || B) -> !A && !B
e = expr_alloc_and(expr_alloc_one(E_NOT, e->left.expr->left.expr),
expr_alloc_one(E_NOT, e->left.expr->right.expr));
e = expr_transform(e);
break;
case E_AND:
// !(a && b) -> !a || !b
tmp = e->left.expr;
e->type = E_OR;
e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
tmp->type = E_NOT;
tmp->right.expr = NULL;
// !(A && B) -> !A || !B
e = expr_alloc_or(expr_alloc_one(E_NOT, e->left.expr->left.expr),
expr_alloc_one(E_NOT, e->left.expr->right.expr));
e = expr_transform(e);
break;
case E_SYMBOL:
if (e->left.expr->left.sym == &symbol_yes) {
if (e->left.expr->left.sym == &symbol_yes)
// !'y' -> 'n'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = E_SYMBOL;
e->left.sym = &symbol_no;
break;
}
if (e->left.expr->left.sym == &symbol_mod) {
e = expr_alloc_symbol(&symbol_no);
else if (e->left.expr->left.sym == &symbol_mod)
// !'m' -> 'm'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = E_SYMBOL;
e->left.sym = &symbol_mod;
break;
}
if (e->left.expr->left.sym == &symbol_no) {
e = expr_alloc_symbol(&symbol_mod);
else if (e->left.expr->left.sym == &symbol_no)
// !'n' -> 'y'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = E_SYMBOL;
e->left.sym = &symbol_yes;
break;
}
e = expr_alloc_symbol(&symbol_yes);
break;
default:
;
@ -803,10 +709,10 @@ struct expr *expr_transform(struct expr *e)
return e;
}
int expr_contains_symbol(struct expr *dep, struct symbol *sym)
bool expr_contains_symbol(struct expr *dep, struct symbol *sym)
{
if (!dep)
return 0;
return false;
switch (dep->type) {
case E_AND:
@ -828,7 +734,7 @@ int expr_contains_symbol(struct expr *dep, struct symbol *sym)
default:
;
}
return 0;
return false;
}
bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
@ -915,18 +821,18 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb
case E_EQUAL:
if (type == E_EQUAL) {
if (sym == &symbol_yes)
return expr_copy(e);
return e;
if (sym == &symbol_mod)
return expr_alloc_symbol(&symbol_no);
if (sym == &symbol_no)
return expr_alloc_one(E_NOT, expr_copy(e));
return expr_alloc_one(E_NOT, e);
} else {
if (sym == &symbol_yes)
return expr_alloc_one(E_NOT, expr_copy(e));
return expr_alloc_one(E_NOT, e);
if (sym == &symbol_mod)
return expr_alloc_symbol(&symbol_yes);
if (sym == &symbol_no)
return expr_copy(e);
return e;
}
break;
case E_SYMBOL:
@ -981,7 +887,7 @@ static enum string_value_kind expr_parse_string(const char *str,
? kind : k_string;
}
tristate expr_calc_value(struct expr *e)
static tristate __expr_calc_value(struct expr *e)
{
tristate val1, val2;
const char *str1, *str2;
@ -989,9 +895,6 @@ tristate expr_calc_value(struct expr *e)
union string_value lval = {}, rval = {};
int res;
if (!e)
return yes;
switch (e->type) {
case E_SYMBOL:
sym_calc_value(e->left.sym);
@ -1055,6 +958,35 @@ tristate expr_calc_value(struct expr *e)
}
}
/**
* expr_calc_value - return the tristate value of the given expression
* @e: expression
* return: tristate value of the expression
*/
tristate expr_calc_value(struct expr *e)
{
if (!e)
return yes;
if (!e->val_is_valid) {
e->val = __expr_calc_value(e);
e->val_is_valid = true;
}
return e->val;
}
/**
* expr_invalidate_all - invalidate all cached expression values
*/
void expr_invalidate_all(void)
{
struct expr *e;
hash_for_each(expr_hashtable, e, node)
e->val_is_valid = false;
}
static int expr_compare_type(enum expr_type t1, enum expr_type t2)
{
if (t1 == t2)

View File

@ -29,12 +29,26 @@ enum expr_type {
};
union expr_data {
struct expr *expr;
struct symbol *sym;
struct expr * const expr;
struct symbol * const sym;
void *_initdata;
};
/**
* struct expr - expression
*
* @node: link node for the hash table
* @type: expressoin type
* @val: calculated tristate value
* @val_is_valid: indicate whether the value is valid
* @left: left node
* @right: right node
*/
struct expr {
struct hlist_node node;
enum expr_type type;
tristate val;
bool val_is_valid;
union expr_data left, right;
};
@ -168,7 +182,6 @@ enum prop_type {
P_SELECT, /* select BAR */
P_IMPLY, /* imply BAR */
P_RANGE, /* range 7..100 (for a symbol) */
P_SYMBOL, /* where a symbol is defined */
};
struct property {
@ -276,14 +289,12 @@ struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
struct expr *expr_copy(const struct expr *org);
void expr_free(struct expr *e);
void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
int expr_eq(struct expr *e1, struct expr *e2);
bool expr_eq(struct expr *e1, struct expr *e2);
tristate expr_calc_value(struct expr *e);
struct expr *expr_eliminate_dups(struct expr *e);
struct expr *expr_transform(struct expr *e);
int expr_contains_symbol(struct expr *dep, struct symbol *sym);
bool expr_contains_symbol(struct expr *dep, struct symbol *sym);
bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
@ -293,7 +304,7 @@ void expr_gstr_print(const struct expr *e, struct gstr *gs);
void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
tristate pr_type, const char *title);
static inline int expr_is_yes(const struct expr *e)
static inline bool expr_is_yes(const struct expr *e)
{
return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
}

View File

@ -11,6 +11,12 @@ extern HASHTABLE_DECLARE(sym_hashtable, SYMBOL_HASHSIZE);
#define for_all_symbols(sym) \
hash_for_each(sym_hashtable, sym, node)
#define EXPR_HASHSIZE (1U << 14)
extern HASHTABLE_DECLARE(expr_hashtable, EXPR_HASHSIZE);
void expr_invalidate_all(void);
struct menu;
extern struct menu *current_menu, *current_entry;

View File

@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
#include <xalloc.h>
#include "lkc.h"
#include "preprocess.h"

View File

@ -51,13 +51,7 @@ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
}
/* util.c */
unsigned int strhash(const char *s);
const char *file_lookup(const char *name);
void *xmalloc(size_t size);
void *xcalloc(size_t nmemb, size_t size);
void *xrealloc(void *p, size_t size);
char *xstrdup(const char *s);
char *xstrndup(const char *s, size_t n);
/* lexer.l */
int yylex(void);

View File

@ -20,6 +20,7 @@
#include <unistd.h>
#include <list.h>
#include <xalloc.h>
#include "lkc.h"
#include "lxdialog/dialog.h"
#include "mnconf-common.h"

View File

@ -9,6 +9,7 @@
#include <string.h>
#include <list.h>
#include <xalloc.h>
#include "lkc.h"
#include "internal.h"
@ -78,10 +79,8 @@ void menu_add_entry(struct symbol *sym)
*last_entry_ptr = menu;
last_entry_ptr = &menu->next;
current_entry = menu;
if (sym) {
menu_add_symbol(P_SYMBOL, sym, NULL);
if (sym)
list_add_tail(&menu->link, &sym->menus);
}
}
struct menu *menu_add_menu(void)
@ -108,12 +107,13 @@ static struct expr *rewrite_m(struct expr *e)
switch (e->type) {
case E_NOT:
e->left.expr = rewrite_m(e->left.expr);
e = expr_alloc_one(E_NOT, rewrite_m(e->left.expr));
break;
case E_OR:
case E_AND:
e->left.expr = rewrite_m(e->left.expr);
e->right.expr = rewrite_m(e->right.expr);
e = expr_alloc_two(e->type,
rewrite_m(e->left.expr),
rewrite_m(e->right.expr));
break;
case E_SYMBOL:
/* change 'm' into 'm' && MODULES */
@ -193,21 +193,11 @@ struct property *menu_add_prompt(enum prop_type type, const char *prompt,
struct menu *menu = current_entry;
while ((menu = menu->parent) != NULL) {
struct expr *dup_expr;
if (!menu->visibility)
continue;
/*
* Do not add a reference to the menu's visibility
* expression but use a copy of it. Otherwise the
* expression reduction functions will modify
* expressions that have multiple references which
* can cause unwanted side effects.
*/
dup_expr = expr_copy(menu->visibility);
prop->visible.expr = expr_alloc_and(prop->visible.expr,
dup_expr);
menu->visibility);
}
}
@ -323,7 +313,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
*/
basedep = rewrite_m(menu->dep);
basedep = expr_transform(basedep);
basedep = expr_alloc_and(expr_copy(parent->dep), basedep);
basedep = expr_alloc_and(parent->dep, basedep);
basedep = expr_eliminate_dups(basedep);
menu->dep = basedep;
@ -367,7 +357,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
*/
dep = rewrite_m(prop->visible.expr);
dep = expr_transform(dep);
dep = expr_alloc_and(expr_copy(basedep), dep);
dep = expr_alloc_and(basedep, dep);
dep = expr_eliminate_dups(dep);
prop->visible.expr = dep;
@ -378,11 +368,11 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
if (prop->type == P_SELECT) {
struct symbol *es = prop_get_symbol(prop);
es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
} else if (prop->type == P_IMPLY) {
struct symbol *es = prop_get_symbol(prop);
es->implied.expr = expr_alloc_or(es->implied.expr,
expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
}
}
}
@ -442,22 +432,18 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
*/
dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
dep = expr_eliminate_dups(expr_transform(dep));
dep2 = expr_copy(basedep);
dep2 = basedep;
expr_eliminate_eq(&dep, &dep2);
expr_free(dep);
if (!expr_is_yes(dep2)) {
/* Not superset, quit */
expr_free(dep2);
break;
}
/* Superset, put in submenu */
expr_free(dep2);
next:
_menu_finalize(menu, false);
menu->parent = parent;
last_menu = menu;
}
expr_free(basedep);
if (last_menu) {
parent->list = parent->next;
parent->next = last_menu->next;

View File

@ -12,6 +12,7 @@
#include <stdlib.h>
#include <list.h>
#include <xalloc.h>
#include "lkc.h"
#include "mnconf-common.h"
#include "nconf.h"

View File

@ -4,6 +4,7 @@
*
* Derived from menuconfig.
*/
#include <xalloc.h>
#include "nconf.h"
#include "lkc.h"

View File

@ -11,6 +11,7 @@
#include <string.h>
#include <stdbool.h>
#include <xalloc.h>
#include "lkc.h"
#include "internal.h"
#include "preprocess.h"
@ -530,14 +531,6 @@ void conf_parse(const char *name)
yydebug = 1;
yyparse();
/*
* FIXME:
* cur_filename and cur_lineno are used even after yyparse();
* menu_finalize() calls menu_add_symbol(). This should be fixed.
*/
cur_filename = "<none>";
cur_lineno = 0;
str_printf(&autoconf_cmd,
"\n"
"$(autoconfig): $(deps_config)\n"
@ -715,10 +708,6 @@ static void print_symbol(FILE *out, const struct menu *menu)
print_quoted_string(out, prop->text);
fputc('\n', out);
break;
case P_SYMBOL:
fputs( " symbol ", out);
fprintf(out, "%s\n", prop->menu->sym->name);
break;
default:
fprintf(out, " unknown prop %d!\n", prop->type);
break;

View File

@ -11,6 +11,7 @@
#include <array_size.h>
#include <list.h>
#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
#include "preprocess.h"

View File

@ -22,6 +22,7 @@
#include <stdlib.h>
#include <xalloc.h>
#include "lkc.h"
#include "qconf.h"
@ -1094,7 +1095,6 @@ QString ConfigInfoView::debug_info(struct symbol *sym)
case P_RANGE:
case P_COMMENT:
case P_IMPLY:
case P_SYMBOL:
stream << prop_get_type_name(prop->type);
stream << ": ";
expr_print(prop->expr, expr_print_help,

View File

@ -9,6 +9,8 @@
#include <string.h>
#include <regex.h>
#include <hash.h>
#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
@ -517,6 +519,7 @@ void sym_clear_all_valid(void)
for_all_symbols(sym)
sym->flags &= ~SYMBOL_VALID;
expr_invalidate_all();
conf_set_changed(true);
sym_calc_value(modules_sym);
}
@ -892,7 +895,7 @@ struct symbol *sym_lookup(const char *name, int flags)
case 'n': return &symbol_no;
}
}
hash = strhash(name);
hash = hash_str(name);
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
if (symbol->name &&
@ -935,7 +938,7 @@ struct symbol *sym_find(const char *name)
case 'n': return &symbol_no;
}
}
hash = strhash(name);
hash = hash_str(name);
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
if (symbol->name &&
@ -1321,8 +1324,6 @@ const char *prop_get_type_name(enum prop_type type)
return "imply";
case P_RANGE:
return "range";
case P_SYMBOL:
return "symbol";
case P_UNKNOWN:
break;
}

View File

@ -8,19 +8,11 @@
#include <stdlib.h>
#include <string.h>
#include <hash.h>
#include <hashtable.h>
#include <xalloc.h>
#include "lkc.h"
unsigned int strhash(const char *s)
{
/* fnv32 hash */
unsigned int hash = 2166136261U;
for (; *s; s++)
hash = (hash ^ *s) * 0x01000193;
return hash;
}
/* hash table of all parsed Kconfig files */
static HASHTABLE_DEFINE(file_hashtable, 1U << 11);
@ -34,7 +26,7 @@ const char *file_lookup(const char *name)
{
struct file *file;
size_t len;
int hash = strhash(name);
int hash = hash_str(name);
hash_for_each_possible(file_hashtable, file, node, hash)
if (!strcmp(name, file->name))
@ -102,52 +94,3 @@ char *str_get(const struct gstr *gs)
{
return gs->s;
}
void *xmalloc(size_t size)
{
void *p = malloc(size);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}
void *xcalloc(size_t nmemb, size_t size)
{
void *p = calloc(nmemb, size);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}
void *xrealloc(void *p, size_t size)
{
p = realloc(p, size);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}
char *xstrdup(const char *s)
{
char *p;
p = strdup(s);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}
char *xstrndup(const char *s, size_t n)
{
char *p;
p = strndup(s, n);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}

View File

@ -203,7 +203,7 @@ kallsymso=
strip_debug=
if is_enabled CONFIG_KALLSYMS; then
truncate -s0 .tmp_vmlinux.kallsyms0.syms
true > .tmp_vmlinux.kallsyms0.syms
kallsyms .tmp_vmlinux.kallsyms0.syms .tmp_vmlinux0.kallsyms
fi

View File

@ -8,7 +8,6 @@ int
main(int argc, char **argv)
{
unsigned char ei[EI_NIDENT];
union { short s; char c[2]; } endian_test;
if (fread(ei, 1, EI_NIDENT, stdin) != EI_NIDENT) {
fprintf(stderr, "Error: input truncated\n");
@ -28,30 +27,6 @@ main(int argc, char **argv)
default:
exit(1);
}
switch (ei[EI_DATA]) {
case ELFDATA2LSB:
printf("#define KERNEL_ELFDATA ELFDATA2LSB\n");
break;
case ELFDATA2MSB:
printf("#define KERNEL_ELFDATA ELFDATA2MSB\n");
break;
default:
exit(1);
}
if (sizeof(unsigned long) == 4) {
printf("#define HOST_ELFCLASS ELFCLASS32\n");
} else if (sizeof(unsigned long) == 8) {
printf("#define HOST_ELFCLASS ELFCLASS64\n");
}
endian_test.s = 0x0102;
if (memcmp(endian_test.c, "\x01\x02", 2) == 0)
printf("#define HOST_ELFDATA ELFDATA2MSB\n");
else if (memcmp(endian_test.c, "\x02\x01", 2) == 0)
printf("#define HOST_ELFDATA ELFDATA2LSB\n");
else
exit(1);
return 0;
}

View File

@ -23,6 +23,7 @@
#include <hashtable.h>
#include <list.h>
#include <xalloc.h>
#include "modpost.h"
#include "../../include/linux/license.h"
@ -50,6 +51,9 @@ static bool error_occurred;
static bool extra_warn;
bool target_is_big_endian;
bool host_is_big_endian;
/*
* Cut off the warnings when there are too many. This typically occurs when
* vmlinux is missing. ('make modules' without building vmlinux.)
@ -63,20 +67,15 @@ static unsigned int nr_unresolved;
#define MODULE_NAME_LEN (64 - sizeof(Elf_Addr))
void modpost_log(enum loglevel loglevel, const char *fmt, ...)
void modpost_log(bool is_error, const char *fmt, ...)
{
va_list arglist;
switch (loglevel) {
case LOG_WARN:
fprintf(stderr, "WARNING: ");
break;
case LOG_ERROR:
if (is_error) {
fprintf(stderr, "ERROR: ");
error_occurred = true;
break;
default: /* invalid loglevel, ignore */
break;
} else {
fprintf(stderr, "WARNING: ");
}
fprintf(stderr, "modpost: ");
@ -94,14 +93,6 @@ static inline bool strends(const char *str, const char *postfix)
return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
}
void *do_nofail(void *ptr, const char *expr)
{
if (!ptr)
fatal("Memory allocation failure: %s.\n", expr);
return ptr;
}
char *read_text_file(const char *filename)
{
struct stat st;
@ -120,7 +111,7 @@ char *read_text_file(const char *filename)
exit(1);
}
buf = NOFAIL(malloc(st.st_size + 1));
buf = xmalloc(st.st_size + 1);
nbytes = st.st_size;
@ -178,7 +169,7 @@ static struct module *new_module(const char *name, size_t namelen)
{
struct module *mod;
mod = NOFAIL(malloc(sizeof(*mod) + namelen + 1));
mod = xmalloc(sizeof(*mod) + namelen + 1);
memset(mod, 0, sizeof(*mod));
INIT_LIST_HEAD(&mod->exported_symbols);
@ -237,7 +228,7 @@ static inline unsigned int tdb_hash(const char *name)
**/
static struct symbol *alloc_symbol(const char *name)
{
struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));
struct symbol *s = xmalloc(sizeof(*s) + strlen(name) + 1);
memset(s, 0, sizeof(*s));
strcpy(s->name, name);
@ -310,8 +301,7 @@ static void add_namespace(struct list_head *head, const char *namespace)
struct namespace_list *ns_entry;
if (!contains_namespace(head, namespace)) {
ns_entry = NOFAIL(malloc(sizeof(*ns_entry) +
strlen(namespace) + 1));
ns_entry = xmalloc(sizeof(*ns_entry) + strlen(namespace) + 1);
strcpy(ns_entry->namespace, namespace);
list_add_tail(&ns_entry->list, head);
}
@ -366,7 +356,7 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod,
s = alloc_symbol(name);
s->module = mod;
s->is_gpl_only = gpl_only;
s->namespace = NOFAIL(strdup(namespace));
s->namespace = xstrdup(namespace);
list_add_tail(&s->list, &mod->exported_symbols);
hash_add_symbol(s);
@ -438,6 +428,18 @@ static int parse_elf(struct elf_info *info, const char *filename)
/* Not an ELF file - silently ignore it */
return 0;
}
switch (hdr->e_ident[EI_DATA]) {
case ELFDATA2LSB:
target_is_big_endian = false;
break;
case ELFDATA2MSB:
target_is_big_endian = true;
break;
default:
fatal("target endian is unknown\n");
}
/* Fix endianness in ELF header */
hdr->e_type = TO_NATIVE(hdr->e_type);
hdr->e_machine = TO_NATIVE(hdr->e_machine);
@ -622,7 +624,7 @@ static void handle_symbol(struct module *mod, struct elf_info *info,
if (ELF_ST_TYPE(sym->st_info) == STT_SPARC_REGISTER)
break;
if (symname[0] == '.') {
char *munged = NOFAIL(strdup(symname));
char *munged = xstrdup(symname);
munged[0] = '_';
munged[1] = toupper(munged[1]);
symname = munged;
@ -690,10 +692,7 @@ static char *get_modinfo(struct elf_info *info, const char *tag)
static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
{
if (sym)
return elf->strtab + sym->st_name;
else
return "(unknown)";
return sym ? elf->strtab + sym->st_name : "";
}
/*
@ -1006,6 +1005,7 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
Elf_Sym *from;
const char *tosym;
const char *fromsym;
char taddr_str[16];
from = find_fromsym(elf, faddr, fsecndx);
fromsym = sym_name(elf, from);
@ -1019,10 +1019,17 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
sec_mismatch_count++;
warn("%s: section mismatch in reference: %s+0x%x (section: %s) -> %s (section: %s)\n",
modname, fromsym,
(unsigned int)(faddr - (from ? from->st_value : 0)),
fromsec, tosym, tosec);
if (!tosym[0])
snprintf(taddr_str, sizeof(taddr_str), "0x%x", (unsigned int)taddr);
/*
* The format for the reference source: <symbol_name>+<offset> or <address>
* The format for the reference destination: <symbol_name> or <address>
*/
warn("%s: section mismatch in reference: %s%s0x%x (section: %s) -> %s (section: %s)\n",
modname, fromsym, fromsym[0] ? "+" : "",
(unsigned int)(faddr - (fromsym[0] ? from->st_value : 0)),
fromsec, tosym[0] ? tosym : taddr_str, tosec);
if (mismatch->mismatch == EXTABLE_TO_NON_TEXT) {
if (match(tosec, mismatch->bad_tosec))
@ -1662,7 +1669,7 @@ void buf_write(struct buffer *buf, const char *s, int len)
{
if (buf->size - buf->pos < len) {
buf->size += len + SZ;
buf->p = NOFAIL(realloc(buf->p, buf->size));
buf->p = xrealloc(buf->p, buf->size);
}
strncpy(buf->p + buf->pos, s, len);
buf->pos += len;
@ -1677,7 +1684,7 @@ static void check_exports(struct module *mod)
exp = find_symbol(s->name);
if (!exp) {
if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS)
modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR,
modpost_log(!warn_unresolved,
"\"%s\" [%s.ko] undefined!\n",
s->name, mod->name);
continue;
@ -1700,7 +1707,7 @@ static void check_exports(struct module *mod)
basename = mod->name;
if (!contains_namespace(&mod->imported_namespaces, exp->namespace)) {
modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR,
modpost_log(!allow_missing_ns_imports,
"module %s uses symbol %s from namespace %s, but does not import it.\n",
basename, exp->name, exp->namespace);
add_namespace(&mod->missing_namespaces, exp->namespace);
@ -1748,26 +1755,9 @@ static void check_modname_len(struct module *mod)
static void add_header(struct buffer *b, struct module *mod)
{
buf_printf(b, "#include <linux/module.h>\n");
/*
* Include build-salt.h after module.h in order to
* inherit the definitions.
*/
buf_printf(b, "#define INCLUDE_VERMAGIC\n");
buf_printf(b, "#include <linux/build-salt.h>\n");
buf_printf(b, "#include <linux/elfnote-lto.h>\n");
buf_printf(b, "#include <linux/export-internal.h>\n");
buf_printf(b, "#include <linux/vermagic.h>\n");
buf_printf(b, "#include <linux/compiler.h>\n");
buf_printf(b, "\n");
buf_printf(b, "#ifdef CONFIG_UNWINDER_ORC\n");
buf_printf(b, "#include <asm/orc_header.h>\n");
buf_printf(b, "ORC_HEADER;\n");
buf_printf(b, "#endif\n");
buf_printf(b, "\n");
buf_printf(b, "BUILD_SALT;\n");
buf_printf(b, "BUILD_LTO_INFO;\n");
buf_printf(b, "\n");
buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n");
buf_printf(b, "\n");
buf_printf(b, "__visible struct module __this_module\n");
@ -1785,12 +1775,6 @@ static void add_header(struct buffer *b, struct module *mod)
if (!external_module)
buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
buf_printf(b,
"\n"
"#ifdef CONFIG_MITIGATION_RETPOLINE\n"
"MODULE_INFO(retpoline, \"Y\");\n"
"#endif\n");
if (strstarts(mod->name, "drivers/staging"))
buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
@ -1947,7 +1931,7 @@ static void write_if_changed(struct buffer *b, const char *fname)
if (st.st_size != b->pos)
goto close_write;
tmp = NOFAIL(malloc(b->pos));
tmp = xmalloc(b->pos);
if (fread(tmp, 1, b->pos, file) != b->pos)
goto free_write;
@ -2117,6 +2101,25 @@ struct dump_list {
const char *file;
};
static void check_host_endian(void)
{
static const union {
short s;
char c[2];
} endian_test = { .c = {0x01, 0x02} };
switch (endian_test.s) {
case 0x0102:
host_is_big_endian = true;
break;
case 0x0201:
host_is_big_endian = false;
break;
default:
fatal("Unknown host endian\n");
}
}
int main(int argc, char **argv)
{
struct module *mod;
@ -2133,7 +2136,7 @@ int main(int argc, char **argv)
external_module = true;
break;
case 'i':
dl = NOFAIL(malloc(sizeof(*dl)));
dl = xmalloc(sizeof(*dl));
dl->file = optarg;
list_add_tail(&dl->list, &dump_lists);
break;
@ -2181,6 +2184,8 @@ int main(int argc, char **argv)
}
}
check_host_endian();
list_for_each_entry_safe(dl, dl2, &dump_lists, list) {
read_dump(dl->file);
list_del(&dl->list);

View File

@ -62,22 +62,11 @@
x); \
})
#if KERNEL_ELFDATA != HOST_ELFDATA
#define TO_NATIVE(x) (bswap(x))
#else /* endianness matches */
#define TO_NATIVE(x) (x)
#endif
#define NOFAIL(ptr) do_nofail((ptr), #ptr)
#define TO_NATIVE(x) \
(target_is_big_endian == host_is_big_endian ? x : bswap(x))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
void *do_nofail(void *ptr, const char *expr);
struct buffer {
char *p;
int pos;
@ -187,17 +176,14 @@ void add_moddevtable(struct buffer *buf, struct module *mod);
void get_src_version(const char *modname, char sum[], unsigned sumlen);
/* from modpost.c */
extern bool target_is_big_endian;
extern bool host_is_big_endian;
char *read_text_file(const char *filename);
char *get_line(char **stringp);
void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym);
enum loglevel {
LOG_WARN,
LOG_ERROR,
};
void __attribute__((format(printf, 2, 3)))
modpost_log(enum loglevel loglevel, const char *fmt, ...);
modpost_log(bool is_error, const char *fmt, ...);
/*
* warn - show the given message, then let modpost continue running, still
@ -212,6 +198,6 @@ modpost_log(enum loglevel loglevel, const char *fmt, ...);
* fatal - show the given message, and bail out immediately. This should be
* used when there is no point to continue running modpost.
*/
#define warn(fmt, args...) modpost_log(LOG_WARN, fmt, ##args)
#define error(fmt, args...) modpost_log(LOG_ERROR, fmt, ##args)
#define warn(fmt, args...) modpost_log(false, fmt, ##args)
#define error(fmt, args...) modpost_log(true, fmt, ##args)
#define fatal(fmt, args...) do { error(fmt, ##args); exit(1); } while (1)

View File

@ -8,6 +8,8 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <xalloc.h>
#include "modpost.h"
/*
@ -305,7 +307,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
const char *base;
int dirlen, ret = 0, check_files = 0;
cmd = NOFAIL(malloc(strlen(objfile) + sizeof("..cmd")));
cmd = xmalloc(strlen(objfile) + sizeof("..cmd"));
base = strrchr(objfile, '/');
if (base) {
@ -316,7 +318,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
dirlen = 0;
sprintf(cmd, ".%s.cmd", objfile);
}
dir = NOFAIL(malloc(dirlen + 1));
dir = xmalloc(dirlen + 1);
strncpy(dir, objfile, dirlen);
dir[dirlen] = '\0';

View File

@ -4,7 +4,7 @@
* Helper functions for finding the symbol in an ELF which is "nearest"
* to a given address.
*/
#include <xalloc.h>
#include "modpost.h"
struct syminfo {
@ -125,8 +125,8 @@ void symsearch_init(struct elf_info *elf)
{
unsigned int table_size = symbol_count(elf);
elf->symsearch = NOFAIL(malloc(sizeof(struct symsearch) +
sizeof(struct syminfo) * table_size));
elf->symsearch = xmalloc(sizeof(struct symsearch) +
sizeof(struct syminfo) * table_size);
elf->symsearch->table_size = table_size;
symsearch_populate(elf, elf->symsearch->table, table_size);

25
scripts/module-common.c Normal file
View File

@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
/*
* Include build-salt.h after module.h in order to
* inherit the definitions.
*/
#define INCLUDE_VERMAGIC
#include <linux/build-salt.h>
#include <linux/elfnote-lto.h>
#include <linux/vermagic.h>
#ifdef CONFIG_UNWINDER_ORC
#include <asm/orc_header.h>
ORC_HEADER;
#endif
BUILD_SALT;
BUILD_LTO_INFO;
MODULE_INFO(vermagic, VERMAGIC_STRING);
#ifdef CONFIG_MITIGATION_RETPOLINE
MODULE_INFO(retpoline, "Y");
#endif

View File

@ -3,10 +3,13 @@
# Contributor: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
pkgbase=${PACMAN_PKGBASE:-linux-upstream}
pkgname=("${pkgbase}" "${pkgbase}-api-headers")
if grep -q CONFIG_MODULES=y include/config/auto.conf; then
pkgname+=("${pkgbase}-headers")
fi
pkgname=("${pkgbase}")
_extrapackages=${PACMAN_EXTRAPACKAGES-headers api-headers debug}
for pkg in $_extrapackages; do
pkgname+=("${pkgbase}-${pkg}")
done
pkgver="${KERNELRELEASE//-/_}"
# The PKGBUILD is evaluated multiple times.
# Running scripts/build-version from here would introduce inconsistencies.
@ -33,11 +36,17 @@ makedepends=(
)
options=(!debug !strip !buildflags !makeflags)
build() {
_prologue() {
# MAKEFLAGS from makepkg.conf override the ones inherited from kbuild.
# Bypass this override with a custom variable.
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
cd "${objtree}"
# Kbuild works in the output directory, where this PKGBUILD is located.
cd "$(dirname "${BASH_SOURCE[0]}")"
}
build() {
_prologue
${MAKE} KERNELRELEASE="${KERNELRELEASE}" KBUILD_BUILD_VERSION="${pkgrel}"
}
@ -45,10 +54,10 @@ build() {
_package() {
pkgdesc="The ${pkgdesc} kernel and modules"
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
cd "${objtree}"
local modulesdir="${pkgdir}/usr/${MODLIB}"
_prologue
echo "Installing boot image..."
# systemd expects to find the kernel here to allow hibernation
# https://github.com/systemd/systemd/commit/edda44605f06a41fb86b7ab8128dcf99161d2344
@ -73,14 +82,17 @@ _package() {
_package-headers() {
pkgdesc="Headers and scripts for building modules for the ${pkgdesc} kernel"
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
cd "${objtree}"
local builddir="${pkgdir}/usr/${MODLIB}/build"
_prologue
if grep -q CONFIG_MODULES=y include/config/auto.conf; then
echo "Installing build files..."
"${srctree}/scripts/package/install-extmod-build" "${builddir}"
fi
echo "Installing System.map and config..."
mkdir -p "${builddir}"
cp System.map "${builddir}/System.map"
cp .config "${builddir}/.config"
@ -94,12 +106,24 @@ _package-api-headers() {
provides=(linux-api-headers)
conflicts=(linux-api-headers)
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
cd "${objtree}"
_prologue
${MAKE} headers_install INSTALL_HDR_PATH="${pkgdir}/usr"
}
_package-debug(){
pkgdesc="Non-stripped vmlinux file for the ${pkgdesc} kernel"
local debugdir="${pkgdir}/usr/src/debug/${pkgbase}"
local builddir="${pkgdir}/usr/${MODLIB}/build"
_prologue
install -Dt "${debugdir}" -m644 vmlinux
mkdir -p "${builddir}"
ln -sr "${debugdir}/vmlinux" "${builddir}/vmlinux"
}
for _p in "${pkgname[@]}"; do
eval "package_$_p() {
$(declare -f "_package${_p#$pkgbase}")

View File

@ -9,15 +9,22 @@ is_enabled() {
grep -q "^$1=y" include/config/auto.conf
}
find_in_scripts() {
find scripts \
\( -name atomic -o -name dtc -o -name kconfig -o -name package \) -prune -o \
! -name unifdef -a ! -name mk_elfconfig -a \( -type f -o -type l \) -print
}
mkdir -p "${destdir}"
(
cd "${srctree}"
echo Makefile
find "arch/${SRCARCH}" -maxdepth 1 -name 'Makefile*'
find include scripts -type f -o -type l
find "arch/${SRCARCH}" -name generated -prune -o -name include -type d -print
find "arch/${SRCARCH}" -name Kbuild.platforms -o -name Platform
find "arch/${SRCARCH}" -name include -type d
find include \( -name config -o -name generated \) -prune -o \( -type f -o -type l \) -print
find_in_scripts
) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}"
{
@ -25,12 +32,50 @@ mkdir -p "${destdir}"
echo tools/objtool/objtool
fi
find "arch/${SRCARCH}/include" Module.symvers include scripts -type f
echo Module.symvers
echo "arch/${SRCARCH}/include/generated"
echo include/config/auto.conf
echo include/config/kernel.release
echo include/generated
find_in_scripts
if is_enabled CONFIG_GCC_PLUGINS; then
find scripts/gcc-plugins -name '*.so'
fi
} | tar -c -f - -T - | tar -xf - -C "${destdir}"
# copy .config manually to be where it's expected to be
cp "${KCONFIG_CONFIG}" "${destdir}/.config"
# When ${CC} and ${HOSTCC} differ, we are likely cross-compiling. Rebuild host
# programs using ${CC}. This assumes CC=${CROSS_COMPILE}gcc, which is usually
# the case for package building. It does not cross-compile when CC=clang.
#
# This caters to host programs that participate in Kbuild. objtool and
# resolve_btfids are out of scope.
if [ "${CC}" != "${HOSTCC}" ] && is_enabled CONFIG_CC_CAN_LINK; then
echo "Rebuilding host programs with ${CC}..."
cat <<-'EOF' > "${destdir}/Kbuild"
subdir-y := scripts
EOF
# HOSTCXX is not overridden. The C++ compiler is used to build:
# - scripts/kconfig/qconf, which is unneeded for external module builds
# - GCC plugins, which will not work on the installed system even after
# being rebuilt.
#
# Use the single-target build to avoid the modpost invocation, which
# would overwrite Module.symvers.
"${MAKE}" HOSTCC="${CC}" KBUILD_EXTMOD="${destdir}" scripts/
cat <<-'EOF' > "${destdir}/scripts/Kbuild"
subdir-y := basic
hostprogs-always-y := mod/modpost
mod/modpost-objs := $(addprefix mod/, modpost.o file2alias.o sumversion.o symsearch.o)
EOF
# Run once again to rebuild scripts/basic/ and scripts/mod/modpost.
"${MAKE}" HOSTCC="${CC}" KBUILD_EXTMOD="${destdir}" scripts/
rm -f "${destdir}/Kbuild" "${destdir}/scripts/Kbuild"
fi
find "${destdir}" \( -name '.*.cmd' -o -name '*.o' \) -delete

View File

@ -6,7 +6,7 @@
SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ -e s/sa110/arm/ \
-e /^arm64$$/!s/arm.*/arm/ -e s/sa110/arm/ \
-e s/s390x/s390/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \

370
scripts/verify_builtin_ranges.awk Executable file
View File

@ -0,0 +1,370 @@
#!/usr/bin/gawk -f
# SPDX-License-Identifier: GPL-2.0
# verify_builtin_ranges.awk: Verify address range data for builtin modules
# Written by Kris Van Hees <kris.van.hees@oracle.com>
#
# Usage: verify_builtin_ranges.awk modules.builtin.ranges System.map \
# modules.builtin vmlinux.map vmlinux.o.map
#
# Return the module name(s) (if any) associated with the given object.
#
# If we have seen this object before, return information from the cache.
# Otherwise, retrieve it from the corresponding .cmd file.
#
function get_module_info(fn, mod, obj, s) {
if (fn in omod)
return omod[fn];
if (match(fn, /\/[^/]+$/) == 0)
return "";
obj = fn;
mod = "";
fn = substr(fn, 1, RSTART) "." substr(fn, RSTART + 1) ".cmd";
if (getline s <fn == 1) {
if (match(s, /DKBUILD_MODFILE=['"]+[^'"]+/) > 0) {
mod = substr(s, RSTART + 16, RLENGTH - 16);
gsub(/['"]/, "", mod);
} else if (match(s, /RUST_MODFILE=[^ ]+/) > 0)
mod = substr(s, RSTART + 13, RLENGTH - 13);
} else {
print "ERROR: Failed to read: " fn "\n\n" \
" For kernels built with O=<objdir>, cd to <objdir>\n" \
" and execute this script as ./source/scripts/..." \
>"/dev/stderr";
close(fn);
total = 0;
exit(1);
}
close(fn);
# A single module (common case) also reflects objects that are not part
# of a module. Some of those objects have names that are also a module
# name (e.g. core). We check the associated module file name, and if
# they do not match, the object is not part of a module.
if (mod !~ / /) {
if (!(mod in mods))
mod = "";
}
gsub(/([^/ ]*\/)+/, "", mod);
gsub(/-/, "_", mod);
# At this point, mod is a single (valid) module name, or a list of
# module names (that do not need validation).
omod[obj] = mod;
return mod;
}
# Return a representative integer value for a given hexadecimal address.
#
# Since all kernel addresses fall within the same memory region, we can safely
# strip off the first 6 hex digits before performing the hex-to-dec conversion,
# thereby avoiding integer overflows.
#
function addr2val(val) {
sub(/^0x/, "", val);
if (length(val) == 16)
val = substr(val, 5);
return strtonum("0x" val);
}
# Determine the kernel build directory to use (default is .).
#
BEGIN {
if (ARGC < 6) {
print "Syntax: verify_builtin_ranges.awk <ranges-file> <system-map>\n" \
" <builtin-file> <vmlinux-map> <vmlinux-o-map>\n" \
>"/dev/stderr";
total = 0;
exit(1);
}
}
# (1) Load the built-in module address range data.
#
ARGIND == 1 {
ranges[FNR] = $0;
rcnt++;
next;
}
# (2) Annotate System.map symbols with module names.
#
ARGIND == 2 {
addr = addr2val($1);
name = $3;
while (addr >= mod_eaddr) {
if (sect_symb) {
if (sect_symb != name)
next;
sect_base = addr - sect_off;
if (dbg)
printf "[%s] BASE (%s) %016x - %016x = %016x\n", sect_name, sect_symb, addr, sect_off, sect_base >"/dev/stderr";
sect_symb = 0;
}
if (++ridx > rcnt)
break;
$0 = ranges[ridx];
sub(/-/, " ");
if ($4 != "=") {
sub(/-/, " ");
mod_saddr = strtonum("0x" $2) + sect_base;
mod_eaddr = strtonum("0x" $3) + sect_base;
$1 = $2 = $3 = "";
sub(/^ +/, "");
mod_name = $0;
if (dbg)
printf "[%s] %s from %016x to %016x\n", sect_name, mod_name, mod_saddr, mod_eaddr >"/dev/stderr";
} else {
sect_name = $1;
sect_off = strtonum("0x" $2);
sect_symb = $5;
}
}
idx = addr"-"name;
if (addr >= mod_saddr && addr < mod_eaddr)
sym2mod[idx] = mod_name;
next;
}
# Once we are done annotating the System.map, we no longer need the ranges data.
#
FNR == 1 && ARGIND == 3 {
delete ranges;
}
# (3) Build a lookup map of built-in module names.
#
# Lines from modules.builtin will be like:
# kernel/crypto/lzo-rle.ko
# and we record the object name "crypto/lzo-rle".
#
ARGIND == 3 {
sub(/kernel\//, ""); # strip off "kernel/" prefix
sub(/\.ko$/, ""); # strip off .ko suffix
mods[$1] = 1;
next;
}
# (4) Get a list of symbols (per object).
#
# Symbols by object are read from vmlinux.map, with fallback to vmlinux.o.map
# if vmlinux is found to have inked in vmlinux.o.
#
# If we were able to get the data we need from vmlinux.map, there is no need to
# process vmlinux.o.map.
#
FNR == 1 && ARGIND == 5 && total > 0 {
if (dbg)
printf "Note: %s is not needed.\n", FILENAME >"/dev/stderr";
exit;
}
# First determine whether we are dealing with a GNU ld or LLVM lld linker map.
#
ARGIND >= 4 && FNR == 1 && NF == 7 && $1 == "VMA" && $7 == "Symbol" {
map_is_lld = 1;
next;
}
# (LLD) Convert a section record fronm lld format to ld format.
#
ARGIND >= 4 && map_is_lld && NF == 5 && /[0-9] [^ ]+$/ {
$0 = $5 " 0x"$1 " 0x"$3 " load address 0x"$2;
}
# (LLD) Convert an object record from lld format to ld format.
#
ARGIND >= 4 && map_is_lld && NF == 5 && $5 ~ /:\(/ {
if (/\.a\(/ && !/ vmlinux\.a\(/)
next;
gsub(/\)/, "");
sub(/:\(/, " ");
sub(/ vmlinux\.a\(/, " ");
$0 = " "$6 " 0x"$1 " 0x"$3 " " $5;
}
# (LLD) Convert a symbol record from lld format to ld format.
#
ARGIND >= 4 && map_is_lld && NF == 5 && $5 ~ /^[A-Za-z_][A-Za-z0-9_]*$/ {
$0 = " 0x" $1 " " $5;
}
# (LLD) We do not need any other ldd linker map records.
#
ARGIND >= 4 && map_is_lld && /^[0-9a-f]{16} / {
next;
}
# Handle section records with long section names (spilling onto a 2nd line).
#
ARGIND >= 4 && !map_is_lld && NF == 1 && /^[^ ]/ {
s = $0;
getline;
$0 = s " " $0;
}
# Next section - previous one is done.
#
ARGIND >= 4 && /^[^ ]/ {
sect = 0;
}
# Get the (top level) section name.
#
ARGIND >= 4 && /^\./ {
# Explicitly ignore a few sections that are not relevant here.
if ($1 ~ /^\.orc_/ || $1 ~ /_sites$/ || $1 ~ /\.percpu/)
next;
# Sections with a 0-address can be ignored as well (in vmlinux.map).
if (ARGIND == 4 && $2 ~ /^0x0+$/)
next;
sect = $1;
next;
}
# If we are not currently in a section we care about, ignore records.
#
!sect {
next;
}
# Handle object records with long section names (spilling onto a 2nd line).
#
ARGIND >= 4 && /^ [^ \*]/ && NF == 1 {
# If the section name is long, the remainder of the entry is found on
# the next line.
s = $0;
getline;
$0 = s " " $0;
}
# Objects linked in from static libraries are ignored.
# If the object is vmlinux.o, we need to consult vmlinux.o.map for per-object
# symbol information
#
ARGIND == 4 && /^ [^ ]/ && NF == 4 {
if ($4 ~ /\.a\(/)
next;
idx = sect":"$1;
if (!(idx in sect_addend)) {
sect_addend[idx] = addr2val($2);
if (dbg)
printf "ADDEND %s = %016x\n", idx, sect_addend[idx] >"/dev/stderr";
}
if ($4 == "vmlinux.o") {
need_o_map = 1;
next;
}
}
# If data from vmlinux.o.map is needed, we only process section and object
# records from vmlinux.map to determine which section we need to pay attention
# to in vmlinux.o.map. So skip everything else from vmlinux.map.
#
ARGIND == 4 && need_o_map {
next;
}
# Get module information for the current object.
#
ARGIND >= 4 && /^ [^ ]/ && NF == 4 {
msect = $1;
mod_name = get_module_info($4);
mod_eaddr = addr2val($2) + addr2val($3);
next;
}
# Process a symbol record.
#
# Evaluate the module information obtained from vmlinux.map (or vmlinux.o.map)
# as follows:
# - For all symbols in a given object:
# - If the symbol is annotated with the same module name(s) that the object
# belongs to, count it as a match.
# - Otherwise:
# - If the symbol is known to have duplicates of which at least one is
# in a built-in module, disregard it.
# - If the symbol us not annotated with any module name(s) AND the
# object belongs to built-in modules, count it as missing.
# - Otherwise, count it as a mismatch.
#
ARGIND >= 4 && /^ / && NF == 2 && $1 ~ /^0x/ {
idx = sect":"msect;
if (!(idx in sect_addend))
next;
addr = addr2val($1);
# Handle the rare but annoying case where a 0-size symbol is placed at
# the byte *after* the module range. Based on vmlinux.map it will be
# considered part of the current object, but it falls just beyond the
# module address range. Unfortunately, its address could be at the
# start of another built-in module, so the only safe thing to do is to
# ignore it.
if (mod_name && addr == mod_eaddr)
next;
# If we are processing vmlinux.o.map, we need to apply the base address
# of the section to the relative address on the record.
#
if (ARGIND == 5)
addr += sect_addend[idx];
idx = addr"-"$2;
mod = "";
if (idx in sym2mod) {
mod = sym2mod[idx];
if (sym2mod[idx] == mod_name) {
mod_matches++;
matches++;
} else if (mod_name == "") {
print $2 " in " mod " (should NOT be)";
mismatches++;
} else {
print $2 " in " mod " (should be " mod_name ")";
mismatches++;
}
} else if (mod_name != "") {
print $2 " should be in " mod_name;
missing++;
} else
matches++;
total++;
next;
}
# Issue the comparison report.
#
END {
if (total) {
printf "Verification of %s:\n", ARGV[1];
printf " Correct matches: %6d (%d%% of total)\n", matches, 100 * matches / total;
printf " Module matches: %6d (%d%% of matches)\n", mod_matches, 100 * mod_matches / matches;
printf " Mismatches: %6d (%d%% of total)\n", mismatches, 100 * mismatches / total;
printf " Missing: %6d (%d%% of total)\n", missing, 100 * missing / total;
if (mismatches || missing)
exit(1);
}
}