mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2024-12-29 17:23:36 +00:00
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:
commit
68e5c7d4ce
3
.gitignore
vendored
3
.gitignore
vendored
@ -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
|
||||
*~
|
||||
\#*#
|
||||
|
||||
|
@ -180,6 +180,7 @@ modpost
|
||||
modules-only.symvers
|
||||
modules.builtin
|
||||
modules.builtin.modinfo
|
||||
modules.builtin.ranges
|
||||
modules.nsdeps
|
||||
modules.order
|
||||
modversions.h*
|
||||
|
@ -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.
|
||||
|
@ -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>]
|
||||
|
||||
|
@ -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?
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
****************
|
||||
|
||||
|
7
Makefile
7
Makefile
@ -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
|
||||
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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
|
||||
|
@ -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[];
|
||||
|
@ -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) \
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
142
scripts/Makefile.dtbs
Normal 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)
|
@ -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)
|
||||
|
@ -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
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
@ -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.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
@ -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)
|
||||
|
@ -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)" \
|
||||
|
@ -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.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
@ -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 \
|
||||
|
@ -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);
|
||||
|
508
scripts/generate_builtin_ranges.awk
Executable file
508
scripts/generate_builtin_ranges.awk
Executable 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
28
scripts/include/hash.h
Normal 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 */
|
@ -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 */
|
||||
|
@ -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
53
scripts/include/xalloc.h
Normal 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 */
|
@ -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();
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <xalloc.h>
|
||||
#include "lkc.h"
|
||||
#include "preprocess.h"
|
||||
|
||||
|
@ -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);
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include <list.h>
|
||||
#include <xalloc.h>
|
||||
#include "lkc.h"
|
||||
#include "lxdialog/dialog.h"
|
||||
#include "mnconf-common.h"
|
||||
|
@ -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;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <list.h>
|
||||
#include <xalloc.h>
|
||||
#include "lkc.h"
|
||||
#include "mnconf-common.h"
|
||||
#include "nconf.h"
|
||||
|
@ -4,6 +4,7 @@
|
||||
*
|
||||
* Derived from menuconfig.
|
||||
*/
|
||||
#include <xalloc.h>
|
||||
#include "nconf.h"
|
||||
#include "lkc.h"
|
||||
|
||||
|
@ -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;
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include <array_size.h>
|
||||
#include <list.h>
|
||||
#include <xalloc.h>
|
||||
#include "internal.h"
|
||||
#include "lkc.h"
|
||||
#include "preprocess.h"
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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';
|
||||
|
||||
|
@ -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
25
scripts/module-common.c
Normal 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
|
@ -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}")
|
||||
|
@ -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
|
||||
|
@ -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
370
scripts/verify_builtin_ranges.awk
Executable 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);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user