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

Pull Kbuild updates from Masahiro Yamada:

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

 - Add support for the linux-debug Pacman package

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

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

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

 - Refactor Kconfig and misc tools

 - Update Kbuild and Kconfig documentation

* tag 'kbuild-v6.12' of git://git.kernel.org/pub/scm/linux/kernel/git/masahiroy/linux-kbuild: (51 commits)
  kbuild: doc: replace "gcc" in external module description
  kbuild: doc: describe the -C option precisely for external module builds
  kbuild: doc: remove the description about shipped files
  kbuild: doc: drop section numbering, use references in modules.rst
  kbuild: doc: throw out the local table of contents in modules.rst
  kbuild: doc: remove outdated description of the limitation on -I usage
  kbuild: doc: remove description about grepping CONFIG options
  kbuild: doc: update the description about Kbuild/Makefile split
  kbuild: remove unnecessary export of RUST_LIB_SRC
  kbuild: remove append operation on cmd_ld_ko_o
  kconfig: cache expression values
  kconfig: use hash table to reuse expressions
  kconfig: refactor expr_eliminate_dups()
  kconfig: add comments to expression transformations
  kconfig: change some expr_*() functions to bool
  scripts: move hash function from scripts/kconfig/ to scripts/include/
  kallsyms: change overflow variable to bool type
  kallsyms: squash output_address()
  kbuild: add install target for modules.builtin.ranges
  scripts: add verifier script for builtin module range data
  ...
