mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
Objtool updates for v7.2:
- A large series of KLP fixes and improvements, in preparation of
the arm64 port (Josh Poimboeuf)
- Fix a number of bugs and issues on specific distro, LTO,
FineIBT and kCFI configs (Josh Poimboeuf)
- Misc other fixes by Josh Poimboeuf and Joe Lawrence.
Signed-off-by: Ingo Molnar <mingo@kernel.org>
-----BEGIN PGP SIGNATURE-----
iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmouvAgRHG1pbmdvQGtl
cm5lbC5vcmcACgkQEnMQ0APhK1g1rBAAuJwhyKKa5KxdgoGC3uSCyILZ77n5en+I
e5kCpeON2zbiGzg5N1D6Rgr1D1m1Fj1puiPy6FtqDkIO4a5b86Pt/Nv91CZG3A7n
lYNRJtqVyMNpBr7O9aaxalEnhrEY1kDK5DRebSAidy97hL6HelMFBy3EkV2nzsUI
eekUg3frCNq3k2oYWdtC5DZ6S7oK8rdAi/Ay3A3XTL6CLjYDPpMHd+pjDdtQsBYg
ybMoVQthfKb1SDazy2od9bbTJDngJkMVgIqHPrQcnvqMqp1knI46YvEwMJzQ9DWB
AaS8Xnm1jyk0/cFjIp5xNWo+VVuKiY0upewtFAodH35tvIuqptbt7btIkV5ZX7nu
IaMfqwh88yHvf3PJMrbAy+5lIE9h6dKEDLTJVBT8Swb7Y6rUG0qMyJ3Ah/RrlAgu
jcLUN89u8HXVaffWYAfpEj2ehSRsuy9BX2zCUeQoMH/5MCbDY2q4j6i07ylTJCkr
flYjTosIXma/NwimXIyPzMl+vUtrznMwoubH6Ns2yu7DKAK1Ntb0W9H7oy/vZIPi
ZAiV2OdmRet6iXdcIwBlS+7Du/eUMIkKlJsf4twWMgU7R4ApxTWoylr1z8yKqQam
109MNt7d98wFu+iYEWB0S+Uz7I/ZAGwY1Rj7tZ7mHUlSBfcVsYp3b3NEOG4J9YLy
K6L9ZECJ/Ow=
=D5SB
-----END PGP SIGNATURE-----
Merge tag 'objtool-core-2026-06-14' of gitolite.kernel.org:pub/scm/linux/kernel/git/tip/tip
Pull objtool updates from Ingo Molnar:
- A large series of KLP fixes and improvements, in preparation of the
arm64 port (Josh Poimboeuf)
- Fix a number of bugs and issues on specific distro, LTO, FineIBT and
kCFI configs (Josh Poimboeuf)
- Misc other fixes by Josh Poimboeuf and Joe Lawrence
* tag 'objtool-core-2026-06-14' of gitolite.kernel.org:pub/scm/linux/kernel/git/tip/tip: (53 commits)
objtool/klp: Cache dont_correlate() result
objtool: Improve and simplify prefix symbol detection
objtool/klp: Fix kCFI prefix finding/cloning
objtool: Grow __cfi_* prefix symbols for all CFI+CALL_PADDING
objtool/klp: Fix position-dependent checksums for non-relocated jumps/calls
objtool: Add insn_sym() helper
objtool/klp: Add correlation debugging output
objtool/klp: Rewrite symbol correlation algorithm
objtool/klp: Calculate object checksums
klp-build: Validate short-circuit prerequisites
objtool/klp: Remove "objtool --checksum"
klp-build: Use "objtool klp checksum" subcommand
objtool/klp: Add "objtool klp checksum" subcommand
objtool: Consolidate file decoding into decode_file()
objtool/klp: Extricate checksum calculation from validate_branch()
objtool: Add is_cold_func() helper
objtool: Add is_alias_sym() helper
objtool/klp: Handle Clang .data..Lanon anonymous data sections
objtool/klp: Create empty checksum sections for function-less object files
objtool: Include libsubcmd headers directly from source tree
...
This commit is contained in:
commit
d8d706a274
|
|
@ -2436,10 +2436,6 @@ config CALL_THUNKS
|
|||
def_bool n
|
||||
select CALL_PADDING
|
||||
|
||||
config PREFIX_SYMBOLS
|
||||
def_bool y
|
||||
depends on CALL_PADDING && !CFI
|
||||
|
||||
menuconfig CPU_MITIGATIONS
|
||||
bool "Mitigations for CPU vulnerabilities"
|
||||
default y
|
||||
|
|
|
|||
|
|
@ -3070,7 +3070,7 @@ config FORTIFY_KUNIT_TEST
|
|||
config LONGEST_SYM_KUNIT_TEST
|
||||
tristate "Test the longest symbol possible" if !KUNIT_ALL_TESTS
|
||||
depends on KUNIT && KPROBES
|
||||
depends on !PREFIX_SYMBOLS && !CFI && !GCOV_KERNEL
|
||||
depends on !CALL_PADDING && !CFI && !GCOV_KERNEL
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
Tests the longest symbol possible
|
||||
|
|
|
|||
|
|
@ -190,7 +190,11 @@ objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK) += --hacks=jump_label
|
|||
objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr
|
||||
objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake
|
||||
objtool-args-$(CONFIG_X86_KERNEL_IBT) += --ibt
|
||||
objtool-args-$(CONFIG_FINEIBT) += --cfi
|
||||
objtool-args-$(CONFIG_CALL_PADDING) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
|
||||
ifdef CONFIG_CALL_PADDING
|
||||
objtool-args-$(CONFIG_CFI) += --cfi
|
||||
objtool-args-$(CONFIG_FINEIBT) += --fineibt
|
||||
endif
|
||||
objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL) += --mcount
|
||||
ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL
|
||||
objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT) += --mnop
|
||||
|
|
@ -203,7 +207,6 @@ objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval
|
|||
objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
|
||||
objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
|
||||
objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
|
||||
objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
|
||||
objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror
|
||||
|
||||
objtool-args = $(objtool-args-y) \
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
#
|
||||
# Build a livepatch module
|
||||
|
||||
# shellcheck disable=SC1090,SC2155
|
||||
# shellcheck disable=SC1090,SC2155,SC2164
|
||||
|
||||
if (( BASH_VERSINFO[0] < 4 || \
|
||||
(BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then
|
||||
|
|
@ -11,21 +11,19 @@ if (( BASH_VERSINFO[0] < 4 || \
|
|||
exit 1
|
||||
fi
|
||||
|
||||
set -o errexit
|
||||
set -o errtrace
|
||||
set -o pipefail
|
||||
set -o nounset
|
||||
|
||||
# Allow doing 'cmd | mapfile -t array' instead of 'mapfile -t array < <(cmd)'.
|
||||
# This helps keep execution in pipes so pipefail+errexit can catch errors.
|
||||
# This helps keep execution in pipes so pipefail+ERR trap can catch errors.
|
||||
shopt -s lastpipe
|
||||
|
||||
unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP XTRACE
|
||||
unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP VERBOSE XTRACE
|
||||
|
||||
REPLACE=1
|
||||
SHORT_CIRCUIT=0
|
||||
JOBS="$(getconf _NPROCESSORS_ONLN)"
|
||||
VERBOSE="-s"
|
||||
shopt -o xtrace | grep -q 'on' && XTRACE=1
|
||||
|
||||
# Avoid removing the previous $TMP_DIR until args have been fully processed.
|
||||
|
|
@ -35,16 +33,16 @@ SCRIPT="$(basename "$0")"
|
|||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines"
|
||||
|
||||
SRC="$(pwd)"
|
||||
OBJ="$(pwd)"
|
||||
OBJTOOL="$PWD/tools/objtool/objtool"
|
||||
CONFIG="$PWD/.config"
|
||||
TMP_DIR="$PWD/klp-tmp"
|
||||
|
||||
CONFIG="$OBJ/.config"
|
||||
TMP_DIR="$OBJ/klp-tmp"
|
||||
|
||||
ORIG_DIR="$TMP_DIR/orig"
|
||||
PATCHED_DIR="$TMP_DIR/patched"
|
||||
DIFF_DIR="$TMP_DIR/diff"
|
||||
KMOD_DIR="$TMP_DIR/kmod"
|
||||
ORIG_DIR="$TMP_DIR/1-orig"
|
||||
PATCHED_DIR="$TMP_DIR/2-patched"
|
||||
ORIG_CSUM_DIR="$TMP_DIR/3-checksum-orig"
|
||||
PATCHED_CSUM_DIR="$TMP_DIR/3-checksum-patched"
|
||||
DIFF_DIR="$TMP_DIR/4-diff"
|
||||
KMOD_DIR="$TMP_DIR/5-kmod"
|
||||
|
||||
STASH_DIR="$TMP_DIR/stash"
|
||||
TIMESTAMP="$TMP_DIR/timestamp"
|
||||
|
|
@ -90,7 +88,7 @@ declare -a STASHED_FILES
|
|||
|
||||
stash_file() {
|
||||
local file="$1"
|
||||
local rel_file="${file#"$SRC"/}"
|
||||
local rel_file="${file#"$PWD"/}"
|
||||
|
||||
[[ ! -e "$file" ]] && die "no file to stash: $file"
|
||||
|
||||
|
|
@ -104,7 +102,7 @@ restore_files() {
|
|||
local file
|
||||
|
||||
for file in "${STASHED_FILES[@]}"; do
|
||||
mv -f "$STASH_DIR/$file" "$SRC/$file" || warn "can't restore file: $file"
|
||||
mv -f "$STASH_DIR/$file" "$PWD/$file" || warn "can't restore file: $file"
|
||||
done
|
||||
|
||||
STASHED_FILES=()
|
||||
|
|
@ -140,10 +138,11 @@ Options:
|
|||
Advanced Options:
|
||||
-d, --debug Show symbol/reloc cloning decisions
|
||||
-S, --short-circuit=STEP Start at build step (requires prior --keep-tmp)
|
||||
1|orig Build original kernel (default)
|
||||
2|patched Build patched kernel
|
||||
3|diff Diff objects
|
||||
4|kmod Build patch module
|
||||
1|orig Build original kernel (default)
|
||||
2|patched Build patched kernel
|
||||
3|checksum Generate checksums
|
||||
4|diff Diff objects
|
||||
5|kmod Build patch module
|
||||
-T, --keep-tmp Preserve tmp dir on exit
|
||||
|
||||
EOF
|
||||
|
|
@ -158,6 +157,7 @@ process_args() {
|
|||
local short
|
||||
local long
|
||||
local args
|
||||
local patch
|
||||
|
||||
short="hfj:o:vdS:T"
|
||||
long="help,show-first-changed,jobs:,output:,no-replace,verbose,debug,short-circuit:,keep-tmp"
|
||||
|
|
@ -194,7 +194,7 @@ process_args() {
|
|||
shift
|
||||
;;
|
||||
-v | --verbose)
|
||||
VERBOSE="V=1"
|
||||
VERBOSE=1
|
||||
shift
|
||||
;;
|
||||
-d | --debug)
|
||||
|
|
@ -206,10 +206,11 @@ process_args() {
|
|||
[[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir"
|
||||
keep_tmp=1
|
||||
case "$2" in
|
||||
1 | orig) SHORT_CIRCUIT=1; ;;
|
||||
2 | patched) SHORT_CIRCUIT=2; ;;
|
||||
3 | diff) SHORT_CIRCUIT=3; ;;
|
||||
4 | mod) SHORT_CIRCUIT=4; ;;
|
||||
1 | orig) SHORT_CIRCUIT=1; ;;
|
||||
2 | patched) SHORT_CIRCUIT=2; ;;
|
||||
3 | checksum) SHORT_CIRCUIT=3; ;;
|
||||
4 | diff) SHORT_CIRCUIT=4; ;;
|
||||
5 | kmod) SHORT_CIRCUIT=5; ;;
|
||||
*) die "invalid short-circuit step '$2'" ;;
|
||||
esac
|
||||
shift 2
|
||||
|
|
@ -236,6 +237,10 @@ process_args() {
|
|||
|
||||
KEEP_TMP="$keep_tmp"
|
||||
PATCHES=("$@")
|
||||
|
||||
for patch in "${PATCHES[@]}"; do
|
||||
[[ -f "$patch" ]] || die "$patch doesn't exist"
|
||||
done
|
||||
}
|
||||
|
||||
# temporarily disable xtrace for especially verbose code
|
||||
|
|
@ -270,6 +275,9 @@ validate_config() {
|
|||
[[ "$CONFIG_AS_VERSION" -lt 200000 ]] && \
|
||||
die "Clang assembler version < 20 not supported"
|
||||
|
||||
[[ -x "$OBJTOOL" ]] && "$OBJTOOL" klp 2>&1 | command grep -q "not implemented" && \
|
||||
die "objtool not built with KLP support; install xxhash-devel/libxxhash-dev (version >= 0.8) and recompile"
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
|
|
@ -301,12 +309,17 @@ set_module_name() {
|
|||
# Hardcode the value printed by the localversion script to prevent patch
|
||||
# application from appending it with '+' due to a dirty working tree.
|
||||
set_kernelversion() {
|
||||
local file="$SRC/scripts/setlocalversion"
|
||||
local file="$PWD/scripts/setlocalversion"
|
||||
local kernelrelease
|
||||
|
||||
stash_file "$file"
|
||||
|
||||
kernelrelease="$(cd "$SRC" && make syncconfig &>/dev/null && make -s kernelrelease)"
|
||||
if [[ -n "$(make -s listnewconfig 2>/dev/null)" ]]; then
|
||||
die ".config mismatch, check your .config or run 'make olddefconfig'"
|
||||
fi
|
||||
make syncconfig &>/dev/null || die "make syncconfig failed"
|
||||
|
||||
kernelrelease="$(make -s kernelrelease)"
|
||||
[[ -z "$kernelrelease" ]] && die "failed to get kernel version"
|
||||
|
||||
sed -i "2i echo $kernelrelease; exit 0" scripts/setlocalversion
|
||||
|
|
@ -349,7 +362,7 @@ check_unsupported_patches() {
|
|||
|
||||
for file in "${files[@]}"; do
|
||||
case "$file" in
|
||||
lib/*|*.S)
|
||||
lib/*|*/vdso/*|*/realmode/rm/*|*.S)
|
||||
die "${patch}: unsupported patch to $file"
|
||||
;;
|
||||
esac
|
||||
|
|
@ -367,24 +380,24 @@ apply_patch() {
|
|||
|
||||
[[ ! -f "$patch" ]] && die "$patch doesn't exist"
|
||||
status=0
|
||||
output=$(patch -d "$SRC" -p1 --dry-run --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" < "$patch" 2>&1) || status=$?
|
||||
output=$(patch -p1 --dry-run --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" < "$patch" 2>&1) || status=$?
|
||||
if [[ "$status" -ne 0 ]]; then
|
||||
echo "$output" >&2
|
||||
die "$patch did not apply"
|
||||
elif [[ "$output" =~ $drift_regex ]]; then
|
||||
echo "$output" >&2
|
||||
[[ -v VERBOSE ]] && echo "$output" >&2
|
||||
warn "${patch} applied with fuzz"
|
||||
fi
|
||||
|
||||
patch -d "$SRC" -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch"
|
||||
APPLIED_PATCHES+=("$patch")
|
||||
patch -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch"
|
||||
}
|
||||
|
||||
revert_patch() {
|
||||
local patch="$1"
|
||||
local tmp=()
|
||||
|
||||
patch -d "$SRC" -p1 -R --silent --no-backup-if-mismatch -r /dev/null < "$patch"
|
||||
patch -p1 -R --force --no-backup-if-mismatch -r /dev/null &> /dev/null < "$patch" || true
|
||||
|
||||
for p in "${APPLIED_PATCHES[@]}"; do
|
||||
[[ "$p" == "$patch" ]] && continue
|
||||
|
|
@ -422,8 +435,21 @@ validate_patches() {
|
|||
do_init() {
|
||||
# We're not yet smart enough to handle anything other than in-tree
|
||||
# builds in pwd.
|
||||
[[ ! "$SRC" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
|
||||
[[ ! "$OBJ" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
|
||||
[[ ! "$PWD" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory"
|
||||
|
||||
if (( SHORT_CIRCUIT >= 2 )); then
|
||||
[[ -f "$ORIG_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $ORIG_DIR"
|
||||
fi
|
||||
if (( SHORT_CIRCUIT >= 3 )); then
|
||||
[[ -f "$PATCHED_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $PATCHED_DIR"
|
||||
fi
|
||||
if (( SHORT_CIRCUIT >= 4 )); then
|
||||
[[ -f "$ORIG_CSUM_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $ORIG_CSUM_DIR"
|
||||
[[ -f "$PATCHED_CSUM_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $PATCHED_CSUM_DIR"
|
||||
fi
|
||||
if (( SHORT_CIRCUIT >= 5 )); then
|
||||
[[ -f "$DIFF_DIR/.complete" ]] || die "-S $SHORT_CIRCUIT requires completed $DIFF_DIR"
|
||||
fi
|
||||
|
||||
(( SHORT_CIRCUIT <= 1 )) && rm -rf "$TMP_DIR"
|
||||
mkdir -p "$TMP_DIR"
|
||||
|
|
@ -454,11 +480,11 @@ refresh_patch() {
|
|||
get_patch_output_files "$patch" | mapfile -t output_files
|
||||
|
||||
# Copy orig source files to 'a'
|
||||
( cd "$SRC" && echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" )
|
||||
echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a"
|
||||
|
||||
# Copy patched source files to 'b'
|
||||
apply_patch "$patch" "--silent"
|
||||
( cd "$SRC" && echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" )
|
||||
echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b"
|
||||
revert_patch "$patch"
|
||||
|
||||
# Diff 'a' and 'b' to make a clean patch
|
||||
|
|
@ -502,20 +528,14 @@ clean_kernel() {
|
|||
cmd+=("-j$JOBS")
|
||||
cmd+=("clean")
|
||||
|
||||
(
|
||||
cd "$SRC"
|
||||
"${cmd[@]}"
|
||||
)
|
||||
"${cmd[@]}"
|
||||
}
|
||||
|
||||
build_kernel() {
|
||||
local build="$1"
|
||||
local log="$TMP_DIR/build.log"
|
||||
local objtool_args=()
|
||||
local cmd=()
|
||||
|
||||
objtool_args=("--checksum")
|
||||
|
||||
cmd=("make")
|
||||
|
||||
# When a patch to a kernel module references a newly created unexported
|
||||
|
|
@ -535,19 +555,20 @@ build_kernel() {
|
|||
#
|
||||
cmd+=("KBUILD_MODPOST_WARN=1")
|
||||
|
||||
cmd+=("$VERBOSE")
|
||||
if [[ -v VERBOSE ]]; then
|
||||
cmd+=("V=1")
|
||||
else
|
||||
cmd+=("-s")
|
||||
fi
|
||||
cmd+=("-j$JOBS")
|
||||
cmd+=("KCFLAGS=-ffunction-sections -fdata-sections")
|
||||
cmd+=("OBJTOOL_ARGS=${objtool_args[*]}")
|
||||
cmd+=("vmlinux")
|
||||
cmd+=("modules")
|
||||
|
||||
(
|
||||
cd "$SRC"
|
||||
"${cmd[@]}" \
|
||||
1> >(tee -a "$log") \
|
||||
2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2)
|
||||
) || die "$build kernel build failed"
|
||||
"${cmd[@]}" \
|
||||
1> >(tee -a "$log") \
|
||||
2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2) \
|
||||
|| die "$build kernel build failed"
|
||||
}
|
||||
|
||||
find_objects() {
|
||||
|
|
@ -555,9 +576,9 @@ find_objects() {
|
|||
|
||||
# Find root-level vmlinux.o and non-root-level .ko files,
|
||||
# excluding klp-tmp/ and .git/
|
||||
find "$OBJ" \( -path "$TMP_DIR" -o -path "$OBJ/.git" -o -regex "$OBJ/[^/][^/]*\.ko" \) -prune -o \
|
||||
find "$PWD" \( -path "$TMP_DIR" -o -path "$PWD/.git" -o -regex "$PWD/[^/][^/]*\.ko" \) -prune -o \
|
||||
-type f "${opts[@]}" \
|
||||
\( -name "*.ko" -o -path "$OBJ/vmlinux.o" \) \
|
||||
\( -name "*.ko" -o -path "$PWD/vmlinux.o" \) \
|
||||
-printf '%P\n'
|
||||
}
|
||||
|
||||
|
|
@ -570,10 +591,10 @@ copy_orig_objects() {
|
|||
|
||||
find_objects | mapfile -t files
|
||||
|
||||
xtrace_save "copying orig objects"
|
||||
xtrace_save "copying original objects"
|
||||
for _file in "${files[@]}"; do
|
||||
local rel_file="${_file/.ko/.o}"
|
||||
local file="$OBJ/$rel_file"
|
||||
local file="$PWD/$rel_file"
|
||||
local orig_file="$ORIG_DIR/$rel_file"
|
||||
local orig_dir="$(dirname "$orig_file")"
|
||||
|
||||
|
|
@ -586,6 +607,7 @@ copy_orig_objects() {
|
|||
|
||||
mv -f "$TMP_DIR/build.log" "$ORIG_DIR"
|
||||
touch "$TIMESTAMP"
|
||||
touch "$ORIG_DIR/.complete"
|
||||
}
|
||||
|
||||
# Copy all changed objects to $PATCHED_DIR
|
||||
|
|
@ -606,7 +628,7 @@ copy_patched_objects() {
|
|||
xtrace_save "copying changed objects"
|
||||
for _file in "${files[@]}"; do
|
||||
local rel_file="${_file/.ko/.o}"
|
||||
local file="$OBJ/$rel_file"
|
||||
local file="$PWD/$rel_file"
|
||||
local orig_file="$ORIG_DIR/$rel_file"
|
||||
local patched_file="$PATCHED_DIR/$rel_file"
|
||||
local patched_dir="$(dirname "$patched_file")"
|
||||
|
|
@ -624,6 +646,36 @@ copy_patched_objects() {
|
|||
(( found == 0 )) && die "no changes detected"
|
||||
|
||||
mv -f "$TMP_DIR/build.log" "$PATCHED_DIR"
|
||||
touch "$PATCHED_DIR/.complete"
|
||||
}
|
||||
|
||||
# Copy .o files to a separate directory and run "objtool klp checksum" on each
|
||||
# copy. The checksums are written to a .discard.sym_checksum section.
|
||||
#
|
||||
# If match_dir is given, only process files which also exist there.
|
||||
generate_checksums() {
|
||||
local src_dir="$1"
|
||||
local dest_dir="$2"
|
||||
local match_dir="${3:-}"
|
||||
local files=()
|
||||
local file
|
||||
|
||||
rm -rf "$dest_dir"
|
||||
mkdir -p "$dest_dir"
|
||||
|
||||
find "$src_dir" -type f -name "*.o" | mapfile -t files
|
||||
for file in "${files[@]}"; do
|
||||
local rel="${file#"$src_dir"/}"
|
||||
local dest="$dest_dir/$rel"
|
||||
|
||||
[[ -n "$match_dir" && ! -f "$match_dir/$rel" ]] && continue
|
||||
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
cp -f "$file" "$dest"
|
||||
"$OBJTOOL" klp checksum "$dest"
|
||||
done
|
||||
|
||||
touch "$dest_dir/.complete"
|
||||
}
|
||||
|
||||
# Diff changed objects, writing output object to $DIFF_DIR
|
||||
|
|
@ -635,23 +687,23 @@ diff_objects() {
|
|||
rm -rf "$DIFF_DIR"
|
||||
mkdir -p "$DIFF_DIR"
|
||||
|
||||
find "$PATCHED_DIR" -type f -name "*.o" | mapfile -t files
|
||||
find "$PATCHED_CSUM_DIR" -type f -name "*.o" | mapfile -t files
|
||||
[[ ${#files[@]} -eq 0 ]] && die "no changes detected"
|
||||
|
||||
[[ -v DEBUG_CLONE ]] && opts=("--debug")
|
||||
|
||||
# Diff all changed objects
|
||||
for file in "${files[@]}"; do
|
||||
local rel_file="${file#"$PATCHED_DIR"/}"
|
||||
local rel_file="${file#"$PATCHED_CSUM_DIR"/}"
|
||||
local orig_file="$rel_file"
|
||||
local patched_file="$PATCHED_DIR/$rel_file"
|
||||
local patched_file="$PATCHED_CSUM_DIR/$rel_file"
|
||||
local out_file="$DIFF_DIR/$rel_file"
|
||||
local filter=()
|
||||
local cmd=()
|
||||
|
||||
mkdir -p "$(dirname "$out_file")"
|
||||
|
||||
cmd=("$SRC/tools/objtool/objtool")
|
||||
cmd=("$OBJTOOL")
|
||||
cmd+=("klp")
|
||||
cmd+=("diff")
|
||||
(( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}")
|
||||
|
|
@ -668,18 +720,21 @@ diff_objects() {
|
|||
fi
|
||||
|
||||
(
|
||||
cd "$ORIG_DIR"
|
||||
cd "$ORIG_CSUM_DIR"
|
||||
[[ -v VERBOSE ]] && echo "cd $ORIG_CSUM_DIR && ${cmd[*]}"
|
||||
"${cmd[@]}" \
|
||||
1> >(tee -a "$log") \
|
||||
2> >(tee -a "$log" | "${filter[@]}" >&2) || \
|
||||
die "objtool klp diff failed"
|
||||
)
|
||||
done
|
||||
|
||||
touch "$DIFF_DIR/.complete"
|
||||
}
|
||||
|
||||
# For each changed object, run objtool with --debug-checksum to get the
|
||||
# per-instruction checksums, and then diff those to find the first changed
|
||||
# instruction for each function.
|
||||
# For each changed object, run "objtool klp checksum" with --debug-checksum to
|
||||
# get the per-instruction checksums, and then diff those to find the first
|
||||
# changed instruction for each function.
|
||||
diff_checksums() {
|
||||
local orig_log="$ORIG_DIR/checksum.log"
|
||||
local patched_log="$PATCHED_DIR/checksum.log"
|
||||
|
|
@ -703,9 +758,8 @@ diff_checksums() {
|
|||
fi
|
||||
done
|
||||
|
||||
cmd=("$SRC/tools/objtool/objtool")
|
||||
cmd+=("--checksum")
|
||||
cmd+=("--link")
|
||||
cmd=("$OBJTOOL")
|
||||
cmd+=("klp" "checksum")
|
||||
cmd+=("--dry-run")
|
||||
|
||||
for file in "${!funcs[@]}"; do
|
||||
|
|
@ -714,21 +768,37 @@ diff_checksums() {
|
|||
(
|
||||
cd "$ORIG_DIR"
|
||||
"${cmd[@]}" "$opt" "$file" &> "$orig_log" || \
|
||||
( cat "$orig_log" >&2; die "objtool --debug-checksum failed" )
|
||||
( cat "$orig_log" >&2; die "objtool klp checksum failed" )
|
||||
|
||||
cd "$PATCHED_DIR"
|
||||
"${cmd[@]}" "$opt" "$file" &> "$patched_log" || \
|
||||
( cat "$patched_log" >&2; die "objtool --debug-checksum failed" )
|
||||
( cat "$patched_log" >&2; die "objtool klp checksum failed" )
|
||||
)
|
||||
|
||||
for func in ${funcs[$file]}; do
|
||||
diff <( grep0 -E "^DEBUG: .*checksum: $func " "$orig_log" | sed "s|$ORIG_DIR/||") \
|
||||
<( grep0 -E "^DEBUG: .*checksum: $func " "$patched_log" | sed "s|$PATCHED_DIR/||") \
|
||||
| gawk '/^< DEBUG: / {
|
||||
gsub(/:/, "")
|
||||
printf "%s: %s: %s\n", $3, $5, $6
|
||||
exit
|
||||
}' || true
|
||||
local -a orig patched
|
||||
paste <(grep0 -E "^DEBUG: .*checksum: $func " "$orig_log") \
|
||||
<(grep0 -E "^DEBUG: .*checksum: $func " "$patched_log") |
|
||||
while IFS= read -r line; do
|
||||
read -ra orig <<< "${line%%$'\t'*}"
|
||||
read -ra patched <<< "${line#*$'\t'}"
|
||||
|
||||
if [[ ${#patched[@]} -eq 0 ]]; then
|
||||
printf "%s: %s: %s (removed)\n" "${orig[1]%:}" "${orig[3]}" "${orig[-2]}"
|
||||
break
|
||||
elif [[ ${#orig[@]} -eq 0 ]]; then
|
||||
printf "%s: %s: %s (added)\n" "${patched[1]%:}" "${patched[3]}" "${patched[-2]}"
|
||||
break
|
||||
fi
|
||||
|
||||
[[ "${orig[-1]}" == "${patched[-1]}" ]] && continue
|
||||
|
||||
printf "%s: %s: %s" "${orig[1]%:}" "${orig[3]}" "${orig[-2]}"
|
||||
[[ "${orig[-2]}" != "${patched[-2]}" ]] && \
|
||||
printf " (patched: %s)" "${patched[-2]}"
|
||||
printf "\n"
|
||||
break
|
||||
done || true
|
||||
done
|
||||
done
|
||||
}
|
||||
|
|
@ -745,7 +815,7 @@ build_patch_module() {
|
|||
rm -rf "$KMOD_DIR"
|
||||
mkdir -p "$KMOD_DIR"
|
||||
|
||||
cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR"
|
||||
cp -f "$SCRIPT_DIR/init.c" "$KMOD_DIR"
|
||||
|
||||
echo "obj-m := $NAME.o" > "$makefile"
|
||||
echo -n "$NAME-y := init.o" >> "$makefile"
|
||||
|
|
@ -780,19 +850,20 @@ build_patch_module() {
|
|||
[[ $REPLACE -eq 0 ]] && cflags+=("-DKLP_NO_REPLACE")
|
||||
|
||||
cmd=("make")
|
||||
cmd+=("$VERBOSE")
|
||||
if [[ -v VERBOSE ]]; then
|
||||
cmd+=("V=1")
|
||||
else
|
||||
cmd+=("-s")
|
||||
fi
|
||||
cmd+=("-j$JOBS")
|
||||
cmd+=("--directory=.")
|
||||
cmd+=("M=$KMOD_DIR")
|
||||
cmd+=("KCFLAGS=${cflags[*]}")
|
||||
|
||||
# Build a "normal" kernel module with init.c and the diffed objects
|
||||
(
|
||||
cd "$SRC"
|
||||
"${cmd[@]}" \
|
||||
1> >(tee -a "$log") \
|
||||
2> >(tee -a "$log" >&2)
|
||||
)
|
||||
"${cmd[@]}" \
|
||||
1> >(tee -a "$log") \
|
||||
2> >(tee -a "$log" >&2)
|
||||
|
||||
kmod_file="$KMOD_DIR/$NAME.ko"
|
||||
|
||||
|
|
@ -803,7 +874,7 @@ build_patch_module() {
|
|||
objcopy --remove-section=.BTF "$kmod_file"
|
||||
|
||||
# Fix (and work around) linker wreckage for klp syms / relocs
|
||||
"$SRC/tools/objtool/objtool" klp post-link "$kmod_file" || die "objtool klp post-link failed"
|
||||
"$OBJTOOL" klp post-link "$kmod_file" || die "objtool klp post-link failed"
|
||||
|
||||
cp -f "$kmod_file" "$OUTFILE"
|
||||
}
|
||||
|
|
@ -839,6 +910,13 @@ if (( SHORT_CIRCUIT <= 2 )); then
|
|||
fi
|
||||
|
||||
if (( SHORT_CIRCUIT <= 3 )); then
|
||||
status "Generating original checksums"
|
||||
generate_checksums "$ORIG_DIR" "$ORIG_CSUM_DIR" "$PATCHED_DIR"
|
||||
status "Generating patched checksums"
|
||||
generate_checksums "$PATCHED_DIR" "$PATCHED_CSUM_DIR"
|
||||
fi
|
||||
|
||||
if (( SHORT_CIRCUIT <= 4 )); then
|
||||
status "Diffing objects"
|
||||
diff_objects
|
||||
if [[ -v DIFF_CHECKSUM ]]; then
|
||||
|
|
@ -847,7 +925,7 @@ if (( SHORT_CIRCUIT <= 3 )); then
|
|||
fi
|
||||
fi
|
||||
|
||||
if (( SHORT_CIRCUIT <= 4 )); then
|
||||
if (( SHORT_CIRCUIT <= 5 )); then
|
||||
status "Building patch module: $OUTFILE"
|
||||
build_patch_module
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ objtool-$(BUILD_DISAS) += disas.o
|
|||
objtool-$(BUILD_DISAS) += trace.o
|
||||
|
||||
objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o
|
||||
objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o klp-post-link.o
|
||||
objtool-$(BUILD_KLP) += builtin-klp.o klp-checksum.o klp-diff.o klp-post-link.o
|
||||
|
||||
objtool-y += libstring.o
|
||||
objtool-y += libctype.o
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ INCLUDES := -I$(srctree)/tools/include \
|
|||
-I$(srctree)/tools/arch/$(SRCARCH)/include \
|
||||
-I$(srctree)/tools/objtool/include \
|
||||
-I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \
|
||||
-I$(LIBSUBCMD_OUTPUT)/include
|
||||
-I$(srctree)/tools/lib
|
||||
|
||||
OBJTOOL_CFLAGS := -std=gnu11 -fomit-frame-pointer -O2 -g $(WARNINGS) \
|
||||
$(INCLUDES) $(LIBELF_FLAGS) $(LIBXXHASH_CFLAGS) $(HOSTCFLAGS)
|
||||
|
|
@ -135,7 +135,7 @@ $(LIBSUBCMD): fixdep $(LIBSUBCMD_OUTPUT) FORCE
|
|||
$(Q)$(MAKE) -C $(LIBSUBCMD_DIR) O=$(LIBSUBCMD_OUTPUT) \
|
||||
DESTDIR=$(LIBSUBCMD_OUTPUT) prefix= subdir= \
|
||||
$(HOST_OVERRIDES) EXTRA_CFLAGS="$(OBJTOOL_CFLAGS)" \
|
||||
$@ install_headers
|
||||
$@
|
||||
|
||||
$(LIBSUBCMD)-clean:
|
||||
$(call QUIET_CLEAN, libsubcmd)
|
||||
|
|
|
|||
|
|
@ -805,14 +805,27 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
break;
|
||||
}
|
||||
|
||||
if (ins.immediate.nbytes)
|
||||
if (ins.immediate.nbytes) {
|
||||
insn->immediate = ins.immediate.value;
|
||||
else if (ins.displacement.nbytes)
|
||||
insn->immediate_len = ins.immediate.nbytes;
|
||||
} else if (ins.displacement.nbytes) {
|
||||
insn->immediate = ins.displacement.value;
|
||||
insn->immediate_len = ins.displacement.nbytes;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t arch_jump_opcode_bytes(struct objtool_file *file, struct instruction *insn,
|
||||
unsigned char *buf)
|
||||
{
|
||||
size_t len;
|
||||
|
||||
len = insn->len - insn->immediate_len;
|
||||
memcpy(buf, insn->sec->data->d_buf + insn->offset, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
void arch_initial_func_cfi_state(struct cfi_init_state *state)
|
||||
{
|
||||
int i;
|
||||
|
|
|
|||
|
|
@ -73,8 +73,6 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)
|
|||
|
||||
static const struct option check_options[] = {
|
||||
OPT_GROUP("Actions:"),
|
||||
OPT_BOOLEAN(0, "checksum", &opts.checksum, "generate per-function checksums"),
|
||||
OPT_BOOLEAN(0, "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
|
||||
OPT_STRING_OPTARG('d', "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),
|
||||
OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
|
||||
OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"),
|
||||
|
|
@ -85,7 +83,7 @@ static const struct option check_options[] = {
|
|||
OPT_BOOLEAN('r', "retpoline", &opts.retpoline, "validate and annotate retpoline usage"),
|
||||
OPT_BOOLEAN(0, "rethunk", &opts.rethunk, "validate and annotate rethunk usage"),
|
||||
OPT_BOOLEAN(0, "unret", &opts.unret, "validate entry unret placement"),
|
||||
OPT_INTEGER(0, "prefix", &opts.prefix, "generate prefix symbols"),
|
||||
OPT_INTEGER(0, "prefix", &opts.prefix, "generate or grow prefix symbols for N-byte function padding"),
|
||||
OPT_BOOLEAN('l', "sls", &opts.sls, "validate straight-line-speculation mitigations"),
|
||||
OPT_BOOLEAN('s', "stackval", &opts.stackval, "validate frame pointer rules"),
|
||||
OPT_BOOLEAN('t', "static-call", &opts.static_call, "annotate static calls"),
|
||||
|
|
@ -93,9 +91,10 @@ static const struct option check_options[] = {
|
|||
OPT_CALLBACK_OPTARG(0, "dump", NULL, NULL, "orc", "dump metadata", parse_dump),
|
||||
|
||||
OPT_GROUP("Options:"),
|
||||
OPT_BOOLEAN(0, "cfi", &opts.cfi, "grow kCFI preamble symbols (use with --prefix)"),
|
||||
OPT_BOOLEAN(0, "fineibt", &opts.fineibt, "create .cfi_sites section for FineIBT"),
|
||||
OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"),
|
||||
OPT_BOOLEAN(0, "backup", &opts.backup, "create backup (.orig) file on warning/error"),
|
||||
OPT_STRING(0, "debug-checksum", &opts.debug_checksum, "funcs", "enable checksum debug output"),
|
||||
OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
|
||||
OPT_BOOLEAN(0, "link", &opts.link, "object is a linked object"),
|
||||
OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"),
|
||||
|
|
@ -165,20 +164,17 @@ static bool opts_valid(void)
|
|||
return false;
|
||||
}
|
||||
|
||||
#ifndef BUILD_KLP
|
||||
if (opts.checksum) {
|
||||
ERROR("--checksum not supported; install xxhash-devel/libxxhash-dev (version >= 0.8) and recompile");
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (opts.debug_checksum && !opts.checksum) {
|
||||
ERROR("--debug-checksum requires --checksum");
|
||||
if (opts.cfi && !opts.prefix) {
|
||||
ERROR("--cfi requires --prefix");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.checksum ||
|
||||
opts.disas ||
|
||||
if (opts.fineibt && !opts.cfi) {
|
||||
ERROR("--fineibt requires --cfi");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (opts.disas ||
|
||||
opts.hack_jump_label ||
|
||||
opts.hack_noinstr ||
|
||||
opts.ibt ||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ struct subcmd {
|
|||
};
|
||||
|
||||
static struct subcmd subcmds[] = {
|
||||
{ "checksum", "Generate per-function checksums", cmd_klp_checksum, },
|
||||
{ "diff", "Generate binary diff of two object files", cmd_klp_diff, },
|
||||
{ "post-link", "Finalize klp symbols/relocs after module linking", cmd_klp_post_link, },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@
|
|||
#include <objtool/special.h>
|
||||
#include <objtool/trace.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/checksum.h>
|
||||
#include <objtool/util.h>
|
||||
|
||||
#include <linux/objtool_types.h>
|
||||
|
|
@ -64,8 +63,8 @@ struct instruction *next_insn_same_sec(struct objtool_file *file,
|
|||
return insn;
|
||||
}
|
||||
|
||||
static struct instruction *next_insn_same_func(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
struct instruction *next_insn_same_func(struct objtool_file *file,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct instruction *next = next_insn_same_sec(file, insn);
|
||||
struct symbol *func = insn_func(insn);
|
||||
|
|
@ -113,10 +112,6 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
|
|||
for_each_sec(file->elf, __sec) \
|
||||
sec_for_each_insn(file, __sec, insn)
|
||||
|
||||
#define func_for_each_insn(file, func, insn) \
|
||||
for (insn = find_insn(file, func->sec, func->offset); \
|
||||
insn; \
|
||||
insn = next_insn_same_func(file, insn))
|
||||
|
||||
#define sym_for_each_insn(file, sym, insn) \
|
||||
for (insn = find_insn(file, sym->sec, sym->offset); \
|
||||
|
|
@ -491,7 +486,7 @@ static int decode_instructions(struct objtool_file *file)
|
|||
return -1;
|
||||
}
|
||||
|
||||
if (func->embedded_insn || func->alias != func)
|
||||
if (func->embedded_insn || is_alias_sym(func))
|
||||
continue;
|
||||
|
||||
if (!find_insn(file, sec, func->offset)) {
|
||||
|
|
@ -500,7 +495,7 @@ static int decode_instructions(struct objtool_file *file)
|
|||
}
|
||||
|
||||
sym_for_each_insn(file, func, insn) {
|
||||
insn->sym = func;
|
||||
insn->_sym = func;
|
||||
if (is_func_sym(func) &&
|
||||
insn->type == INSN_ENDBR &&
|
||||
list_empty(&insn->call_node)) {
|
||||
|
|
@ -864,15 +859,14 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
|
|||
list_for_each_entry(insn, &file->endbr_list, call_node) {
|
||||
|
||||
int *site = (int *)sec->data->d_buf + idx;
|
||||
struct symbol *sym = insn->sym;
|
||||
struct symbol *func = insn_func(insn);
|
||||
*site = 0;
|
||||
|
||||
if (opts.module && sym && is_func_sym(sym) &&
|
||||
insn->offset == sym->offset &&
|
||||
(!strcmp(sym->name, "init_module") ||
|
||||
!strcmp(sym->name, "cleanup_module"))) {
|
||||
if (opts.module && func && insn->offset == func->offset &&
|
||||
(!strcmp(func->name, "init_module") ||
|
||||
!strcmp(func->name, "cleanup_module"))) {
|
||||
ERROR("%s(): Magic init_module() function name is deprecated, use module_init(fn) instead",
|
||||
sym->name);
|
||||
func->name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
@ -887,6 +881,31 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Grow __cfi_ symbols to fill the NOP gap between the 'mov <hash>, %rax' and
|
||||
* the start of the function.
|
||||
*/
|
||||
static int grow_cfi_symbols(struct objtool_file *file)
|
||||
{
|
||||
struct symbol *sym;
|
||||
|
||||
for_each_sym(file->elf, sym) {
|
||||
if (!is_func_sym(sym) || !strstarts(sym->name, "__cfi_") ||
|
||||
sym->len != 5)
|
||||
continue;
|
||||
|
||||
if (!find_func_by_offset(sym->sec, sym->offset + sym->len + opts.prefix))
|
||||
continue;
|
||||
|
||||
sym->len += opts.prefix;
|
||||
sym->sym.st_size = sym->len;
|
||||
if (elf_write_symbol(file->elf, sym))
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int create_cfi_sections(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec;
|
||||
|
|
@ -1023,59 +1042,6 @@ static int create_direct_call_sections(struct objtool_file *file)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef BUILD_KLP
|
||||
static int create_sym_checksum_section(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec;
|
||||
struct symbol *sym;
|
||||
unsigned int idx = 0;
|
||||
struct sym_checksum *checksum;
|
||||
size_t entsize = sizeof(struct sym_checksum);
|
||||
|
||||
sec = find_section_by_name(file->elf, ".discard.sym_checksum");
|
||||
if (sec) {
|
||||
if (!opts.dryrun)
|
||||
WARN("file already has .discard.sym_checksum section, skipping");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for_each_sym(file->elf, sym)
|
||||
if (sym->csum.checksum)
|
||||
idx++;
|
||||
|
||||
if (!idx)
|
||||
return 0;
|
||||
|
||||
sec = elf_create_section_pair(file->elf, ".discard.sym_checksum", entsize,
|
||||
idx, idx);
|
||||
if (!sec)
|
||||
return -1;
|
||||
|
||||
idx = 0;
|
||||
for_each_sym(file->elf, sym) {
|
||||
if (!sym->csum.checksum)
|
||||
continue;
|
||||
|
||||
if (!elf_init_reloc(file->elf, sec->rsec, idx, idx * entsize,
|
||||
sym, 0, R_TEXT64))
|
||||
return -1;
|
||||
|
||||
checksum = (struct sym_checksum *)sec->data->d_buf + idx;
|
||||
checksum->addr = 0; /* reloc */
|
||||
checksum->checksum = sym->csum.checksum;
|
||||
|
||||
mark_sec_changed(file->elf, sec, true);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static int create_sym_checksum_section(struct objtool_file *file) { return -EINVAL; }
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Warnings shouldn't be reported for ignored functions.
|
||||
*/
|
||||
|
|
@ -1349,14 +1315,11 @@ __weak bool arch_is_embedded_insn(struct symbol *sym)
|
|||
return false;
|
||||
}
|
||||
|
||||
static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
|
||||
struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn)
|
||||
{
|
||||
struct reloc *reloc;
|
||||
|
||||
if (insn->no_reloc)
|
||||
return NULL;
|
||||
|
||||
if (!file)
|
||||
if (!file || insn->no_reloc || insn->fake)
|
||||
return NULL;
|
||||
|
||||
reloc = find_reloc_by_dest_range(file->elf, insn->sec,
|
||||
|
|
@ -1642,7 +1605,7 @@ static int add_jump_destinations(struct objtool_file *file)
|
|||
}
|
||||
|
||||
if (!dest_sym || is_sec_sym(dest_sym)) {
|
||||
dest_sym = dest_insn->sym;
|
||||
dest_sym = insn_sym(dest_insn);
|
||||
if (!dest_sym)
|
||||
goto set_jump_dest;
|
||||
}
|
||||
|
|
@ -1658,7 +1621,7 @@ static int add_jump_destinations(struct objtool_file *file)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!insn->sym || insn->sym->pfunc == dest_sym->pfunc)
|
||||
if (!insn_sym(insn) || insn_sym(insn)->pfunc == dest_sym->pfunc)
|
||||
goto set_jump_dest;
|
||||
|
||||
/*
|
||||
|
|
@ -1831,7 +1794,6 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
nop->offset = special_alt->new_off + special_alt->new_len;
|
||||
nop->len = special_alt->orig_len - special_alt->new_len;
|
||||
nop->type = INSN_NOP;
|
||||
nop->sym = orig_insn->sym;
|
||||
nop->alt_group = new_alt_group;
|
||||
nop->fake = 1;
|
||||
}
|
||||
|
|
@ -1850,7 +1812,6 @@ static int handle_group_alt(struct objtool_file *file,
|
|||
|
||||
last_new_insn = insn;
|
||||
|
||||
insn->sym = orig_insn->sym;
|
||||
insn->alt_group = new_alt_group;
|
||||
|
||||
/*
|
||||
|
|
@ -2232,7 +2193,7 @@ static int add_jump_table_alts(struct objtool_file *file)
|
|||
return 0;
|
||||
|
||||
for_each_sym(file->elf, func) {
|
||||
if (!is_func_sym(func) || func->alias != func)
|
||||
if (!is_func_sym(func) || is_alias_sym(func))
|
||||
continue;
|
||||
|
||||
mark_func_jump_tables(file, func);
|
||||
|
|
@ -2493,12 +2454,12 @@ static int __annotate_late(struct objtool_file *file, int type, struct instructi
|
|||
break;
|
||||
|
||||
case ANNOTYPE_NOCFI:
|
||||
sym = insn->sym;
|
||||
sym = insn_sym(insn);
|
||||
if (!sym) {
|
||||
ERROR_INSN(insn, "dodgy NOCFI annotation");
|
||||
return -1;
|
||||
}
|
||||
insn->sym->nocfi = 1;
|
||||
sym->nocfi = 1;
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -2566,7 +2527,6 @@ static int classify_symbols(struct objtool_file *file)
|
|||
static void mark_rodata(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec;
|
||||
bool found = false;
|
||||
|
||||
/*
|
||||
* Search for the following rodata sections, each of which can
|
||||
|
|
@ -2579,15 +2539,11 @@ static void mark_rodata(struct objtool_file *file)
|
|||
* .rodata.str1.* sections are ignored; they don't contain jump tables.
|
||||
*/
|
||||
for_each_sec(file->elf, sec) {
|
||||
if ((!strncmp(sec->name, ".rodata", 7) &&
|
||||
!strstr(sec->name, ".str1.")) ||
|
||||
!strncmp(sec->name, ".data.rel.ro", 12)) {
|
||||
sec->rodata = true;
|
||||
found = true;
|
||||
if (is_rodata_sec(sec)) {
|
||||
file->rodata = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
file->rodata = found;
|
||||
}
|
||||
|
||||
static void mark_holes(struct objtool_file *file)
|
||||
|
|
@ -2604,7 +2560,7 @@ static void mark_holes(struct objtool_file *file)
|
|||
* favour of a regular symbol, but leaves the code in place.
|
||||
*/
|
||||
for_each_insn(file, insn) {
|
||||
if (insn->sym || !find_symbol_hole_containing(insn->sec, insn->offset)) {
|
||||
if (insn_sym(insn) || !find_symbol_hole_containing(insn->sec, insn->offset)) {
|
||||
in_hole = false;
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2622,7 +2578,7 @@ static void mark_holes(struct objtool_file *file)
|
|||
if (insn->jump_dest) {
|
||||
struct symbol *dest_func = insn_func(insn->jump_dest);
|
||||
|
||||
if (dest_func && dest_func->cold)
|
||||
if (dest_func && is_cold_func(dest_func))
|
||||
dest_func->ignore = true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2630,14 +2586,35 @@ static void mark_holes(struct objtool_file *file)
|
|||
|
||||
static bool validate_branch_enabled(void)
|
||||
{
|
||||
return opts.stackval ||
|
||||
opts.orc ||
|
||||
opts.uaccess ||
|
||||
return opts.stackval ||
|
||||
opts.orc ||
|
||||
opts.uaccess;
|
||||
}
|
||||
|
||||
static bool alts_needed(void)
|
||||
{
|
||||
return validate_branch_enabled() ||
|
||||
opts.noinstr ||
|
||||
opts.hack_jump_label ||
|
||||
opts.disas ||
|
||||
opts.checksum;
|
||||
}
|
||||
|
||||
static int decode_sections(struct objtool_file *file)
|
||||
int decode_file(struct objtool_file *file)
|
||||
{
|
||||
arch_initial_func_cfi_state(&initial_func_cfi);
|
||||
init_cfi_state(&init_cfi);
|
||||
init_cfi_state(&func_cfi);
|
||||
set_func_state(&func_cfi);
|
||||
init_cfi_state(&force_undefined_cfi);
|
||||
force_undefined_cfi.force_undefined = true;
|
||||
|
||||
if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3)))
|
||||
return -1;
|
||||
|
||||
cfi_hash_add(&init_cfi);
|
||||
cfi_hash_add(&func_cfi);
|
||||
|
||||
file->klp = is_livepatch_module(file);
|
||||
|
||||
mark_rodata(file);
|
||||
|
|
@ -2666,7 +2643,7 @@ static int decode_sections(struct objtool_file *file)
|
|||
* Must be before add_jump_destinations(), which depends on 'func'
|
||||
* being set for alternatives, to enable proper sibling call detection.
|
||||
*/
|
||||
if (validate_branch_enabled() || opts.noinstr || opts.hack_jump_label || opts.disas) {
|
||||
if (alts_needed()) {
|
||||
if (add_special_section_alts(file))
|
||||
return -1;
|
||||
}
|
||||
|
|
@ -3027,7 +3004,7 @@ static int update_cfi_state(struct instruction *insn,
|
|||
}
|
||||
|
||||
if (op->dest.reg == CFI_BP && op->src.reg == CFI_SP &&
|
||||
insn->sym->frame_pointer) {
|
||||
insn_sym(insn)->frame_pointer) {
|
||||
/* addi.d fp,sp,imm on LoongArch */
|
||||
if (cfa->base == CFI_SP && cfa->offset == op->src.offset) {
|
||||
cfa->base = CFI_BP;
|
||||
|
|
@ -3039,7 +3016,7 @@ static int update_cfi_state(struct instruction *insn,
|
|||
if (op->dest.reg == CFI_SP && op->src.reg == CFI_BP) {
|
||||
/* addi.d sp,fp,imm on LoongArch */
|
||||
if (cfa->base == CFI_BP && cfa->offset == 0) {
|
||||
if (insn->sym->frame_pointer) {
|
||||
if (insn_sym(insn)->frame_pointer) {
|
||||
cfa->base = CFI_SP;
|
||||
cfa->offset = -op->src.offset;
|
||||
}
|
||||
|
|
@ -3662,88 +3639,6 @@ static bool skip_alt_group(struct instruction *insn)
|
|||
return alt_insn->type == INSN_CLAC || alt_insn->type == INSN_STAC;
|
||||
}
|
||||
|
||||
static int checksum_debug_init(struct objtool_file *file)
|
||||
{
|
||||
char *dup, *s;
|
||||
|
||||
if (!opts.debug_checksum)
|
||||
return 0;
|
||||
|
||||
dup = strdup(opts.debug_checksum);
|
||||
if (!dup) {
|
||||
ERROR_GLIBC("strdup");
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = dup;
|
||||
while (*s) {
|
||||
struct symbol *func;
|
||||
char *comma;
|
||||
|
||||
comma = strchr(s, ',');
|
||||
if (comma)
|
||||
*comma = '\0';
|
||||
|
||||
func = find_symbol_by_name(file->elf, s);
|
||||
if (!func || !is_func_sym(func))
|
||||
WARN("--debug-checksum: can't find '%s'", s);
|
||||
else
|
||||
func->debug_checksum = 1;
|
||||
|
||||
if (!comma)
|
||||
break;
|
||||
|
||||
s = comma + 1;
|
||||
}
|
||||
|
||||
free(dup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct reloc *reloc = insn_reloc(file, insn);
|
||||
unsigned long offset;
|
||||
struct symbol *sym;
|
||||
|
||||
if (insn->fake)
|
||||
return;
|
||||
|
||||
checksum_update(func, insn, insn->sec->data->d_buf + insn->offset, insn->len);
|
||||
|
||||
if (!reloc) {
|
||||
struct symbol *call_dest = insn_call_dest(insn);
|
||||
|
||||
if (call_dest)
|
||||
checksum_update(func, insn, call_dest->demangled_name,
|
||||
strlen(call_dest->demangled_name));
|
||||
return;
|
||||
}
|
||||
|
||||
sym = reloc->sym;
|
||||
offset = arch_insn_adjusted_addend(insn, reloc);
|
||||
|
||||
if (is_string_sec(sym->sec)) {
|
||||
char *str;
|
||||
|
||||
str = sym->sec->data->d_buf + sym->offset + offset;
|
||||
checksum_update(func, insn, str, strlen(str));
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_sec_sym(sym)) {
|
||||
sym = find_symbol_containing(reloc->sym->sec, offset);
|
||||
if (!sym)
|
||||
return;
|
||||
|
||||
offset -= sym->offset;
|
||||
}
|
||||
|
||||
checksum_update(func, insn, sym->demangled_name, strlen(sym->demangled_name));
|
||||
checksum_update(func, insn, &offset, sizeof(offset));
|
||||
}
|
||||
|
||||
static int validate_branch(struct objtool_file *file, struct symbol *func,
|
||||
struct instruction *insn, struct insn_state state);
|
||||
static int do_validate_branch(struct objtool_file *file, struct symbol *func,
|
||||
|
|
@ -4025,9 +3920,6 @@ static int do_validate_branch(struct objtool_file *file, struct symbol *func,
|
|||
insn->trace = 0;
|
||||
next_insn = next_insn_to_validate(file, insn);
|
||||
|
||||
if (opts.checksum && func && insn->sec)
|
||||
checksum_update_insn(file, func, insn);
|
||||
|
||||
if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
|
||||
/* Ignore KCFI type preambles, which always fall through */
|
||||
if (is_prefix_func(func))
|
||||
|
|
@ -4093,9 +3985,6 @@ static int validate_unwind_hint(struct objtool_file *file,
|
|||
struct symbol *func = insn_func(insn);
|
||||
int ret;
|
||||
|
||||
if (opts.checksum)
|
||||
checksum_init(func);
|
||||
|
||||
ret = validate_branch(file, func, insn, *state);
|
||||
if (ret)
|
||||
BT_INSN(insn, "<=== (hint)");
|
||||
|
|
@ -4304,7 +4193,7 @@ static int validate_retpoline(struct objtool_file *file)
|
|||
* broken.
|
||||
*/
|
||||
list_for_each_entry(insn, &file->retpoline_call_list, call_node) {
|
||||
struct symbol *sym = insn->sym;
|
||||
struct symbol *sym = insn_sym(insn);
|
||||
|
||||
if (sym && (is_notype_sym(sym) ||
|
||||
is_func_sym(sym)) && !sym->nocfi) {
|
||||
|
|
@ -4407,17 +4296,6 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio
|
|||
* For FineIBT or kCFI, a certain number of bytes preceding the function may be
|
||||
* NOPs. Those NOPs may be rewritten at runtime and executed, so give them a
|
||||
* proper function name: __pfx_<func>.
|
||||
*
|
||||
* The NOPs may not exist for the following cases:
|
||||
*
|
||||
* - compiler cloned functions (*.cold, *.part0, etc)
|
||||
* - asm functions created with inline asm or without SYM_FUNC_START()
|
||||
*
|
||||
* Also, the function may already have a prefix from a previous objtool run
|
||||
* (livepatch extracted functions, or manually running objtool multiple times).
|
||||
*
|
||||
* So return 0 if the NOPs are missing or the function already has a prefix
|
||||
* symbol.
|
||||
*/
|
||||
static int create_prefix_symbol(struct objtool_file *file, struct symbol *func)
|
||||
{
|
||||
|
|
@ -4425,10 +4303,6 @@ static int create_prefix_symbol(struct objtool_file *file, struct symbol *func)
|
|||
char name[SYM_NAME_LEN];
|
||||
struct cfi_state *cfi;
|
||||
|
||||
if (!is_func_sym(func) || is_prefix_func(func) ||
|
||||
func->cold || func->static_call_tramp)
|
||||
return 0;
|
||||
|
||||
if ((strlen(func->name) + sizeof("__pfx_") > SYM_NAME_LEN)) {
|
||||
WARN("%s: symbol name too long, can't create __pfx_ symbol",
|
||||
func->name);
|
||||
|
|
@ -4438,59 +4312,21 @@ static int create_prefix_symbol(struct objtool_file *file, struct symbol *func)
|
|||
if (snprintf_check(name, SYM_NAME_LEN, "__pfx_%s", func->name))
|
||||
return -1;
|
||||
|
||||
if (file->klp) {
|
||||
struct symbol *pfx;
|
||||
|
||||
pfx = find_symbol_by_offset(func->sec, func->offset - opts.prefix);
|
||||
if (pfx && is_prefix_func(pfx) && !strcmp(pfx->name, name))
|
||||
return 0;
|
||||
}
|
||||
|
||||
insn = find_insn(file, func->sec, func->offset);
|
||||
if (!insn) {
|
||||
WARN("%s: can't find starting instruction", func->name);
|
||||
if (!elf_create_symbol(file->elf, name, func->sec,
|
||||
GELF_ST_BIND(func->sym.st_info),
|
||||
GELF_ST_TYPE(func->sym.st_info),
|
||||
func->offset - opts.prefix, opts.prefix))
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (prev = prev_insn_same_sec(file, insn);
|
||||
prev;
|
||||
prev = prev_insn_same_sec(file, prev)) {
|
||||
u64 offset;
|
||||
|
||||
if (prev->type != INSN_NOP)
|
||||
return 0;
|
||||
|
||||
offset = func->offset - prev->offset;
|
||||
|
||||
if (offset > opts.prefix)
|
||||
return 0;
|
||||
|
||||
if (offset < opts.prefix)
|
||||
continue;
|
||||
|
||||
if (!elf_create_symbol(file->elf, name, func->sec,
|
||||
GELF_ST_BIND(func->sym.st_info),
|
||||
GELF_ST_TYPE(func->sym.st_info),
|
||||
prev->offset, opts.prefix))
|
||||
return -1;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (!prev)
|
||||
return 0;
|
||||
|
||||
if (!insn->cfi) {
|
||||
/*
|
||||
* This can happen if stack validation isn't enabled or the
|
||||
* function is annotated with STACK_FRAME_NON_STANDARD.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Propagate insn->cfi to the prefix code */
|
||||
insn = find_insn(file, func->sec, func->offset);
|
||||
if (!insn || !insn->cfi)
|
||||
return 0;
|
||||
|
||||
cfi = cfi_hash_find_or_add(insn->cfi);
|
||||
for (; prev != insn; prev = next_insn_same_sec(file, prev))
|
||||
for (prev = find_insn(file, func->sec, func->offset - opts.prefix);
|
||||
prev && prev != insn;
|
||||
prev = next_insn_same_sec(file, prev))
|
||||
prev->cfi = cfi;
|
||||
|
||||
return 0;
|
||||
|
|
@ -4498,15 +4334,20 @@ static int create_prefix_symbol(struct objtool_file *file, struct symbol *func)
|
|||
|
||||
static int create_prefix_symbols(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec;
|
||||
struct section *pfe_sec;
|
||||
struct symbol *func;
|
||||
struct reloc *reloc;
|
||||
|
||||
for_each_sec(file->elf, sec) {
|
||||
if (!is_text_sec(sec))
|
||||
for_each_sec(file->elf, pfe_sec) {
|
||||
if (strcmp(pfe_sec->name, "__patchable_function_entries"))
|
||||
continue;
|
||||
if (!pfe_sec->rsec)
|
||||
continue;
|
||||
|
||||
sec_for_each_sym(sec, func) {
|
||||
if (create_prefix_symbol(file, func))
|
||||
for_each_reloc(pfe_sec->rsec, reloc) {
|
||||
func = find_func_by_offset(reloc->sym->sec,
|
||||
reloc->sym->offset + reloc_addend(reloc) + opts.prefix);
|
||||
if (func && create_prefix_symbol(file, func))
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
@ -4526,7 +4367,7 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (sym->pfunc != sym || sym->alias != sym)
|
||||
if (sym->pfunc != sym || is_alias_sym(sym))
|
||||
return 0;
|
||||
|
||||
insn = find_insn(file, sec, sym->offset);
|
||||
|
|
@ -4538,9 +4379,6 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
|
|||
|
||||
func = insn_func(insn);
|
||||
|
||||
if (opts.checksum)
|
||||
checksum_init(func);
|
||||
|
||||
if (opts.trace && !fnmatch(opts.trace, sym->name, 0)) {
|
||||
trace_enable();
|
||||
TRACE("%s: validation begin\n", sym->name);
|
||||
|
|
@ -4553,9 +4391,6 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
|
|||
TRACE("%s: validation %s\n\n", sym->name, ret ? "failed" : "end");
|
||||
trace_disable();
|
||||
|
||||
if (opts.checksum)
|
||||
checksum_finish(func);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -4948,7 +4783,7 @@ struct insn_chunk {
|
|||
* which can trigger more allocations for .debug_* sections whose data hasn't
|
||||
* been read yet.
|
||||
*/
|
||||
static void free_insns(struct objtool_file *file)
|
||||
void free_insns(struct objtool_file *file)
|
||||
{
|
||||
struct instruction *insn;
|
||||
struct insn_chunk *chunks = NULL, *chunk;
|
||||
|
|
@ -4995,26 +4830,7 @@ int check(struct objtool_file *file)
|
|||
objtool_disas_ctx = disas_ctx;
|
||||
}
|
||||
|
||||
arch_initial_func_cfi_state(&initial_func_cfi);
|
||||
init_cfi_state(&init_cfi);
|
||||
init_cfi_state(&func_cfi);
|
||||
set_func_state(&func_cfi);
|
||||
init_cfi_state(&force_undefined_cfi);
|
||||
force_undefined_cfi.force_undefined = true;
|
||||
|
||||
if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3))) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
cfi_hash_add(&init_cfi);
|
||||
cfi_hash_add(&func_cfi);
|
||||
|
||||
ret = checksum_debug_init(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = decode_sections(file);
|
||||
ret = decode_file(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
|
|
@ -5064,12 +4880,6 @@ int check(struct objtool_file *file)
|
|||
goto out;
|
||||
}
|
||||
|
||||
if (opts.cfi) {
|
||||
ret = create_cfi_sections(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (opts.rethunk) {
|
||||
ret = create_return_sites_sections(file);
|
||||
if (ret)
|
||||
|
|
@ -5089,9 +4899,21 @@ int check(struct objtool_file *file)
|
|||
}
|
||||
|
||||
if (opts.prefix) {
|
||||
ret = create_prefix_symbols(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
if (!opts.cfi) {
|
||||
ret = create_prefix_symbols(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
} else {
|
||||
ret = grow_cfi_symbols(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
if (opts.fineibt) {
|
||||
ret = create_cfi_sections(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.ibt) {
|
||||
|
|
@ -5103,12 +4925,6 @@ int check(struct objtool_file *file)
|
|||
if (opts.noabs)
|
||||
warnings += check_abs_references(file);
|
||||
|
||||
if (opts.checksum) {
|
||||
ret = create_sym_checksum_section(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (opts.orc && nr_insns) {
|
||||
ret = orc_create(file);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -210,7 +210,7 @@ static bool disas_print_addr_alt(bfd_vma addr, struct disassemble_info *dinfo)
|
|||
offset = addr - alt_group->first_insn->offset;
|
||||
|
||||
addr = orig_first_insn->offset + offset;
|
||||
sym = orig_first_insn->sym;
|
||||
sym = insn_sym(orig_first_insn);
|
||||
|
||||
disas_print_addr_sym(orig_first_insn->sec, sym, addr, dinfo);
|
||||
|
||||
|
|
@ -222,15 +222,13 @@ static void disas_print_addr_noreloc(bfd_vma addr,
|
|||
{
|
||||
struct disas_context *dctx = dinfo->application_data;
|
||||
struct instruction *insn = dctx->insn;
|
||||
struct symbol *sym = NULL;
|
||||
struct symbol *sym = insn_sym(insn);
|
||||
|
||||
if (disas_print_addr_alt(addr, dinfo))
|
||||
return;
|
||||
|
||||
if (insn->sym && addr >= insn->sym->offset &&
|
||||
addr < insn->sym->offset + insn->sym->len) {
|
||||
sym = insn->sym;
|
||||
}
|
||||
if (sym && (addr < sym->offset || addr >= sym->offset + sym->len))
|
||||
sym = NULL;
|
||||
|
||||
disas_print_addr_sym(insn->sec, sym, addr, dinfo);
|
||||
}
|
||||
|
|
@ -291,9 +289,9 @@ static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
|
|||
* up. So check it first.
|
||||
*/
|
||||
jump_dest = insn->jump_dest;
|
||||
if (jump_dest && jump_dest->sym && jump_dest->offset == addr) {
|
||||
if (jump_dest && insn_sym(jump_dest) && jump_dest->offset == addr) {
|
||||
if (!disas_print_addr_alt(addr, dinfo))
|
||||
disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
|
||||
disas_print_addr_sym(jump_dest->sec, insn_sym(jump_dest),
|
||||
addr, dinfo);
|
||||
return;
|
||||
}
|
||||
|
|
@ -768,8 +766,8 @@ static int disas_alt_jump(struct disas_alt *dalt)
|
|||
if (orig_insn->len == 5)
|
||||
suffix[0] = 'q';
|
||||
str = strfmt("jmp%-3s %lx <%s+0x%lx>", suffix,
|
||||
dest_insn->offset, dest_insn->sym->name,
|
||||
dest_insn->offset - dest_insn->sym->offset);
|
||||
dest_insn->offset, insn_sym(dest_insn)->name,
|
||||
dest_insn->offset - insn_sym(dest_insn)->offset);
|
||||
nops = 0;
|
||||
} else {
|
||||
str = strfmt("nop%d", orig_insn->len);
|
||||
|
|
@ -794,8 +792,8 @@ static int disas_alt_extable(struct disas_alt *dalt)
|
|||
|
||||
alt_insn = dalt->alt->insn;
|
||||
str = strfmt("resume at 0x%lx <%s+0x%lx>",
|
||||
alt_insn->offset, alt_insn->sym->name,
|
||||
alt_insn->offset - alt_insn->sym->offset);
|
||||
alt_insn->offset, insn_sym(alt_insn)->name,
|
||||
alt_insn->offset - insn_sym(alt_insn)->offset);
|
||||
if (!str)
|
||||
return -1;
|
||||
|
||||
|
|
|
|||
|
|
@ -27,27 +27,16 @@
|
|||
|
||||
static ssize_t demangled_name_len(const char *name);
|
||||
|
||||
static inline u32 str_hash(const char *str)
|
||||
{
|
||||
return jhash(str, strlen(str), 0);
|
||||
}
|
||||
|
||||
static inline u32 str_hash_demangled(const char *str)
|
||||
u32 str_hash_demangled(const char *str)
|
||||
{
|
||||
return jhash(str, demangled_name_len(str), 0);
|
||||
}
|
||||
|
||||
#define __elf_table(name) (elf->name##_hash)
|
||||
#define __elf_bits(name) (elf->name##_bits)
|
||||
|
||||
#define __elf_table_entry(name, key) \
|
||||
__elf_table(name)[hash_min(key, __elf_bits(name))]
|
||||
|
||||
#define elf_hash_add(name, node, key) \
|
||||
({ \
|
||||
struct elf_hash_node *__node = node; \
|
||||
__node->next = __elf_table_entry(name, key); \
|
||||
__elf_table_entry(name, key) = __node; \
|
||||
__node->next = __elf_table_entry(elf, name, key); \
|
||||
__elf_table_entry(elf, name, key) = __node; \
|
||||
})
|
||||
|
||||
static inline void __elf_hash_del(struct elf_hash_node *node,
|
||||
|
|
@ -69,30 +58,20 @@ static inline void __elf_hash_del(struct elf_hash_node *node,
|
|||
}
|
||||
|
||||
#define elf_hash_del(name, node, key) \
|
||||
__elf_hash_del(node, &__elf_table_entry(name, key))
|
||||
|
||||
#define elf_list_entry(ptr, type, member) \
|
||||
({ \
|
||||
typeof(ptr) __ptr = (ptr); \
|
||||
__ptr ? container_of(__ptr, type, member) : NULL; \
|
||||
})
|
||||
|
||||
#define elf_hash_for_each_possible(name, obj, member, key) \
|
||||
for (obj = elf_list_entry(__elf_table_entry(name, key), typeof(*obj), member); \
|
||||
obj; \
|
||||
obj = elf_list_entry(obj->member.next, typeof(*(obj)), member))
|
||||
__elf_hash_del(node, &__elf_table_entry(elf, name, key))
|
||||
|
||||
#define elf_alloc_hash(name, size) \
|
||||
({ \
|
||||
__elf_bits(name) = max(10, ilog2(size)); \
|
||||
__elf_table(name) = mmap(NULL, sizeof(struct elf_hash_node *) << __elf_bits(name), \
|
||||
__elf_bits(elf, name) = max(10, ilog2(size)); \
|
||||
__elf_table(elf, name) = mmap(NULL, \
|
||||
sizeof(struct elf_hash_node *) << __elf_bits(elf, name), \
|
||||
PROT_READ|PROT_WRITE, \
|
||||
MAP_PRIVATE|MAP_ANON, -1, 0); \
|
||||
if (__elf_table(name) == (void *)-1L) { \
|
||||
if (__elf_table(elf, name) == (void *)-1L) { \
|
||||
ERROR_GLIBC("mmap fail " #name); \
|
||||
__elf_table(name) = NULL; \
|
||||
__elf_table(elf, name) = NULL; \
|
||||
} \
|
||||
__elf_table(name); \
|
||||
__elf_table(elf, name); \
|
||||
})
|
||||
|
||||
static inline unsigned long __sym_start(struct symbol *s)
|
||||
|
|
@ -141,7 +120,7 @@ struct section *find_section_by_name(const struct elf *elf, const char *name)
|
|||
{
|
||||
struct section *sec;
|
||||
|
||||
elf_hash_for_each_possible(section_name, sec, name_hash, str_hash(name)) {
|
||||
elf_hash_for_each_possible(elf, section_name, sec, name_hash, str_hash(name)) {
|
||||
if (!strcmp(sec->name, name))
|
||||
return sec;
|
||||
}
|
||||
|
|
@ -154,7 +133,7 @@ static struct section *find_section_by_index(struct elf *elf,
|
|||
{
|
||||
struct section *sec;
|
||||
|
||||
elf_hash_for_each_possible(section, sec, hash, idx) {
|
||||
elf_hash_for_each_possible(elf, section, sec, hash, idx) {
|
||||
if (sec->idx == idx)
|
||||
return sec;
|
||||
}
|
||||
|
|
@ -166,7 +145,7 @@ static struct symbol *find_symbol_by_index(struct elf *elf, unsigned int idx)
|
|||
{
|
||||
struct symbol *sym;
|
||||
|
||||
elf_hash_for_each_possible(symbol, sym, hash, idx) {
|
||||
elf_hash_for_each_possible(elf, symbol, sym, hash, idx) {
|
||||
if (sym->idx == idx)
|
||||
return sym;
|
||||
}
|
||||
|
|
@ -228,6 +207,20 @@ struct symbol *find_symbol_containing(const struct section *sec, unsigned long o
|
|||
return sym ? sym->alias : NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Also match the symbol end address which can be used for a bounds comparison.
|
||||
*/
|
||||
struct symbol *find_symbol_containing_inclusive(const struct section *sec,
|
||||
unsigned long offset)
|
||||
{
|
||||
struct symbol *sym = find_symbol_containing(sec, offset);
|
||||
|
||||
if (!sym && offset)
|
||||
sym = find_symbol_containing(sec, offset - 1);
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns size of hole starting at @offset.
|
||||
*/
|
||||
|
|
@ -285,7 +278,7 @@ struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)
|
|||
{
|
||||
struct symbol *sym;
|
||||
|
||||
elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) {
|
||||
elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, str_hash(name)) {
|
||||
if (!strcmp(sym->name, name))
|
||||
return sym;
|
||||
}
|
||||
|
|
@ -300,7 +293,7 @@ static struct symbol *find_local_symbol_by_file_and_name(const struct elf *elf,
|
|||
{
|
||||
struct symbol *sym;
|
||||
|
||||
elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) {
|
||||
elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, str_hash_demangled(name)) {
|
||||
if (sym->bind == STB_LOCAL && sym->file == file &&
|
||||
!strcmp(sym->name, name)) {
|
||||
return sym;
|
||||
|
|
@ -314,7 +307,7 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam
|
|||
{
|
||||
struct symbol *sym;
|
||||
|
||||
elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) {
|
||||
elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, str_hash_demangled(name)) {
|
||||
if (!strcmp(sym->name, name) && !is_local_sym(sym))
|
||||
return sym;
|
||||
}
|
||||
|
|
@ -322,21 +315,9 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void iterate_global_symbol_by_demangled_name(const struct elf *elf,
|
||||
const char *demangled_name,
|
||||
void (*process)(struct symbol *sym, void *data),
|
||||
void *data)
|
||||
{
|
||||
struct symbol *sym;
|
||||
|
||||
elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(demangled_name)) {
|
||||
if (!strcmp(sym->demangled_name, demangled_name) && !is_local_sym(sym))
|
||||
process(sym, data);
|
||||
}
|
||||
}
|
||||
|
||||
/* If there are multiple matches, return the first one in the range */
|
||||
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
|
||||
unsigned long offset, unsigned int len)
|
||||
unsigned long offset, unsigned int len)
|
||||
{
|
||||
struct reloc *reloc, *r = NULL;
|
||||
struct section *rsec;
|
||||
|
|
@ -347,7 +328,7 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se
|
|||
return NULL;
|
||||
|
||||
for_offset_range(o, offset, offset + len) {
|
||||
elf_hash_for_each_possible(reloc, reloc, hash,
|
||||
elf_hash_for_each_possible(elf, reloc, reloc, hash,
|
||||
sec_offset_hash(rsec, o)) {
|
||||
if (reloc->sec != rsec)
|
||||
continue;
|
||||
|
|
@ -358,11 +339,11 @@ struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *se
|
|||
r = reloc;
|
||||
}
|
||||
}
|
||||
if (r)
|
||||
if (r && (reloc_offset(r) & OFFSET_STRIDE_MASK) == o)
|
||||
return r;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
return r;
|
||||
}
|
||||
|
||||
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset)
|
||||
|
|
@ -668,7 +649,7 @@ static int read_symbols(struct elf *elf)
|
|||
|
||||
if (is_file_sym(sym))
|
||||
file = sym;
|
||||
else if (sym->bind == STB_LOCAL)
|
||||
else if (sym->bind == STB_LOCAL && !is_sec_sym(sym))
|
||||
sym->file = file;
|
||||
}
|
||||
|
||||
|
|
@ -1016,6 +997,26 @@ struct symbol *elf_create_symbol(struct elf *elf, const char *name,
|
|||
return sym;
|
||||
}
|
||||
|
||||
int elf_write_symbol(struct elf *elf, struct symbol *sym)
|
||||
{
|
||||
struct section *symtab, *symtab_shndx;
|
||||
|
||||
symtab = find_section_by_name(elf, ".symtab");
|
||||
if (!symtab) {
|
||||
ERROR("no .symtab");
|
||||
return -1;
|
||||
}
|
||||
|
||||
symtab_shndx = find_section_by_name(elf, ".symtab_shndx");
|
||||
|
||||
if (elf_update_symbol(elf, symtab, symtab_shndx, sym))
|
||||
return -1;
|
||||
|
||||
mark_sec_changed(elf, symtab, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec)
|
||||
{
|
||||
struct symbol *sym = calloc(1, sizeof(*sym));
|
||||
|
|
@ -1172,6 +1173,17 @@ static int read_relocs(struct elf *elf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void mark_rodata(struct elf *elf)
|
||||
{
|
||||
struct section *sec;
|
||||
|
||||
for_each_sec(elf, sec) {
|
||||
if ((strstarts(sec->name, ".rodata") && !strstr(sec->name, ".str1.")) ||
|
||||
strstarts(sec->name, ".data.rel.ro"))
|
||||
sec->rodata = true;
|
||||
}
|
||||
}
|
||||
|
||||
struct elf *elf_open_read(const char *name, int flags)
|
||||
{
|
||||
struct elf *elf;
|
||||
|
|
@ -1222,6 +1234,8 @@ struct elf *elf_open_read(const char *name, int flags)
|
|||
if (read_sections(elf))
|
||||
goto err;
|
||||
|
||||
mark_rodata(elf);
|
||||
|
||||
if (read_symbols(elf))
|
||||
goto err;
|
||||
|
||||
|
|
|
|||
|
|
@ -79,6 +79,9 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec
|
|||
unsigned long offset, unsigned int maxlen,
|
||||
struct instruction *insn);
|
||||
|
||||
size_t arch_jump_opcode_bytes(struct objtool_file *file, struct instruction *insn,
|
||||
unsigned char *buf);
|
||||
|
||||
bool arch_callee_saved_reg(unsigned char reg);
|
||||
|
||||
unsigned long arch_jump_destination(struct instruction *insn);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@
|
|||
|
||||
struct opts {
|
||||
/* actions: */
|
||||
bool cfi;
|
||||
bool checksum;
|
||||
const char *disas;
|
||||
bool dump_orc;
|
||||
bool hack_jump_label;
|
||||
bool hack_noinstr;
|
||||
|
|
@ -20,6 +20,7 @@ struct opts {
|
|||
bool noabs;
|
||||
bool noinstr;
|
||||
bool orc;
|
||||
int prefix;
|
||||
bool retpoline;
|
||||
bool rethunk;
|
||||
bool unret;
|
||||
|
|
@ -27,14 +28,14 @@ struct opts {
|
|||
bool stackval;
|
||||
bool static_call;
|
||||
bool uaccess;
|
||||
int prefix;
|
||||
const char *disas;
|
||||
|
||||
/* options: */
|
||||
bool backtrace;
|
||||
bool backup;
|
||||
bool cfi;
|
||||
const char *debug_checksum;
|
||||
bool dryrun;
|
||||
bool fineibt;
|
||||
bool link;
|
||||
bool mnop;
|
||||
bool module;
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ struct instruction {
|
|||
s8 instr;
|
||||
|
||||
u32 idx : INSN_CHUNK_BITS,
|
||||
immediate_len : 4,
|
||||
dead_end : 1,
|
||||
ignore_alts : 1,
|
||||
hint : 1,
|
||||
|
|
@ -81,7 +82,7 @@ struct instruction {
|
|||
hole : 1,
|
||||
fake : 1,
|
||||
trace : 1;
|
||||
/* 9 bit hole */
|
||||
/* 4 bit hole */
|
||||
|
||||
struct alt_group *alt_group;
|
||||
struct instruction *jump_dest;
|
||||
|
|
@ -94,14 +95,30 @@ struct instruction {
|
|||
};
|
||||
};
|
||||
struct alternative *alts;
|
||||
struct symbol *sym;
|
||||
struct symbol *_sym;
|
||||
struct stack_op *stack_ops;
|
||||
struct cfi_state *cfi;
|
||||
};
|
||||
|
||||
/*
|
||||
* Return the symbol associated with an instruction. For alternative
|
||||
* replacements, return the symbol of the original code being replaced rather
|
||||
* than NULL. insn->_sym reflects the actual location in the ELF file.
|
||||
*/
|
||||
static inline struct symbol *insn_sym(struct instruction *insn)
|
||||
{
|
||||
struct symbol *sym = insn->_sym;
|
||||
|
||||
if ((!sym || !is_func_sym(sym)) &&
|
||||
insn->alt_group && insn->alt_group->orig_group)
|
||||
sym = insn->alt_group->orig_group->first_insn->_sym;
|
||||
|
||||
return sym;
|
||||
}
|
||||
|
||||
static inline struct symbol *insn_func(struct instruction *insn)
|
||||
{
|
||||
struct symbol *sym = insn->sym;
|
||||
struct symbol *sym = insn_sym(insn);
|
||||
|
||||
if (sym && sym->type != STT_FUNC)
|
||||
sym = NULL;
|
||||
|
|
@ -144,6 +161,12 @@ struct instruction *find_insn(struct objtool_file *file,
|
|||
struct section *sec, unsigned long offset);
|
||||
|
||||
struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn);
|
||||
struct instruction *next_insn_same_func(struct objtool_file *file, struct instruction *insn);
|
||||
|
||||
#define func_for_each_insn(file, func, insn) \
|
||||
for (insn = find_insn(file, func->sec, func->offset); \
|
||||
insn; \
|
||||
insn = next_insn_same_func(file, insn))
|
||||
|
||||
#define sec_for_each_insn(file, _sec, insn) \
|
||||
for (insn = find_insn(file, _sec, 0); \
|
||||
|
|
@ -155,6 +178,11 @@ struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruc
|
|||
insn && insn->offset < sym->offset + sym->len; \
|
||||
insn = next_insn_same_sec(file, insn))
|
||||
|
||||
struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn);
|
||||
|
||||
int decode_file(struct objtool_file *file);
|
||||
void free_insns(struct objtool_file *file);
|
||||
|
||||
const char *objtool_disas_insn(struct instruction *insn);
|
||||
|
||||
extern size_t sym_name_max_len;
|
||||
|
|
|
|||
|
|
@ -6,37 +6,54 @@
|
|||
|
||||
#ifdef BUILD_KLP
|
||||
|
||||
static inline void checksum_init(struct symbol *func)
|
||||
static inline void checksum_init(struct symbol *sym)
|
||||
{
|
||||
if (func && !func->csum.state) {
|
||||
func->csum.state = XXH3_createState();
|
||||
XXH3_64bits_reset(func->csum.state);
|
||||
if (sym && !sym->csum.state) {
|
||||
sym->csum.state = XXH3_createState();
|
||||
XXH3_64bits_reset(sym->csum.state);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void checksum_update(struct symbol *func,
|
||||
struct instruction *insn,
|
||||
const void *data, size_t size)
|
||||
static inline void __checksum_update(struct symbol *sym, const void *data,
|
||||
size_t size)
|
||||
{
|
||||
XXH3_64bits_update(func->csum.state, data, size);
|
||||
dbg_checksum(func, insn, XXH3_64bits_digest(func->csum.state));
|
||||
XXH3_64bits_update(sym->csum.state, data, size);
|
||||
}
|
||||
|
||||
static inline void checksum_finish(struct symbol *func)
|
||||
static inline void __checksum_update_insn(struct symbol *sym,
|
||||
struct instruction *insn,
|
||||
const void *data, size_t size)
|
||||
{
|
||||
if (func && func->csum.state) {
|
||||
func->csum.checksum = XXH3_64bits_digest(func->csum.state);
|
||||
func->csum.state = NULL;
|
||||
__checksum_update(sym, data, size);
|
||||
dbg_checksum_insn(sym, insn, XXH3_64bits_digest(sym->csum.state));
|
||||
}
|
||||
|
||||
static inline void __checksum_update_object(struct symbol *sym,
|
||||
unsigned long offset,
|
||||
const char *what, const void *data,
|
||||
size_t size)
|
||||
{
|
||||
__checksum_update(sym, &offset, sizeof(offset));
|
||||
__checksum_update(sym, data, size);
|
||||
dbg_checksum_object(sym, offset, what, XXH3_64bits_digest(sym->csum.state));
|
||||
}
|
||||
|
||||
static inline void checksum_finish(struct symbol *sym)
|
||||
{
|
||||
if (sym && sym->csum.state) {
|
||||
sym->csum.checksum = XXH3_64bits_digest(sym->csum.state);
|
||||
XXH3_freeState(sym->csum.state);
|
||||
sym->csum.state = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int calculate_checksums(struct objtool_file *file);
|
||||
int create_sym_checksum_section(struct objtool_file *file);
|
||||
|
||||
#else /* !BUILD_KLP */
|
||||
|
||||
static inline void checksum_init(struct symbol *func) {}
|
||||
static inline void checksum_update(struct symbol *func,
|
||||
struct instruction *insn,
|
||||
const void *data, size_t size) {}
|
||||
static inline void checksum_finish(struct symbol *func) {}
|
||||
static inline int calculate_checksums(struct objtool_file *file) { return -ENOSYS; }
|
||||
static inline int create_sym_checksum_section(struct objtool_file *file) { return -EINVAL; }
|
||||
|
||||
#endif /* !BUILD_KLP */
|
||||
|
||||
|
|
|
|||
|
|
@ -21,6 +21,13 @@
|
|||
#define SEC_NAME_LEN 1024
|
||||
#define SYM_NAME_LEN 512
|
||||
|
||||
static inline u32 str_hash(const char *str)
|
||||
{
|
||||
return jhash(str, strlen(str), 0);
|
||||
}
|
||||
|
||||
u32 str_hash_demangled(const char *str);
|
||||
|
||||
#define bswap_if_needed(elf, val) __bswap_if_needed(&elf->ehdr, val)
|
||||
|
||||
#ifdef LIBELF_USE_DEPRECATED
|
||||
|
|
@ -89,6 +96,7 @@ struct symbol {
|
|||
u8 changed : 1;
|
||||
u8 included : 1;
|
||||
u8 klp : 1;
|
||||
u8 dont_correlate : 1;
|
||||
struct list_head pv_target;
|
||||
struct reloc *relocs;
|
||||
struct section *group_sec;
|
||||
|
|
@ -130,6 +138,23 @@ struct elf {
|
|||
struct symbol *symbol_data;
|
||||
};
|
||||
|
||||
#define __elf_table(elf, name) ((elf)->name##_hash)
|
||||
#define __elf_bits(elf, name) ((elf)->name##_bits)
|
||||
|
||||
#define __elf_table_entry(elf, name, key) \
|
||||
__elf_table(elf, name)[hash_min(key, __elf_bits(elf, name))]
|
||||
|
||||
#define elf_list_entry(ptr, type, member) \
|
||||
({ \
|
||||
typeof(ptr) __ptr = (ptr); \
|
||||
__ptr ? container_of(__ptr, type, member) : NULL; \
|
||||
})
|
||||
|
||||
#define elf_hash_for_each_possible(elf, name, obj, member, key) \
|
||||
for (obj = elf_list_entry(__elf_table_entry(elf, name, key), typeof(*obj), member); \
|
||||
obj; \
|
||||
obj = elf_list_entry(obj->member.next, typeof(*(obj)), member))
|
||||
|
||||
struct elf *elf_open_read(const char *name, int flags);
|
||||
struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name);
|
||||
|
||||
|
|
@ -175,6 +200,7 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,
|
|||
struct symbol *sym,
|
||||
s64 addend);
|
||||
|
||||
int elf_write_symbol(struct elf *elf, struct symbol *sym);
|
||||
int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset,
|
||||
unsigned int len, const char *insn);
|
||||
|
||||
|
|
@ -186,10 +212,8 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset);
|
|||
struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset);
|
||||
struct symbol *find_symbol_by_name(const struct elf *elf, const char *name);
|
||||
struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name);
|
||||
void iterate_global_symbol_by_demangled_name(const struct elf *elf, const char *demangled_name,
|
||||
void (*process)(struct symbol *sym, void *data),
|
||||
void *data);
|
||||
struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset);
|
||||
struct symbol *find_symbol_containing_inclusive(const struct section *sec, unsigned long offset);
|
||||
int find_symbol_hole_containing(const struct section *sec, unsigned long offset);
|
||||
struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset);
|
||||
struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec,
|
||||
|
|
@ -276,11 +300,21 @@ static inline bool is_local_sym(struct symbol *sym)
|
|||
return sym->bind == STB_LOCAL;
|
||||
}
|
||||
|
||||
static inline bool is_alias_sym(struct symbol *sym)
|
||||
{
|
||||
return sym->alias != sym;
|
||||
}
|
||||
|
||||
static inline bool is_prefix_func(struct symbol *sym)
|
||||
{
|
||||
return sym->prefix;
|
||||
}
|
||||
|
||||
static inline bool is_cold_func(struct symbol *sym)
|
||||
{
|
||||
return sym->cold;
|
||||
}
|
||||
|
||||
static inline bool is_reloc_sec(struct section *sec)
|
||||
{
|
||||
return sec->sh.sh_type == SHT_RELA || sec->sh.sh_type == SHT_REL;
|
||||
|
|
@ -296,6 +330,11 @@ static inline bool is_text_sec(struct section *sec)
|
|||
return sec->sh.sh_flags & SHF_EXECINSTR;
|
||||
}
|
||||
|
||||
static inline bool is_rodata_sec(struct section *sec)
|
||||
{
|
||||
return sec->rodata;
|
||||
}
|
||||
|
||||
static inline bool sec_changed(struct section *sec)
|
||||
{
|
||||
return sec->_changed;
|
||||
|
|
@ -468,6 +507,16 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next)
|
|||
#define for_each_sym_continue(elf, sym) \
|
||||
list_for_each_entry_continue(sym, &elf->symbols, global_list)
|
||||
|
||||
#define for_each_sym_by_name(elf, _name, sym) \
|
||||
elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, \
|
||||
str_hash_demangled(_name)) \
|
||||
if (strcmp(sym->name, _name)) {} else
|
||||
|
||||
#define for_each_sym_by_demangled_name(elf, name, sym) \
|
||||
elf_hash_for_each_possible(elf, symbol_name, sym, name_hash, \
|
||||
str_hash(name)) \
|
||||
if (strcmp(sym->demangled_name, name)) {} else
|
||||
|
||||
#define rsec_next_reloc(rsec, reloc) \
|
||||
reloc_idx(reloc) < sec_num_entries(rsec) - 1 ? reloc + 1 : NULL
|
||||
|
||||
|
|
@ -491,10 +540,10 @@ static inline struct symbol *get_func_prefix(struct symbol *func)
|
|||
{
|
||||
struct symbol *prev;
|
||||
|
||||
if (!is_func_sym(func))
|
||||
if (!is_func_sym(func) || !func->offset)
|
||||
return NULL;
|
||||
|
||||
prev = sec_prev_sym(func);
|
||||
prev = find_func_containing(func->sec, func->offset - 1);
|
||||
if (prev && is_prefix_func(prev))
|
||||
return prev;
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ struct klp_reloc {
|
|||
u32 type;
|
||||
};
|
||||
|
||||
int cmd_klp_checksum(int argc, const char **argv);
|
||||
int cmd_klp_diff(int argc, const char **argv);
|
||||
int cmd_klp_post_link(int argc, const char **argv);
|
||||
|
||||
|
|
|
|||
|
|
@ -77,13 +77,13 @@ static inline char *offstr(struct section *sec, unsigned long offset)
|
|||
#define WARN_INSN(insn, format, ...) \
|
||||
({ \
|
||||
struct instruction *_insn = (insn); \
|
||||
if (!_insn->sym || !_insn->sym->warned) { \
|
||||
if (!insn_sym(_insn) || !insn_sym(_insn)->warned) { \
|
||||
WARN_FUNC(_insn->sec, _insn->offset, format, \
|
||||
##__VA_ARGS__); \
|
||||
BT_INSN(_insn, ""); \
|
||||
} \
|
||||
if (_insn->sym) \
|
||||
_insn->sym->warned = 1; \
|
||||
if (insn_sym(_insn)) \
|
||||
insn_sym(_insn)->warned = 1; \
|
||||
})
|
||||
|
||||
#define BT_INSN(insn, format, ...) \
|
||||
|
|
@ -109,7 +109,7 @@ static inline char *offstr(struct section *sec, unsigned long offset)
|
|||
#define ERROR_FUNC(sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__)
|
||||
#define ERROR_INSN(insn, format, ...) ERROR_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__)
|
||||
|
||||
extern bool debug;
|
||||
extern bool debug, debug_correlate, debug_clone;
|
||||
extern int indent;
|
||||
|
||||
static inline void unindent(int *unused) { indent--; }
|
||||
|
|
@ -130,32 +130,39 @@ static inline void unindent(int *unused) { indent--; }
|
|||
objname ? ": " : "", \
|
||||
##__VA_ARGS__)
|
||||
|
||||
#define dbg(args...) \
|
||||
#define dbg_checksum_insn(func, insn, checksum) \
|
||||
({ \
|
||||
if (unlikely(debug)) \
|
||||
__dbg(args); \
|
||||
})
|
||||
|
||||
#define __dbg_indent(format, ...) \
|
||||
({ \
|
||||
if (unlikely(debug)) \
|
||||
__dbg("%*s" format, indent * 8, "", ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define dbg_indent(args...) \
|
||||
int __cleanup(unindent) __dummy_##__COUNTER__; \
|
||||
__dbg_indent(args); \
|
||||
indent++
|
||||
|
||||
#define dbg_checksum(func, insn, checksum) \
|
||||
({ \
|
||||
if (unlikely(insn->sym && insn->sym->pfunc && \
|
||||
insn->sym->pfunc->debug_checksum)) { \
|
||||
if (unlikely(func->debug_checksum)) { \
|
||||
char *insn_off = offstr(insn->sec, insn->offset); \
|
||||
__dbg("checksum: %s %s %016llx", \
|
||||
__dbg("checksum: %s(): %s %016llx", \
|
||||
func->name, insn_off, (unsigned long long)checksum);\
|
||||
free(insn_off); \
|
||||
} \
|
||||
})
|
||||
|
||||
#define dbg_checksum_object(sym, offset, what, checksum) \
|
||||
({ \
|
||||
if (unlikely(sym->debug_checksum)) \
|
||||
__dbg("checksum: %s+0x%lx: %s %016llx", \
|
||||
sym->name, offset, what, \
|
||||
(unsigned long long)checksum); \
|
||||
})
|
||||
|
||||
#define dbg_correlate(args...) \
|
||||
({ \
|
||||
if (unlikely(debug_correlate)) \
|
||||
__dbg(args); \
|
||||
})
|
||||
|
||||
#define __dbg_clone(format, ...) \
|
||||
({ \
|
||||
if (unlikely(debug_clone)) \
|
||||
__dbg("%*s" format, indent * 8, "", ##__VA_ARGS__); \
|
||||
})
|
||||
|
||||
#define dbg_clone(args...) \
|
||||
int __cleanup(unindent) __dummy_##__COUNTER__; \
|
||||
__dbg_clone(args); \
|
||||
indent++
|
||||
|
||||
#endif /* _WARN_H */
|
||||
|
|
|
|||
347
tools/objtool/klp-checksum.c
Normal file
347
tools/objtool/klp-checksum.c
Normal file
|
|
@ -0,0 +1,347 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <string.h>
|
||||
#include <subcmd/parse-options.h>
|
||||
|
||||
#include <objtool/arch.h>
|
||||
#include <objtool/builtin.h>
|
||||
#include <objtool/check.h>
|
||||
#include <objtool/elf.h>
|
||||
#include <objtool/klp.h>
|
||||
#include <objtool/objtool.h>
|
||||
#include <objtool/warn.h>
|
||||
#include <objtool/checksum.h>
|
||||
|
||||
static int checksum_debug_init(struct objtool_file *file)
|
||||
{
|
||||
char *dup, *s;
|
||||
|
||||
if (!opts.debug_checksum)
|
||||
return 0;
|
||||
|
||||
dup = strdup(opts.debug_checksum);
|
||||
if (!dup) {
|
||||
ERROR_GLIBC("strdup");
|
||||
return -1;
|
||||
}
|
||||
|
||||
s = dup;
|
||||
while (*s) {
|
||||
bool found = false;
|
||||
struct symbol *sym;
|
||||
char *comma;
|
||||
|
||||
comma = strchr(s, ',');
|
||||
if (comma)
|
||||
*comma = '\0';
|
||||
|
||||
for_each_sym_by_name(file->elf, s, sym) {
|
||||
if (!is_func_sym(sym) && !is_object_sym(sym))
|
||||
continue;
|
||||
sym->debug_checksum = 1;
|
||||
found = true;
|
||||
}
|
||||
|
||||
if (!found)
|
||||
WARN("--debug-checksum: can't find '%s'", s);
|
||||
|
||||
if (!comma)
|
||||
break;
|
||||
|
||||
s = comma + 1;
|
||||
}
|
||||
|
||||
free(dup);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
|
||||
struct instruction *insn)
|
||||
{
|
||||
struct reloc *reloc = insn_reloc(file, insn);
|
||||
struct alternative *alt;
|
||||
unsigned long offset;
|
||||
struct symbol *sym;
|
||||
static bool in_alt;
|
||||
|
||||
if (insn->fake)
|
||||
return;
|
||||
|
||||
if (!reloc) {
|
||||
struct symbol *call_dest = insn_call_dest(insn);
|
||||
struct instruction *jump_dest = insn->jump_dest;
|
||||
|
||||
/*
|
||||
* For a jump/call non-relocated dest offset embedded in the
|
||||
* instruction, the offset may vary due to changes in
|
||||
* surrounding code. Just hash the opcode and a
|
||||
* position-independent representation of the destination.
|
||||
*/
|
||||
|
||||
if (call_dest || jump_dest) {
|
||||
unsigned char buf[16];
|
||||
size_t len;
|
||||
|
||||
len = arch_jump_opcode_bytes(file, insn, buf);
|
||||
__checksum_update_insn(func, insn, buf, len);
|
||||
|
||||
if (call_dest) {
|
||||
__checksum_update_insn(func, insn, call_dest->demangled_name,
|
||||
strlen(call_dest->demangled_name));
|
||||
|
||||
} else if (jump_dest) {
|
||||
struct symbol *dest_sym;
|
||||
unsigned long offset;
|
||||
|
||||
/*
|
||||
* use insn->_sym instead of insn_sym() here.
|
||||
* For alternative replacements, the latter
|
||||
* would give the function of the code being
|
||||
* replaced.
|
||||
*/
|
||||
dest_sym = jump_dest->_sym;
|
||||
if (!dest_sym)
|
||||
goto alts;
|
||||
|
||||
__checksum_update_insn(func, insn, dest_sym->demangled_name,
|
||||
strlen(dest_sym->demangled_name));
|
||||
|
||||
offset = jump_dest->offset - dest_sym->offset;
|
||||
__checksum_update_insn(func, insn, &offset, sizeof(offset));
|
||||
}
|
||||
|
||||
goto alts;
|
||||
}
|
||||
}
|
||||
|
||||
__checksum_update_insn(func, insn, insn->sec->data->d_buf + insn->offset, insn->len);
|
||||
|
||||
if (!reloc)
|
||||
goto alts;
|
||||
|
||||
sym = reloc->sym;
|
||||
offset = arch_insn_adjusted_addend(insn, reloc);
|
||||
|
||||
if (is_string_sec(sym->sec)) {
|
||||
char *str;
|
||||
|
||||
str = sym->sec->data->d_buf + sym->offset + offset;
|
||||
__checksum_update_insn(func, insn, str, strlen(str));
|
||||
goto alts;
|
||||
}
|
||||
|
||||
if (is_sec_sym(sym)) {
|
||||
sym = find_symbol_containing(reloc->sym->sec, offset);
|
||||
if (!sym)
|
||||
goto alts;
|
||||
|
||||
offset -= sym->offset;
|
||||
}
|
||||
|
||||
__checksum_update_insn(func, insn, sym->demangled_name,
|
||||
strlen(sym->demangled_name));
|
||||
__checksum_update_insn(func, insn, &offset, sizeof(offset));
|
||||
|
||||
alts:
|
||||
for (alt = insn->alts; alt; alt = alt->next) {
|
||||
struct alt_group *alt_group = alt->insn->alt_group;
|
||||
|
||||
/* Prevent __ex_table recursion, e.g. LOAD_SEGMENT() */
|
||||
if (in_alt)
|
||||
break;
|
||||
in_alt = true;
|
||||
|
||||
__checksum_update_insn(func, insn, &alt->type,
|
||||
sizeof(alt->type));
|
||||
|
||||
if (alt_group && alt_group->orig_group) {
|
||||
struct instruction *alt_insn;
|
||||
|
||||
__checksum_update_insn(func, insn, &alt_group->feature,sizeof(alt_group->feature));
|
||||
|
||||
for (alt_insn = alt->insn; alt_insn; alt_insn = next_insn_same_sec(file, alt_insn)) {
|
||||
checksum_update_insn(file, func, alt_insn);
|
||||
if (!alt_group->last_insn || alt_insn == alt_group->last_insn)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
checksum_update_insn(file, func, alt->insn);
|
||||
}
|
||||
|
||||
in_alt = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void checksum_update_object(struct objtool_file *file, struct symbol *sym)
|
||||
{
|
||||
struct reloc *reloc;
|
||||
|
||||
__checksum_update_object(sym, 0, "len", &sym->len, sizeof(sym->len));
|
||||
|
||||
if (sym->sec->data->d_buf)
|
||||
__checksum_update_object(sym, 0, "data",
|
||||
sym->sec->data->d_buf + sym->offset,
|
||||
sym->len);
|
||||
|
||||
sym_for_each_reloc(file->elf, sym, reloc) {
|
||||
unsigned long sym_offset = reloc_offset(reloc) - sym->offset;
|
||||
struct symbol *target = reloc->sym;
|
||||
s64 offset;
|
||||
|
||||
offset = reloc_addend(reloc);
|
||||
|
||||
if (is_string_sec(target->sec)) {
|
||||
char *str;
|
||||
|
||||
str = target->sec->data->d_buf + target->offset + offset;
|
||||
__checksum_update_object(sym, sym_offset,
|
||||
"reloc string", str, strlen(str));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (is_sec_sym(target)) {
|
||||
target = find_symbol_containing(reloc->sym->sec, offset);
|
||||
if (!target)
|
||||
continue;
|
||||
|
||||
offset -= target->offset;
|
||||
}
|
||||
|
||||
__checksum_update_object(sym, sym_offset, "reloc name",
|
||||
target->demangled_name,
|
||||
strlen(target->demangled_name));
|
||||
__checksum_update_object(sym, sym_offset, "reloc addend",
|
||||
&offset, sizeof(offset));
|
||||
}
|
||||
}
|
||||
|
||||
int calculate_checksums(struct objtool_file *file)
|
||||
{
|
||||
struct instruction *insn;
|
||||
struct symbol *sym;
|
||||
|
||||
if (checksum_debug_init(file))
|
||||
return -1;
|
||||
|
||||
for_each_sym(file->elf, sym) {
|
||||
|
||||
/*
|
||||
* Skip cold subfunctions and aliases: they share the
|
||||
* parent's checksum via func_for_each_insn() which
|
||||
* follows func->cfunc into the cold subfunction.
|
||||
*/
|
||||
if (is_cold_func(sym) || is_alias_sym(sym) || !sym->len ||
|
||||
!sym->sec || !sym->sec->data)
|
||||
continue;
|
||||
|
||||
if (is_func_sym(sym)) {
|
||||
checksum_init(sym);
|
||||
func_for_each_insn(file, sym, insn)
|
||||
checksum_update_insn(file, sym, insn);
|
||||
checksum_finish(sym);
|
||||
|
||||
} else if (is_object_sym(sym)) {
|
||||
checksum_init(sym);
|
||||
checksum_update_object(file, sym);
|
||||
checksum_finish(sym);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int create_sym_checksum_section(struct objtool_file *file)
|
||||
{
|
||||
struct section *sec;
|
||||
struct symbol *sym;
|
||||
unsigned int idx = 0;
|
||||
struct sym_checksum *checksum;
|
||||
size_t entsize = sizeof(struct sym_checksum);
|
||||
|
||||
sec = find_section_by_name(file->elf, ".discard.sym_checksum");
|
||||
if (sec) {
|
||||
if (!opts.dryrun)
|
||||
WARN("file already has .discard.sym_checksum section, skipping");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
for_each_sym(file->elf, sym)
|
||||
if (sym->csum.checksum)
|
||||
idx++;
|
||||
|
||||
sec = elf_create_section_pair(file->elf, ".discard.sym_checksum", entsize,
|
||||
idx, idx);
|
||||
if (!sec)
|
||||
return -1;
|
||||
|
||||
idx = 0;
|
||||
for_each_sym(file->elf, sym) {
|
||||
if (!sym->csum.checksum)
|
||||
continue;
|
||||
|
||||
if (!elf_init_reloc(file->elf, sec->rsec, idx, idx * entsize,
|
||||
sym, 0, R_TEXT64))
|
||||
return -1;
|
||||
|
||||
checksum = (struct sym_checksum *)sec->data->d_buf + idx;
|
||||
checksum->addr = 0; /* reloc */
|
||||
checksum->checksum = sym->csum.checksum;
|
||||
|
||||
mark_sec_changed(file->elf, sec, true);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char * const klp_checksum_usage[] = {
|
||||
"objtool klp checksum [<options>] file.o",
|
||||
NULL,
|
||||
};
|
||||
|
||||
int cmd_klp_checksum(int argc, const char **argv)
|
||||
{
|
||||
struct objtool_file *file;
|
||||
int ret;
|
||||
|
||||
const struct option options[] = {
|
||||
OPT_STRING(0, "debug-checksum", &opts.debug_checksum, "syms", "enable checksum debug output"),
|
||||
OPT_BOOLEAN(0, "dry-run", &opts.dryrun, "don't write modifications"),
|
||||
OPT_END(),
|
||||
};
|
||||
|
||||
argc = parse_options(argc, argv, options, klp_checksum_usage, 0);
|
||||
if (argc != 1)
|
||||
usage_with_options(klp_checksum_usage, options);
|
||||
|
||||
opts.checksum = true;
|
||||
|
||||
objname = argv[0];
|
||||
|
||||
file = objtool_open_read(objname);
|
||||
if (!file)
|
||||
return 1;
|
||||
|
||||
ret = decode_file(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = calculate_checksums(file);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
ret = create_sym_checksum_section(file);
|
||||
|
||||
out:
|
||||
free_insns(file);
|
||||
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!opts.dryrun && file->elf->changed && elf_write(file->elf))
|
||||
return 1;
|
||||
|
||||
return elf_close(file->elf);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -16,9 +16,6 @@
|
|||
#include <objtool/objtool.h>
|
||||
#include <objtool/warn.h>
|
||||
|
||||
bool debug;
|
||||
int indent;
|
||||
|
||||
static struct objtool_file file;
|
||||
|
||||
struct objtool_file *objtool_open_read(const char *filename)
|
||||
|
|
|
|||
|
|
@ -169,8 +169,8 @@ void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
|
|||
*/
|
||||
TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s for instruction at 0x%lx <%s+0x%lx>",
|
||||
alt_name,
|
||||
orig_insn->offset, orig_insn->sym->name,
|
||||
orig_insn->offset - orig_insn->sym->offset);
|
||||
orig_insn->offset, insn_sym(orig_insn)->name,
|
||||
orig_insn->offset - insn_sym(orig_insn)->offset);
|
||||
} else {
|
||||
TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s", alt_name);
|
||||
}
|
||||
|
|
@ -185,8 +185,8 @@ void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
|
|||
if (orig_insn->type == INSN_NOP) {
|
||||
suffix[0] = (orig_insn->len == 5) ? 'q' : '\0';
|
||||
TRACE_ADDR(orig_insn, "jmp%-3s %lx <%s+0x%lx>", suffix,
|
||||
alt_insn->offset, alt_insn->sym->name,
|
||||
alt_insn->offset - alt_insn->sym->offset);
|
||||
alt_insn->offset, insn_sym(alt_insn)->name,
|
||||
alt_insn->offset - insn_sym(alt_insn)->offset);
|
||||
} else {
|
||||
TRACE_ADDR(orig_insn, "nop%d", orig_insn->len);
|
||||
trace_depth--;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user