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
56 changed files with 1867 additions and 909 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -47,7 +47,6 @@
|
||||||
*.so.dbg
|
*.so.dbg
|
||||||
*.su
|
*.su
|
||||||
*.symtypes
|
*.symtypes
|
||||||
*.symversions
|
|
||||||
*.tab.[ch]
|
*.tab.[ch]
|
||||||
*.tar
|
*.tar
|
||||||
*.xz
|
*.xz
|
||||||
|
@ -71,6 +70,7 @@ modules.order
|
||||||
/Module.markers
|
/Module.markers
|
||||||
/modules.builtin
|
/modules.builtin
|
||||||
/modules.builtin.modinfo
|
/modules.builtin.modinfo
|
||||||
|
/modules.builtin.ranges
|
||||||
/modules.nsdeps
|
/modules.nsdeps
|
||||||
|
|
||||||
#
|
#
|
||||||
|
@ -143,7 +143,6 @@ GTAGS
|
||||||
# id-utils files
|
# id-utils files
|
||||||
ID
|
ID
|
||||||
|
|
||||||
*.orig
|
|
||||||
*~
|
*~
|
||||||
\#*#
|
\#*#
|
||||||
|
|
||||||
|
|
|
@ -180,6 +180,7 @@ modpost
|
||||||
modules-only.symvers
|
modules-only.symvers
|
||||||
modules.builtin
|
modules.builtin
|
||||||
modules.builtin.modinfo
|
modules.builtin.modinfo
|
||||||
|
modules.builtin.ranges
|
||||||
modules.nsdeps
|
modules.nsdeps
|
||||||
modules.order
|
modules.order
|
||||||
modversions.h*
|
modversions.h*
|
||||||
|
|
|
@ -22,6 +22,11 @@ modules.builtin.modinfo
|
||||||
This file contains modinfo from all modules that are built into the kernel.
|
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.
|
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
|
Environment variables
|
||||||
=====================
|
=====================
|
||||||
|
@ -129,6 +134,11 @@ KBUILD_OUTPUT
|
||||||
-------------
|
-------------
|
||||||
Specify the output directory when building the kernel.
|
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=...".
|
The output directory can also be specified using "O=...".
|
||||||
|
|
||||||
Setting "O=..." takes precedence over KBUILD_OUTPUT.
|
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
|
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
|
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>]
|
- default value: "default" <expr> ["if" <expr>]
|
||||||
|
|
||||||
|
|
|
@ -1665,6 +1665,5 @@ Credits
|
||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
- Describe how kbuild supports shipped files with _shipped.
|
|
||||||
- Generating offset header files.
|
- Generating offset header files.
|
||||||
- Add more variables to chapters 7 or 9?
|
- 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.
|
This document describes how to build an out-of-tree kernel module.
|
||||||
|
|
||||||
.. Table of Contents
|
Introduction
|
||||||
|
============
|
||||||
=== 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
|
|
||||||
===============
|
|
||||||
|
|
||||||
"kbuild" is the build system used by the Linux kernel. Modules must use
|
"kbuild" is the build system used by the Linux kernel. Modules must use
|
||||||
kbuild to stay compatible with changes in the build infrastructure and
|
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
|
both in-tree and out-of-tree is provided. The method for building
|
||||||
either is similar, and all modules are initially developed and built
|
either is similar, and all modules are initially developed and built
|
||||||
out-of-tree.
|
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
|
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
|
complexity, so one only has to type "make" to build the module. This is
|
||||||
easily accomplished, and a complete example will be presented in
|
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
|
To build external modules, you must have a prebuilt kernel available
|
||||||
that contains the configuration and header files used in the build.
|
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
|
CONFIG_MODVERSIONS is set; therefore, a full kernel build needs to be
|
||||||
executed to make module versioning work.
|
executed to make module versioning work.
|
||||||
|
|
||||||
2.1 Command Syntax
|
Command Syntax
|
||||||
==================
|
--------------
|
||||||
|
|
||||||
The command to build an external module is::
|
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
|
The kbuild system knows that an external module is being built
|
||||||
due to the "M=<dir>" option given in the command.
|
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
|
$ 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
|
make -C $KDIR M=$PWD
|
||||||
|
|
||||||
-C $KDIR
|
-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
|
"make" will actually change to the specified directory
|
||||||
when executing and will change back when finished.
|
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
|
directory where the external module (kbuild file) is
|
||||||
located.
|
located.
|
||||||
|
|
||||||
2.3 Targets
|
Targets
|
||||||
===========
|
-------
|
||||||
|
|
||||||
When building an external module, only a subset of the "make"
|
When building an external module, only a subset of the "make"
|
||||||
targets are available.
|
targets are available.
|
||||||
|
@ -129,7 +103,8 @@ executed to make module versioning work.
|
||||||
modules_install
|
modules_install
|
||||||
Install the external module(s). The default location is
|
Install the external module(s). The default location is
|
||||||
/lib/modules/<kernel_release>/updates/, but a prefix may
|
/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
|
clean
|
||||||
Remove all generated files in the module directory only.
|
Remove all generated files in the module directory only.
|
||||||
|
@ -137,8 +112,8 @@ executed to make module versioning work.
|
||||||
help
|
help
|
||||||
List the available targets for external modules.
|
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.
|
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
|
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 ./
|
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
|
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
|
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.c
|
||||||
8123_if.h
|
8123_if.h
|
||||||
8123_pci.c
|
8123_pci.c
|
||||||
8123_bin.o_shipped <= Binary blob
|
|
||||||
|
|
||||||
3.1 Shared Makefile
|
Shared Makefile
|
||||||
-------------------
|
---------------
|
||||||
|
|
||||||
An external module always includes a wrapper makefile that
|
An external module always includes a wrapper makefile that
|
||||||
supports building the module using "make" with no arguments.
|
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),)
|
ifneq ($(KERNELRELEASE),)
|
||||||
# kbuild part of makefile
|
# kbuild part of makefile
|
||||||
obj-m := 8123.o
|
obj-m := 8123.o
|
||||||
8123-y := 8123_if.o 8123_pci.o 8123_bin.o
|
8123-y := 8123_if.o 8123_pci.o
|
||||||
|
|
||||||
else
|
else
|
||||||
# normal makefile
|
# normal makefile
|
||||||
|
@ -207,10 +181,6 @@ module 8123.ko, which is built from the following files::
|
||||||
default:
|
default:
|
||||||
$(MAKE) -C $(KDIR) M=$$PWD
|
$(MAKE) -C $(KDIR) M=$$PWD
|
||||||
|
|
||||||
# Module specific targets
|
|
||||||
genbin:
|
|
||||||
echo "X" > 8123_bin.o_shipped
|
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
The check for KERNELRELEASE is used to separate the two parts
|
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
|
line; the second pass is by the kbuild system, which is
|
||||||
initiated by the parameterized "make" in the default target.
|
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
|
Kbuild will first look for a file named "Kbuild", and if it is not
|
||||||
file named "Kbuild," and only if that is not found, will it
|
found, it will then look for "Makefile". Utilizing a "Kbuild" file
|
||||||
then look for a makefile. Utilizing a "Kbuild" file allows us
|
allows us to split up the "Makefile" from example 1 into two files:
|
||||||
to split up the makefile from example 1 into two files:
|
|
||||||
|
|
||||||
Example 2::
|
Example 2::
|
||||||
|
|
||||||
--> filename: Kbuild
|
--> filename: Kbuild
|
||||||
obj-m := 8123.o
|
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
|
--> filename: Makefile
|
||||||
KDIR ?= /lib/modules/`uname -r`/build
|
KDIR ?= /lib/modules/`uname -r`/build
|
||||||
|
@ -241,68 +210,13 @@ module 8123.ko, which is built from the following files::
|
||||||
default:
|
default:
|
||||||
$(MAKE) -C $(KDIR) M=$$PWD
|
$(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
|
The split in example 2 is questionable due to the simplicity of
|
||||||
each file; however, some external modules use makefiles
|
each file; however, some external modules use makefiles
|
||||||
consisting of several hundred lines, and here it really pays
|
consisting of several hundred lines, and here it really pays
|
||||||
off to separate the kbuild part from the rest.
|
off to separate the kbuild part from the rest.
|
||||||
|
|
||||||
The next example shows a backward compatible version.
|
Building Multiple Modules
|
||||||
|
-------------------------
|
||||||
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
|
|
||||||
=============================
|
|
||||||
|
|
||||||
kbuild supports building multiple modules with a single build
|
kbuild supports building multiple modules with a single build
|
||||||
file. For example, if you wanted to build two modules, foo.ko
|
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!
|
It is that simple!
|
||||||
|
|
||||||
|
|
||||||
4. Include Files
|
Include Files
|
||||||
================
|
=============
|
||||||
|
|
||||||
Within the kernel, header files are kept in standard locations
|
Within the kernel, header files are kept in standard locations
|
||||||
according to the following rule:
|
according to the following rule:
|
||||||
|
@ -334,19 +248,19 @@ according to the following rule:
|
||||||
include/scsi; and architecture specific headers are located
|
include/scsi; and architecture specific headers are located
|
||||||
under arch/$(SRCARCH)/include/.
|
under arch/$(SRCARCH)/include/.
|
||||||
|
|
||||||
4.1 Kernel Includes
|
Kernel Includes
|
||||||
-------------------
|
---------------
|
||||||
|
|
||||||
To include a header file located under include/linux/, simply
|
To include a header file located under include/linux/, simply
|
||||||
use::
|
use::
|
||||||
|
|
||||||
#include <linux/module.h>
|
#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.
|
are searched.
|
||||||
|
|
||||||
4.2 Single Subdirectory
|
Single Subdirectory
|
||||||
-----------------------
|
-------------------
|
||||||
|
|
||||||
External modules tend to place header files in a separate
|
External modules tend to place header files in a separate
|
||||||
include/ directory where their source is located, although this
|
include/ directory where their source is located, although this
|
||||||
|
@ -360,15 +274,11 @@ according to the following rule:
|
||||||
--> filename: Kbuild
|
--> filename: Kbuild
|
||||||
obj-m := 8123.o
|
obj-m := 8123.o
|
||||||
|
|
||||||
ccflags-y := -Iinclude
|
ccflags-y := -I $(src)/include
|
||||||
8123-y := 8123_if.o 8123_pci.o 8123_bin.o
|
8123-y := 8123_if.o 8123_pci.o
|
||||||
|
|
||||||
Note that in the assignment there is no space between -I and
|
Several Subdirectories
|
||||||
the path. This is a limitation of kbuild: there must be no
|
----------------------
|
||||||
space present.
|
|
||||||
|
|
||||||
4.3 Several Subdirectories
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
kbuild can handle files that are spread over several directories.
|
kbuild can handle files that are spread over several directories.
|
||||||
Consider the following example::
|
Consider the following example::
|
||||||
|
@ -407,8 +317,8 @@ according to the following rule:
|
||||||
file is located.
|
file is located.
|
||||||
|
|
||||||
|
|
||||||
5. Module Installation
|
Module Installation
|
||||||
======================
|
===================
|
||||||
|
|
||||||
Modules which are included in the kernel are installed in the
|
Modules which are included in the kernel are installed in the
|
||||||
directory:
|
directory:
|
||||||
|
@ -419,8 +329,8 @@ And external modules are installed in:
|
||||||
|
|
||||||
/lib/modules/$(KERNELRELEASE)/updates/
|
/lib/modules/$(KERNELRELEASE)/updates/
|
||||||
|
|
||||||
5.1 INSTALL_MOD_PATH
|
INSTALL_MOD_PATH
|
||||||
--------------------
|
----------------
|
||||||
|
|
||||||
Above are the default directories but as always some level of
|
Above are the default directories but as always some level of
|
||||||
customization is possible. A prefix can be added to the
|
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
|
calling "make." This has effect when installing both in-tree
|
||||||
and out-of-tree modules.
|
and out-of-tree modules.
|
||||||
|
|
||||||
5.2 INSTALL_MOD_DIR
|
INSTALL_MOD_DIR
|
||||||
-------------------
|
---------------
|
||||||
|
|
||||||
External modules are by default installed to a directory under
|
External modules are by default installed to a directory under
|
||||||
/lib/modules/$(KERNELRELEASE)/updates/, but you may wish to
|
/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/
|
=> Install dir: /lib/modules/$(KERNELRELEASE)/gandalf/
|
||||||
|
|
||||||
|
|
||||||
6. Module Versioning
|
Module Versioning
|
||||||
====================
|
=================
|
||||||
|
|
||||||
Module versioning is enabled by the CONFIG_MODVERSIONS tag, and is used
|
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
|
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
|
Module.symvers contains a list of all exported symbols from a kernel
|
||||||
build.
|
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
|
During a kernel build, a file named Module.symvers will be
|
||||||
generated. Module.symvers contains all exported symbols from
|
generated. Module.symvers contains all exported symbols from
|
||||||
|
@ -486,8 +396,8 @@ build.
|
||||||
1) It lists all exported symbols from vmlinux and all modules.
|
1) It lists all exported symbols from vmlinux and all modules.
|
||||||
2) It lists the CRC if CONFIG_MODVERSIONS is enabled.
|
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
|
When building an external module, the build system needs access
|
||||||
to the symbols from the kernel to check if all external symbols
|
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
|
tree. During the MODPOST step, a new Module.symvers file will be
|
||||||
written containing all exported symbols from that external module.
|
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
|
Sometimes, an external module uses exported symbols from
|
||||||
another external module. Kbuild needs to have full knowledge of
|
another external module. Kbuild needs to have full knowledge of
|
||||||
|
@ -537,11 +447,11 @@ build.
|
||||||
initialization of its symbol tables.
|
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
|
Modules often need to check for certain `CONFIG_` options to
|
||||||
decide if a specific feature is included in the module. In
|
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-y := balloc.o bitmap.o dir.o
|
||||||
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.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
|
gtags (optional) 6.6.5 gtags --version
|
||||||
mkimage (optional) 2017.01 mkimage --version
|
mkimage (optional) 2017.01 mkimage --version
|
||||||
Python (optional) 3.5.x python3 --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
|
.. [#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
|
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
|
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
|
System utilities
|
||||||
****************
|
****************
|
||||||
|
|
||||||
|
|
7
Makefile
7
Makefile
|
@ -579,10 +579,6 @@ else
|
||||||
RUSTC_OR_CLIPPY = $(RUSTC)
|
RUSTC_OR_CLIPPY = $(RUSTC)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
ifdef RUST_LIB_SRC
|
|
||||||
export RUST_LIB_SRC
|
|
||||||
endif
|
|
||||||
|
|
||||||
# Allows the usage of unstable features in stable compilers.
|
# Allows the usage of unstable features in stable compilers.
|
||||||
export RUSTC_BOOTSTRAP := 1
|
export RUSTC_BOOTSTRAP := 1
|
||||||
|
|
||||||
|
@ -1483,6 +1479,7 @@ endif # CONFIG_MODULES
|
||||||
# Directories & files removed with 'make clean'
|
# Directories & files removed with 'make clean'
|
||||||
CLEAN_FILES += vmlinux.symvers modules-only.symvers \
|
CLEAN_FILES += vmlinux.symvers modules-only.symvers \
|
||||||
modules.builtin modules.builtin.modinfo modules.nsdeps \
|
modules.builtin modules.builtin.modinfo modules.nsdeps \
|
||||||
|
modules.builtin.ranges vmlinux.o.map \
|
||||||
compile_commands.json rust/test \
|
compile_commands.json rust/test \
|
||||||
rust-project.json .vmlinux.objs .vmlinux.export.c
|
rust-project.json .vmlinux.objs .vmlinux.export.c
|
||||||
|
|
||||||
|
@ -1947,7 +1944,7 @@ clean: $(clean-dirs)
|
||||||
-o -name '*.c.[012]*.*' \
|
-o -name '*.c.[012]*.*' \
|
||||||
-o -name '*.ll' \
|
-o -name '*.ll' \
|
||||||
-o -name '*.gcno' \
|
-o -name '*.gcno' \
|
||||||
-o -name '*.*.symversions' \) -type f -print \
|
\) -type f -print \
|
||||||
-o -name '.tmp_*' -print \
|
-o -name '.tmp_*' -print \
|
||||||
| xargs rm -rf
|
| xargs rm -rf
|
||||||
|
|
||||||
|
|
|
@ -554,7 +554,7 @@ config ARC_BUILTIN_DTB_NAME
|
||||||
string "Built in DTB"
|
string "Built in DTB"
|
||||||
help
|
help
|
||||||
Set the name of the DTB to embed in the vmlinux binary
|
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"
|
endmenu # "ARC Architecture Configuration"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,2 @@
|
||||||
CONFIG_NOHIGHMEM=y
|
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_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
|
* __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_begin[];
|
||||||
extern uint8_t __dtb_empty_root_end[];
|
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;
|
struct device_node *unittest_data_node = NULL, *np;
|
||||||
/*
|
/*
|
||||||
* __dtbo_testcases_begin[] and __dtbo_testcases_end[] are magically
|
* __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_begin[];
|
||||||
extern uint8_t __dtbo_testcases_end[];
|
extern uint8_t __dtbo_testcases_end[];
|
||||||
|
@ -3525,7 +3525,7 @@ out_skip_tests:
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* __dtbo_##overlay_name##_begin[] and __dtbo_##overlay_name##_end[] are
|
* __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) \
|
#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_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_XZ=y
|
||||||
# CONFIG_KERNEL_LZO is not set
|
|
||||||
# CONFIG_KERNEL_LZ4 is not set
|
|
||||||
CONFIG_SLUB=y
|
CONFIG_SLUB=y
|
||||||
CONFIG_SLUB_TINY=y
|
CONFIG_SLUB_TINY=y
|
||||||
|
|
|
@ -573,6 +573,21 @@ config VMLINUX_MAP
|
||||||
pieces of code get eliminated with
|
pieces of code get eliminated with
|
||||||
CONFIG_LD_DEAD_CODE_DATA_ELIMINATION.
|
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
|
config DEBUG_FORCE_WEAK_PER_CPU
|
||||||
bool "Force weak per-cpu definitions"
|
bool "Force weak per-cpu definitions"
|
||||||
depends on DEBUG_KERNEL
|
depends on DEBUG_KERNEL
|
||||||
|
|
|
@ -41,20 +41,6 @@ include $(srctree)/scripts/Makefile.compiler
|
||||||
include $(kbuild-file)
|
include $(kbuild-file)
|
||||||
include $(srctree)/scripts/Makefile.lib
|
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
|
ifndef obj
|
||||||
$(warning kbuild: Makefile.build is included improperly)
|
$(warning kbuild: Makefile.build is included improperly)
|
||||||
endif
|
endif
|
||||||
|
@ -71,7 +57,6 @@ endif
|
||||||
# subdir-builtin and subdir-modorder may contain duplications. Use $(sort ...)
|
# subdir-builtin and subdir-modorder may contain duplications. Use $(sort ...)
|
||||||
subdir-builtin := $(sort $(filter %/built-in.a, $(real-obj-y)))
|
subdir-builtin := $(sort $(filter %/built-in.a, $(real-obj-y)))
|
||||||
subdir-modorder := $(sort $(filter %/modules.order, $(obj-m)))
|
subdir-modorder := $(sort $(filter %/modules.order, $(obj-m)))
|
||||||
subdir-dtbslist := $(sort $(filter %/dtbs-list, $(dtb-y)))
|
|
||||||
|
|
||||||
targets-for-builtin := $(extra-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-builtin), $(real-obj-y))
|
||||||
targets += $(filter-out $(subdir-modorder), $(real-obj-m))
|
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)
|
# 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
|
# To build objects in subdirs, we need to descend into the directories
|
||||||
$(subdir-builtin): $(obj)/%/built-in.a: $(obj)/% ;
|
$(subdir-builtin): $(obj)/%/built-in.a: $(obj)/% ;
|
||||||
$(subdir-modorder): $(obj)/%/modules.order: $(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)
|
# 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
|
$(obj)/built-in.a: $(real-obj-y) FORCE
|
||||||
$(call if_changed,ar_builtin)
|
$(call if_changed,ar_builtin)
|
||||||
|
|
||||||
#
|
# This is a list of build artifacts from the current Makefile and its
|
||||||
# Rule to create modules.order and dtbs-list
|
# sub-directories. The timestamp should be updated when any of the member files.
|
||||||
#
|
|
||||||
# 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.
|
|
||||||
|
|
||||||
cmd_gen_order = { $(foreach m, $(real-prereqs), \
|
cmd_gen_order = { $(foreach m, $(real-prereqs), \
|
||||||
$(if $(filter %/$(notdir $@), $m), cat $m, echo $m);) :; } \
|
$(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
|
$(obj)/modules.order: $(obj-m) FORCE
|
||||||
$(call if_changed,gen_order)
|
$(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)
|
# 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), \
|
$(patsubst %$(strip $(1)),%$(sfx), \
|
||||||
$(filter %$(strip $(1)), $(targets))))
|
$(filter %$(strip $(1)), $(targets))))
|
||||||
# %.asn1.o <- %.asn1.[ch] <- %.asn1
|
# %.asn1.o <- %.asn1.[ch] <- %.asn1
|
||||||
# %.dtb.o <- %.dtb.S <- %.dtb <- %.dts
|
targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h)
|
||||||
# %.dtbo.o <- %.dtbo.S <- %.dtbo <- %.dtso
|
|
||||||
# %.lex.o <- %.lex.c <- %.l
|
# Include additional build rules when necessary
|
||||||
# %.tab.o <- %.tab.[ch] <- %.y
|
# ---------------------------------------------------------------------------
|
||||||
targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \
|
|
||||||
$(call intermediate_targets, .dtb.o, .dtb.S .dtb) \
|
# $(sort ...) is used here to remove duplicated words and excessive spaces.
|
||||||
$(call intermediate_targets, .dtbo.o, .dtbo.S .dtbo) \
|
hostprogs := $(sort $(hostprogs))
|
||||||
$(call intermediate_targets, .lex.o, .lex.c) \
|
ifneq ($(hostprogs),)
|
||||||
$(call intermediate_targets, .tab.o, .tab.c .tab.h)
|
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
|
# 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) \
|
targets += $(host-csingle) $(host-cmulti) $(host-cobjs) \
|
||||||
$(host-cxxmulti) $(host-cxxobjs) $(host-rust)
|
$(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))
|
obj-y := $(filter-out %/, $(obj-y))
|
||||||
endif
|
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
|
# Expand $(foo-objs) $(foo-y) etc. by replacing their individuals
|
||||||
suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s))))
|
suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s))))
|
||||||
# List composite targets that are constructed by combining other targets
|
# 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)
|
userprogs += $(userprogs-always-y) $(userprogs-always-m)
|
||||||
always-y += $(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
|
# Add subdir path
|
||||||
|
|
||||||
ifneq ($(obj),.)
|
ifneq ($(obj),.)
|
||||||
|
@ -104,9 +86,6 @@ lib-y := $(addprefix $(obj)/,$(lib-y))
|
||||||
real-obj-y := $(addprefix $(obj)/,$(real-obj-y))
|
real-obj-y := $(addprefix $(obj)/,$(real-obj-y))
|
||||||
real-obj-m := $(addprefix $(obj)/,$(real-obj-m))
|
real-obj-m := $(addprefix $(obj)/,$(real-obj-m))
|
||||||
multi-obj-m := $(addprefix $(obj)/, $(multi-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))
|
subdir-ym := $(addprefix $(obj)/,$(subdir-ym))
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
@ -238,7 +217,7 @@ modkern_rustflags = \
|
||||||
|
|
||||||
modkern_aflags = $(if $(part-of-module), \
|
modkern_aflags = $(if $(part-of-module), \
|
||||||
$(KBUILD_AFLAGS_MODULE) $(AFLAGS_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) \
|
c_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
|
||||||
-include $(srctree)/include/linux/compiler_types.h \
|
-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
|
rust_flags = $(_rust_flags) $(modkern_rustflags) @$(objtree)/include/generated/rustc_cfg
|
||||||
|
|
||||||
a_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
|
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 = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
|
||||||
$(_cpp_flags)
|
$(_cpp_flags)
|
||||||
|
|
||||||
ld_flags = $(KBUILD_LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F))
|
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
|
ifdef CONFIG_OBJTOOL
|
||||||
|
|
||||||
objtool := $(objtree)/tools/objtool/objtool
|
objtool := $(objtree)/tools/objtool/objtool
|
||||||
|
@ -350,94 +323,6 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
|
||||||
quiet_cmd_gzip = GZIP $@
|
quiet_cmd_gzip = GZIP $@
|
||||||
cmd_gzip = cat $(real-prereqs) | $(KGZIP) -n -f -9 > $@
|
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
|
# Bzip2
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
|
@ -30,8 +30,11 @@ quiet_cmd_cc_o_c = CC [M] $@
|
||||||
%.mod.o: %.mod.c FORCE
|
%.mod.o: %.mod.c FORCE
|
||||||
$(call if_changed_dep,cc_o_c)
|
$(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] $@
|
quiet_cmd_ld_ko_o = LD [M] $@
|
||||||
cmd_ld_ko_o += \
|
cmd_ld_ko_o = \
|
||||||
$(LD) -r $(KBUILD_LDFLAGS) \
|
$(LD) -r $(KBUILD_LDFLAGS) \
|
||||||
$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
|
$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
|
||||||
-T scripts/module.lds -o $@ $(filter %.o, $^)
|
-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, @:)
|
printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
|
||||||
|
|
||||||
# Re-generate module BTFs if either module's .ko or vmlinux changed
|
# 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)
|
+$(call if_changed_except,ld_ko_o,vmlinux)
|
||||||
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
|
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
|
||||||
+$(if $(newer-prereqs),$(call cmd,btf_ko))
|
+$(if $(newer-prereqs),$(call cmd,btf_ko))
|
||||||
endif
|
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.
|
# 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 $@
|
quiet_cmd_install_modorder = INSTALL $@
|
||||||
cmd_install_modorder = sed 's:^\(.*\)\.o$$:kernel/\1.ko:' $< > $@
|
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)
|
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)
|
$(call cmd,install)
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
@ -146,7 +148,7 @@ quiet_cmd_gzip = GZIP $@
|
||||||
quiet_cmd_xz = XZ $@
|
quiet_cmd_xz = XZ $@
|
||||||
cmd_xz = $(XZ) --check=crc32 --lzma2=dict=1MiB -f $<
|
cmd_xz = $(XZ) --check=crc32 --lzma2=dict=1MiB -f $<
|
||||||
quiet_cmd_zstd = ZSTD $@
|
quiet_cmd_zstd = ZSTD $@
|
||||||
cmd_zstd = $(ZSTD) -T0 --rm -f -q $<
|
cmd_zstd = $(ZSTD) --rm -f -q $<
|
||||||
|
|
||||||
$(dst)/%.ko.gz: $(dst)/%.ko FORCE
|
$(dst)/%.ko.gz: $(dst)/%.ko FORCE
|
||||||
$(call cmd,gzip)
|
$(call cmd,gzip)
|
||||||
|
|
|
@ -147,8 +147,7 @@ snap-pkg:
|
||||||
PHONY += pacman-pkg
|
PHONY += pacman-pkg
|
||||||
pacman-pkg:
|
pacman-pkg:
|
||||||
@ln -srf $(srctree)/scripts/package/PKGBUILD $(objtree)/PKGBUILD
|
@ln -srf $(srctree)/scripts/package/PKGBUILD $(objtree)/PKGBUILD
|
||||||
+objtree="$(realpath $(objtree))" \
|
+BUILDDIR="$(realpath $(objtree))/pacman" \
|
||||||
BUILDDIR="$(realpath $(objtree))/pacman" \
|
|
||||||
CARCH="$(UTS_MACHINE)" \
|
CARCH="$(UTS_MACHINE)" \
|
||||||
KBUILD_MAKEFLAGS="$(MAKEFLAGS)" \
|
KBUILD_MAKEFLAGS="$(MAKEFLAGS)" \
|
||||||
KBUILD_REVISION="$(shell $(srctree)/scripts/build-version)" \
|
KBUILD_REVISION="$(shell $(srctree)/scripts/build-version)" \
|
||||||
|
|
|
@ -33,6 +33,24 @@ targets += vmlinux
|
||||||
vmlinux: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE
|
vmlinux: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE
|
||||||
+$(call if_changed_dep,link_vmlinux)
|
+$(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.
|
# 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
|
# 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 $@
|
quiet_cmd_ld_vmlinux.o = LD $@
|
||||||
cmd_ld_vmlinux.o = \
|
cmd_ld_vmlinux.o = \
|
||||||
$(LD) ${KBUILD_LDFLAGS} -r -o $@ \
|
$(LD) ${KBUILD_LDFLAGS} -r -o $@ \
|
||||||
|
$(vmlinux-o-ld-args-y) \
|
||||||
$(addprefix -T , $(initcalls-lds)) \
|
$(addprefix -T , $(initcalls-lds)) \
|
||||||
--whole-archive vmlinux.a --no-whole-archive \
|
--whole-archive vmlinux.a --no-whole-archive \
|
||||||
--start-group $(KBUILD_VMLINUX_LIBS) --end-group \
|
--start-group $(KBUILD_VMLINUX_LIBS) --end-group \
|
||||||
|
|
|
@ -99,6 +99,8 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
|
||||||
|
#include <xalloc.h>
|
||||||
|
|
||||||
static void usage(void)
|
static void usage(void)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
|
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,
|
static void add_to_hashtable(const char *name, int len, unsigned int hash,
|
||||||
struct item *hashtab[])
|
struct item *hashtab[])
|
||||||
{
|
{
|
||||||
struct item *aux = malloc(sizeof(*aux) + len);
|
struct item *aux;
|
||||||
|
|
||||||
if (!aux) {
|
aux = xmalloc(sizeof(*aux) + len);
|
||||||
perror("fixdep:malloc");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
memcpy(aux->name, name, len);
|
memcpy(aux->name, name, len);
|
||||||
aux->len = len;
|
aux->len = len;
|
||||||
aux->hash = hash;
|
aux->hash = hash;
|
||||||
|
@ -228,11 +227,7 @@ static void *read_file(const char *filename)
|
||||||
perror(filename);
|
perror(filename);
|
||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
buf = malloc(st.st_size + 1);
|
buf = xmalloc(st.st_size + 1);
|
||||||
if (!buf) {
|
|
||||||
perror("fixdep: malloc");
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
if (read(fd, buf, st.st_size) != st.st_size) {
|
if (read(fd, buf, st.st_size) != st.st_size) {
|
||||||
perror("fixdep: read");
|
perror("fixdep: read");
|
||||||
exit(2);
|
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)])
|
#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
|
* hash_add - add an object to a hashtable
|
||||||
* @table: hashtable to add to
|
* @table: hashtable to add to
|
||||||
|
@ -24,6 +41,15 @@
|
||||||
#define hash_add(table, node, key) \
|
#define hash_add(table, node, key) \
|
||||||
hlist_add_head(node, hash_head(table, 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
|
* hash_for_each - iterate over a hashtable
|
||||||
* @table: hashtable to iterate
|
* @table: hashtable to iterate
|
||||||
|
@ -34,6 +60,18 @@
|
||||||
for (int _bkt = 0; _bkt < HASH_SIZE(table); _bkt++) \
|
for (int _bkt = 0; _bkt < HASH_SIZE(table); _bkt++) \
|
||||||
hlist_for_each_entry(obj, &table[_bkt], member)
|
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
|
* hash_for_each_possible - iterate over all possible objects hashing to the
|
||||||
* same bucket
|
* same bucket
|
||||||
|
@ -45,4 +83,16 @@
|
||||||
#define hash_for_each_possible(table, obj, member, key) \
|
#define hash_for_each_possible(table, obj, member, key) \
|
||||||
hlist_for_each_entry(obj, hash_head(table, key), member)
|
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 */
|
#endif /* HASHTABLE_H */
|
||||||
|
|
|
@ -268,6 +268,63 @@ static inline int list_empty(const struct list_head *head)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#define HLIST_HEAD_INIT { .first = NULL }
|
#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
|
* 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; \
|
||||||
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
|
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 */
|
#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 <ctype.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <xalloc.h>
|
||||||
|
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
|
||||||
|
|
||||||
#define KSYM_NAME_LEN 512
|
#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 */
|
* compressed together */
|
||||||
len++;
|
len++;
|
||||||
|
|
||||||
sym = malloc(sizeof(*sym) + len + 1);
|
sym = xmalloc(sizeof(*sym) + len + 1);
|
||||||
if (!sym) {
|
|
||||||
fprintf(stderr, "kallsyms failure: "
|
|
||||||
"unable to allocate required amount of memory\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
sym->addr = addr;
|
sym->addr = addr;
|
||||||
sym->len = len;
|
sym->len = len;
|
||||||
sym->sym[0] = type;
|
sym->sym[0] = type;
|
||||||
|
@ -278,12 +275,7 @@ static void read_map(const char *in)
|
||||||
|
|
||||||
if (table_cnt >= table_size) {
|
if (table_cnt >= table_size) {
|
||||||
table_size += 10000;
|
table_size += 10000;
|
||||||
table = realloc(table, sizeof(*table) * table_size);
|
table = xrealloc(table, sizeof(*table) * table_size);
|
||||||
if (!table) {
|
|
||||||
fprintf(stderr, "out of memory\n");
|
|
||||||
fclose(fp);
|
|
||||||
exit (1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
table[table_cnt++] = sym;
|
table[table_cnt++] = sym;
|
||||||
|
@ -300,15 +292,6 @@ static void output_label(const char *label)
|
||||||
printf("%s:\n", 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
|
/* uncompress a compressed symbol. When this function is called, the best table
|
||||||
* might still be compressed itself, so the function needs to be recursive */
|
* might still be compressed itself, so the function needs to be recursive */
|
||||||
static int expand_symbol(const unsigned char *data, int len, char *result)
|
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
|
/* table of offset markers, that give the offset in the compressed stream
|
||||||
* every 256 symbols */
|
* every 256 symbols */
|
||||||
markers_cnt = (table_cnt + 255) / 256;
|
markers_cnt = (table_cnt + 255) / 256;
|
||||||
markers = malloc(sizeof(*markers) * markers_cnt);
|
markers = xmalloc(sizeof(*markers) * markers_cnt);
|
||||||
if (!markers) {
|
|
||||||
fprintf(stderr, "kallsyms failure: "
|
|
||||||
"unable to allocate required memory\n");
|
|
||||||
exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
output_label("kallsyms_names");
|
output_label("kallsyms_names");
|
||||||
off = 0;
|
off = 0;
|
||||||
|
@ -477,17 +455,17 @@ static void write_src(void)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
long long offset;
|
long long offset;
|
||||||
int overflow;
|
bool overflow;
|
||||||
|
|
||||||
if (!absolute_percpu) {
|
if (!absolute_percpu) {
|
||||||
offset = table[i]->addr - relative_base;
|
offset = table[i]->addr - relative_base;
|
||||||
overflow = (offset < 0 || offset > UINT_MAX);
|
overflow = offset < 0 || offset > UINT_MAX;
|
||||||
} else if (symbol_absolute(table[i])) {
|
} else if (symbol_absolute(table[i])) {
|
||||||
offset = table[i]->addr;
|
offset = table[i]->addr;
|
||||||
overflow = (offset < 0 || offset > INT_MAX);
|
overflow = offset < 0 || offset > INT_MAX;
|
||||||
} else {
|
} else {
|
||||||
offset = relative_base - table[i]->addr - 1;
|
offset = relative_base - table[i]->addr - 1;
|
||||||
overflow = (offset < INT_MIN || offset >= 0);
|
overflow = offset < INT_MIN || offset >= 0;
|
||||||
}
|
}
|
||||||
if (overflow) {
|
if (overflow) {
|
||||||
fprintf(stderr, "kallsyms failure: "
|
fprintf(stderr, "kallsyms failure: "
|
||||||
|
@ -501,7 +479,11 @@ static void write_src(void)
|
||||||
printf("\n");
|
printf("\n");
|
||||||
|
|
||||||
output_label("kallsyms_relative_base");
|
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");
|
printf("\n");
|
||||||
|
|
||||||
sort_symbols_by_name();
|
sort_symbols_by_name();
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <xalloc.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
|
|
||||||
|
@ -395,6 +396,8 @@ load:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
expr_invalidate_all();
|
||||||
|
|
||||||
while (getline_stripped(&line, &line_asize, in) != -1) {
|
while (getline_stripped(&line, &line_asize, in) != -1) {
|
||||||
struct menu *choice;
|
struct menu *choice;
|
||||||
|
|
||||||
|
|
|
@ -9,44 +9,68 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <hash.h>
|
||||||
|
#include <xalloc.h>
|
||||||
|
#include "internal.h"
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
|
|
||||||
#define DEBUG_EXPR 0
|
#define DEBUG_EXPR 0
|
||||||
|
|
||||||
|
HASHTABLE_DEFINE(expr_hashtable, EXPR_HASHSIZE);
|
||||||
|
|
||||||
static struct expr *expr_eliminate_yn(struct expr *e);
|
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 *expr_alloc_symbol(struct symbol *sym)
|
||||||
{
|
{
|
||||||
struct expr *e = xcalloc(1, sizeof(*e));
|
return expr_lookup(E_SYMBOL, sym, NULL);
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = sym;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
|
struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
|
||||||
{
|
{
|
||||||
struct expr *e = xcalloc(1, sizeof(*e));
|
return expr_lookup(type, ce, NULL);
|
||||||
e->type = type;
|
|
||||||
e->left.expr = ce;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
|
struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
|
||||||
{
|
{
|
||||||
struct expr *e = xcalloc(1, sizeof(*e));
|
return expr_lookup(type, e1, e2);
|
||||||
e->type = type;
|
|
||||||
e->left.expr = e1;
|
|
||||||
e->right.expr = e2;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
|
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
|
||||||
{
|
{
|
||||||
struct expr *e = xcalloc(1, sizeof(*e));
|
return expr_lookup(type, s1, s2);
|
||||||
e->type = type;
|
|
||||||
e->left.sym = s1;
|
|
||||||
e->right.sym = s2;
|
|
||||||
return e;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
|
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;
|
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;
|
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)
|
static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
|
||||||
{
|
{
|
||||||
|
struct expr *l, *r;
|
||||||
|
|
||||||
/* Recurse down to leaves */
|
/* Recurse down to leaves */
|
||||||
|
|
||||||
if ((*ep1)->type == type) {
|
if ((*ep1)->type == type) {
|
||||||
__expr_eliminate_eq(type, &(*ep1)->left.expr, ep2);
|
l = (*ep1)->left.expr;
|
||||||
__expr_eliminate_eq(type, &(*ep1)->right.expr, ep2);
|
r = (*ep1)->right.expr;
|
||||||
|
__expr_eliminate_eq(type, &l, ep2);
|
||||||
|
__expr_eliminate_eq(type, &r, ep2);
|
||||||
|
*ep1 = expr_alloc_two(type, l, r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((*ep2)->type == type) {
|
if ((*ep2)->type == type) {
|
||||||
__expr_eliminate_eq(type, ep1, &(*ep2)->left.expr);
|
l = (*ep2)->left.expr;
|
||||||
__expr_eliminate_eq(type, ep1, &(*ep2)->right.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;
|
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. */
|
/* *ep1 and *ep2 are equal leaves. Prepare them for elimination. */
|
||||||
|
|
||||||
trans_count++;
|
trans_count++;
|
||||||
expr_free(*ep1); expr_free(*ep2);
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case E_OR:
|
case E_OR:
|
||||||
*ep1 = expr_alloc_symbol(&symbol_no);
|
*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
|
* equals some operand in the other (operands do not need to appear in the same
|
||||||
* order), recursively.
|
* 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
|
* 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);
|
return expr_is_yes(e1) && expr_is_yes(e2);
|
||||||
|
|
||||||
if (e1->type != e2->type)
|
if (e1->type != e2->type)
|
||||||
return 0;
|
return false;
|
||||||
switch (e1->type) {
|
switch (e1->type) {
|
||||||
case E_EQUAL:
|
case E_EQUAL:
|
||||||
case E_GEQ:
|
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);
|
return expr_eq(e1->left.expr, e2->left.expr);
|
||||||
case E_AND:
|
case E_AND:
|
||||||
case E_OR:
|
case E_OR:
|
||||||
e1 = expr_copy(e1);
|
|
||||||
e2 = expr_copy(e2);
|
|
||||||
old_count = trans_count;
|
old_count = trans_count;
|
||||||
expr_eliminate_eq(&e1, &e2);
|
expr_eliminate_eq(&e1, &e2);
|
||||||
res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
|
res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
|
||||||
e1->left.sym == e2->left.sym);
|
e1->left.sym == e2->left.sym);
|
||||||
expr_free(e1);
|
|
||||||
expr_free(e2);
|
|
||||||
trans_count = old_count;
|
trans_count = old_count;
|
||||||
return res;
|
return res;
|
||||||
case E_RANGE:
|
case E_RANGE:
|
||||||
|
@ -291,11 +249,11 @@ int expr_eq(struct expr *e1, struct expr *e2)
|
||||||
printf(" ?\n");
|
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):
|
* corresponding simplifications with swapped operands):
|
||||||
*
|
*
|
||||||
* expr && n -> n
|
* 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)
|
static struct expr *expr_eliminate_yn(struct expr *e)
|
||||||
{
|
{
|
||||||
struct expr *tmp;
|
struct expr *l, *r;
|
||||||
|
|
||||||
if (e) switch (e->type) {
|
if (e) switch (e->type) {
|
||||||
case E_AND:
|
case E_AND:
|
||||||
e->left.expr = expr_eliminate_yn(e->left.expr);
|
l = expr_eliminate_yn(e->left.expr);
|
||||||
e->right.expr = expr_eliminate_yn(e->right.expr);
|
r = expr_eliminate_yn(e->right.expr);
|
||||||
if (e->left.expr->type == E_SYMBOL) {
|
if (l->type == E_SYMBOL) {
|
||||||
if (e->left.expr->left.sym == &symbol_no) {
|
if (l->left.sym == &symbol_no)
|
||||||
expr_free(e->left.expr);
|
return l;
|
||||||
expr_free(e->right.expr);
|
else if (l->left.sym == &symbol_yes)
|
||||||
e->type = E_SYMBOL;
|
return r;
|
||||||
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 (r->type == E_SYMBOL) {
|
||||||
if (e->right.expr->left.sym == &symbol_no) {
|
if (r->left.sym == &symbol_no)
|
||||||
expr_free(e->left.expr);
|
return r;
|
||||||
expr_free(e->right.expr);
|
else if (r->left.sym == &symbol_yes)
|
||||||
e->type = E_SYMBOL;
|
return l;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case E_OR:
|
case E_OR:
|
||||||
e->left.expr = expr_eliminate_yn(e->left.expr);
|
l = expr_eliminate_yn(e->left.expr);
|
||||||
e->right.expr = expr_eliminate_yn(e->right.expr);
|
r = expr_eliminate_yn(e->right.expr);
|
||||||
if (e->left.expr->type == E_SYMBOL) {
|
if (l->type == E_SYMBOL) {
|
||||||
if (e->left.expr->left.sym == &symbol_no) {
|
if (l->left.sym == &symbol_no)
|
||||||
free(e->left.expr);
|
return r;
|
||||||
tmp = e->right.expr;
|
else if (l->left.sym == &symbol_yes)
|
||||||
*e = *(e->right.expr);
|
return l;
|
||||||
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 (r->type == E_SYMBOL) {
|
||||||
if (e->right.expr->left.sym == &symbol_no) {
|
if (r->left.sym == &symbol_no)
|
||||||
free(e->right.expr);
|
return l;
|
||||||
tmp = e->left.expr;
|
else if (r->left.sym == &symbol_yes)
|
||||||
*e = *(e->left.expr);
|
return r;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
@ -397,7 +315,7 @@ static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
|
||||||
struct symbol *sym1, *sym2;
|
struct symbol *sym1, *sym2;
|
||||||
|
|
||||||
if (expr_eq(e1, e2))
|
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)
|
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
|
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) {
|
if (sym1->type == S_BOOLEAN) {
|
||||||
|
// a || !a -> y
|
||||||
if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
|
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))
|
(e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
|
||||||
return expr_alloc_symbol(&symbol_yes);
|
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;
|
struct symbol *sym1, *sym2;
|
||||||
|
|
||||||
if (expr_eq(e1, e2))
|
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)
|
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
|
||||||
return NULL;
|
return NULL;
|
||||||
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
|
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)
|
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 */
|
/* Recurse down to leaves */
|
||||||
|
|
||||||
if ((*ep1)->type == type) {
|
if ((*ep1)->type == type) {
|
||||||
expr_eliminate_dups1(type, &(*ep1)->left.expr, ep2);
|
l = (*ep1)->left.expr;
|
||||||
expr_eliminate_dups1(type, &(*ep1)->right.expr, ep2);
|
r = (*ep1)->right.expr;
|
||||||
|
expr_eliminate_dups1(type, &l, ep2);
|
||||||
|
expr_eliminate_dups1(type, &r, ep2);
|
||||||
|
*ep1 = expr_alloc_two(type, l, r);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((*ep2)->type == type) {
|
if ((*ep2)->type == type) {
|
||||||
expr_eliminate_dups1(type, ep1, &(*ep2)->left.expr);
|
l = (*ep2)->left.expr;
|
||||||
expr_eliminate_dups1(type, ep1, &(*ep2)->right.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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* *ep1 and *ep2 are leaves. Compare and process them. */
|
/* *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) {
|
switch (type) {
|
||||||
case E_OR:
|
case E_OR:
|
||||||
tmp = expr_join_or(*ep1, *ep2);
|
tmp = expr_join_or(*ep1, *ep2);
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
expr_free(*ep1); expr_free(*ep2);
|
|
||||||
*ep1 = expr_alloc_symbol(&symbol_no);
|
*ep1 = expr_alloc_symbol(&symbol_no);
|
||||||
*ep2 = tmp;
|
*ep2 = tmp;
|
||||||
trans_count++;
|
trans_count++;
|
||||||
|
@ -598,7 +512,6 @@ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct
|
||||||
case E_AND:
|
case E_AND:
|
||||||
tmp = expr_join_and(*ep1, *ep2);
|
tmp = expr_join_and(*ep1, *ep2);
|
||||||
if (tmp) {
|
if (tmp) {
|
||||||
expr_free(*ep1); expr_free(*ep2);
|
|
||||||
*ep1 = expr_alloc_symbol(&symbol_yes);
|
*ep1 = expr_alloc_symbol(&symbol_yes);
|
||||||
*ep2 = tmp;
|
*ep2 = tmp;
|
||||||
trans_count++;
|
trans_count++;
|
||||||
|
@ -628,10 +541,15 @@ struct expr *expr_eliminate_dups(struct expr *e)
|
||||||
|
|
||||||
oldcount = trans_count;
|
oldcount = trans_count;
|
||||||
do {
|
do {
|
||||||
|
struct expr *l, *r;
|
||||||
|
|
||||||
trans_count = 0;
|
trans_count = 0;
|
||||||
switch (e->type) {
|
switch (e->type) {
|
||||||
case E_OR: case E_AND:
|
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:
|
default:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
@ -645,12 +563,34 @@ struct expr *expr_eliminate_dups(struct expr *e)
|
||||||
* Performs various simplifications involving logical operators and
|
* Performs various simplifications involving logical operators and
|
||||||
* comparisons.
|
* 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.
|
* Allocates and returns a new expression.
|
||||||
*/
|
*/
|
||||||
struct expr *expr_transform(struct expr *e)
|
struct expr *expr_transform(struct expr *e)
|
||||||
{
|
{
|
||||||
struct expr *tmp;
|
|
||||||
|
|
||||||
if (!e)
|
if (!e)
|
||||||
return NULL;
|
return NULL;
|
||||||
switch (e->type) {
|
switch (e->type) {
|
||||||
|
@ -663,8 +603,9 @@ struct expr *expr_transform(struct expr *e)
|
||||||
case E_SYMBOL:
|
case E_SYMBOL:
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
e->left.expr = expr_transform(e->left.expr);
|
e = expr_alloc_two(e->type,
|
||||||
e->right.expr = expr_transform(e->right.expr);
|
expr_transform(e->left.expr),
|
||||||
|
expr_transform(e->right.expr));
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (e->type) {
|
switch (e->type) {
|
||||||
|
@ -672,21 +613,19 @@ struct expr *expr_transform(struct expr *e)
|
||||||
if (e->left.sym->type != S_BOOLEAN)
|
if (e->left.sym->type != S_BOOLEAN)
|
||||||
break;
|
break;
|
||||||
if (e->right.sym == &symbol_no) {
|
if (e->right.sym == &symbol_no) {
|
||||||
e->type = E_NOT;
|
// A=n -> !A
|
||||||
e->left.expr = expr_alloc_symbol(e->left.sym);
|
e = expr_alloc_one(E_NOT, expr_alloc_symbol(e->left.sym));
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e->right.sym == &symbol_mod) {
|
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);
|
printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
|
||||||
e->type = E_SYMBOL;
|
e = expr_alloc_symbol(&symbol_no);
|
||||||
e->left.sym = &symbol_no;
|
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e->right.sym == &symbol_yes) {
|
if (e->right.sym == &symbol_yes) {
|
||||||
e->type = E_SYMBOL;
|
// A=y -> A
|
||||||
e->right.sym = NULL;
|
e = expr_alloc_symbol(e->left.sym);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -694,104 +633,71 @@ struct expr *expr_transform(struct expr *e)
|
||||||
if (e->left.sym->type != S_BOOLEAN)
|
if (e->left.sym->type != S_BOOLEAN)
|
||||||
break;
|
break;
|
||||||
if (e->right.sym == &symbol_no) {
|
if (e->right.sym == &symbol_no) {
|
||||||
e->type = E_SYMBOL;
|
// A!=n -> A
|
||||||
e->right.sym = NULL;
|
e = expr_alloc_symbol(e->left.sym);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e->right.sym == &symbol_mod) {
|
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);
|
printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
|
||||||
e->type = E_SYMBOL;
|
e = expr_alloc_symbol(&symbol_yes);
|
||||||
e->left.sym = &symbol_yes;
|
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (e->right.sym == &symbol_yes) {
|
if (e->right.sym == &symbol_yes) {
|
||||||
e->type = E_NOT;
|
// A!=y -> !A
|
||||||
e->left.expr = expr_alloc_symbol(e->left.sym);
|
e = expr_alloc_one(E_NOT, e->left.expr);
|
||||||
e->right.sym = NULL;
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case E_NOT:
|
case E_NOT:
|
||||||
switch (e->left.expr->type) {
|
switch (e->left.expr->type) {
|
||||||
case E_NOT:
|
case E_NOT:
|
||||||
// !!a -> a
|
// !!A -> A
|
||||||
tmp = e->left.expr->left.expr;
|
e = e->left.expr->left.expr;
|
||||||
free(e->left.expr);
|
|
||||||
free(e);
|
|
||||||
e = tmp;
|
|
||||||
e = expr_transform(e);
|
|
||||||
break;
|
break;
|
||||||
case E_EQUAL:
|
case E_EQUAL:
|
||||||
case E_UNEQUAL:
|
case E_UNEQUAL:
|
||||||
// !a='x' -> a!='x'
|
// !(A=B) -> A!=B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_comp(e->left.expr->type == E_EQUAL ? E_UNEQUAL : E_EQUAL,
|
||||||
free(e);
|
e->left.expr->left.sym,
|
||||||
e = tmp;
|
e->left.expr->right.sym);
|
||||||
e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
|
|
||||||
break;
|
break;
|
||||||
case E_LEQ:
|
case E_LEQ:
|
||||||
case E_GEQ:
|
case E_GEQ:
|
||||||
// !a<='x' -> a>'x'
|
// !(A<=B) -> A>B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_comp(e->left.expr->type == E_LEQ ? E_GTH : E_LTH,
|
||||||
free(e);
|
e->left.expr->left.sym,
|
||||||
e = tmp;
|
e->left.expr->right.sym);
|
||||||
e->type = e->type == E_LEQ ? E_GTH : E_LTH;
|
|
||||||
break;
|
break;
|
||||||
case E_LTH:
|
case E_LTH:
|
||||||
case E_GTH:
|
case E_GTH:
|
||||||
// !a<'x' -> a>='x'
|
// !(A<B) -> A>=B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_comp(e->left.expr->type == E_LTH ? E_GEQ : E_LEQ,
|
||||||
free(e);
|
e->left.expr->left.sym,
|
||||||
e = tmp;
|
e->left.expr->right.sym);
|
||||||
e->type = e->type == E_LTH ? E_GEQ : E_LEQ;
|
|
||||||
break;
|
break;
|
||||||
case E_OR:
|
case E_OR:
|
||||||
// !(a || b) -> !a && !b
|
// !(A || B) -> !A && !B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_and(expr_alloc_one(E_NOT, e->left.expr->left.expr),
|
||||||
e->type = E_AND;
|
expr_alloc_one(E_NOT, e->left.expr->right.expr));
|
||||||
e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
|
|
||||||
tmp->type = E_NOT;
|
|
||||||
tmp->right.expr = NULL;
|
|
||||||
e = expr_transform(e);
|
e = expr_transform(e);
|
||||||
break;
|
break;
|
||||||
case E_AND:
|
case E_AND:
|
||||||
// !(a && b) -> !a || !b
|
// !(A && B) -> !A || !B
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_or(expr_alloc_one(E_NOT, e->left.expr->left.expr),
|
||||||
e->type = E_OR;
|
expr_alloc_one(E_NOT, e->left.expr->right.expr));
|
||||||
e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
|
|
||||||
tmp->type = E_NOT;
|
|
||||||
tmp->right.expr = NULL;
|
|
||||||
e = expr_transform(e);
|
e = expr_transform(e);
|
||||||
break;
|
break;
|
||||||
case E_SYMBOL:
|
case E_SYMBOL:
|
||||||
if (e->left.expr->left.sym == &symbol_yes) {
|
if (e->left.expr->left.sym == &symbol_yes)
|
||||||
// !'y' -> 'n'
|
// !'y' -> 'n'
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_symbol(&symbol_no);
|
||||||
free(e);
|
else if (e->left.expr->left.sym == &symbol_mod)
|
||||||
e = tmp;
|
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = &symbol_no;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (e->left.expr->left.sym == &symbol_mod) {
|
|
||||||
// !'m' -> 'm'
|
// !'m' -> 'm'
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_symbol(&symbol_mod);
|
||||||
free(e);
|
else if (e->left.expr->left.sym == &symbol_no)
|
||||||
e = tmp;
|
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = &symbol_mod;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (e->left.expr->left.sym == &symbol_no) {
|
|
||||||
// !'n' -> 'y'
|
// !'n' -> 'y'
|
||||||
tmp = e->left.expr;
|
e = expr_alloc_symbol(&symbol_yes);
|
||||||
free(e);
|
|
||||||
e = tmp;
|
|
||||||
e->type = E_SYMBOL;
|
|
||||||
e->left.sym = &symbol_yes;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
|
@ -803,10 +709,10 @@ struct expr *expr_transform(struct expr *e)
|
||||||
return 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)
|
if (!dep)
|
||||||
return 0;
|
return false;
|
||||||
|
|
||||||
switch (dep->type) {
|
switch (dep->type) {
|
||||||
case E_AND:
|
case E_AND:
|
||||||
|
@ -828,7 +734,7 @@ int expr_contains_symbol(struct expr *dep, struct symbol *sym)
|
||||||
default:
|
default:
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
return 0;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
|
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:
|
case E_EQUAL:
|
||||||
if (type == E_EQUAL) {
|
if (type == E_EQUAL) {
|
||||||
if (sym == &symbol_yes)
|
if (sym == &symbol_yes)
|
||||||
return expr_copy(e);
|
return e;
|
||||||
if (sym == &symbol_mod)
|
if (sym == &symbol_mod)
|
||||||
return expr_alloc_symbol(&symbol_no);
|
return expr_alloc_symbol(&symbol_no);
|
||||||
if (sym == &symbol_no)
|
if (sym == &symbol_no)
|
||||||
return expr_alloc_one(E_NOT, expr_copy(e));
|
return expr_alloc_one(E_NOT, e);
|
||||||
} else {
|
} else {
|
||||||
if (sym == &symbol_yes)
|
if (sym == &symbol_yes)
|
||||||
return expr_alloc_one(E_NOT, expr_copy(e));
|
return expr_alloc_one(E_NOT, e);
|
||||||
if (sym == &symbol_mod)
|
if (sym == &symbol_mod)
|
||||||
return expr_alloc_symbol(&symbol_yes);
|
return expr_alloc_symbol(&symbol_yes);
|
||||||
if (sym == &symbol_no)
|
if (sym == &symbol_no)
|
||||||
return expr_copy(e);
|
return e;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case E_SYMBOL:
|
case E_SYMBOL:
|
||||||
|
@ -981,7 +887,7 @@ static enum string_value_kind expr_parse_string(const char *str,
|
||||||
? kind : k_string;
|
? kind : k_string;
|
||||||
}
|
}
|
||||||
|
|
||||||
tristate expr_calc_value(struct expr *e)
|
static tristate __expr_calc_value(struct expr *e)
|
||||||
{
|
{
|
||||||
tristate val1, val2;
|
tristate val1, val2;
|
||||||
const char *str1, *str2;
|
const char *str1, *str2;
|
||||||
|
@ -989,9 +895,6 @@ tristate expr_calc_value(struct expr *e)
|
||||||
union string_value lval = {}, rval = {};
|
union string_value lval = {}, rval = {};
|
||||||
int res;
|
int res;
|
||||||
|
|
||||||
if (!e)
|
|
||||||
return yes;
|
|
||||||
|
|
||||||
switch (e->type) {
|
switch (e->type) {
|
||||||
case E_SYMBOL:
|
case E_SYMBOL:
|
||||||
sym_calc_value(e->left.sym);
|
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)
|
static int expr_compare_type(enum expr_type t1, enum expr_type t2)
|
||||||
{
|
{
|
||||||
if (t1 == t2)
|
if (t1 == t2)
|
||||||
|
|
|
@ -29,12 +29,26 @@ enum expr_type {
|
||||||
};
|
};
|
||||||
|
|
||||||
union expr_data {
|
union expr_data {
|
||||||
struct expr *expr;
|
struct expr * const expr;
|
||||||
struct symbol *sym;
|
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 expr {
|
||||||
|
struct hlist_node node;
|
||||||
enum expr_type type;
|
enum expr_type type;
|
||||||
|
tristate val;
|
||||||
|
bool val_is_valid;
|
||||||
union expr_data left, right;
|
union expr_data left, right;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -168,7 +182,6 @@ enum prop_type {
|
||||||
P_SELECT, /* select BAR */
|
P_SELECT, /* select BAR */
|
||||||
P_IMPLY, /* imply BAR */
|
P_IMPLY, /* imply BAR */
|
||||||
P_RANGE, /* range 7..100 (for a symbol) */
|
P_RANGE, /* range 7..100 (for a symbol) */
|
||||||
P_SYMBOL, /* where a symbol is defined */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct property {
|
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_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_and(struct expr *e1, struct expr *e2);
|
||||||
struct expr *expr_alloc_or(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);
|
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);
|
tristate expr_calc_value(struct expr *e);
|
||||||
struct expr *expr_eliminate_dups(struct expr *e);
|
struct expr *expr_eliminate_dups(struct expr *e);
|
||||||
struct expr *expr_transform(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);
|
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);
|
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,
|
void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
|
||||||
tristate pr_type, const char *title);
|
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);
|
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) \
|
#define for_all_symbols(sym) \
|
||||||
hash_for_each(sym_hashtable, sym, node)
|
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;
|
struct menu;
|
||||||
|
|
||||||
extern struct menu *current_menu, *current_entry;
|
extern struct menu *current_menu, *current_entry;
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <xalloc.h>
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
#include "preprocess.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 */
|
/* util.c */
|
||||||
unsigned int strhash(const char *s);
|
|
||||||
const char *file_lookup(const char *name);
|
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 */
|
/* lexer.l */
|
||||||
int yylex(void);
|
int yylex(void);
|
||||||
|
|
|
@ -20,6 +20,7 @@
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <list.h>
|
#include <list.h>
|
||||||
|
#include <xalloc.h>
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
#include "lxdialog/dialog.h"
|
#include "lxdialog/dialog.h"
|
||||||
#include "mnconf-common.h"
|
#include "mnconf-common.h"
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include <list.h>
|
#include <list.h>
|
||||||
|
#include <xalloc.h>
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
|
|
||||||
|
@ -78,10 +79,8 @@ void menu_add_entry(struct symbol *sym)
|
||||||
*last_entry_ptr = menu;
|
*last_entry_ptr = menu;
|
||||||
last_entry_ptr = &menu->next;
|
last_entry_ptr = &menu->next;
|
||||||
current_entry = menu;
|
current_entry = menu;
|
||||||
if (sym) {
|
if (sym)
|
||||||
menu_add_symbol(P_SYMBOL, sym, NULL);
|
|
||||||
list_add_tail(&menu->link, &sym->menus);
|
list_add_tail(&menu->link, &sym->menus);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct menu *menu_add_menu(void)
|
struct menu *menu_add_menu(void)
|
||||||
|
@ -108,12 +107,13 @@ static struct expr *rewrite_m(struct expr *e)
|
||||||
|
|
||||||
switch (e->type) {
|
switch (e->type) {
|
||||||
case E_NOT:
|
case E_NOT:
|
||||||
e->left.expr = rewrite_m(e->left.expr);
|
e = expr_alloc_one(E_NOT, rewrite_m(e->left.expr));
|
||||||
break;
|
break;
|
||||||
case E_OR:
|
case E_OR:
|
||||||
case E_AND:
|
case E_AND:
|
||||||
e->left.expr = rewrite_m(e->left.expr);
|
e = expr_alloc_two(e->type,
|
||||||
e->right.expr = rewrite_m(e->right.expr);
|
rewrite_m(e->left.expr),
|
||||||
|
rewrite_m(e->right.expr));
|
||||||
break;
|
break;
|
||||||
case E_SYMBOL:
|
case E_SYMBOL:
|
||||||
/* change 'm' into 'm' && MODULES */
|
/* 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;
|
struct menu *menu = current_entry;
|
||||||
|
|
||||||
while ((menu = menu->parent) != NULL) {
|
while ((menu = menu->parent) != NULL) {
|
||||||
struct expr *dup_expr;
|
|
||||||
|
|
||||||
if (!menu->visibility)
|
if (!menu->visibility)
|
||||||
continue;
|
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,
|
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 = rewrite_m(menu->dep);
|
||||||
basedep = expr_transform(basedep);
|
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);
|
basedep = expr_eliminate_dups(basedep);
|
||||||
menu->dep = 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 = rewrite_m(prop->visible.expr);
|
||||||
dep = expr_transform(dep);
|
dep = expr_transform(dep);
|
||||||
dep = expr_alloc_and(expr_copy(basedep), dep);
|
dep = expr_alloc_and(basedep, dep);
|
||||||
dep = expr_eliminate_dups(dep);
|
dep = expr_eliminate_dups(dep);
|
||||||
prop->visible.expr = dep;
|
prop->visible.expr = dep;
|
||||||
|
|
||||||
|
@ -378,11 +368,11 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
|
||||||
if (prop->type == P_SELECT) {
|
if (prop->type == P_SELECT) {
|
||||||
struct symbol *es = prop_get_symbol(prop);
|
struct symbol *es = prop_get_symbol(prop);
|
||||||
es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
|
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) {
|
} else if (prop->type == P_IMPLY) {
|
||||||
struct symbol *es = prop_get_symbol(prop);
|
struct symbol *es = prop_get_symbol(prop);
|
||||||
es->implied.expr = expr_alloc_or(es->implied.expr,
|
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_trans_compare(dep, E_UNEQUAL, &symbol_no);
|
||||||
dep = expr_eliminate_dups(expr_transform(dep));
|
dep = expr_eliminate_dups(expr_transform(dep));
|
||||||
dep2 = expr_copy(basedep);
|
dep2 = basedep;
|
||||||
expr_eliminate_eq(&dep, &dep2);
|
expr_eliminate_eq(&dep, &dep2);
|
||||||
expr_free(dep);
|
|
||||||
if (!expr_is_yes(dep2)) {
|
if (!expr_is_yes(dep2)) {
|
||||||
/* Not superset, quit */
|
/* Not superset, quit */
|
||||||
expr_free(dep2);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
/* Superset, put in submenu */
|
/* Superset, put in submenu */
|
||||||
expr_free(dep2);
|
|
||||||
next:
|
next:
|
||||||
_menu_finalize(menu, false);
|
_menu_finalize(menu, false);
|
||||||
menu->parent = parent;
|
menu->parent = parent;
|
||||||
last_menu = menu;
|
last_menu = menu;
|
||||||
}
|
}
|
||||||
expr_free(basedep);
|
|
||||||
if (last_menu) {
|
if (last_menu) {
|
||||||
parent->list = parent->next;
|
parent->list = parent->next;
|
||||||
parent->next = last_menu->next;
|
parent->next = last_menu->next;
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <list.h>
|
#include <list.h>
|
||||||
|
#include <xalloc.h>
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
#include "mnconf-common.h"
|
#include "mnconf-common.h"
|
||||||
#include "nconf.h"
|
#include "nconf.h"
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
*
|
*
|
||||||
* Derived from menuconfig.
|
* Derived from menuconfig.
|
||||||
*/
|
*/
|
||||||
|
#include <xalloc.h>
|
||||||
#include "nconf.h"
|
#include "nconf.h"
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#include <xalloc.h>
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "preprocess.h"
|
#include "preprocess.h"
|
||||||
|
@ -530,14 +531,6 @@ void conf_parse(const char *name)
|
||||||
yydebug = 1;
|
yydebug = 1;
|
||||||
yyparse();
|
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,
|
str_printf(&autoconf_cmd,
|
||||||
"\n"
|
"\n"
|
||||||
"$(autoconfig): $(deps_config)\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);
|
print_quoted_string(out, prop->text);
|
||||||
fputc('\n', out);
|
fputc('\n', out);
|
||||||
break;
|
break;
|
||||||
case P_SYMBOL:
|
|
||||||
fputs( " symbol ", out);
|
|
||||||
fprintf(out, "%s\n", prop->menu->sym->name);
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
fprintf(out, " unknown prop %d!\n", prop->type);
|
fprintf(out, " unknown prop %d!\n", prop->type);
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include <array_size.h>
|
#include <array_size.h>
|
||||||
#include <list.h>
|
#include <list.h>
|
||||||
|
#include <xalloc.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
#include "preprocess.h"
|
#include "preprocess.h"
|
||||||
|
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include <xalloc.h>
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
#include "qconf.h"
|
#include "qconf.h"
|
||||||
|
|
||||||
|
@ -1094,7 +1095,6 @@ QString ConfigInfoView::debug_info(struct symbol *sym)
|
||||||
case P_RANGE:
|
case P_RANGE:
|
||||||
case P_COMMENT:
|
case P_COMMENT:
|
||||||
case P_IMPLY:
|
case P_IMPLY:
|
||||||
case P_SYMBOL:
|
|
||||||
stream << prop_get_type_name(prop->type);
|
stream << prop_get_type_name(prop->type);
|
||||||
stream << ": ";
|
stream << ": ";
|
||||||
expr_print(prop->expr, expr_print_help,
|
expr_print(prop->expr, expr_print_help,
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <regex.h>
|
#include <regex.h>
|
||||||
|
|
||||||
|
#include <hash.h>
|
||||||
|
#include <xalloc.h>
|
||||||
#include "internal.h"
|
#include "internal.h"
|
||||||
#include "lkc.h"
|
#include "lkc.h"
|
||||||
|
|
||||||
|
@ -517,6 +519,7 @@ void sym_clear_all_valid(void)
|
||||||
|
|
||||||
for_all_symbols(sym)
|
for_all_symbols(sym)
|
||||||
sym->flags &= ~SYMBOL_VALID;
|
sym->flags &= ~SYMBOL_VALID;
|
||||||
|
expr_invalidate_all();
|
||||||
conf_set_changed(true);
|
conf_set_changed(true);
|
||||||
sym_calc_value(modules_sym);
|
sym_calc_value(modules_sym);
|
||||||
}
|
}
|
||||||
|
@ -892,7 +895,7 @@ struct symbol *sym_lookup(const char *name, int flags)
|
||||||
case 'n': return &symbol_no;
|
case 'n': return &symbol_no;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash = strhash(name);
|
hash = hash_str(name);
|
||||||
|
|
||||||
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
|
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
|
||||||
if (symbol->name &&
|
if (symbol->name &&
|
||||||
|
@ -935,7 +938,7 @@ struct symbol *sym_find(const char *name)
|
||||||
case 'n': return &symbol_no;
|
case 'n': return &symbol_no;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash = strhash(name);
|
hash = hash_str(name);
|
||||||
|
|
||||||
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
|
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
|
||||||
if (symbol->name &&
|
if (symbol->name &&
|
||||||
|
@ -1321,8 +1324,6 @@ const char *prop_get_type_name(enum prop_type type)
|
||||||
return "imply";
|
return "imply";
|
||||||
case P_RANGE:
|
case P_RANGE:
|
||||||
return "range";
|
return "range";
|
||||||
case P_SYMBOL:
|
|
||||||
return "symbol";
|
|
||||||
case P_UNKNOWN:
|
case P_UNKNOWN:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,19 +8,11 @@
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <hash.h>
|
||||||
#include <hashtable.h>
|
#include <hashtable.h>
|
||||||
|
#include <xalloc.h>
|
||||||
#include "lkc.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 */
|
/* hash table of all parsed Kconfig files */
|
||||||
static HASHTABLE_DEFINE(file_hashtable, 1U << 11);
|
static HASHTABLE_DEFINE(file_hashtable, 1U << 11);
|
||||||
|
|
||||||
|
@ -34,7 +26,7 @@ const char *file_lookup(const char *name)
|
||||||
{
|
{
|
||||||
struct file *file;
|
struct file *file;
|
||||||
size_t len;
|
size_t len;
|
||||||
int hash = strhash(name);
|
int hash = hash_str(name);
|
||||||
|
|
||||||
hash_for_each_possible(file_hashtable, file, node, hash)
|
hash_for_each_possible(file_hashtable, file, node, hash)
|
||||||
if (!strcmp(name, file->name))
|
if (!strcmp(name, file->name))
|
||||||
|
@ -102,52 +94,3 @@ char *str_get(const struct gstr *gs)
|
||||||
{
|
{
|
||||||
return gs->s;
|
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=
|
strip_debug=
|
||||||
|
|
||||||
if is_enabled CONFIG_KALLSYMS; then
|
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
|
kallsyms .tmp_vmlinux.kallsyms0.syms .tmp_vmlinux0.kallsyms
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,6 @@ int
|
||||||
main(int argc, char **argv)
|
main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
unsigned char ei[EI_NIDENT];
|
unsigned char ei[EI_NIDENT];
|
||||||
union { short s; char c[2]; } endian_test;
|
|
||||||
|
|
||||||
if (fread(ei, 1, EI_NIDENT, stdin) != EI_NIDENT) {
|
if (fread(ei, 1, EI_NIDENT, stdin) != EI_NIDENT) {
|
||||||
fprintf(stderr, "Error: input truncated\n");
|
fprintf(stderr, "Error: input truncated\n");
|
||||||
|
@ -28,30 +27,6 @@ main(int argc, char **argv)
|
||||||
default:
|
default:
|
||||||
exit(1);
|
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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
|
|
||||||
#include <hashtable.h>
|
#include <hashtable.h>
|
||||||
#include <list.h>
|
#include <list.h>
|
||||||
|
#include <xalloc.h>
|
||||||
#include "modpost.h"
|
#include "modpost.h"
|
||||||
#include "../../include/linux/license.h"
|
#include "../../include/linux/license.h"
|
||||||
|
|
||||||
|
@ -50,6 +51,9 @@ static bool error_occurred;
|
||||||
|
|
||||||
static bool extra_warn;
|
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
|
* Cut off the warnings when there are too many. This typically occurs when
|
||||||
* vmlinux is missing. ('make modules' without building vmlinux.)
|
* 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))
|
#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;
|
va_list arglist;
|
||||||
|
|
||||||
switch (loglevel) {
|
if (is_error) {
|
||||||
case LOG_WARN:
|
|
||||||
fprintf(stderr, "WARNING: ");
|
|
||||||
break;
|
|
||||||
case LOG_ERROR:
|
|
||||||
fprintf(stderr, "ERROR: ");
|
fprintf(stderr, "ERROR: ");
|
||||||
error_occurred = true;
|
error_occurred = true;
|
||||||
break;
|
} else {
|
||||||
default: /* invalid loglevel, ignore */
|
fprintf(stderr, "WARNING: ");
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(stderr, "modpost: ");
|
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;
|
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)
|
char *read_text_file(const char *filename)
|
||||||
{
|
{
|
||||||
struct stat st;
|
struct stat st;
|
||||||
|
@ -120,7 +111,7 @@ char *read_text_file(const char *filename)
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
buf = NOFAIL(malloc(st.st_size + 1));
|
buf = xmalloc(st.st_size + 1);
|
||||||
|
|
||||||
nbytes = st.st_size;
|
nbytes = st.st_size;
|
||||||
|
|
||||||
|
@ -178,7 +169,7 @@ static struct module *new_module(const char *name, size_t namelen)
|
||||||
{
|
{
|
||||||
struct module *mod;
|
struct module *mod;
|
||||||
|
|
||||||
mod = NOFAIL(malloc(sizeof(*mod) + namelen + 1));
|
mod = xmalloc(sizeof(*mod) + namelen + 1);
|
||||||
memset(mod, 0, sizeof(*mod));
|
memset(mod, 0, sizeof(*mod));
|
||||||
|
|
||||||
INIT_LIST_HEAD(&mod->exported_symbols);
|
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)
|
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));
|
memset(s, 0, sizeof(*s));
|
||||||
strcpy(s->name, name);
|
strcpy(s->name, name);
|
||||||
|
@ -310,8 +301,7 @@ static void add_namespace(struct list_head *head, const char *namespace)
|
||||||
struct namespace_list *ns_entry;
|
struct namespace_list *ns_entry;
|
||||||
|
|
||||||
if (!contains_namespace(head, namespace)) {
|
if (!contains_namespace(head, namespace)) {
|
||||||
ns_entry = NOFAIL(malloc(sizeof(*ns_entry) +
|
ns_entry = xmalloc(sizeof(*ns_entry) + strlen(namespace) + 1);
|
||||||
strlen(namespace) + 1));
|
|
||||||
strcpy(ns_entry->namespace, namespace);
|
strcpy(ns_entry->namespace, namespace);
|
||||||
list_add_tail(&ns_entry->list, head);
|
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 = alloc_symbol(name);
|
||||||
s->module = mod;
|
s->module = mod;
|
||||||
s->is_gpl_only = gpl_only;
|
s->is_gpl_only = gpl_only;
|
||||||
s->namespace = NOFAIL(strdup(namespace));
|
s->namespace = xstrdup(namespace);
|
||||||
list_add_tail(&s->list, &mod->exported_symbols);
|
list_add_tail(&s->list, &mod->exported_symbols);
|
||||||
hash_add_symbol(s);
|
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 */
|
/* Not an ELF file - silently ignore it */
|
||||||
return 0;
|
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 */
|
/* Fix endianness in ELF header */
|
||||||
hdr->e_type = TO_NATIVE(hdr->e_type);
|
hdr->e_type = TO_NATIVE(hdr->e_type);
|
||||||
hdr->e_machine = TO_NATIVE(hdr->e_machine);
|
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)
|
if (ELF_ST_TYPE(sym->st_info) == STT_SPARC_REGISTER)
|
||||||
break;
|
break;
|
||||||
if (symname[0] == '.') {
|
if (symname[0] == '.') {
|
||||||
char *munged = NOFAIL(strdup(symname));
|
char *munged = xstrdup(symname);
|
||||||
munged[0] = '_';
|
munged[0] = '_';
|
||||||
munged[1] = toupper(munged[1]);
|
munged[1] = toupper(munged[1]);
|
||||||
symname = munged;
|
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)
|
static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
|
||||||
{
|
{
|
||||||
if (sym)
|
return sym ? elf->strtab + sym->st_name : "";
|
||||||
return elf->strtab + sym->st_name;
|
|
||||||
else
|
|
||||||
return "(unknown)";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1006,6 +1005,7 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
|
||||||
Elf_Sym *from;
|
Elf_Sym *from;
|
||||||
const char *tosym;
|
const char *tosym;
|
||||||
const char *fromsym;
|
const char *fromsym;
|
||||||
|
char taddr_str[16];
|
||||||
|
|
||||||
from = find_fromsym(elf, faddr, fsecndx);
|
from = find_fromsym(elf, faddr, fsecndx);
|
||||||
fromsym = sym_name(elf, from);
|
fromsym = sym_name(elf, from);
|
||||||
|
@ -1019,10 +1019,17 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
|
||||||
|
|
||||||
sec_mismatch_count++;
|
sec_mismatch_count++;
|
||||||
|
|
||||||
warn("%s: section mismatch in reference: %s+0x%x (section: %s) -> %s (section: %s)\n",
|
if (!tosym[0])
|
||||||
modname, fromsym,
|
snprintf(taddr_str, sizeof(taddr_str), "0x%x", (unsigned int)taddr);
|
||||||
(unsigned int)(faddr - (from ? from->st_value : 0)),
|
|
||||||
fromsec, tosym, tosec);
|
/*
|
||||||
|
* 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 (mismatch->mismatch == EXTABLE_TO_NON_TEXT) {
|
||||||
if (match(tosec, mismatch->bad_tosec))
|
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) {
|
if (buf->size - buf->pos < len) {
|
||||||
buf->size += len + SZ;
|
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);
|
strncpy(buf->p + buf->pos, s, len);
|
||||||
buf->pos += len;
|
buf->pos += len;
|
||||||
|
@ -1677,7 +1684,7 @@ static void check_exports(struct module *mod)
|
||||||
exp = find_symbol(s->name);
|
exp = find_symbol(s->name);
|
||||||
if (!exp) {
|
if (!exp) {
|
||||||
if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS)
|
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\" [%s.ko] undefined!\n",
|
||||||
s->name, mod->name);
|
s->name, mod->name);
|
||||||
continue;
|
continue;
|
||||||
|
@ -1700,7 +1707,7 @@ static void check_exports(struct module *mod)
|
||||||
basename = mod->name;
|
basename = mod->name;
|
||||||
|
|
||||||
if (!contains_namespace(&mod->imported_namespaces, exp->namespace)) {
|
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",
|
"module %s uses symbol %s from namespace %s, but does not import it.\n",
|
||||||
basename, exp->name, exp->namespace);
|
basename, exp->name, exp->namespace);
|
||||||
add_namespace(&mod->missing_namespaces, 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)
|
static void add_header(struct buffer *b, struct module *mod)
|
||||||
{
|
{
|
||||||
buf_printf(b, "#include <linux/module.h>\n");
|
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/export-internal.h>\n");
|
||||||
buf_printf(b, "#include <linux/vermagic.h>\n");
|
|
||||||
buf_printf(b, "#include <linux/compiler.h>\n");
|
buf_printf(b, "#include <linux/compiler.h>\n");
|
||||||
buf_printf(b, "\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, "MODULE_INFO(name, KBUILD_MODNAME);\n");
|
||||||
buf_printf(b, "\n");
|
buf_printf(b, "\n");
|
||||||
buf_printf(b, "__visible struct module __this_module\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)
|
if (!external_module)
|
||||||
buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
|
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"))
|
if (strstarts(mod->name, "drivers/staging"))
|
||||||
buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
|
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)
|
if (st.st_size != b->pos)
|
||||||
goto close_write;
|
goto close_write;
|
||||||
|
|
||||||
tmp = NOFAIL(malloc(b->pos));
|
tmp = xmalloc(b->pos);
|
||||||
if (fread(tmp, 1, b->pos, file) != b->pos)
|
if (fread(tmp, 1, b->pos, file) != b->pos)
|
||||||
goto free_write;
|
goto free_write;
|
||||||
|
|
||||||
|
@ -2117,6 +2101,25 @@ struct dump_list {
|
||||||
const char *file;
|
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)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
struct module *mod;
|
struct module *mod;
|
||||||
|
@ -2133,7 +2136,7 @@ int main(int argc, char **argv)
|
||||||
external_module = true;
|
external_module = true;
|
||||||
break;
|
break;
|
||||||
case 'i':
|
case 'i':
|
||||||
dl = NOFAIL(malloc(sizeof(*dl)));
|
dl = xmalloc(sizeof(*dl));
|
||||||
dl->file = optarg;
|
dl->file = optarg;
|
||||||
list_add_tail(&dl->list, &dump_lists);
|
list_add_tail(&dl->list, &dump_lists);
|
||||||
break;
|
break;
|
||||||
|
@ -2181,6 +2184,8 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
check_host_endian();
|
||||||
|
|
||||||
list_for_each_entry_safe(dl, dl2, &dump_lists, list) {
|
list_for_each_entry_safe(dl, dl2, &dump_lists, list) {
|
||||||
read_dump(dl->file);
|
read_dump(dl->file);
|
||||||
list_del(&dl->list);
|
list_del(&dl->list);
|
||||||
|
|
|
@ -62,22 +62,11 @@
|
||||||
x); \
|
x); \
|
||||||
})
|
})
|
||||||
|
|
||||||
#if KERNEL_ELFDATA != HOST_ELFDATA
|
#define TO_NATIVE(x) \
|
||||||
|
(target_is_big_endian == host_is_big_endian ? x : bswap(x))
|
||||||
#define TO_NATIVE(x) (bswap(x))
|
|
||||||
|
|
||||||
#else /* endianness matches */
|
|
||||||
|
|
||||||
#define TO_NATIVE(x) (x)
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define NOFAIL(ptr) do_nofail((ptr), #ptr)
|
|
||||||
|
|
||||||
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
|
||||||
|
|
||||||
void *do_nofail(void *ptr, const char *expr);
|
|
||||||
|
|
||||||
struct buffer {
|
struct buffer {
|
||||||
char *p;
|
char *p;
|
||||||
int pos;
|
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);
|
void get_src_version(const char *modname, char sum[], unsigned sumlen);
|
||||||
|
|
||||||
/* from modpost.c */
|
/* from modpost.c */
|
||||||
|
extern bool target_is_big_endian;
|
||||||
|
extern bool host_is_big_endian;
|
||||||
char *read_text_file(const char *filename);
|
char *read_text_file(const char *filename);
|
||||||
char *get_line(char **stringp);
|
char *get_line(char **stringp);
|
||||||
void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym);
|
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)))
|
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
|
* 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
|
* fatal - show the given message, and bail out immediately. This should be
|
||||||
* used when there is no point to continue running modpost.
|
* used when there is no point to continue running modpost.
|
||||||
*/
|
*/
|
||||||
#define warn(fmt, args...) modpost_log(LOG_WARN, fmt, ##args)
|
#define warn(fmt, args...) modpost_log(false, fmt, ##args)
|
||||||
#define error(fmt, args...) modpost_log(LOG_ERROR, fmt, ##args)
|
#define error(fmt, args...) modpost_log(true, fmt, ##args)
|
||||||
#define fatal(fmt, args...) do { error(fmt, ##args); exit(1); } while (1)
|
#define fatal(fmt, args...) do { error(fmt, ##args); exit(1); } while (1)
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
|
#include <xalloc.h>
|
||||||
#include "modpost.h"
|
#include "modpost.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -305,7 +307,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
|
||||||
const char *base;
|
const char *base;
|
||||||
int dirlen, ret = 0, check_files = 0;
|
int dirlen, ret = 0, check_files = 0;
|
||||||
|
|
||||||
cmd = NOFAIL(malloc(strlen(objfile) + sizeof("..cmd")));
|
cmd = xmalloc(strlen(objfile) + sizeof("..cmd"));
|
||||||
|
|
||||||
base = strrchr(objfile, '/');
|
base = strrchr(objfile, '/');
|
||||||
if (base) {
|
if (base) {
|
||||||
|
@ -316,7 +318,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
|
||||||
dirlen = 0;
|
dirlen = 0;
|
||||||
sprintf(cmd, ".%s.cmd", objfile);
|
sprintf(cmd, ".%s.cmd", objfile);
|
||||||
}
|
}
|
||||||
dir = NOFAIL(malloc(dirlen + 1));
|
dir = xmalloc(dirlen + 1);
|
||||||
strncpy(dir, objfile, dirlen);
|
strncpy(dir, objfile, dirlen);
|
||||||
dir[dirlen] = '\0';
|
dir[dirlen] = '\0';
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* Helper functions for finding the symbol in an ELF which is "nearest"
|
* Helper functions for finding the symbol in an ELF which is "nearest"
|
||||||
* to a given address.
|
* to a given address.
|
||||||
*/
|
*/
|
||||||
|
#include <xalloc.h>
|
||||||
#include "modpost.h"
|
#include "modpost.h"
|
||||||
|
|
||||||
struct syminfo {
|
struct syminfo {
|
||||||
|
@ -125,8 +125,8 @@ void symsearch_init(struct elf_info *elf)
|
||||||
{
|
{
|
||||||
unsigned int table_size = symbol_count(elf);
|
unsigned int table_size = symbol_count(elf);
|
||||||
|
|
||||||
elf->symsearch = NOFAIL(malloc(sizeof(struct symsearch) +
|
elf->symsearch = xmalloc(sizeof(struct symsearch) +
|
||||||
sizeof(struct syminfo) * table_size));
|
sizeof(struct syminfo) * table_size);
|
||||||
elf->symsearch->table_size = table_size;
|
elf->symsearch->table_size = table_size;
|
||||||
|
|
||||||
symsearch_populate(elf, elf->symsearch->table, 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>
|
# Contributor: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
|
||||||
|
|
||||||
pkgbase=${PACMAN_PKGBASE:-linux-upstream}
|
pkgbase=${PACMAN_PKGBASE:-linux-upstream}
|
||||||
pkgname=("${pkgbase}" "${pkgbase}-api-headers")
|
pkgname=("${pkgbase}")
|
||||||
if grep -q CONFIG_MODULES=y include/config/auto.conf; then
|
|
||||||
pkgname+=("${pkgbase}-headers")
|
_extrapackages=${PACMAN_EXTRAPACKAGES-headers api-headers debug}
|
||||||
fi
|
for pkg in $_extrapackages; do
|
||||||
|
pkgname+=("${pkgbase}-${pkg}")
|
||||||
|
done
|
||||||
|
|
||||||
pkgver="${KERNELRELEASE//-/_}"
|
pkgver="${KERNELRELEASE//-/_}"
|
||||||
# The PKGBUILD is evaluated multiple times.
|
# The PKGBUILD is evaluated multiple times.
|
||||||
# Running scripts/build-version from here would introduce inconsistencies.
|
# Running scripts/build-version from here would introduce inconsistencies.
|
||||||
|
@ -33,11 +36,17 @@ makedepends=(
|
||||||
)
|
)
|
||||||
options=(!debug !strip !buildflags !makeflags)
|
options=(!debug !strip !buildflags !makeflags)
|
||||||
|
|
||||||
build() {
|
_prologue() {
|
||||||
# MAKEFLAGS from makepkg.conf override the ones inherited from kbuild.
|
# MAKEFLAGS from makepkg.conf override the ones inherited from kbuild.
|
||||||
# Bypass this override with a custom variable.
|
# Bypass this override with a custom variable.
|
||||||
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
|
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}"
|
${MAKE} KERNELRELEASE="${KERNELRELEASE}" KBUILD_BUILD_VERSION="${pkgrel}"
|
||||||
}
|
}
|
||||||
|
@ -45,10 +54,10 @@ build() {
|
||||||
_package() {
|
_package() {
|
||||||
pkgdesc="The ${pkgdesc} kernel and modules"
|
pkgdesc="The ${pkgdesc} kernel and modules"
|
||||||
|
|
||||||
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
|
|
||||||
cd "${objtree}"
|
|
||||||
local modulesdir="${pkgdir}/usr/${MODLIB}"
|
local modulesdir="${pkgdir}/usr/${MODLIB}"
|
||||||
|
|
||||||
|
_prologue
|
||||||
|
|
||||||
echo "Installing boot image..."
|
echo "Installing boot image..."
|
||||||
# systemd expects to find the kernel here to allow hibernation
|
# systemd expects to find the kernel here to allow hibernation
|
||||||
# https://github.com/systemd/systemd/commit/edda44605f06a41fb86b7ab8128dcf99161d2344
|
# https://github.com/systemd/systemd/commit/edda44605f06a41fb86b7ab8128dcf99161d2344
|
||||||
|
@ -73,14 +82,17 @@ _package() {
|
||||||
_package-headers() {
|
_package-headers() {
|
||||||
pkgdesc="Headers and scripts for building modules for the ${pkgdesc} kernel"
|
pkgdesc="Headers and scripts for building modules for the ${pkgdesc} kernel"
|
||||||
|
|
||||||
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
|
|
||||||
cd "${objtree}"
|
|
||||||
local builddir="${pkgdir}/usr/${MODLIB}/build"
|
local builddir="${pkgdir}/usr/${MODLIB}/build"
|
||||||
|
|
||||||
echo "Installing build files..."
|
_prologue
|
||||||
"${srctree}/scripts/package/install-extmod-build" "${builddir}"
|
|
||||||
|
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..."
|
echo "Installing System.map and config..."
|
||||||
|
mkdir -p "${builddir}"
|
||||||
cp System.map "${builddir}/System.map"
|
cp System.map "${builddir}/System.map"
|
||||||
cp .config "${builddir}/.config"
|
cp .config "${builddir}/.config"
|
||||||
|
|
||||||
|
@ -94,12 +106,24 @@ _package-api-headers() {
|
||||||
provides=(linux-api-headers)
|
provides=(linux-api-headers)
|
||||||
conflicts=(linux-api-headers)
|
conflicts=(linux-api-headers)
|
||||||
|
|
||||||
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
|
_prologue
|
||||||
cd "${objtree}"
|
|
||||||
|
|
||||||
${MAKE} headers_install INSTALL_HDR_PATH="${pkgdir}/usr"
|
${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
|
for _p in "${pkgname[@]}"; do
|
||||||
eval "package_$_p() {
|
eval "package_$_p() {
|
||||||
$(declare -f "_package${_p#$pkgbase}")
|
$(declare -f "_package${_p#$pkgbase}")
|
||||||
|
|
|
@ -9,15 +9,22 @@ is_enabled() {
|
||||||
grep -q "^$1=y" include/config/auto.conf
|
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}"
|
mkdir -p "${destdir}"
|
||||||
|
|
||||||
(
|
(
|
||||||
cd "${srctree}"
|
cd "${srctree}"
|
||||||
echo Makefile
|
echo Makefile
|
||||||
find "arch/${SRCARCH}" -maxdepth 1 -name '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 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}"
|
) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}"
|
||||||
|
|
||||||
{
|
{
|
||||||
|
@ -25,12 +32,50 @@ mkdir -p "${destdir}"
|
||||||
echo tools/objtool/objtool
|
echo tools/objtool/objtool
|
||||||
fi
|
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
|
if is_enabled CONFIG_GCC_PLUGINS; then
|
||||||
find scripts/gcc-plugins -name '*.so'
|
find scripts/gcc-plugins -name '*.so'
|
||||||
fi
|
fi
|
||||||
} | tar -c -f - -T - | tar -xf - -C "${destdir}"
|
} | tar -c -f - -T - | tar -xf - -C "${destdir}"
|
||||||
|
|
||||||
# copy .config manually to be where it's expected to be
|
# When ${CC} and ${HOSTCC} differ, we are likely cross-compiling. Rebuild host
|
||||||
cp "${KCONFIG_CONFIG}" "${destdir}/.config"
|
# 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/ \
|
SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
|
||||||
-e s/sun4u/sparc64/ \
|
-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/s390x/s390/ \
|
||||||
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
|
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
|
||||||
-e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \
|
-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…
Add table
Reference in a new issue