This commit is contained in:
Linus Torvalds
2024-09-24 13:02:06 -07:00
56 changed files with 1867 additions and 909 deletions
+23 -35
View File
@@ -41,20 +41,6 @@ include $(srctree)/scripts/Makefile.compiler
include $(kbuild-file)
include $(srctree)/scripts/Makefile.lib
# Do not include hostprogs rules unless needed.
# $(sort ...) is used here to remove duplicated words and excessive spaces.
hostprogs := $(sort $(hostprogs))
ifneq ($(hostprogs),)
include $(srctree)/scripts/Makefile.host
endif
# Do not include userprogs rules unless needed.
# $(sort ...) is used here to remove duplicated words and excessive spaces.
userprogs := $(sort $(userprogs))
ifneq ($(userprogs),)
include $(srctree)/scripts/Makefile.userprogs
endif
ifndef obj
$(warning kbuild: Makefile.build is included improperly)
endif
@@ -71,7 +57,6 @@ endif
# subdir-builtin and subdir-modorder may contain duplications. Use $(sort ...)
subdir-builtin := $(sort $(filter %/built-in.a, $(real-obj-y)))
subdir-modorder := $(sort $(filter %/modules.order, $(obj-m)))
subdir-dtbslist := $(sort $(filter %/dtbs-list, $(dtb-y)))
targets-for-builtin := $(extra-y)
@@ -363,7 +348,7 @@ $(obj)/%.o: $(obj)/%.S FORCE
targets += $(filter-out $(subdir-builtin), $(real-obj-y))
targets += $(filter-out $(subdir-modorder), $(real-obj-m))
targets += $(real-dtb-y) $(lib-y) $(always-y)
targets += $(lib-y) $(always-y)
# Linker scripts preprocessor (.lds.S -> .lds)
# ---------------------------------------------------------------------------
@@ -389,7 +374,6 @@ $(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
# To build objects in subdirs, we need to descend into the directories
$(subdir-builtin): $(obj)/%/built-in.a: $(obj)/% ;
$(subdir-modorder): $(obj)/%/modules.order: $(obj)/% ;
$(subdir-dtbslist): $(obj)/%/dtbs-list: $(obj)/% ;
#
# Rule to compile a set of .o files into one .a file (without symbol table)
@@ -405,12 +389,8 @@ quiet_cmd_ar_builtin = AR $@
$(obj)/built-in.a: $(real-obj-y) FORCE
$(call if_changed,ar_builtin)
#
# Rule to create modules.order and dtbs-list
#
# This is a list of build artifacts (module or dtb) from the current Makefile
# and its sub-directories. The timestamp should be updated when any of the
# member files.
# This is a list of build artifacts from the current Makefile and its
# sub-directories. The timestamp should be updated when any of the member files.
cmd_gen_order = { $(foreach m, $(real-prereqs), \
$(if $(filter %/$(notdir $@), $m), cat $m, echo $m);) :; } \
@@ -419,9 +399,6 @@ cmd_gen_order = { $(foreach m, $(real-prereqs), \
$(obj)/modules.order: $(obj-m) FORCE
$(call if_changed,gen_order)
$(obj)/dtbs-list: $(dtb-y) FORCE
$(call if_changed,gen_order)
#
# Rule to compile a set of .o files into one .a file (with symbol table)
#
@@ -450,15 +427,26 @@ intermediate_targets = $(foreach sfx, $(2), \
$(patsubst %$(strip $(1)),%$(sfx), \
$(filter %$(strip $(1)), $(targets))))
# %.asn1.o <- %.asn1.[ch] <- %.asn1
# %.dtb.o <- %.dtb.S <- %.dtb <- %.dts
# %.dtbo.o <- %.dtbo.S <- %.dtbo <- %.dtso
# %.lex.o <- %.lex.c <- %.l
# %.tab.o <- %.tab.[ch] <- %.y
targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h) \
$(call intermediate_targets, .dtb.o, .dtb.S .dtb) \
$(call intermediate_targets, .dtbo.o, .dtbo.S .dtbo) \
$(call intermediate_targets, .lex.o, .lex.c) \
$(call intermediate_targets, .tab.o, .tab.c .tab.h)
targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h)
# Include additional build rules when necessary
# ---------------------------------------------------------------------------
# $(sort ...) is used here to remove duplicated words and excessive spaces.
hostprogs := $(sort $(hostprogs))
ifneq ($(hostprogs),)
include $(srctree)/scripts/Makefile.host
endif
# $(sort ...) is used here to remove duplicated words and excessive spaces.
userprogs := $(sort $(userprogs))
ifneq ($(userprogs),)
include $(srctree)/scripts/Makefile.userprogs
endif
ifneq ($(need-dtbslist)$(dtb-y)$(dtb-)$(filter %.dtb %.dtb.o %.dtbo.o,$(targets)),)
include $(srctree)/scripts/Makefile.dtbs
endif
# Build
# ---------------------------------------------------------------------------
+142
View File
@@ -0,0 +1,142 @@
# SPDX-License-Identifier: GPL-2.0-only
# If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built
dtb-$(CONFIG_OF_ALL_DTBS) += $(dtb-)
# Composite DTB (i.e. DTB constructed by overlay)
multi-dtb-y := $(call multi-search, $(dtb-y), .dtb, -dtbs)
# Primitive DTB compiled from *.dts
real-dtb-y := $(call real-search, $(dtb-y), .dtb, -dtbs)
# Base DTB that overlay is applied onto
base-dtb-y := $(filter %.dtb, $(call real-search, $(multi-dtb-y), .dtb, -dtbs))
dtb-y := $(addprefix $(obj)/, $(dtb-y))
multi-dtb-y := $(addprefix $(obj)/, $(multi-dtb-y))
real-dtb-y := $(addprefix $(obj)/, $(real-dtb-y))
always-y += $(dtb-y)
targets += $(real-dtb-y)
# dtbs-list
# ---------------------------------------------------------------------------
ifdef need-dtbslist
subdir-dtbslist := $(addsuffix /dtbs-list, $(subdir-ym))
dtb-y += $(subdir-dtbslist)
always-y += $(obj)/dtbs-list
endif
$(subdir-dtbslist): $(obj)/%/dtbs-list: $(obj)/% ;
$(obj)/dtbs-list: $(dtb-y) FORCE
$(call if_changed,gen_order)
# Assembly file to wrap dtb(o)
# ---------------------------------------------------------------------------
# Generate an assembly file to wrap the output of the device tree compiler
quiet_cmd_wrap_S_dtb = WRAP $@
cmd_wrap_S_dtb = { \
symbase=__$(patsubst .%,%,$(suffix $<))_$(subst -,_,$(notdir $*)); \
echo '\#include <asm-generic/vmlinux.lds.h>'; \
echo '.section .dtb.init.rodata,"a"'; \
echo '.balign STRUCT_ALIGNMENT'; \
echo ".global $${symbase}_begin"; \
echo "$${symbase}_begin:"; \
echo '.incbin "$<" '; \
echo ".global $${symbase}_end"; \
echo "$${symbase}_end:"; \
echo '.balign STRUCT_ALIGNMENT'; \
} > $@
$(obj)/%.dtb.S: $(obj)/%.dtb FORCE
$(call if_changed,wrap_S_dtb)
$(obj)/%.dtbo.S: $(obj)/%.dtbo FORCE
$(call if_changed,wrap_S_dtb)
# Schema check
# ---------------------------------------------------------------------------
ifneq ($(CHECK_DTBS),)
DT_CHECKER ?= dt-validate
DT_CHECKER_FLAGS ?= $(if $(DT_SCHEMA_FILES),-l $(DT_SCHEMA_FILES),-m)
DT_BINDING_DIR := Documentation/devicetree/bindings
DT_TMP_SCHEMA := $(objtree)/$(DT_BINDING_DIR)/processed-schema.json
dtb-check-enabled = $(if $(filter %.dtb, $@),y)
endif
quiet_dtb_check_tag = $(if $(dtb-check-enabled),[C], )
cmd_dtb_check = $(if $(dtb-check-enabled),; $(DT_CHECKER) $(DT_CHECKER_FLAGS) -u $(srctree)/$(DT_BINDING_DIR) -p $(DT_TMP_SCHEMA) $@ || true)
# Overlay
# ---------------------------------------------------------------------------
# NOTE:
# Do not replace $(filter %.dtb %.dtbo, $^) with $(real-prereqs). When a single
# DTB is turned into a multi-blob DTB, $^ will contain header file dependencies
# recorded in the .*.cmd file.
quiet_cmd_fdtoverlay = OVL $(quiet_dtb_check_tag) $@
cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^) $(cmd_dtb_check)
$(multi-dtb-y): $(DT_TMP_SCHEMA) FORCE
$(call if_changed,fdtoverlay)
$(call multi_depend, $(multi-dtb-y), .dtb, -dtbs)
# DTC
# ---------------------------------------------------------------------------
DTC ?= $(objtree)/scripts/dtc/dtc
DTC_FLAGS += -Wno-unique_unit_address
# Disable noisy checks by default
ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
DTC_FLAGS += -Wno-unit_address_vs_reg \
-Wno-avoid_unnecessary_addr_size \
-Wno-alias_paths \
-Wno-graph_child_address \
-Wno-simple_bus_reg
else
DTC_FLAGS += -Wunique_unit_address_if_enabled
endif
ifneq ($(findstring 2,$(KBUILD_EXTRA_WARN)),)
DTC_FLAGS += -Wnode_name_chars_strict \
-Wproperty_name_chars_strict \
-Wunique_unit_address
endif
DTC_FLAGS += $(DTC_FLAGS_$(target-stem))
# Set -@ if the target is a base DTB that overlay is applied onto
DTC_FLAGS += $(if $(filter $(patsubst $(obj)/%,%,$@), $(base-dtb-y)), -@)
DTC_INCLUDE := $(srctree)/scripts/dtc/include-prefixes
dtc_cpp_flags = -Wp,-MMD,$(depfile).pre.tmp -nostdinc -I $(DTC_INCLUDE) -undef -D__DTS__
dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
quiet_cmd_dtc = DTC $(quiet_dtb_check_tag) $@
cmd_dtc = \
$(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
$(DTC) -o $@ -b 0 $(addprefix -i,$(dir $<) $(DTC_INCLUDE)) \
$(DTC_FLAGS) -d $(depfile).dtc.tmp $(dtc-tmp) ; \
cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) \
$(cmd_dtb_check)
$(obj)/%.dtb: $(obj)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE
$(call if_changed_dep,dtc)
$(obj)/%.dtbo: $(src)/%.dtso $(DTC) FORCE
$(call if_changed_dep,dtc)
# targets
# ---------------------------------------------------------------------------
targets += $(always-y)
# %.dtb.o <- %.dtb.S <- %.dtb <- %.dts
# %.dtbo.o <- %.dtbo.S <- %.dtbo <- %.dtso
targets += $(call intermediate_targets, .dtb.o, .dtb.S .dtb) \
$(call intermediate_targets, .dtbo.o, .dtbo.S .dtbo)
+5
View File
@@ -160,3 +160,8 @@ $(host-rust): $(obj)/%: $(src)/%.rs FORCE
targets += $(host-csingle) $(host-cmulti) $(host-cobjs) \
$(host-cxxmulti) $(host-cxxobjs) $(host-rust)
# %.lex.o <- %.lex.c <- %.l
# %.tab.o <- %.tab.[ch] <- %.y
targets += $(call intermediate_targets, .lex.o, .lex.c) \
$(call intermediate_targets, .tab.o, .tab.c .tab.h)
+2 -117
View File
@@ -45,11 +45,6 @@ else
obj-y := $(filter-out %/, $(obj-y))
endif
ifdef need-dtbslist
dtb-y += $(addsuffix /dtbs-list, $(subdir-ym))
always-y += dtbs-list
endif
# Expand $(foo-objs) $(foo-y) etc. by replacing their individuals
suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s))))
# List composite targets that are constructed by combining other targets
@@ -80,19 +75,6 @@ always-y += $(hostprogs-always-y) $(hostprogs-always-m)
userprogs += $(userprogs-always-y) $(userprogs-always-m)
always-y += $(userprogs-always-y) $(userprogs-always-m)
# DTB
# If CONFIG_OF_ALL_DTBS is enabled, all DT blobs are built
dtb-$(CONFIG_OF_ALL_DTBS) += $(dtb-)
# Composite DTB (i.e. DTB constructed by overlay)
multi-dtb-y := $(call multi-search, $(dtb-y), .dtb, -dtbs)
# Primitive DTB compiled from *.dts
real-dtb-y := $(call real-search, $(dtb-y), .dtb, -dtbs)
# Base DTB that overlay is applied onto
base-dtb-y := $(filter %.dtb, $(call real-search, $(multi-dtb-y), .dtb, -dtbs))
always-y += $(dtb-y)
# Add subdir path
ifneq ($(obj),.)
@@ -104,9 +86,6 @@ lib-y := $(addprefix $(obj)/,$(lib-y))
real-obj-y := $(addprefix $(obj)/,$(real-obj-y))
real-obj-m := $(addprefix $(obj)/,$(real-obj-m))
multi-obj-m := $(addprefix $(obj)/, $(multi-obj-m))
dtb-y := $(addprefix $(obj)/, $(dtb-y))
multi-dtb-y := $(addprefix $(obj)/, $(multi-dtb-y))
real-dtb-y := $(addprefix $(obj)/, $(real-dtb-y))
subdir-ym := $(addprefix $(obj)/,$(subdir-ym))
endif
@@ -238,7 +217,7 @@ modkern_rustflags = \
modkern_aflags = $(if $(part-of-module), \
$(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE), \
$(KBUILD_AFLAGS_KERNEL) $(AFLAGS_KERNEL))
$(KBUILD_AFLAGS_KERNEL) $(AFLAGS_KERNEL) $(modfile_flags))
c_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
-include $(srctree)/include/linux/compiler_types.h \
@@ -248,19 +227,13 @@ c_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
rust_flags = $(_rust_flags) $(modkern_rustflags) @$(objtree)/include/generated/rustc_cfg
a_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(_a_flags) $(modkern_aflags)
$(_a_flags) $(modkern_aflags) $(modname_flags)
cpp_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(_cpp_flags)
ld_flags = $(KBUILD_LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F))
DTC_INCLUDE := $(srctree)/scripts/dtc/include-prefixes
dtc_cpp_flags = -Wp,-MMD,$(depfile).pre.tmp -nostdinc \
$(addprefix -I,$(DTC_INCLUDE)) \
-undef -D__DTS__
ifdef CONFIG_OBJTOOL
objtool := $(objtree)/tools/objtool/objtool
@@ -350,94 +323,6 @@ cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
quiet_cmd_gzip = GZIP $@
cmd_gzip = cat $(real-prereqs) | $(KGZIP) -n -f -9 > $@
# DTC
# ---------------------------------------------------------------------------
DTC ?= $(objtree)/scripts/dtc/dtc
DTC_FLAGS += \
-Wno-unique_unit_address
# Disable noisy checks by default
ifeq ($(findstring 1,$(KBUILD_EXTRA_WARN)),)
DTC_FLAGS += -Wno-unit_address_vs_reg \
-Wno-avoid_unnecessary_addr_size \
-Wno-alias_paths \
-Wno-graph_child_address \
-Wno-simple_bus_reg
else
DTC_FLAGS += \
-Wunique_unit_address_if_enabled
endif
ifneq ($(findstring 2,$(KBUILD_EXTRA_WARN)),)
DTC_FLAGS += -Wnode_name_chars_strict \
-Wproperty_name_chars_strict \
-Wunique_unit_address
endif
DTC_FLAGS += $(DTC_FLAGS_$(target-stem))
# Set -@ if the target is a base DTB that overlay is applied onto
DTC_FLAGS += $(if $(filter $(patsubst $(obj)/%,%,$@), $(base-dtb-y)), -@)
# Generate an assembly file to wrap the output of the device tree compiler
quiet_cmd_wrap_S_dtb = WRAP $@
cmd_wrap_S_dtb = { \
symbase=__$(patsubst .%,%,$(suffix $<))_$(subst -,_,$(notdir $*)); \
echo '\#include <asm-generic/vmlinux.lds.h>'; \
echo '.section .dtb.init.rodata,"a"'; \
echo '.balign STRUCT_ALIGNMENT'; \
echo ".global $${symbase}_begin"; \
echo "$${symbase}_begin:"; \
echo '.incbin "$<" '; \
echo ".global $${symbase}_end"; \
echo "$${symbase}_end:"; \
echo '.balign STRUCT_ALIGNMENT'; \
} > $@
$(obj)/%.dtb.S: $(obj)/%.dtb FORCE
$(call if_changed,wrap_S_dtb)
$(obj)/%.dtbo.S: $(obj)/%.dtbo FORCE
$(call if_changed,wrap_S_dtb)
quiet_dtb_check_tag = $(if $(dtb-check-enabled),[C], )
cmd_dtb_check = $(if $(dtb-check-enabled),; $(DT_CHECKER) $(DT_CHECKER_FLAGS) -u $(srctree)/$(DT_BINDING_DIR) -p $(DT_TMP_SCHEMA) $@ || true)
quiet_cmd_dtc = DTC $(quiet_dtb_check_tag) $@
cmd_dtc = $(HOSTCC) -E $(dtc_cpp_flags) -x assembler-with-cpp -o $(dtc-tmp) $< ; \
$(DTC) -o $@ -b 0 \
$(addprefix -i,$(dir $<) $(DTC_INCLUDE)) $(DTC_FLAGS) \
-d $(depfile).dtc.tmp $(dtc-tmp) ; \
cat $(depfile).pre.tmp $(depfile).dtc.tmp > $(depfile) \
$(cmd_dtb_check)
# NOTE:
# Do not replace $(filter %.dtb %.dtbo, $^) with $(real-prereqs). When a single
# DTB is turned into a multi-blob DTB, $^ will contain header file dependencies
# recorded in the .*.cmd file.
quiet_cmd_fdtoverlay = OVL $(quiet_dtb_check_tag) $@
cmd_fdtoverlay = $(objtree)/scripts/dtc/fdtoverlay -o $@ -i $(filter %.dtb %.dtbo, $^) $(cmd_dtb_check)
$(multi-dtb-y): FORCE
$(call if_changed,fdtoverlay)
$(call multi_depend, $(multi-dtb-y), .dtb, -dtbs)
ifneq ($(CHECK_DTBS),)
DT_CHECKER ?= dt-validate
DT_CHECKER_FLAGS ?= $(if $(DT_SCHEMA_FILES),-l $(DT_SCHEMA_FILES),-m)
DT_BINDING_DIR := Documentation/devicetree/bindings
DT_TMP_SCHEMA := $(objtree)/$(DT_BINDING_DIR)/processed-schema.json
dtb-check-enabled = $(if $(filter %.dtb, $@),y)
endif
$(obj)/%.dtb: $(obj)/%.dts $(DTC) $(DT_TMP_SCHEMA) FORCE
$(call if_changed_dep,dtc)
$(obj)/%.dtbo: $(src)/%.dtso $(DTC) FORCE
$(call if_changed_dep,dtc)
dtc-tmp = $(subst $(comma),_,$(dot-target).dts.tmp)
# Bzip2
# ---------------------------------------------------------------------------
+6 -3
View File
@@ -30,8 +30,11 @@ quiet_cmd_cc_o_c = CC [M] $@
%.mod.o: %.mod.c FORCE
$(call if_changed_dep,cc_o_c)
$(extmod_prefix).module-common.o: $(srctree)/scripts/module-common.c FORCE
$(call if_changed_dep,cc_o_c)
quiet_cmd_ld_ko_o = LD [M] $@
cmd_ld_ko_o += \
cmd_ld_ko_o = \
$(LD) -r $(KBUILD_LDFLAGS) \
$(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \
-T scripts/module.lds -o $@ $(filter %.o, $^)
@@ -54,13 +57,13 @@ if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \
printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
# Re-generate module BTFs if either module's .ko or vmlinux changed
%.ko: %.o %.mod.o scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),vmlinux) FORCE
%.ko: %.o %.mod.o $(extmod_prefix).module-common.o scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),vmlinux) FORCE
+$(call if_changed_except,ld_ko_o,vmlinux)
ifdef CONFIG_DEBUG_INFO_BTF_MODULES
+$(if $(newer-prereqs),$(call cmd,btf_ko))
endif
targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o)
targets += $(modules:%.o=%.ko) $(modules:%.o=%.mod.o) $(extmod_prefix).module-common.o
# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
+5 -3
View File
@@ -30,10 +30,12 @@ $(MODLIB)/modules.order: modules.order FORCE
quiet_cmd_install_modorder = INSTALL $@
cmd_install_modorder = sed 's:^\(.*\)\.o$$:kernel/\1.ko:' $< > $@
# Install modules.builtin(.modinfo) even when CONFIG_MODULES is disabled.
# Install modules.builtin(.modinfo,.ranges) even when CONFIG_MODULES is disabled.
install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo)
$(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo): $(MODLIB)/%: % FORCE
install-$(CONFIG_BUILTIN_MODULE_RANGES) += $(MODLIB)/modules.builtin.ranges
$(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo modules.builtin.ranges): $(MODLIB)/%: % FORCE
$(call cmd,install)
endif
@@ -146,7 +148,7 @@ quiet_cmd_gzip = GZIP $@
quiet_cmd_xz = XZ $@
cmd_xz = $(XZ) --check=crc32 --lzma2=dict=1MiB -f $<
quiet_cmd_zstd = ZSTD $@
cmd_zstd = $(ZSTD) -T0 --rm -f -q $<
cmd_zstd = $(ZSTD) --rm -f -q $<
$(dst)/%.ko.gz: $(dst)/%.ko FORCE
$(call cmd,gzip)
+1 -2
View File
@@ -147,8 +147,7 @@ snap-pkg:
PHONY += pacman-pkg
pacman-pkg:
@ln -srf $(srctree)/scripts/package/PKGBUILD $(objtree)/PKGBUILD
+objtree="$(realpath $(objtree))" \
BUILDDIR="$(realpath $(objtree))/pacman" \
+BUILDDIR="$(realpath $(objtree))/pacman" \
CARCH="$(UTS_MACHINE)" \
KBUILD_MAKEFLAGS="$(MAKEFLAGS)" \
KBUILD_REVISION="$(shell $(srctree)/scripts/build-version)" \
+18
View File
@@ -33,6 +33,24 @@ targets += vmlinux
vmlinux: scripts/link-vmlinux.sh vmlinux.o $(KBUILD_LDS) FORCE
+$(call if_changed_dep,link_vmlinux)
# module.builtin.ranges
# ---------------------------------------------------------------------------
ifdef CONFIG_BUILTIN_MODULE_RANGES
__default: modules.builtin.ranges
quiet_cmd_modules_builtin_ranges = GEN $@
cmd_modules_builtin_ranges = gawk -f $(real-prereqs) > $@
targets += modules.builtin.ranges
modules.builtin.ranges: $(srctree)/scripts/generate_builtin_ranges.awk \
modules.builtin vmlinux.map vmlinux.o.map FORCE
$(call if_changed,modules_builtin_ranges)
vmlinux.map: vmlinux
@:
endif
# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
+3
View File
@@ -45,9 +45,12 @@ objtool-args = $(vmlinux-objtool-args-y) --link
# Link of vmlinux.o used for section mismatch analysis
# ---------------------------------------------------------------------------
vmlinux-o-ld-args-$(CONFIG_BUILTIN_MODULE_RANGES) += -Map=$@.map
quiet_cmd_ld_vmlinux.o = LD $@
cmd_ld_vmlinux.o = \
$(LD) ${KBUILD_LDFLAGS} -r -o $@ \
$(vmlinux-o-ld-args-y) \
$(addprefix -T , $(initcalls-lds)) \
--whole-archive vmlinux.a --no-whole-archive \
--start-group $(KBUILD_VMLINUX_LIBS) --end-group \
+5 -10
View File
@@ -99,6 +99,8 @@
#include <stdio.h>
#include <ctype.h>
#include <xalloc.h>
static void usage(void)
{
fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
@@ -131,12 +133,9 @@ static unsigned int strhash(const char *str, unsigned int sz)
static void add_to_hashtable(const char *name, int len, unsigned int hash,
struct item *hashtab[])
{
struct item *aux = malloc(sizeof(*aux) + len);
struct item *aux;
if (!aux) {
perror("fixdep:malloc");
exit(1);
}
aux = xmalloc(sizeof(*aux) + len);
memcpy(aux->name, name, len);
aux->len = len;
aux->hash = hash;
@@ -228,11 +227,7 @@ static void *read_file(const char *filename)
perror(filename);
exit(2);
}
buf = malloc(st.st_size + 1);
if (!buf) {
perror("fixdep: malloc");
exit(2);
}
buf = xmalloc(st.st_size + 1);
if (read(fd, buf, st.st_size) != st.st_size) {
perror("fixdep: read");
exit(2);
+508
View File
@@ -0,0 +1,508 @@
#!/usr/bin/gawk -f
# SPDX-License-Identifier: GPL-2.0
# generate_builtin_ranges.awk: Generate address range data for builtin modules
# Written by Kris Van Hees <kris.van.hees@oracle.com>
#
# Usage: generate_builtin_ranges.awk modules.builtin vmlinux.map \
# vmlinux.o.map > modules.builtin.ranges
#
# Return the module name(s) (if any) associated with the given object.
#
# If we have seen this object before, return information from the cache.
# Otherwise, retrieve it from the corresponding .cmd file.
#
function get_module_info(fn, mod, obj, s) {
if (fn in omod)
return omod[fn];
if (match(fn, /\/[^/]+$/) == 0)
return "";
obj = fn;
mod = "";
fn = substr(fn, 1, RSTART) "." substr(fn, RSTART + 1) ".cmd";
if (getline s <fn == 1) {
if (match(s, /DKBUILD_MODFILE=['"]+[^'"]+/) > 0) {
mod = substr(s, RSTART + 16, RLENGTH - 16);
gsub(/['"]/, "", mod);
} else if (match(s, /RUST_MODFILE=[^ ]+/) > 0)
mod = substr(s, RSTART + 13, RLENGTH - 13);
}
close(fn);
# A single module (common case) also reflects objects that are not part
# of a module. Some of those objects have names that are also a module
# name (e.g. core). We check the associated module file name, and if
# they do not match, the object is not part of a module.
if (mod !~ / /) {
if (!(mod in mods))
mod = "";
}
gsub(/([^/ ]*\/)+/, "", mod);
gsub(/-/, "_", mod);
# At this point, mod is a single (valid) module name, or a list of
# module names (that do not need validation).
omod[obj] = mod;
return mod;
}
# Update the ranges entry for the given module 'mod' in section 'osect'.
#
# We use a modified absolute start address (soff + base) as index because we
# may need to insert an anchor record later that must be at the start of the
# section data, and the first module may very well start at the same address.
# So, we use (addr << 1) + 1 to allow a possible anchor record to be placed at
# (addr << 1). This is safe because the index is only used to sort the entries
# before writing them out.
#
function update_entry(osect, mod, soff, eoff, sect, idx) {
sect = sect_in[osect];
idx = sprintf("%016x", (soff + sect_base[osect]) * 2 + 1);
entries[idx] = sprintf("%s %08x-%08x %s", sect, soff, eoff, mod);
count[sect]++;
}
# (1) Build a lookup map of built-in module names.
#
# The first file argument is used as input (modules.builtin).
#
# Lines will be like:
# kernel/crypto/lzo-rle.ko
# and we record the object name "crypto/lzo-rle".
#
ARGIND == 1 {
sub(/kernel\//, ""); # strip off "kernel/" prefix
sub(/\.ko$/, ""); # strip off .ko suffix
mods[$1] = 1;
next;
}
# (2) Collect address information for each section.
#
# The second file argument is used as input (vmlinux.map).
#
# We collect the base address of the section in order to convert all addresses
# in the section into offset values.
#
# We collect the address of the anchor (or first symbol in the section if there
# is no explicit anchor) to allow users of the range data to calculate address
# ranges based on the actual load address of the section in the running kernel.
#
# We collect the start address of any sub-section (section included in the top
# level section being processed). This is needed when the final linking was
# done using vmlinux.a because then the list of objects contained in each
# section is to be obtained from vmlinux.o.map. The offset of the sub-section
# is recorded here, to be used as an addend when processing vmlinux.o.map
# later.
#
# Both GNU ld and LLVM lld linker map format are supported by converting LLVM
# lld linker map records into equivalent GNU ld linker map records.
#
# The first record of the vmlinux.map file provides enough information to know
# which format we are dealing with.
#
ARGIND == 2 && FNR == 1 && NF == 7 && $1 == "VMA" && $7 == "Symbol" {
map_is_lld = 1;
if (dbg)
printf "NOTE: %s uses LLVM lld linker map format\n", FILENAME >"/dev/stderr";
next;
}
# (LLD) Convert a section record fronm lld format to ld format.
#
# lld: ffffffff82c00000 2c00000 2493c0 8192 .data
# ->
# ld: .data 0xffffffff82c00000 0x2493c0 load address 0x0000000002c00000
#
ARGIND == 2 && map_is_lld && NF == 5 && /[0-9] [^ ]+$/ {
$0 = $5 " 0x"$1 " 0x"$3 " load address 0x"$2;
}
# (LLD) Convert an anchor record from lld format to ld format.
#
# lld: ffffffff81000000 1000000 0 1 _text = .
# ->
# ld: 0xffffffff81000000 _text = .
#
ARGIND == 2 && map_is_lld && !anchor && NF == 7 && raw_addr == "0x"$1 && $6 == "=" && $7 == "." {
$0 = " 0x"$1 " " $5 " = .";
}
# (LLD) Convert an object record from lld format to ld format.
#
# lld: 11480 11480 1f07 16 vmlinux.a(arch/x86/events/amd/uncore.o):(.text)
# ->
# ld: .text 0x0000000000011480 0x1f07 arch/x86/events/amd/uncore.o
#
ARGIND == 2 && map_is_lld && NF == 5 && $5 ~ /:\(/ {
gsub(/\)/, "");
sub(/ vmlinux\.a\(/, " ");
sub(/:\(/, " ");
$0 = " "$6 " 0x"$1 " 0x"$3 " " $5;
}
# (LLD) Convert a symbol record from lld format to ld format.
#
# We only care about these while processing a section for which no anchor has
# been determined yet.
#
# lld: ffffffff82a859a4 2a859a4 0 1 btf_ksym_iter_id
# ->
# ld: 0xffffffff82a859a4 btf_ksym_iter_id
#
ARGIND == 2 && map_is_lld && sect && !anchor && NF == 5 && $5 ~ /^[_A-Za-z][_A-Za-z0-9]*$/ {
$0 = " 0x"$1 " " $5;
}
# (LLD) We do not need any other ldd linker map records.
#
ARGIND == 2 && map_is_lld && /^[0-9a-f]{16} / {
next;
}
# (LD) Section records with just the section name at the start of the line
# need to have the next line pulled in to determine whether it is a
# loadable section. If it is, the next line will contains a hex value
# as first and second items.
#
ARGIND == 2 && !map_is_lld && NF == 1 && /^[^ ]/ {
s = $0;
getline;
if ($1 !~ /^0x/ || $2 !~ /^0x/)
next;
$0 = s " " $0;
}
# (LD) Object records with just the section name denote records with a long
# section name for which the remainder of the record can be found on the
# next line.
#
# (This is also needed for vmlinux.o.map, when used.)
#
ARGIND >= 2 && !map_is_lld && NF == 1 && /^ [^ \*]/ {
s = $0;
getline;
$0 = s " " $0;
}
# Beginning a new section - done with the previous one (if any).
#
ARGIND == 2 && /^[^ ]/ {
sect = 0;
}
# Process a loadable section (we only care about .-sections).
#
# Record the section name and its base address.
# We also record the raw (non-stripped) address of the section because it can
# be used to identify an anchor record.
#
# Note:
# Since some AWK implementations cannot handle large integers, we strip off the
# first 4 hex digits from the address. This is safe because the kernel space
# is not large enough for addresses to extend into those digits. The portion
# to strip off is stored in addr_prefix as a regexp, so further clauses can
# perform a simple substitution to do the address stripping.
#
ARGIND == 2 && /^\./ {
# Explicitly ignore a few sections that are not relevant here.
if ($1 ~ /^\.orc_/ || $1 ~ /_sites$/ || $1 ~ /\.percpu/)
next;
# Sections with a 0-address can be ignored as well.
if ($2 ~ /^0x0+$/)
next;
raw_addr = $2;
addr_prefix = "^" substr($2, 1, 6);
base = $2;
sub(addr_prefix, "0x", base);
base = strtonum(base);
sect = $1;
anchor = 0;
sect_base[sect] = base;
sect_size[sect] = strtonum($3);
if (dbg)
printf "[%s] BASE %016x\n", sect, base >"/dev/stderr";
next;
}
# If we are not in a section we care about, we ignore the record.
#
ARGIND == 2 && !sect {
next;
}
# Record the first anchor symbol for the current section.
#
# An anchor record for the section bears the same raw address as the section
# record.
#
ARGIND == 2 && !anchor && NF == 4 && raw_addr == $1 && $3 == "=" && $4 == "." {
anchor = sprintf("%s %08x-%08x = %s", sect, 0, 0, $2);
sect_anchor[sect] = anchor;
if (dbg)
printf "[%s] ANCHOR %016x = %s (.)\n", sect, 0, $2 >"/dev/stderr";
next;
}
# If no anchor record was found for the current section, use the first symbol
# in the section as anchor.
#
ARGIND == 2 && !anchor && NF == 2 && $1 ~ /^0x/ && $2 !~ /^0x/ {
addr = $1;
sub(addr_prefix, "0x", addr);
addr = strtonum(addr) - base;
anchor = sprintf("%s %08x-%08x = %s", sect, addr, addr, $2);
sect_anchor[sect] = anchor;
if (dbg)
printf "[%s] ANCHOR %016x = %s\n", sect, addr, $2 >"/dev/stderr";
next;
}
# The first occurrence of a section name in an object record establishes the
# addend (often 0) for that section. This information is needed to handle
# sections that get combined in the final linking of vmlinux (e.g. .head.text
# getting included at the start of .text).
#
# If the section does not have a base yet, use the base of the encapsulating
# section.
#
ARGIND == 2 && sect && NF == 4 && /^ [^ \*]/ && !($1 in sect_addend) {
if (!($1 in sect_base)) {
sect_base[$1] = base;
if (dbg)
printf "[%s] BASE %016x\n", $1, base >"/dev/stderr";
}
addr = $2;
sub(addr_prefix, "0x", addr);
addr = strtonum(addr);
sect_addend[$1] = addr - sect_base[$1];
sect_in[$1] = sect;
if (dbg)
printf "[%s] ADDEND %016x - %016x = %016x\n", $1, addr, base, sect_addend[$1] >"/dev/stderr";
# If the object is vmlinux.o then we will need vmlinux.o.map to get the
# actual offsets of objects.
if ($4 == "vmlinux.o")
need_o_map = 1;
}
# (3) Collect offset ranges (relative to the section base address) for built-in
# modules.
#
# If the final link was done using the actual objects, vmlinux.map contains all
# the information we need (see section (3a)).
# If linking was done using vmlinux.a as intermediary, we will need to process
# vmlinux.o.map (see section (3b)).
# (3a) Determine offset range info using vmlinux.map.
#
# Since we are already processing vmlinux.map, the top level section that is
# being processed is already known. If we do not have a base address for it,
# we do not need to process records for it.
#
# Given the object name, we determine the module(s) (if any) that the current
# object is associated with.
#
# If we were already processing objects for a (list of) module(s):
# - If the current object belongs to the same module(s), update the range data
# to include the current object.
# - Otherwise, ensure that the end offset of the range is valid.
#
# If the current object does not belong to a built-in module, ignore it.
#
# If it does, we add a new built-in module offset range record.
#
ARGIND == 2 && !need_o_map && /^ [^ ]/ && NF == 4 && $3 != "0x0" {
if (!(sect in sect_base))
next;
# Turn the address into an offset from the section base.
soff = $2;
sub(addr_prefix, "0x", soff);
soff = strtonum(soff) - sect_base[sect];
eoff = soff + strtonum($3);
# Determine which (if any) built-in modules the object belongs to.
mod = get_module_info($4);
# If we are processing a built-in module:
# - If the current object is within the same module, we update its
# entry by extending the range and move on
# - Otherwise:
# + If we are still processing within the same main section, we
# validate the end offset against the start offset of the
# current object (e.g. .rodata.str1.[18] objects are often
# listed with an incorrect size in the linker map)
# + Otherwise, we validate the end offset against the section
# size
if (mod_name) {
if (mod == mod_name) {
mod_eoff = eoff;
update_entry(mod_sect, mod_name, mod_soff, eoff);
next;
} else if (sect == sect_in[mod_sect]) {
if (mod_eoff > soff)
update_entry(mod_sect, mod_name, mod_soff, soff);
} else {
v = sect_size[sect_in[mod_sect]];
if (mod_eoff > v)
update_entry(mod_sect, mod_name, mod_soff, v);
}
}
mod_name = mod;
# If we encountered an object that is not part of a built-in module, we
# do not need to record any data.
if (!mod)
next;
# At this point, we encountered the start of a new built-in module.
mod_name = mod;
mod_soff = soff;
mod_eoff = eoff;
mod_sect = $1;
update_entry($1, mod, soff, mod_eoff);
next;
}
# If we do not need to parse the vmlinux.o.map file, we are done.
#
ARGIND == 3 && !need_o_map {
if (dbg)
printf "Note: %s is not needed.\n", FILENAME >"/dev/stderr";
exit;
}
# (3) Collect offset ranges (relative to the section base address) for built-in
# modules.
#
# (LLD) Convert an object record from lld format to ld format.
#
ARGIND == 3 && map_is_lld && NF == 5 && $5 ~ /:\(/ {
gsub(/\)/, "");
sub(/:\(/, " ");
sect = $6;
if (!(sect in sect_addend))
next;
sub(/ vmlinux\.a\(/, " ");
$0 = " "sect " 0x"$1 " 0x"$3 " " $5;
}
# (3b) Determine offset range info using vmlinux.o.map.
#
# If we do not know an addend for the object's section, we are interested in
# anything within that section.
#
# Determine the top-level section that the object's section was included in
# during the final link. This is the section name offset range data will be
# associated with for this object.
#
# The remainder of the processing of the current object record follows the
# procedure outlined in (3a).
#
ARGIND == 3 && /^ [^ ]/ && NF == 4 && $3 != "0x0" {
osect = $1;
if (!(osect in sect_addend))
next;
# We need to work with the main section.
sect = sect_in[osect];
# Turn the address into an offset from the section base.
soff = $2;
sub(addr_prefix, "0x", soff);
soff = strtonum(soff) + sect_addend[osect];
eoff = soff + strtonum($3);
# Determine which (if any) built-in modules the object belongs to.
mod = get_module_info($4);
# If we are processing a built-in module:
# - If the current object is within the same module, we update its
# entry by extending the range and move on
# - Otherwise:
# + If we are still processing within the same main section, we
# validate the end offset against the start offset of the
# current object (e.g. .rodata.str1.[18] objects are often
# listed with an incorrect size in the linker map)
# + Otherwise, we validate the end offset against the section
# size
if (mod_name) {
if (mod == mod_name) {
mod_eoff = eoff;
update_entry(mod_sect, mod_name, mod_soff, eoff);
next;
} else if (sect == sect_in[mod_sect]) {
if (mod_eoff > soff)
update_entry(mod_sect, mod_name, mod_soff, soff);
} else {
v = sect_size[sect_in[mod_sect]];
if (mod_eoff > v)
update_entry(mod_sect, mod_name, mod_soff, v);
}
}
mod_name = mod;
# If we encountered an object that is not part of a built-in module, we
# do not need to record any data.
if (!mod)
next;
# At this point, we encountered the start of a new built-in module.
mod_name = mod;
mod_soff = soff;
mod_eoff = eoff;
mod_sect = osect;
update_entry(osect, mod, soff, mod_eoff);
next;
}
# (4) Generate the output.
#
# Anchor records are added for each section that contains offset range data
# records. They are added at an adjusted section base address (base << 1) to
# ensure they come first in the second records (see update_entry() above for
# more information).
#
# All entries are sorted by (adjusted) address to ensure that the output can be
# parsed in strict ascending address order.
#
END {
for (sect in count) {
if (sect in sect_anchor) {
idx = sprintf("%016x", sect_base[sect] * 2);
entries[idx] = sect_anchor[sect];
}
}
n = asorti(entries, indices);
for (i = 1; i <= n; i++)
print entries[indices[i]];
}
+28
View File
@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef HASH_H
#define HASH_H
static inline unsigned int hash_str(const char *s)
{
/* fnv32 hash */
unsigned int hash = 2166136261U;
for (; *s; s++)
hash = (hash ^ *s) * 0x01000193;
return hash;
}
/* simplified version of functions from include/linux/hash.h */
#define GOLDEN_RATIO_32 0x61C88647
static inline unsigned int hash_32(unsigned int val)
{
return 0x61C88647 * val;
}
static inline unsigned int hash_ptr(const void *ptr)
{
return hash_32((unsigned int)(unsigned long)ptr);
}
#endif /* HASH_H */
+50
View File
@@ -15,6 +15,23 @@
#define hash_head(table, key) (&(table)[(key) % HASH_SIZE(table)])
static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
{
unsigned int i;
for (i = 0; i < sz; i++)
INIT_HLIST_HEAD(&ht[i]);
}
/**
* hash_init - initialize a hash table
* @table: hashtable to be initialized
*
* This has to be a macro since HASH_SIZE() will not work on pointers since
* it calculates the size during preprocessing.
*/
#define hash_init(table) __hash_init(table, HASH_SIZE(table))
/**
* hash_add - add an object to a hashtable
* @table: hashtable to add to
@@ -24,6 +41,15 @@
#define hash_add(table, node, key) \
hlist_add_head(node, hash_head(table, key))
/**
* hash_del - remove an object from a hashtable
* @node: &struct hlist_node of the object to remove
*/
static inline void hash_del(struct hlist_node *node)
{
hlist_del_init(node);
}
/**
* hash_for_each - iterate over a hashtable
* @table: hashtable to iterate
@@ -34,6 +60,18 @@
for (int _bkt = 0; _bkt < HASH_SIZE(table); _bkt++) \
hlist_for_each_entry(obj, &table[_bkt], member)
/**
* hash_for_each_safe - iterate over a hashtable safe against removal of
* hash entry
* @table: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @tmp: a &struct hlist_node used for temporary storage
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each_safe(table, obj, tmp, member) \
for (int _bkt = 0; _bkt < HASH_SIZE(table); _bkt++) \
hlist_for_each_entry_safe(obj, tmp, &table[_bkt], member)
/**
* hash_for_each_possible - iterate over all possible objects hashing to the
* same bucket
@@ -45,4 +83,16 @@
#define hash_for_each_possible(table, obj, member, key) \
hlist_for_each_entry(obj, hash_head(table, key), member)
/**
* hash_for_each_possible_safe - iterate over all possible objects hashing to the
* same bucket safe against removals
* @table: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @tmp: a &struct hlist_node used for temporary storage
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible_safe(table, obj, tmp, member, key) \
hlist_for_each_entry_safe(obj, tmp, hash_head(table, key), member)
#endif /* HASHTABLE_H */
+69
View File
@@ -268,6 +268,63 @@ static inline int list_empty(const struct list_head *head)
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
/**
* hlist_unhashed - Has node been removed from list and reinitialized?
* @h: Node to be checked
*
* Not that not all removal functions will leave a node in unhashed
* state. For example, hlist_nulls_del_init_rcu() does leave the
* node in unhashed state, but hlist_nulls_del() does not.
*/
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}
/**
* hlist_del - Delete the specified hlist_node from its list
* @n: Node to delete.
*
* Note that this function leaves the node in hashed state. Use
* hlist_del_init() or similar instead to unhash @n.
*/
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
/**
* hlist_del_init - Delete the specified hlist_node from its list and initialize
* @n: Node to delete.
*
* Note that this function leaves the node in unhashed state.
*/
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
/**
* hlist_add_head - add a new entry at the beginning of the hlist
@@ -306,4 +363,16 @@ static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: a &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, typeof(*pos), member))
#endif /* LIST_H */
+53
View File
@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef XALLOC_H
#define XALLOC_H
#include <stdlib.h>
#include <string.h>
static inline void *xmalloc(size_t size)
{
void *p = malloc(size);
if (!p)
exit(1);
return p;
}
static inline void *xcalloc(size_t nmemb, size_t size)
{
void *p = calloc(nmemb, size);
if (!p)
exit(1);
return p;
}
static inline void *xrealloc(void *p, size_t size)
{
p = realloc(p, size);
if (!p)
exit(1);
return p;
}
static inline char *xstrdup(const char *s)
{
char *p = strdup(s);
if (!p)
exit(1);
return p;
}
static inline char *xstrndup(const char *s, size_t n)
{
char *p = strndup(s, n);
if (!p)
exit(1);
return p;
}
#endif /* XALLOC_H */
+14 -32
View File
@@ -27,6 +27,8 @@
#include <ctype.h>
#include <limits.h>
#include <xalloc.h>
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof(arr[0]))
#define KSYM_NAME_LEN 512
@@ -168,12 +170,7 @@ static struct sym_entry *read_symbol(FILE *in, char **buf, size_t *buf_len)
* compressed together */
len++;
sym = malloc(sizeof(*sym) + len + 1);
if (!sym) {
fprintf(stderr, "kallsyms failure: "
"unable to allocate required amount of memory\n");
exit(EXIT_FAILURE);
}
sym = xmalloc(sizeof(*sym) + len + 1);
sym->addr = addr;
sym->len = len;
sym->sym[0] = type;
@@ -278,12 +275,7 @@ static void read_map(const char *in)
if (table_cnt >= table_size) {
table_size += 10000;
table = realloc(table, sizeof(*table) * table_size);
if (!table) {
fprintf(stderr, "out of memory\n");
fclose(fp);
exit (1);
}
table = xrealloc(table, sizeof(*table) * table_size);
}
table[table_cnt++] = sym;
@@ -300,15 +292,6 @@ static void output_label(const char *label)
printf("%s:\n", label);
}
/* Provide proper symbols relocatability by their '_text' relativeness. */
static void output_address(unsigned long long addr)
{
if (_text <= addr)
printf("\tPTR\t_text + %#llx\n", addr - _text);
else
printf("\tPTR\t_text - %#llx\n", _text - addr);
}
/* uncompress a compressed symbol. When this function is called, the best table
* might still be compressed itself, so the function needs to be recursive */
static int expand_symbol(const unsigned char *data, int len, char *result)
@@ -391,12 +374,7 @@ static void write_src(void)
/* table of offset markers, that give the offset in the compressed stream
* every 256 symbols */
markers_cnt = (table_cnt + 255) / 256;
markers = malloc(sizeof(*markers) * markers_cnt);
if (!markers) {
fprintf(stderr, "kallsyms failure: "
"unable to allocate required memory\n");
exit(EXIT_FAILURE);
}
markers = xmalloc(sizeof(*markers) * markers_cnt);
output_label("kallsyms_names");
off = 0;
@@ -477,17 +455,17 @@ static void write_src(void)
*/
long long offset;
int overflow;
bool overflow;
if (!absolute_percpu) {
offset = table[i]->addr - relative_base;
overflow = (offset < 0 || offset > UINT_MAX);
overflow = offset < 0 || offset > UINT_MAX;
} else if (symbol_absolute(table[i])) {
offset = table[i]->addr;
overflow = (offset < 0 || offset > INT_MAX);
overflow = offset < 0 || offset > INT_MAX;
} else {
offset = relative_base - table[i]->addr - 1;
overflow = (offset < INT_MIN || offset >= 0);
overflow = offset < INT_MIN || offset >= 0;
}
if (overflow) {
fprintf(stderr, "kallsyms failure: "
@@ -501,7 +479,11 @@ static void write_src(void)
printf("\n");
output_label("kallsyms_relative_base");
output_address(relative_base);
/* Provide proper symbols relocatability by their '_text' relativeness. */
if (_text <= relative_base)
printf("\tPTR\t_text + %#llx\n", relative_base - _text);
else
printf("\tPTR\t_text - %#llx\n", _text - relative_base);
printf("\n");
sort_symbols_by_name();
+3
View File
@@ -18,6 +18,7 @@
#include <time.h>
#include <unistd.h>
#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
@@ -395,6 +396,8 @@ load:
}
}
expr_invalidate_all();
while (getline_stripped(&line, &line_asize, in) != -1) {
struct menu *choice;
+207 -275
View File
@@ -9,44 +9,68 @@
#include <stdlib.h>
#include <string.h>
#include <hash.h>
#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
#define DEBUG_EXPR 0
HASHTABLE_DEFINE(expr_hashtable, EXPR_HASHSIZE);
static struct expr *expr_eliminate_yn(struct expr *e);
/**
* expr_lookup - return the expression with the given type and sub-nodes
* This looks up an expression with the specified type and sub-nodes. If such
* an expression is found in the hash table, it is returned. Otherwise, a new
* expression node is allocated and added to the hash table.
* @type: expression type
* @l: left node
* @r: right node
* return: expression
*/
static struct expr *expr_lookup(enum expr_type type, void *l, void *r)
{
struct expr *e;
int hash;
hash = hash_32((unsigned int)type ^ hash_ptr(l) ^ hash_ptr(r));
hash_for_each_possible(expr_hashtable, e, node, hash) {
if (e->type == type && e->left._initdata == l &&
e->right._initdata == r)
return e;
}
e = xmalloc(sizeof(*e));
e->type = type;
e->left._initdata = l;
e->right._initdata = r;
hash_add(expr_hashtable, &e->node, hash);
return e;
}
struct expr *expr_alloc_symbol(struct symbol *sym)
{
struct expr *e = xcalloc(1, sizeof(*e));
e->type = E_SYMBOL;
e->left.sym = sym;
return e;
return expr_lookup(E_SYMBOL, sym, NULL);
}
struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
{
struct expr *e = xcalloc(1, sizeof(*e));
e->type = type;
e->left.expr = ce;
return e;
return expr_lookup(type, ce, NULL);
}
struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
{
struct expr *e = xcalloc(1, sizeof(*e));
e->type = type;
e->left.expr = e1;
e->right.expr = e2;
return e;
return expr_lookup(type, e1, e2);
}
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
{
struct expr *e = xcalloc(1, sizeof(*e));
e->type = type;
e->left.sym = s1;
e->right.sym = s2;
return e;
return expr_lookup(type, s1, s2);
}
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
@@ -63,76 +87,6 @@ struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
}
struct expr *expr_copy(const struct expr *org)
{
struct expr *e;
if (!org)
return NULL;
e = xmalloc(sizeof(*org));
memcpy(e, org, sizeof(*org));
switch (org->type) {
case E_SYMBOL:
e->left = org->left;
break;
case E_NOT:
e->left.expr = expr_copy(org->left.expr);
break;
case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL:
e->left.sym = org->left.sym;
e->right.sym = org->right.sym;
break;
case E_AND:
case E_OR:
e->left.expr = expr_copy(org->left.expr);
e->right.expr = expr_copy(org->right.expr);
break;
default:
fprintf(stderr, "can't copy type %d\n", e->type);
free(e);
e = NULL;
break;
}
return e;
}
void expr_free(struct expr *e)
{
if (!e)
return;
switch (e->type) {
case E_SYMBOL:
break;
case E_NOT:
expr_free(e->left.expr);
break;
case E_EQUAL:
case E_GEQ:
case E_GTH:
case E_LEQ:
case E_LTH:
case E_UNEQUAL:
break;
case E_OR:
case E_AND:
expr_free(e->left.expr);
expr_free(e->right.expr);
break;
default:
fprintf(stderr, "how to free type %d?\n", e->type);
break;
}
free(e);
}
static int trans_count;
/*
@@ -145,16 +99,24 @@ static int trans_count;
*/
static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
{
struct expr *l, *r;
/* Recurse down to leaves */
if ((*ep1)->type == type) {
__expr_eliminate_eq(type, &(*ep1)->left.expr, ep2);
__expr_eliminate_eq(type, &(*ep1)->right.expr, ep2);
l = (*ep1)->left.expr;
r = (*ep1)->right.expr;
__expr_eliminate_eq(type, &l, ep2);
__expr_eliminate_eq(type, &r, ep2);
*ep1 = expr_alloc_two(type, l, r);
return;
}
if ((*ep2)->type == type) {
__expr_eliminate_eq(type, ep1, &(*ep2)->left.expr);
__expr_eliminate_eq(type, ep1, &(*ep2)->right.expr);
l = (*ep2)->left.expr;
r = (*ep2)->right.expr;
__expr_eliminate_eq(type, ep1, &l);
__expr_eliminate_eq(type, ep1, &r);
*ep2 = expr_alloc_two(type, l, r);
return;
}
@@ -170,7 +132,6 @@ static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct e
/* *ep1 and *ep2 are equal leaves. Prepare them for elimination. */
trans_count++;
expr_free(*ep1); expr_free(*ep2);
switch (type) {
case E_OR:
*ep1 = expr_alloc_symbol(&symbol_no);
@@ -242,9 +203,10 @@ void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
* equals some operand in the other (operands do not need to appear in the same
* order), recursively.
*/
int expr_eq(struct expr *e1, struct expr *e2)
bool expr_eq(struct expr *e1, struct expr *e2)
{
int res, old_count;
int old_count;
bool res;
/*
* A NULL expr is taken to be yes, but there's also a different way to
@@ -254,7 +216,7 @@ int expr_eq(struct expr *e1, struct expr *e2)
return expr_is_yes(e1) && expr_is_yes(e2);
if (e1->type != e2->type)
return 0;
return false;
switch (e1->type) {
case E_EQUAL:
case E_GEQ:
@@ -269,14 +231,10 @@ int expr_eq(struct expr *e1, struct expr *e2)
return expr_eq(e1->left.expr, e2->left.expr);
case E_AND:
case E_OR:
e1 = expr_copy(e1);
e2 = expr_copy(e2);
old_count = trans_count;
expr_eliminate_eq(&e1, &e2);
res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
e1->left.sym == e2->left.sym);
expr_free(e1);
expr_free(e2);
trans_count = old_count;
return res;
case E_RANGE:
@@ -291,11 +249,11 @@ int expr_eq(struct expr *e1, struct expr *e2)
printf(" ?\n");
}
return 0;
return false;
}
/*
* Recursively performs the following simplifications in-place (as well as the
* Recursively performs the following simplifications (as well as the
* corresponding simplifications with swapped operands):
*
* expr && n -> n
@@ -307,79 +265,39 @@ int expr_eq(struct expr *e1, struct expr *e2)
*/
static struct expr *expr_eliminate_yn(struct expr *e)
{
struct expr *tmp;
struct expr *l, *r;
if (e) switch (e->type) {
case E_AND:
e->left.expr = expr_eliminate_yn(e->left.expr);
e->right.expr = expr_eliminate_yn(e->right.expr);
if (e->left.expr->type == E_SYMBOL) {
if (e->left.expr->left.sym == &symbol_no) {
expr_free(e->left.expr);
expr_free(e->right.expr);
e->type = E_SYMBOL;
e->left.sym = &symbol_no;
e->right.expr = NULL;
return e;
} else if (e->left.expr->left.sym == &symbol_yes) {
free(e->left.expr);
tmp = e->right.expr;
*e = *(e->right.expr);
free(tmp);
return e;
}
l = expr_eliminate_yn(e->left.expr);
r = expr_eliminate_yn(e->right.expr);
if (l->type == E_SYMBOL) {
if (l->left.sym == &symbol_no)
return l;
else if (l->left.sym == &symbol_yes)
return r;
}
if (e->right.expr->type == E_SYMBOL) {
if (e->right.expr->left.sym == &symbol_no) {
expr_free(e->left.expr);
expr_free(e->right.expr);
e->type = E_SYMBOL;
e->left.sym = &symbol_no;
e->right.expr = NULL;
return e;
} else if (e->right.expr->left.sym == &symbol_yes) {
free(e->right.expr);
tmp = e->left.expr;
*e = *(e->left.expr);
free(tmp);
return e;
}
if (r->type == E_SYMBOL) {
if (r->left.sym == &symbol_no)
return r;
else if (r->left.sym == &symbol_yes)
return l;
}
break;
case E_OR:
e->left.expr = expr_eliminate_yn(e->left.expr);
e->right.expr = expr_eliminate_yn(e->right.expr);
if (e->left.expr->type == E_SYMBOL) {
if (e->left.expr->left.sym == &symbol_no) {
free(e->left.expr);
tmp = e->right.expr;
*e = *(e->right.expr);
free(tmp);
return e;
} else if (e->left.expr->left.sym == &symbol_yes) {
expr_free(e->left.expr);
expr_free(e->right.expr);
e->type = E_SYMBOL;
e->left.sym = &symbol_yes;
e->right.expr = NULL;
return e;
}
l = expr_eliminate_yn(e->left.expr);
r = expr_eliminate_yn(e->right.expr);
if (l->type == E_SYMBOL) {
if (l->left.sym == &symbol_no)
return r;
else if (l->left.sym == &symbol_yes)
return l;
}
if (e->right.expr->type == E_SYMBOL) {
if (e->right.expr->left.sym == &symbol_no) {
free(e->right.expr);
tmp = e->left.expr;
*e = *(e->left.expr);
free(tmp);
return e;
} else if (e->right.expr->left.sym == &symbol_yes) {
expr_free(e->left.expr);
expr_free(e->right.expr);
e->type = E_SYMBOL;
e->left.sym = &symbol_yes;
e->right.expr = NULL;
return e;
}
if (r->type == E_SYMBOL) {
if (r->left.sym == &symbol_no)
return l;
else if (r->left.sym == &symbol_yes)
return r;
}
break;
default:
@@ -397,7 +315,7 @@ static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
struct symbol *sym1, *sym2;
if (expr_eq(e1, e2))
return expr_copy(e1);
return e1;
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
return NULL;
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
@@ -440,6 +358,7 @@ static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
}
}
if (sym1->type == S_BOOLEAN) {
// a || !a -> y
if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
(e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
return expr_alloc_symbol(&symbol_yes);
@@ -461,7 +380,7 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
struct symbol *sym1, *sym2;
if (expr_eq(e1, e2))
return expr_copy(e1);
return e1;
if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
return NULL;
if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
@@ -558,38 +477,33 @@ static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
*/
static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
{
struct expr *tmp;
struct expr *tmp, *l, *r;
/* Recurse down to leaves */
if ((*ep1)->type == type) {
expr_eliminate_dups1(type, &(*ep1)->left.expr, ep2);
expr_eliminate_dups1(type, &(*ep1)->right.expr, ep2);
l = (*ep1)->left.expr;
r = (*ep1)->right.expr;
expr_eliminate_dups1(type, &l, ep2);
expr_eliminate_dups1(type, &r, ep2);
*ep1 = expr_alloc_two(type, l, r);
return;
}
if ((*ep2)->type == type) {
expr_eliminate_dups1(type, ep1, &(*ep2)->left.expr);
expr_eliminate_dups1(type, ep1, &(*ep2)->right.expr);
l = (*ep2)->left.expr;
r = (*ep2)->right.expr;
expr_eliminate_dups1(type, ep1, &l);
expr_eliminate_dups1(type, ep1, &r);
*ep2 = expr_alloc_two(type, l, r);
return;
}
/* *ep1 and *ep2 are leaves. Compare and process them. */
if (*ep1 == *ep2)
return;
switch ((*ep1)->type) {
case E_OR: case E_AND:
expr_eliminate_dups1((*ep1)->type, ep1, ep1);
default:
;
}
switch (type) {
case E_OR:
tmp = expr_join_or(*ep1, *ep2);
if (tmp) {
expr_free(*ep1); expr_free(*ep2);
*ep1 = expr_alloc_symbol(&symbol_no);
*ep2 = tmp;
trans_count++;
@@ -598,7 +512,6 @@ static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct
case E_AND:
tmp = expr_join_and(*ep1, *ep2);
if (tmp) {
expr_free(*ep1); expr_free(*ep2);
*ep1 = expr_alloc_symbol(&symbol_yes);
*ep2 = tmp;
trans_count++;
@@ -628,10 +541,15 @@ struct expr *expr_eliminate_dups(struct expr *e)
oldcount = trans_count;
do {
struct expr *l, *r;
trans_count = 0;
switch (e->type) {
case E_OR: case E_AND:
expr_eliminate_dups1(e->type, &e, &e);
l = expr_eliminate_dups(e->left.expr);
r = expr_eliminate_dups(e->right.expr);
expr_eliminate_dups1(e->type, &l, &r);
e = expr_alloc_two(e->type, l, r);
default:
;
}
@@ -645,12 +563,34 @@ struct expr *expr_eliminate_dups(struct expr *e)
* Performs various simplifications involving logical operators and
* comparisons.
*
* For bool type:
* A=n -> !A
* A=m -> n
* A=y -> A
* A!=n -> A
* A!=m -> y
* A!=y -> !A
*
* For any type:
* !!A -> A
* !(A=B) -> A!=B
* !(A!=B) -> A=B
* !(A<=B) -> A>B
* !(A>=B) -> A<B
* !(A<B) -> A>=B
* !(A>B) -> A<=B
* !(A || B) -> !A && !B
* !(A && B) -> !A || !B
*
* For constant:
* !y -> n
* !m -> m
* !n -> y
*
* Allocates and returns a new expression.
*/
struct expr *expr_transform(struct expr *e)
{
struct expr *tmp;
if (!e)
return NULL;
switch (e->type) {
@@ -663,8 +603,9 @@ struct expr *expr_transform(struct expr *e)
case E_SYMBOL:
break;
default:
e->left.expr = expr_transform(e->left.expr);
e->right.expr = expr_transform(e->right.expr);
e = expr_alloc_two(e->type,
expr_transform(e->left.expr),
expr_transform(e->right.expr));
}
switch (e->type) {
@@ -672,21 +613,19 @@ struct expr *expr_transform(struct expr *e)
if (e->left.sym->type != S_BOOLEAN)
break;
if (e->right.sym == &symbol_no) {
e->type = E_NOT;
e->left.expr = expr_alloc_symbol(e->left.sym);
e->right.sym = NULL;
// A=n -> !A
e = expr_alloc_one(E_NOT, expr_alloc_symbol(e->left.sym));
break;
}
if (e->right.sym == &symbol_mod) {
// A=m -> n
printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
e->type = E_SYMBOL;
e->left.sym = &symbol_no;
e->right.sym = NULL;
e = expr_alloc_symbol(&symbol_no);
break;
}
if (e->right.sym == &symbol_yes) {
e->type = E_SYMBOL;
e->right.sym = NULL;
// A=y -> A
e = expr_alloc_symbol(e->left.sym);
break;
}
break;
@@ -694,104 +633,71 @@ struct expr *expr_transform(struct expr *e)
if (e->left.sym->type != S_BOOLEAN)
break;
if (e->right.sym == &symbol_no) {
e->type = E_SYMBOL;
e->right.sym = NULL;
// A!=n -> A
e = expr_alloc_symbol(e->left.sym);
break;
}
if (e->right.sym == &symbol_mod) {
// A!=m -> y
printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
e->type = E_SYMBOL;
e->left.sym = &symbol_yes;
e->right.sym = NULL;
e = expr_alloc_symbol(&symbol_yes);
break;
}
if (e->right.sym == &symbol_yes) {
e->type = E_NOT;
e->left.expr = expr_alloc_symbol(e->left.sym);
e->right.sym = NULL;
// A!=y -> !A
e = expr_alloc_one(E_NOT, e->left.expr);
break;
}
break;
case E_NOT:
switch (e->left.expr->type) {
case E_NOT:
// !!a -> a
tmp = e->left.expr->left.expr;
free(e->left.expr);
free(e);
e = tmp;
e = expr_transform(e);
// !!A -> A
e = e->left.expr->left.expr;
break;
case E_EQUAL:
case E_UNEQUAL:
// !a='x' -> a!='x'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
// !(A=B) -> A!=B
e = expr_alloc_comp(e->left.expr->type == E_EQUAL ? E_UNEQUAL : E_EQUAL,
e->left.expr->left.sym,
e->left.expr->right.sym);
break;
case E_LEQ:
case E_GEQ:
// !a<='x' -> a>'x'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = e->type == E_LEQ ? E_GTH : E_LTH;
// !(A<=B) -> A>B
e = expr_alloc_comp(e->left.expr->type == E_LEQ ? E_GTH : E_LTH,
e->left.expr->left.sym,
e->left.expr->right.sym);
break;
case E_LTH:
case E_GTH:
// !a<'x' -> a>='x'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = e->type == E_LTH ? E_GEQ : E_LEQ;
// !(A<B) -> A>=B
e = expr_alloc_comp(e->left.expr->type == E_LTH ? E_GEQ : E_LEQ,
e->left.expr->left.sym,
e->left.expr->right.sym);
break;
case E_OR:
// !(a || b) -> !a && !b
tmp = e->left.expr;
e->type = E_AND;
e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
tmp->type = E_NOT;
tmp->right.expr = NULL;
// !(A || B) -> !A && !B
e = expr_alloc_and(expr_alloc_one(E_NOT, e->left.expr->left.expr),
expr_alloc_one(E_NOT, e->left.expr->right.expr));
e = expr_transform(e);
break;
case E_AND:
// !(a && b) -> !a || !b
tmp = e->left.expr;
e->type = E_OR;
e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
tmp->type = E_NOT;
tmp->right.expr = NULL;
// !(A && B) -> !A || !B
e = expr_alloc_or(expr_alloc_one(E_NOT, e->left.expr->left.expr),
expr_alloc_one(E_NOT, e->left.expr->right.expr));
e = expr_transform(e);
break;
case E_SYMBOL:
if (e->left.expr->left.sym == &symbol_yes) {
if (e->left.expr->left.sym == &symbol_yes)
// !'y' -> 'n'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = E_SYMBOL;
e->left.sym = &symbol_no;
break;
}
if (e->left.expr->left.sym == &symbol_mod) {
e = expr_alloc_symbol(&symbol_no);
else if (e->left.expr->left.sym == &symbol_mod)
// !'m' -> 'm'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = E_SYMBOL;
e->left.sym = &symbol_mod;
break;
}
if (e->left.expr->left.sym == &symbol_no) {
e = expr_alloc_symbol(&symbol_mod);
else if (e->left.expr->left.sym == &symbol_no)
// !'n' -> 'y'
tmp = e->left.expr;
free(e);
e = tmp;
e->type = E_SYMBOL;
e->left.sym = &symbol_yes;
break;
}
e = expr_alloc_symbol(&symbol_yes);
break;
default:
;
@@ -803,10 +709,10 @@ struct expr *expr_transform(struct expr *e)
return e;
}
int expr_contains_symbol(struct expr *dep, struct symbol *sym)
bool expr_contains_symbol(struct expr *dep, struct symbol *sym)
{
if (!dep)
return 0;
return false;
switch (dep->type) {
case E_AND:
@@ -828,7 +734,7 @@ int expr_contains_symbol(struct expr *dep, struct symbol *sym)
default:
;
}
return 0;
return false;
}
bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
@@ -915,18 +821,18 @@ struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symb
case E_EQUAL:
if (type == E_EQUAL) {
if (sym == &symbol_yes)
return expr_copy(e);
return e;
if (sym == &symbol_mod)
return expr_alloc_symbol(&symbol_no);
if (sym == &symbol_no)
return expr_alloc_one(E_NOT, expr_copy(e));
return expr_alloc_one(E_NOT, e);
} else {
if (sym == &symbol_yes)
return expr_alloc_one(E_NOT, expr_copy(e));
return expr_alloc_one(E_NOT, e);
if (sym == &symbol_mod)
return expr_alloc_symbol(&symbol_yes);
if (sym == &symbol_no)
return expr_copy(e);
return e;
}
break;
case E_SYMBOL:
@@ -981,7 +887,7 @@ static enum string_value_kind expr_parse_string(const char *str,
? kind : k_string;
}
tristate expr_calc_value(struct expr *e)
static tristate __expr_calc_value(struct expr *e)
{
tristate val1, val2;
const char *str1, *str2;
@@ -989,9 +895,6 @@ tristate expr_calc_value(struct expr *e)
union string_value lval = {}, rval = {};
int res;
if (!e)
return yes;
switch (e->type) {
case E_SYMBOL:
sym_calc_value(e->left.sym);
@@ -1055,6 +958,35 @@ tristate expr_calc_value(struct expr *e)
}
}
/**
* expr_calc_value - return the tristate value of the given expression
* @e: expression
* return: tristate value of the expression
*/
tristate expr_calc_value(struct expr *e)
{
if (!e)
return yes;
if (!e->val_is_valid) {
e->val = __expr_calc_value(e);
e->val_is_valid = true;
}
return e->val;
}
/**
* expr_invalidate_all - invalidate all cached expression values
*/
void expr_invalidate_all(void)
{
struct expr *e;
hash_for_each(expr_hashtable, e, node)
e->val_is_valid = false;
}
static int expr_compare_type(enum expr_type t1, enum expr_type t2)
{
if (t1 == t2)
+19 -8
View File
@@ -29,12 +29,26 @@ enum expr_type {
};
union expr_data {
struct expr *expr;
struct symbol *sym;
struct expr * const expr;
struct symbol * const sym;
void *_initdata;
};
/**
* struct expr - expression
*
* @node: link node for the hash table
* @type: expressoin type
* @val: calculated tristate value
* @val_is_valid: indicate whether the value is valid
* @left: left node
* @right: right node
*/
struct expr {
struct hlist_node node;
enum expr_type type;
tristate val;
bool val_is_valid;
union expr_data left, right;
};
@@ -168,7 +182,6 @@ enum prop_type {
P_SELECT, /* select BAR */
P_IMPLY, /* imply BAR */
P_RANGE, /* range 7..100 (for a symbol) */
P_SYMBOL, /* where a symbol is defined */
};
struct property {
@@ -276,14 +289,12 @@ struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
struct expr *expr_copy(const struct expr *org);
void expr_free(struct expr *e);
void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
int expr_eq(struct expr *e1, struct expr *e2);
bool expr_eq(struct expr *e1, struct expr *e2);
tristate expr_calc_value(struct expr *e);
struct expr *expr_eliminate_dups(struct expr *e);
struct expr *expr_transform(struct expr *e);
int expr_contains_symbol(struct expr *dep, struct symbol *sym);
bool expr_contains_symbol(struct expr *dep, struct symbol *sym);
bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
@@ -293,7 +304,7 @@ void expr_gstr_print(const struct expr *e, struct gstr *gs);
void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
tristate pr_type, const char *title);
static inline int expr_is_yes(const struct expr *e)
static inline bool expr_is_yes(const struct expr *e)
{
return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
}
+6
View File
@@ -11,6 +11,12 @@ extern HASHTABLE_DECLARE(sym_hashtable, SYMBOL_HASHSIZE);
#define for_all_symbols(sym) \
hash_for_each(sym_hashtable, sym, node)
#define EXPR_HASHSIZE (1U << 14)
extern HASHTABLE_DECLARE(expr_hashtable, EXPR_HASHSIZE);
void expr_invalidate_all(void);
struct menu;
extern struct menu *current_menu, *current_entry;
+1
View File
@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
#include <xalloc.h>
#include "lkc.h"
#include "preprocess.h"
-6
View File
@@ -51,13 +51,7 @@ static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
}
/* util.c */
unsigned int strhash(const char *s);
const char *file_lookup(const char *name);
void *xmalloc(size_t size);
void *xcalloc(size_t nmemb, size_t size);
void *xrealloc(void *p, size_t size);
char *xstrdup(const char *s);
char *xstrndup(const char *s, size_t n);
/* lexer.l */
int yylex(void);
+1
View File
@@ -20,6 +20,7 @@
#include <unistd.h>
#include <list.h>
#include <xalloc.h>
#include "lkc.h"
#include "lxdialog/dialog.h"
#include "mnconf-common.h"
+12 -26
View File
@@ -9,6 +9,7 @@
#include <string.h>
#include <list.h>
#include <xalloc.h>
#include "lkc.h"
#include "internal.h"
@@ -78,10 +79,8 @@ void menu_add_entry(struct symbol *sym)
*last_entry_ptr = menu;
last_entry_ptr = &menu->next;
current_entry = menu;
if (sym) {
menu_add_symbol(P_SYMBOL, sym, NULL);
if (sym)
list_add_tail(&menu->link, &sym->menus);
}
}
struct menu *menu_add_menu(void)
@@ -108,12 +107,13 @@ static struct expr *rewrite_m(struct expr *e)
switch (e->type) {
case E_NOT:
e->left.expr = rewrite_m(e->left.expr);
e = expr_alloc_one(E_NOT, rewrite_m(e->left.expr));
break;
case E_OR:
case E_AND:
e->left.expr = rewrite_m(e->left.expr);
e->right.expr = rewrite_m(e->right.expr);
e = expr_alloc_two(e->type,
rewrite_m(e->left.expr),
rewrite_m(e->right.expr));
break;
case E_SYMBOL:
/* change 'm' into 'm' && MODULES */
@@ -193,21 +193,11 @@ struct property *menu_add_prompt(enum prop_type type, const char *prompt,
struct menu *menu = current_entry;
while ((menu = menu->parent) != NULL) {
struct expr *dup_expr;
if (!menu->visibility)
continue;
/*
* Do not add a reference to the menu's visibility
* expression but use a copy of it. Otherwise the
* expression reduction functions will modify
* expressions that have multiple references which
* can cause unwanted side effects.
*/
dup_expr = expr_copy(menu->visibility);
prop->visible.expr = expr_alloc_and(prop->visible.expr,
dup_expr);
menu->visibility);
}
}
@@ -323,7 +313,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
*/
basedep = rewrite_m(menu->dep);
basedep = expr_transform(basedep);
basedep = expr_alloc_and(expr_copy(parent->dep), basedep);
basedep = expr_alloc_and(parent->dep, basedep);
basedep = expr_eliminate_dups(basedep);
menu->dep = basedep;
@@ -367,7 +357,7 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
*/
dep = rewrite_m(prop->visible.expr);
dep = expr_transform(dep);
dep = expr_alloc_and(expr_copy(basedep), dep);
dep = expr_alloc_and(basedep, dep);
dep = expr_eliminate_dups(dep);
prop->visible.expr = dep;
@@ -378,11 +368,11 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
if (prop->type == P_SELECT) {
struct symbol *es = prop_get_symbol(prop);
es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
} else if (prop->type == P_IMPLY) {
struct symbol *es = prop_get_symbol(prop);
es->implied.expr = expr_alloc_or(es->implied.expr,
expr_alloc_and(expr_alloc_symbol(menu->sym), expr_copy(dep)));
expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
}
}
}
@@ -442,22 +432,18 @@ static void _menu_finalize(struct menu *parent, bool inside_choice)
*/
dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
dep = expr_eliminate_dups(expr_transform(dep));
dep2 = expr_copy(basedep);
dep2 = basedep;
expr_eliminate_eq(&dep, &dep2);
expr_free(dep);
if (!expr_is_yes(dep2)) {
/* Not superset, quit */
expr_free(dep2);
break;
}
/* Superset, put in submenu */
expr_free(dep2);
next:
_menu_finalize(menu, false);
menu->parent = parent;
last_menu = menu;
}
expr_free(basedep);
if (last_menu) {
parent->list = parent->next;
parent->next = last_menu->next;
+1
View File
@@ -12,6 +12,7 @@
#include <stdlib.h>
#include <list.h>
#include <xalloc.h>
#include "lkc.h"
#include "mnconf-common.h"
#include "nconf.h"
+1
View File
@@ -4,6 +4,7 @@
*
* Derived from menuconfig.
*/
#include <xalloc.h>
#include "nconf.h"
#include "lkc.h"
+1 -12
View File
@@ -11,6 +11,7 @@
#include <string.h>
#include <stdbool.h>
#include <xalloc.h>
#include "lkc.h"
#include "internal.h"
#include "preprocess.h"
@@ -530,14 +531,6 @@ void conf_parse(const char *name)
yydebug = 1;
yyparse();
/*
* FIXME:
* cur_filename and cur_lineno are used even after yyparse();
* menu_finalize() calls menu_add_symbol(). This should be fixed.
*/
cur_filename = "<none>";
cur_lineno = 0;
str_printf(&autoconf_cmd,
"\n"
"$(autoconfig): $(deps_config)\n"
@@ -715,10 +708,6 @@ static void print_symbol(FILE *out, const struct menu *menu)
print_quoted_string(out, prop->text);
fputc('\n', out);
break;
case P_SYMBOL:
fputs( " symbol ", out);
fprintf(out, "%s\n", prop->menu->sym->name);
break;
default:
fprintf(out, " unknown prop %d!\n", prop->type);
break;
+1
View File
@@ -11,6 +11,7 @@
#include <array_size.h>
#include <list.h>
#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
#include "preprocess.h"
+1 -1
View File
@@ -22,6 +22,7 @@
#include <stdlib.h>
#include <xalloc.h>
#include "lkc.h"
#include "qconf.h"
@@ -1094,7 +1095,6 @@ QString ConfigInfoView::debug_info(struct symbol *sym)
case P_RANGE:
case P_COMMENT:
case P_IMPLY:
case P_SYMBOL:
stream << prop_get_type_name(prop->type);
stream << ": ";
expr_print(prop->expr, expr_print_help,
+5 -4
View File
@@ -9,6 +9,8 @@
#include <string.h>
#include <regex.h>
#include <hash.h>
#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
@@ -517,6 +519,7 @@ void sym_clear_all_valid(void)
for_all_symbols(sym)
sym->flags &= ~SYMBOL_VALID;
expr_invalidate_all();
conf_set_changed(true);
sym_calc_value(modules_sym);
}
@@ -892,7 +895,7 @@ struct symbol *sym_lookup(const char *name, int flags)
case 'n': return &symbol_no;
}
}
hash = strhash(name);
hash = hash_str(name);
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
if (symbol->name &&
@@ -935,7 +938,7 @@ struct symbol *sym_find(const char *name)
case 'n': return &symbol_no;
}
}
hash = strhash(name);
hash = hash_str(name);
hash_for_each_possible(sym_hashtable, symbol, node, hash) {
if (symbol->name &&
@@ -1321,8 +1324,6 @@ const char *prop_get_type_name(enum prop_type type)
return "imply";
case P_RANGE:
return "range";
case P_SYMBOL:
return "symbol";
case P_UNKNOWN:
break;
}
+3 -60
View File
@@ -8,19 +8,11 @@
#include <stdlib.h>
#include <string.h>
#include <hash.h>
#include <hashtable.h>
#include <xalloc.h>
#include "lkc.h"
unsigned int strhash(const char *s)
{
/* fnv32 hash */
unsigned int hash = 2166136261U;
for (; *s; s++)
hash = (hash ^ *s) * 0x01000193;
return hash;
}
/* hash table of all parsed Kconfig files */
static HASHTABLE_DEFINE(file_hashtable, 1U << 11);
@@ -34,7 +26,7 @@ const char *file_lookup(const char *name)
{
struct file *file;
size_t len;
int hash = strhash(name);
int hash = hash_str(name);
hash_for_each_possible(file_hashtable, file, node, hash)
if (!strcmp(name, file->name))
@@ -102,52 +94,3 @@ char *str_get(const struct gstr *gs)
{
return gs->s;
}
void *xmalloc(size_t size)
{
void *p = malloc(size);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}
void *xcalloc(size_t nmemb, size_t size)
{
void *p = calloc(nmemb, size);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}
void *xrealloc(void *p, size_t size)
{
p = realloc(p, size);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}
char *xstrdup(const char *s)
{
char *p;
p = strdup(s);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}
char *xstrndup(const char *s, size_t n)
{
char *p;
p = strndup(s, n);
if (p)
return p;
fprintf(stderr, "Out of memory.\n");
exit(1);
}
+1 -1
View File
@@ -203,7 +203,7 @@ kallsymso=
strip_debug=
if is_enabled CONFIG_KALLSYMS; then
truncate -s0 .tmp_vmlinux.kallsyms0.syms
true > .tmp_vmlinux.kallsyms0.syms
kallsyms .tmp_vmlinux.kallsyms0.syms .tmp_vmlinux0.kallsyms
fi
-25
View File
@@ -8,7 +8,6 @@ int
main(int argc, char **argv)
{
unsigned char ei[EI_NIDENT];
union { short s; char c[2]; } endian_test;
if (fread(ei, 1, EI_NIDENT, stdin) != EI_NIDENT) {
fprintf(stderr, "Error: input truncated\n");
@@ -28,30 +27,6 @@ main(int argc, char **argv)
default:
exit(1);
}
switch (ei[EI_DATA]) {
case ELFDATA2LSB:
printf("#define KERNEL_ELFDATA ELFDATA2LSB\n");
break;
case ELFDATA2MSB:
printf("#define KERNEL_ELFDATA ELFDATA2MSB\n");
break;
default:
exit(1);
}
if (sizeof(unsigned long) == 4) {
printf("#define HOST_ELFCLASS ELFCLASS32\n");
} else if (sizeof(unsigned long) == 8) {
printf("#define HOST_ELFCLASS ELFCLASS64\n");
}
endian_test.s = 0x0102;
if (memcmp(endian_test.c, "\x01\x02", 2) == 0)
printf("#define HOST_ELFDATA ELFDATA2MSB\n");
else if (memcmp(endian_test.c, "\x02\x01", 2) == 0)
printf("#define HOST_ELFDATA ELFDATA2LSB\n");
else
exit(1);
return 0;
}
+65 -60
View File
@@ -23,6 +23,7 @@
#include <hashtable.h>
#include <list.h>
#include <xalloc.h>
#include "modpost.h"
#include "../../include/linux/license.h"
@@ -50,6 +51,9 @@ static bool error_occurred;
static bool extra_warn;
bool target_is_big_endian;
bool host_is_big_endian;
/*
* Cut off the warnings when there are too many. This typically occurs when
* vmlinux is missing. ('make modules' without building vmlinux.)
@@ -63,20 +67,15 @@ static unsigned int nr_unresolved;
#define MODULE_NAME_LEN (64 - sizeof(Elf_Addr))
void modpost_log(enum loglevel loglevel, const char *fmt, ...)
void modpost_log(bool is_error, const char *fmt, ...)
{
va_list arglist;
switch (loglevel) {
case LOG_WARN:
fprintf(stderr, "WARNING: ");
break;
case LOG_ERROR:
if (is_error) {
fprintf(stderr, "ERROR: ");
error_occurred = true;
break;
default: /* invalid loglevel, ignore */
break;
} else {
fprintf(stderr, "WARNING: ");
}
fprintf(stderr, "modpost: ");
@@ -94,14 +93,6 @@ static inline bool strends(const char *str, const char *postfix)
return strcmp(str + strlen(str) - strlen(postfix), postfix) == 0;
}
void *do_nofail(void *ptr, const char *expr)
{
if (!ptr)
fatal("Memory allocation failure: %s.\n", expr);
return ptr;
}
char *read_text_file(const char *filename)
{
struct stat st;
@@ -120,7 +111,7 @@ char *read_text_file(const char *filename)
exit(1);
}
buf = NOFAIL(malloc(st.st_size + 1));
buf = xmalloc(st.st_size + 1);
nbytes = st.st_size;
@@ -178,7 +169,7 @@ static struct module *new_module(const char *name, size_t namelen)
{
struct module *mod;
mod = NOFAIL(malloc(sizeof(*mod) + namelen + 1));
mod = xmalloc(sizeof(*mod) + namelen + 1);
memset(mod, 0, sizeof(*mod));
INIT_LIST_HEAD(&mod->exported_symbols);
@@ -237,7 +228,7 @@ static inline unsigned int tdb_hash(const char *name)
**/
static struct symbol *alloc_symbol(const char *name)
{
struct symbol *s = NOFAIL(malloc(sizeof(*s) + strlen(name) + 1));
struct symbol *s = xmalloc(sizeof(*s) + strlen(name) + 1);
memset(s, 0, sizeof(*s));
strcpy(s->name, name);
@@ -310,8 +301,7 @@ static void add_namespace(struct list_head *head, const char *namespace)
struct namespace_list *ns_entry;
if (!contains_namespace(head, namespace)) {
ns_entry = NOFAIL(malloc(sizeof(*ns_entry) +
strlen(namespace) + 1));
ns_entry = xmalloc(sizeof(*ns_entry) + strlen(namespace) + 1);
strcpy(ns_entry->namespace, namespace);
list_add_tail(&ns_entry->list, head);
}
@@ -366,7 +356,7 @@ static struct symbol *sym_add_exported(const char *name, struct module *mod,
s = alloc_symbol(name);
s->module = mod;
s->is_gpl_only = gpl_only;
s->namespace = NOFAIL(strdup(namespace));
s->namespace = xstrdup(namespace);
list_add_tail(&s->list, &mod->exported_symbols);
hash_add_symbol(s);
@@ -438,6 +428,18 @@ static int parse_elf(struct elf_info *info, const char *filename)
/* Not an ELF file - silently ignore it */
return 0;
}
switch (hdr->e_ident[EI_DATA]) {
case ELFDATA2LSB:
target_is_big_endian = false;
break;
case ELFDATA2MSB:
target_is_big_endian = true;
break;
default:
fatal("target endian is unknown\n");
}
/* Fix endianness in ELF header */
hdr->e_type = TO_NATIVE(hdr->e_type);
hdr->e_machine = TO_NATIVE(hdr->e_machine);
@@ -622,7 +624,7 @@ static void handle_symbol(struct module *mod, struct elf_info *info,
if (ELF_ST_TYPE(sym->st_info) == STT_SPARC_REGISTER)
break;
if (symname[0] == '.') {
char *munged = NOFAIL(strdup(symname));
char *munged = xstrdup(symname);
munged[0] = '_';
munged[1] = toupper(munged[1]);
symname = munged;
@@ -690,10 +692,7 @@ static char *get_modinfo(struct elf_info *info, const char *tag)
static const char *sym_name(struct elf_info *elf, Elf_Sym *sym)
{
if (sym)
return elf->strtab + sym->st_name;
else
return "(unknown)";
return sym ? elf->strtab + sym->st_name : "";
}
/*
@@ -1006,6 +1005,7 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
Elf_Sym *from;
const char *tosym;
const char *fromsym;
char taddr_str[16];
from = find_fromsym(elf, faddr, fsecndx);
fromsym = sym_name(elf, from);
@@ -1019,10 +1019,17 @@ static void default_mismatch_handler(const char *modname, struct elf_info *elf,
sec_mismatch_count++;
warn("%s: section mismatch in reference: %s+0x%x (section: %s) -> %s (section: %s)\n",
modname, fromsym,
(unsigned int)(faddr - (from ? from->st_value : 0)),
fromsec, tosym, tosec);
if (!tosym[0])
snprintf(taddr_str, sizeof(taddr_str), "0x%x", (unsigned int)taddr);
/*
* The format for the reference source: <symbol_name>+<offset> or <address>
* The format for the reference destination: <symbol_name> or <address>
*/
warn("%s: section mismatch in reference: %s%s0x%x (section: %s) -> %s (section: %s)\n",
modname, fromsym, fromsym[0] ? "+" : "",
(unsigned int)(faddr - (fromsym[0] ? from->st_value : 0)),
fromsec, tosym[0] ? tosym : taddr_str, tosec);
if (mismatch->mismatch == EXTABLE_TO_NON_TEXT) {
if (match(tosec, mismatch->bad_tosec))
@@ -1662,7 +1669,7 @@ void buf_write(struct buffer *buf, const char *s, int len)
{
if (buf->size - buf->pos < len) {
buf->size += len + SZ;
buf->p = NOFAIL(realloc(buf->p, buf->size));
buf->p = xrealloc(buf->p, buf->size);
}
strncpy(buf->p + buf->pos, s, len);
buf->pos += len;
@@ -1677,7 +1684,7 @@ static void check_exports(struct module *mod)
exp = find_symbol(s->name);
if (!exp) {
if (!s->weak && nr_unresolved++ < MAX_UNRESOLVED_REPORTS)
modpost_log(warn_unresolved ? LOG_WARN : LOG_ERROR,
modpost_log(!warn_unresolved,
"\"%s\" [%s.ko] undefined!\n",
s->name, mod->name);
continue;
@@ -1700,7 +1707,7 @@ static void check_exports(struct module *mod)
basename = mod->name;
if (!contains_namespace(&mod->imported_namespaces, exp->namespace)) {
modpost_log(allow_missing_ns_imports ? LOG_WARN : LOG_ERROR,
modpost_log(!allow_missing_ns_imports,
"module %s uses symbol %s from namespace %s, but does not import it.\n",
basename, exp->name, exp->namespace);
add_namespace(&mod->missing_namespaces, exp->namespace);
@@ -1748,26 +1755,9 @@ static void check_modname_len(struct module *mod)
static void add_header(struct buffer *b, struct module *mod)
{
buf_printf(b, "#include <linux/module.h>\n");
/*
* Include build-salt.h after module.h in order to
* inherit the definitions.
*/
buf_printf(b, "#define INCLUDE_VERMAGIC\n");
buf_printf(b, "#include <linux/build-salt.h>\n");
buf_printf(b, "#include <linux/elfnote-lto.h>\n");
buf_printf(b, "#include <linux/export-internal.h>\n");
buf_printf(b, "#include <linux/vermagic.h>\n");
buf_printf(b, "#include <linux/compiler.h>\n");
buf_printf(b, "\n");
buf_printf(b, "#ifdef CONFIG_UNWINDER_ORC\n");
buf_printf(b, "#include <asm/orc_header.h>\n");
buf_printf(b, "ORC_HEADER;\n");
buf_printf(b, "#endif\n");
buf_printf(b, "\n");
buf_printf(b, "BUILD_SALT;\n");
buf_printf(b, "BUILD_LTO_INFO;\n");
buf_printf(b, "\n");
buf_printf(b, "MODULE_INFO(vermagic, VERMAGIC_STRING);\n");
buf_printf(b, "MODULE_INFO(name, KBUILD_MODNAME);\n");
buf_printf(b, "\n");
buf_printf(b, "__visible struct module __this_module\n");
@@ -1785,12 +1775,6 @@ static void add_header(struct buffer *b, struct module *mod)
if (!external_module)
buf_printf(b, "\nMODULE_INFO(intree, \"Y\");\n");
buf_printf(b,
"\n"
"#ifdef CONFIG_MITIGATION_RETPOLINE\n"
"MODULE_INFO(retpoline, \"Y\");\n"
"#endif\n");
if (strstarts(mod->name, "drivers/staging"))
buf_printf(b, "\nMODULE_INFO(staging, \"Y\");\n");
@@ -1947,7 +1931,7 @@ static void write_if_changed(struct buffer *b, const char *fname)
if (st.st_size != b->pos)
goto close_write;
tmp = NOFAIL(malloc(b->pos));
tmp = xmalloc(b->pos);
if (fread(tmp, 1, b->pos, file) != b->pos)
goto free_write;
@@ -2117,6 +2101,25 @@ struct dump_list {
const char *file;
};
static void check_host_endian(void)
{
static const union {
short s;
char c[2];
} endian_test = { .c = {0x01, 0x02} };
switch (endian_test.s) {
case 0x0102:
host_is_big_endian = true;
break;
case 0x0201:
host_is_big_endian = false;
break;
default:
fatal("Unknown host endian\n");
}
}
int main(int argc, char **argv)
{
struct module *mod;
@@ -2133,7 +2136,7 @@ int main(int argc, char **argv)
external_module = true;
break;
case 'i':
dl = NOFAIL(malloc(sizeof(*dl)));
dl = xmalloc(sizeof(*dl));
dl->file = optarg;
list_add_tail(&dl->list, &dump_lists);
break;
@@ -2181,6 +2184,8 @@ int main(int argc, char **argv)
}
}
check_host_endian();
list_for_each_entry_safe(dl, dl2, &dump_lists, list) {
read_dump(dl->file);
list_del(&dl->list);
+7 -21
View File
@@ -62,22 +62,11 @@
x); \
})
#if KERNEL_ELFDATA != HOST_ELFDATA
#define TO_NATIVE(x) (bswap(x))
#else /* endianness matches */
#define TO_NATIVE(x) (x)
#endif
#define NOFAIL(ptr) do_nofail((ptr), #ptr)
#define TO_NATIVE(x) \
(target_is_big_endian == host_is_big_endian ? x : bswap(x))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
void *do_nofail(void *ptr, const char *expr);
struct buffer {
char *p;
int pos;
@@ -187,17 +176,14 @@ void add_moddevtable(struct buffer *buf, struct module *mod);
void get_src_version(const char *modname, char sum[], unsigned sumlen);
/* from modpost.c */
extern bool target_is_big_endian;
extern bool host_is_big_endian;
char *read_text_file(const char *filename);
char *get_line(char **stringp);
void *sym_get_data(const struct elf_info *info, const Elf_Sym *sym);
enum loglevel {
LOG_WARN,
LOG_ERROR,
};
void __attribute__((format(printf, 2, 3)))
modpost_log(enum loglevel loglevel, const char *fmt, ...);
modpost_log(bool is_error, const char *fmt, ...);
/*
* warn - show the given message, then let modpost continue running, still
@@ -212,6 +198,6 @@ modpost_log(enum loglevel loglevel, const char *fmt, ...);
* fatal - show the given message, and bail out immediately. This should be
* used when there is no point to continue running modpost.
*/
#define warn(fmt, args...) modpost_log(LOG_WARN, fmt, ##args)
#define error(fmt, args...) modpost_log(LOG_ERROR, fmt, ##args)
#define warn(fmt, args...) modpost_log(false, fmt, ##args)
#define error(fmt, args...) modpost_log(true, fmt, ##args)
#define fatal(fmt, args...) do { error(fmt, ##args); exit(1); } while (1)
+4 -2
View File
@@ -8,6 +8,8 @@
#include <errno.h>
#include <string.h>
#include <limits.h>
#include <xalloc.h>
#include "modpost.h"
/*
@@ -305,7 +307,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
const char *base;
int dirlen, ret = 0, check_files = 0;
cmd = NOFAIL(malloc(strlen(objfile) + sizeof("..cmd")));
cmd = xmalloc(strlen(objfile) + sizeof("..cmd"));
base = strrchr(objfile, '/');
if (base) {
@@ -316,7 +318,7 @@ static int parse_source_files(const char *objfile, struct md4_ctx *md)
dirlen = 0;
sprintf(cmd, ".%s.cmd", objfile);
}
dir = NOFAIL(malloc(dirlen + 1));
dir = xmalloc(dirlen + 1);
strncpy(dir, objfile, dirlen);
dir[dirlen] = '\0';
+3 -3
View File
@@ -4,7 +4,7 @@
* Helper functions for finding the symbol in an ELF which is "nearest"
* to a given address.
*/
#include <xalloc.h>
#include "modpost.h"
struct syminfo {
@@ -125,8 +125,8 @@ void symsearch_init(struct elf_info *elf)
{
unsigned int table_size = symbol_count(elf);
elf->symsearch = NOFAIL(malloc(sizeof(struct symsearch) +
sizeof(struct syminfo) * table_size));
elf->symsearch = xmalloc(sizeof(struct symsearch) +
sizeof(struct syminfo) * table_size);
elf->symsearch->table_size = table_size;
symsearch_populate(elf, elf->symsearch->table, table_size);
+25
View File
@@ -0,0 +1,25 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/module.h>
/*
* Include build-salt.h after module.h in order to
* inherit the definitions.
*/
#define INCLUDE_VERMAGIC
#include <linux/build-salt.h>
#include <linux/elfnote-lto.h>
#include <linux/vermagic.h>
#ifdef CONFIG_UNWINDER_ORC
#include <asm/orc_header.h>
ORC_HEADER;
#endif
BUILD_SALT;
BUILD_LTO_INFO;
MODULE_INFO(vermagic, VERMAGIC_STRING);
#ifdef CONFIG_MITIGATION_RETPOLINE
MODULE_INFO(retpoline, "Y");
#endif
+38 -14
View File
@@ -3,10 +3,13 @@
# Contributor: Jan Alexander Steffens (heftig) <heftig@archlinux.org>
pkgbase=${PACMAN_PKGBASE:-linux-upstream}
pkgname=("${pkgbase}" "${pkgbase}-api-headers")
if grep -q CONFIG_MODULES=y include/config/auto.conf; then
pkgname+=("${pkgbase}-headers")
fi
pkgname=("${pkgbase}")
_extrapackages=${PACMAN_EXTRAPACKAGES-headers api-headers debug}
for pkg in $_extrapackages; do
pkgname+=("${pkgbase}-${pkg}")
done
pkgver="${KERNELRELEASE//-/_}"
# The PKGBUILD is evaluated multiple times.
# Running scripts/build-version from here would introduce inconsistencies.
@@ -33,11 +36,17 @@ makedepends=(
)
options=(!debug !strip !buildflags !makeflags)
build() {
_prologue() {
# MAKEFLAGS from makepkg.conf override the ones inherited from kbuild.
# Bypass this override with a custom variable.
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
cd "${objtree}"
# Kbuild works in the output directory, where this PKGBUILD is located.
cd "$(dirname "${BASH_SOURCE[0]}")"
}
build() {
_prologue
${MAKE} KERNELRELEASE="${KERNELRELEASE}" KBUILD_BUILD_VERSION="${pkgrel}"
}
@@ -45,10 +54,10 @@ build() {
_package() {
pkgdesc="The ${pkgdesc} kernel and modules"
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
cd "${objtree}"
local modulesdir="${pkgdir}/usr/${MODLIB}"
_prologue
echo "Installing boot image..."
# systemd expects to find the kernel here to allow hibernation
# https://github.com/systemd/systemd/commit/edda44605f06a41fb86b7ab8128dcf99161d2344
@@ -73,14 +82,17 @@ _package() {
_package-headers() {
pkgdesc="Headers and scripts for building modules for the ${pkgdesc} kernel"
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
cd "${objtree}"
local builddir="${pkgdir}/usr/${MODLIB}/build"
echo "Installing build files..."
"${srctree}/scripts/package/install-extmod-build" "${builddir}"
_prologue
if grep -q CONFIG_MODULES=y include/config/auto.conf; then
echo "Installing build files..."
"${srctree}/scripts/package/install-extmod-build" "${builddir}"
fi
echo "Installing System.map and config..."
mkdir -p "${builddir}"
cp System.map "${builddir}/System.map"
cp .config "${builddir}/.config"
@@ -94,12 +106,24 @@ _package-api-headers() {
provides=(linux-api-headers)
conflicts=(linux-api-headers)
export MAKEFLAGS="${KBUILD_MAKEFLAGS}"
cd "${objtree}"
_prologue
${MAKE} headers_install INSTALL_HDR_PATH="${pkgdir}/usr"
}
_package-debug(){
pkgdesc="Non-stripped vmlinux file for the ${pkgdesc} kernel"
local debugdir="${pkgdir}/usr/src/debug/${pkgbase}"
local builddir="${pkgdir}/usr/${MODLIB}/build"
_prologue
install -Dt "${debugdir}" -m644 vmlinux
mkdir -p "${builddir}"
ln -sr "${debugdir}/vmlinux" "${builddir}/vmlinux"
}
for _p in "${pkgname[@]}"; do
eval "package_$_p() {
$(declare -f "_package${_p#$pkgbase}")
+50 -5
View File
@@ -9,15 +9,22 @@ is_enabled() {
grep -q "^$1=y" include/config/auto.conf
}
find_in_scripts() {
find scripts \
\( -name atomic -o -name dtc -o -name kconfig -o -name package \) -prune -o \
! -name unifdef -a ! -name mk_elfconfig -a \( -type f -o -type l \) -print
}
mkdir -p "${destdir}"
(
cd "${srctree}"
echo Makefile
find "arch/${SRCARCH}" -maxdepth 1 -name 'Makefile*'
find include scripts -type f -o -type l
find "arch/${SRCARCH}" -name generated -prune -o -name include -type d -print
find "arch/${SRCARCH}" -name Kbuild.platforms -o -name Platform
find "arch/${SRCARCH}" -name include -type d
find include \( -name config -o -name generated \) -prune -o \( -type f -o -type l \) -print
find_in_scripts
) | tar -c -f - -C "${srctree}" -T - | tar -xf - -C "${destdir}"
{
@@ -25,12 +32,50 @@ mkdir -p "${destdir}"
echo tools/objtool/objtool
fi
find "arch/${SRCARCH}/include" Module.symvers include scripts -type f
echo Module.symvers
echo "arch/${SRCARCH}/include/generated"
echo include/config/auto.conf
echo include/config/kernel.release
echo include/generated
find_in_scripts
if is_enabled CONFIG_GCC_PLUGINS; then
find scripts/gcc-plugins -name '*.so'
fi
} | tar -c -f - -T - | tar -xf - -C "${destdir}"
# copy .config manually to be where it's expected to be
cp "${KCONFIG_CONFIG}" "${destdir}/.config"
# When ${CC} and ${HOSTCC} differ, we are likely cross-compiling. Rebuild host
# programs using ${CC}. This assumes CC=${CROSS_COMPILE}gcc, which is usually
# the case for package building. It does not cross-compile when CC=clang.
#
# This caters to host programs that participate in Kbuild. objtool and
# resolve_btfids are out of scope.
if [ "${CC}" != "${HOSTCC}" ] && is_enabled CONFIG_CC_CAN_LINK; then
echo "Rebuilding host programs with ${CC}..."
cat <<-'EOF' > "${destdir}/Kbuild"
subdir-y := scripts
EOF
# HOSTCXX is not overridden. The C++ compiler is used to build:
# - scripts/kconfig/qconf, which is unneeded for external module builds
# - GCC plugins, which will not work on the installed system even after
# being rebuilt.
#
# Use the single-target build to avoid the modpost invocation, which
# would overwrite Module.symvers.
"${MAKE}" HOSTCC="${CC}" KBUILD_EXTMOD="${destdir}" scripts/
cat <<-'EOF' > "${destdir}/scripts/Kbuild"
subdir-y := basic
hostprogs-always-y := mod/modpost
mod/modpost-objs := $(addprefix mod/, modpost.o file2alias.o sumversion.o symsearch.o)
EOF
# Run once again to rebuild scripts/basic/ and scripts/mod/modpost.
"${MAKE}" HOSTCC="${CC}" KBUILD_EXTMOD="${destdir}" scripts/
rm -f "${destdir}/Kbuild" "${destdir}/scripts/Kbuild"
fi
find "${destdir}" \( -name '.*.cmd' -o -name '*.o' \) -delete
+1 -1
View File
@@ -6,7 +6,7 @@
SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
-e s/sun4u/sparc64/ \
-e s/arm.*/arm/ -e s/sa110/arm/ \
-e /^arm64$$/!s/arm.*/arm/ -e s/sa110/arm/ \
-e s/s390x/s390/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \
+370
View File
@@ -0,0 +1,370 @@
#!/usr/bin/gawk -f
# SPDX-License-Identifier: GPL-2.0
# verify_builtin_ranges.awk: Verify address range data for builtin modules
# Written by Kris Van Hees <kris.van.hees@oracle.com>
#
# Usage: verify_builtin_ranges.awk modules.builtin.ranges System.map \
# modules.builtin vmlinux.map vmlinux.o.map
#
# Return the module name(s) (if any) associated with the given object.
#
# If we have seen this object before, return information from the cache.
# Otherwise, retrieve it from the corresponding .cmd file.
#
function get_module_info(fn, mod, obj, s) {
if (fn in omod)
return omod[fn];
if (match(fn, /\/[^/]+$/) == 0)
return "";
obj = fn;
mod = "";
fn = substr(fn, 1, RSTART) "." substr(fn, RSTART + 1) ".cmd";
if (getline s <fn == 1) {
if (match(s, /DKBUILD_MODFILE=['"]+[^'"]+/) > 0) {
mod = substr(s, RSTART + 16, RLENGTH - 16);
gsub(/['"]/, "", mod);
} else if (match(s, /RUST_MODFILE=[^ ]+/) > 0)
mod = substr(s, RSTART + 13, RLENGTH - 13);
} else {
print "ERROR: Failed to read: " fn "\n\n" \
" For kernels built with O=<objdir>, cd to <objdir>\n" \
" and execute this script as ./source/scripts/..." \
>"/dev/stderr";
close(fn);
total = 0;
exit(1);
}
close(fn);
# A single module (common case) also reflects objects that are not part
# of a module. Some of those objects have names that are also a module
# name (e.g. core). We check the associated module file name, and if
# they do not match, the object is not part of a module.
if (mod !~ / /) {
if (!(mod in mods))
mod = "";
}
gsub(/([^/ ]*\/)+/, "", mod);
gsub(/-/, "_", mod);
# At this point, mod is a single (valid) module name, or a list of
# module names (that do not need validation).
omod[obj] = mod;
return mod;
}
# Return a representative integer value for a given hexadecimal address.
#
# Since all kernel addresses fall within the same memory region, we can safely
# strip off the first 6 hex digits before performing the hex-to-dec conversion,
# thereby avoiding integer overflows.
#
function addr2val(val) {
sub(/^0x/, "", val);
if (length(val) == 16)
val = substr(val, 5);
return strtonum("0x" val);
}
# Determine the kernel build directory to use (default is .).
#
BEGIN {
if (ARGC < 6) {
print "Syntax: verify_builtin_ranges.awk <ranges-file> <system-map>\n" \
" <builtin-file> <vmlinux-map> <vmlinux-o-map>\n" \
>"/dev/stderr";
total = 0;
exit(1);
}
}
# (1) Load the built-in module address range data.
#
ARGIND == 1 {
ranges[FNR] = $0;
rcnt++;
next;
}
# (2) Annotate System.map symbols with module names.
#
ARGIND == 2 {
addr = addr2val($1);
name = $3;
while (addr >= mod_eaddr) {
if (sect_symb) {
if (sect_symb != name)
next;
sect_base = addr - sect_off;
if (dbg)
printf "[%s] BASE (%s) %016x - %016x = %016x\n", sect_name, sect_symb, addr, sect_off, sect_base >"/dev/stderr";
sect_symb = 0;
}
if (++ridx > rcnt)
break;
$0 = ranges[ridx];
sub(/-/, " ");
if ($4 != "=") {
sub(/-/, " ");
mod_saddr = strtonum("0x" $2) + sect_base;
mod_eaddr = strtonum("0x" $3) + sect_base;
$1 = $2 = $3 = "";
sub(/^ +/, "");
mod_name = $0;
if (dbg)
printf "[%s] %s from %016x to %016x\n", sect_name, mod_name, mod_saddr, mod_eaddr >"/dev/stderr";
} else {
sect_name = $1;
sect_off = strtonum("0x" $2);
sect_symb = $5;
}
}
idx = addr"-"name;
if (addr >= mod_saddr && addr < mod_eaddr)
sym2mod[idx] = mod_name;
next;
}
# Once we are done annotating the System.map, we no longer need the ranges data.
#
FNR == 1 && ARGIND == 3 {
delete ranges;
}
# (3) Build a lookup map of built-in module names.
#
# Lines from modules.builtin will be like:
# kernel/crypto/lzo-rle.ko
# and we record the object name "crypto/lzo-rle".
#
ARGIND == 3 {
sub(/kernel\//, ""); # strip off "kernel/" prefix
sub(/\.ko$/, ""); # strip off .ko suffix
mods[$1] = 1;
next;
}
# (4) Get a list of symbols (per object).
#
# Symbols by object are read from vmlinux.map, with fallback to vmlinux.o.map
# if vmlinux is found to have inked in vmlinux.o.
#
# If we were able to get the data we need from vmlinux.map, there is no need to
# process vmlinux.o.map.
#
FNR == 1 && ARGIND == 5 && total > 0 {
if (dbg)
printf "Note: %s is not needed.\n", FILENAME >"/dev/stderr";
exit;
}
# First determine whether we are dealing with a GNU ld or LLVM lld linker map.
#
ARGIND >= 4 && FNR == 1 && NF == 7 && $1 == "VMA" && $7 == "Symbol" {
map_is_lld = 1;
next;
}
# (LLD) Convert a section record fronm lld format to ld format.
#
ARGIND >= 4 && map_is_lld && NF == 5 && /[0-9] [^ ]+$/ {
$0 = $5 " 0x"$1 " 0x"$3 " load address 0x"$2;
}
# (LLD) Convert an object record from lld format to ld format.
#
ARGIND >= 4 && map_is_lld && NF == 5 && $5 ~ /:\(/ {
if (/\.a\(/ && !/ vmlinux\.a\(/)
next;
gsub(/\)/, "");
sub(/:\(/, " ");
sub(/ vmlinux\.a\(/, " ");
$0 = " "$6 " 0x"$1 " 0x"$3 " " $5;
}
# (LLD) Convert a symbol record from lld format to ld format.
#
ARGIND >= 4 && map_is_lld && NF == 5 && $5 ~ /^[A-Za-z_][A-Za-z0-9_]*$/ {
$0 = " 0x" $1 " " $5;
}
# (LLD) We do not need any other ldd linker map records.
#
ARGIND >= 4 && map_is_lld && /^[0-9a-f]{16} / {
next;
}
# Handle section records with long section names (spilling onto a 2nd line).
#
ARGIND >= 4 && !map_is_lld && NF == 1 && /^[^ ]/ {
s = $0;
getline;
$0 = s " " $0;
}
# Next section - previous one is done.
#
ARGIND >= 4 && /^[^ ]/ {
sect = 0;
}
# Get the (top level) section name.
#
ARGIND >= 4 && /^\./ {
# Explicitly ignore a few sections that are not relevant here.
if ($1 ~ /^\.orc_/ || $1 ~ /_sites$/ || $1 ~ /\.percpu/)
next;
# Sections with a 0-address can be ignored as well (in vmlinux.map).
if (ARGIND == 4 && $2 ~ /^0x0+$/)
next;
sect = $1;
next;
}
# If we are not currently in a section we care about, ignore records.
#
!sect {
next;
}
# Handle object records with long section names (spilling onto a 2nd line).
#
ARGIND >= 4 && /^ [^ \*]/ && NF == 1 {
# If the section name is long, the remainder of the entry is found on
# the next line.
s = $0;
getline;
$0 = s " " $0;
}
# Objects linked in from static libraries are ignored.
# If the object is vmlinux.o, we need to consult vmlinux.o.map for per-object
# symbol information
#
ARGIND == 4 && /^ [^ ]/ && NF == 4 {
if ($4 ~ /\.a\(/)
next;
idx = sect":"$1;
if (!(idx in sect_addend)) {
sect_addend[idx] = addr2val($2);
if (dbg)
printf "ADDEND %s = %016x\n", idx, sect_addend[idx] >"/dev/stderr";
}
if ($4 == "vmlinux.o") {
need_o_map = 1;
next;
}
}
# If data from vmlinux.o.map is needed, we only process section and object
# records from vmlinux.map to determine which section we need to pay attention
# to in vmlinux.o.map. So skip everything else from vmlinux.map.
#
ARGIND == 4 && need_o_map {
next;
}
# Get module information for the current object.
#
ARGIND >= 4 && /^ [^ ]/ && NF == 4 {
msect = $1;
mod_name = get_module_info($4);
mod_eaddr = addr2val($2) + addr2val($3);
next;
}
# Process a symbol record.
#
# Evaluate the module information obtained from vmlinux.map (or vmlinux.o.map)
# as follows:
# - For all symbols in a given object:
# - If the symbol is annotated with the same module name(s) that the object
# belongs to, count it as a match.
# - Otherwise:
# - If the symbol is known to have duplicates of which at least one is
# in a built-in module, disregard it.
# - If the symbol us not annotated with any module name(s) AND the
# object belongs to built-in modules, count it as missing.
# - Otherwise, count it as a mismatch.
#
ARGIND >= 4 && /^ / && NF == 2 && $1 ~ /^0x/ {
idx = sect":"msect;
if (!(idx in sect_addend))
next;
addr = addr2val($1);
# Handle the rare but annoying case where a 0-size symbol is placed at
# the byte *after* the module range. Based on vmlinux.map it will be
# considered part of the current object, but it falls just beyond the
# module address range. Unfortunately, its address could be at the
# start of another built-in module, so the only safe thing to do is to
# ignore it.
if (mod_name && addr == mod_eaddr)
next;
# If we are processing vmlinux.o.map, we need to apply the base address
# of the section to the relative address on the record.
#
if (ARGIND == 5)
addr += sect_addend[idx];
idx = addr"-"$2;
mod = "";
if (idx in sym2mod) {
mod = sym2mod[idx];
if (sym2mod[idx] == mod_name) {
mod_matches++;
matches++;
} else if (mod_name == "") {
print $2 " in " mod " (should NOT be)";
mismatches++;
} else {
print $2 " in " mod " (should be " mod_name ")";
mismatches++;
}
} else if (mod_name != "") {
print $2 " should be in " mod_name;
missing++;
} else
matches++;
total++;
next;
}
# Issue the comparison report.
#
END {
if (total) {
printf "Verification of %s:\n", ARGV[1];
printf " Correct matches: %6d (%d%% of total)\n", matches, 100 * matches / total;
printf " Module matches: %6d (%d%% of matches)\n", mod_matches, 100 * mod_matches / matches;
printf " Mismatches: %6d (%d%% of total)\n", mismatches, 100 * mismatches / total;
printf " Missing: %6d (%d%% of total)\n", missing, 100 * missing / total;
if (mismatches || missing)
exit(1);
}
}