Objtool changes for v6.11:
- Fix bug that caused objtool to confuse certain memory ops added by KASAN instrumentation as stack accesses - Various faddr2line optimizations - Improve error messages Signed-off-by: Ingo Molnar <mingo@kernel.org> -----BEGIN PGP SIGNATURE----- iQJFBAABCgAvFiEEBpT5eoXrXCwVQwEKEnMQ0APhK1gFAmaVsrARHG1pbmdvQGtl cm5lbC5vcmcACgkQEnMQ0APhK1jvlhAAj9I7UXMMWRq+lOtYp/nUNpq4XxETeEbx IvF6b1TVbD2M8+a5y8Wo9cy+UL1i781oHaM+cggDWMnvt6IV6dUIbb72cLjtWYzx bLD1NDw4exrSFrlbn2ri8U1dkpHHmxoSLHmFdcpKNCV5NLEtFPSj9Bmd6DCmrQ2U kbgAAjtc2XWx2ObXtgId8MfQso1/xzX6u2KhduQCEa/vuAzEUy7ppVXC5lwpKaVY MSo3vzC2kt5HlZirsp4ALiHkmJgxytoXVtzzKKnsxyLeG84GcUQy8aQndCevOhqU nPI3BCyDs0o1DYNWj/qP5d1HnPdHKPptMZtPlmg6ukBNqIuFXSGwaLdWp06dTQ+6 Q7COfVwG24H6QQD8aixvBDCIVGbzDJRF6DCL1zZWVtV1faxL1iM+vjOsh5C7l5A4 ZuaiZCa79JT3nQ6NHD5k5rv++FWEcC2dpwRBgSZU8HbfCtDIEEKB6ZYA/YVkt0Ae hAfkPX6j4Cl4MuFS2VmuPeaEmnciuTUHq65MGM1PxW7qaYgDDdT58T3Kt6SFrPy0 8KOeVKUMI7sRNDOcIIRbsoSzQ4xtXSeurX6zNsyj17L9O8QxzMCTLHi1tZGSPd9g VtKNkIHBNOch6CAcE2tgntzJ/0c0NTyN8F7Xh7PK2o981Kmj/Ic0r1TVMdY7PCm3 riLimaXEhJ8= =RwK9 -----END PGP SIGNATURE----- Merge tag 'objtool-core-2024-07-16' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull objtool updates from Ingo Molnar: - Fix bug that caused objtool to confuse certain memory ops added by KASAN instrumentation as stack accesses - Various faddr2line optimizations - Improve error messages * tag 'objtool-core-2024-07-16' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: objtool/x86: objtool can confuse memory and stack access objtool: Use "action" in error message to be consistent with help scripts/faddr2line: Check only two symbols when calculating symbol size scripts/faddr2line: Remove call to addr2line from find_dir_prefix() scripts/faddr2line: Invoke addr2line as a single long-running process scripts/faddr2line: Pass --addresses argument to addr2line scripts/faddr2line: Check vmlinux only once scripts/faddr2line: Combine three readelf calls into one scripts/faddr2line: Reduce number of readelf calls to three
This commit is contained in:
commit
0c182ac2eb
@ -85,15 +85,17 @@ command -v ${ADDR2LINE} >/dev/null 2>&1 || die "${ADDR2LINE} isn't installed"
|
||||
# init/main.c! This only works for vmlinux. Otherwise it falls back to
|
||||
# printing the absolute path.
|
||||
find_dir_prefix() {
|
||||
local objfile=$1
|
||||
|
||||
local start_kernel_addr=$(${READELF} --symbols --wide $objfile | sed 's/\[.*\]//' |
|
||||
local start_kernel_addr=$(echo "${ELF_SYMS}" | sed 's/\[.*\]//' |
|
||||
${AWK} '$8 == "start_kernel" {printf "0x%s", $2}')
|
||||
[[ -z $start_kernel_addr ]] && return
|
||||
|
||||
local file_line=$(${ADDR2LINE} -e $objfile $start_kernel_addr)
|
||||
[[ -z $file_line ]] && return
|
||||
run_addr2line ${start_kernel_addr} ""
|
||||
[[ -z $ADDR2LINE_OUT ]] && return
|
||||
|
||||
local file_line=${ADDR2LINE_OUT#* at }
|
||||
if [[ -z $file_line ]] || [[ $file_line = $ADDR2LINE_OUT ]]; then
|
||||
return
|
||||
fi
|
||||
local prefix=${file_line%init/main.c:*}
|
||||
if [[ -z $prefix ]] || [[ $prefix = $file_line ]]; then
|
||||
return
|
||||
@ -103,6 +105,71 @@ find_dir_prefix() {
|
||||
return 0
|
||||
}
|
||||
|
||||
run_readelf() {
|
||||
local objfile=$1
|
||||
local out=$(${READELF} --file-header --section-headers --symbols --wide $objfile)
|
||||
|
||||
# This assumes that readelf first prints the file header, then the section headers, then the symbols.
|
||||
# Note: It seems that GNU readelf does not prefix section headers with the "There are X section headers"
|
||||
# line when multiple options are given, so let's also match with the "Section Headers:" line.
|
||||
ELF_FILEHEADER=$(echo "${out}" | sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/q;p')
|
||||
ELF_SECHEADERS=$(echo "${out}" | sed -n '/There are [0-9]* section headers, starting at offset\|Section Headers:/,$p' | sed -n '/Symbol table .* contains [0-9]* entries:/q;p')
|
||||
ELF_SYMS=$(echo "${out}" | sed -n '/Symbol table .* contains [0-9]* entries:/,$p')
|
||||
}
|
||||
|
||||
check_vmlinux() {
|
||||
# vmlinux uses absolute addresses in the section table rather than
|
||||
# section offsets.
|
||||
IS_VMLINUX=0
|
||||
local file_type=$(echo "${ELF_FILEHEADER}" |
|
||||
${AWK} '$1 == "Type:" { print $2; exit }')
|
||||
if [[ $file_type = "EXEC" ]] || [[ $file_type == "DYN" ]]; then
|
||||
IS_VMLINUX=1
|
||||
fi
|
||||
}
|
||||
|
||||
init_addr2line() {
|
||||
local objfile=$1
|
||||
|
||||
check_vmlinux
|
||||
|
||||
ADDR2LINE_ARGS="--functions --pretty-print --inlines --addresses --exe=$objfile"
|
||||
if [[ $IS_VMLINUX = 1 ]]; then
|
||||
# If the executable file is vmlinux, we don't pass section names to
|
||||
# addr2line, so we can launch it now as a single long-running process.
|
||||
coproc ADDR2LINE_PROC (${ADDR2LINE} ${ADDR2LINE_ARGS})
|
||||
fi
|
||||
}
|
||||
|
||||
run_addr2line() {
|
||||
local addr=$1
|
||||
local sec_name=$2
|
||||
|
||||
if [[ $IS_VMLINUX = 1 ]]; then
|
||||
# We send to the addr2line process: (1) the address, then (2) a sentinel
|
||||
# value, i.e., something that can't be interpreted as a valid address
|
||||
# (i.e., ","). This causes addr2line to write out: (1) the answer for
|
||||
# our address, then (2) either "?? ??:0" or "0x0...0: ..." (if
|
||||
# using binutils' addr2line), or "," (if using LLVM's addr2line).
|
||||
echo ${addr} >& "${ADDR2LINE_PROC[1]}"
|
||||
echo "," >& "${ADDR2LINE_PROC[1]}"
|
||||
local first_line
|
||||
read -r first_line <& "${ADDR2LINE_PROC[0]}"
|
||||
ADDR2LINE_OUT=$(echo "${first_line}" | sed 's/^0x[0-9a-fA-F]*: //')
|
||||
while read -r line <& "${ADDR2LINE_PROC[0]}"; do
|
||||
if [[ "$line" == "?? ??:0" ]] || [[ "$line" == "," ]] || [[ $(echo "$line" | ${GREP} "^0x00*: ") ]]; then
|
||||
break
|
||||
fi
|
||||
ADDR2LINE_OUT+=$'\n'$(echo "$line" | sed 's/^0x[0-9a-fA-F]*: //')
|
||||
done
|
||||
else
|
||||
# Run addr2line as a single invocation.
|
||||
local sec_arg
|
||||
[[ -z $sec_name ]] && sec_arg="" || sec_arg="--section=${sec_name}"
|
||||
ADDR2LINE_OUT=$(${ADDR2LINE} ${ADDR2LINE_ARGS} ${sec_arg} ${addr} | sed 's/^0x[0-9a-fA-F]*: //')
|
||||
fi
|
||||
}
|
||||
|
||||
__faddr2line() {
|
||||
local objfile=$1
|
||||
local func_addr=$2
|
||||
@ -113,8 +180,6 @@ __faddr2line() {
|
||||
local func_offset=${func_addr#*+}
|
||||
func_offset=${func_offset%/*}
|
||||
local user_size=
|
||||
local file_type
|
||||
local is_vmlinux=0
|
||||
[[ $func_addr =~ "/" ]] && user_size=${func_addr#*/}
|
||||
|
||||
if [[ -z $sym_name ]] || [[ -z $func_offset ]] || [[ $sym_name = $func_addr ]]; then
|
||||
@ -123,14 +188,6 @@ __faddr2line() {
|
||||
return
|
||||
fi
|
||||
|
||||
# vmlinux uses absolute addresses in the section table rather than
|
||||
# section offsets.
|
||||
local file_type=$(${READELF} --file-header $objfile |
|
||||
${AWK} '$1 == "Type:" { print $2; exit }')
|
||||
if [[ $file_type = "EXEC" ]] || [[ $file_type == "DYN" ]]; then
|
||||
is_vmlinux=1
|
||||
fi
|
||||
|
||||
# Go through each of the object's symbols which match the func name.
|
||||
# In rare cases there might be duplicates, in which case we print all
|
||||
# matches.
|
||||
@ -143,8 +200,7 @@ __faddr2line() {
|
||||
local sec_name
|
||||
|
||||
# Get the section size:
|
||||
sec_size=$(${READELF} --section-headers --wide $objfile |
|
||||
sed 's/\[ /\[/' |
|
||||
sec_size=$(echo "${ELF_SECHEADERS}" | sed 's/\[ /\[/' |
|
||||
${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print "0x" $6; exit }')
|
||||
|
||||
if [[ -z $sec_size ]]; then
|
||||
@ -154,8 +210,7 @@ __faddr2line() {
|
||||
fi
|
||||
|
||||
# Get the section name:
|
||||
sec_name=$(${READELF} --section-headers --wide $objfile |
|
||||
sed 's/\[ /\[/' |
|
||||
sec_name=$(echo "${ELF_SECHEADERS}" | sed 's/\[ /\[/' |
|
||||
${AWK} -v sec=$sym_sec '$1 == "[" sec "]" { print $2; exit }')
|
||||
|
||||
if [[ -z $sec_name ]]; then
|
||||
@ -197,7 +252,7 @@ __faddr2line() {
|
||||
found=2
|
||||
break
|
||||
fi
|
||||
done < <(${READELF} --symbols --wide $objfile | sed 's/\[.*\]//' | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2)
|
||||
done < <(echo "${ELF_SYMS}" | sed 's/\[.*\]//' | ${AWK} -v sec=$sym_sec '$7 == sec' | sort --key=2 | ${GREP} -A1 --no-group-separator " ${sym_name}$")
|
||||
|
||||
if [[ $found = 0 ]]; then
|
||||
warn "can't find symbol: sym_name: $sym_name sym_sec: $sym_sec sym_addr: $sym_addr sym_elf_size: $sym_elf_size"
|
||||
@ -249,9 +304,8 @@ __faddr2line() {
|
||||
|
||||
# Pass section address to addr2line and strip absolute paths
|
||||
# from the output:
|
||||
local args="--functions --pretty-print --inlines --exe=$objfile"
|
||||
[[ $is_vmlinux = 0 ]] && args="$args --section=$sec_name"
|
||||
local output=$(${ADDR2LINE} $args $addr | sed "s; $dir_prefix\(\./\)*; ;")
|
||||
run_addr2line $addr $sec_name
|
||||
local output=$(echo "${ADDR2LINE_OUT}" | sed "s; $dir_prefix\(\./\)*; ;")
|
||||
[[ -z $output ]] && continue
|
||||
|
||||
# Default output (non --list):
|
||||
@ -278,7 +332,7 @@ __faddr2line() {
|
||||
|
||||
DONE=1
|
||||
|
||||
done < <(${READELF} --symbols --wide $objfile | sed 's/\[.*\]//' | ${AWK} -v fn=$sym_name '$8 == fn')
|
||||
done < <(echo "${ELF_SYMS}" | sed 's/\[.*\]//' | ${AWK} -v fn=$sym_name '$8 == fn')
|
||||
}
|
||||
|
||||
[[ $# -lt 2 ]] && usage
|
||||
@ -291,10 +345,14 @@ LIST=0
|
||||
[[ ! -f $objfile ]] && die "can't find objfile $objfile"
|
||||
shift
|
||||
|
||||
${READELF} --section-headers --wide $objfile | ${GREP} -q '\.debug_info' || die "CONFIG_DEBUG_INFO not enabled"
|
||||
run_readelf $objfile
|
||||
|
||||
echo "${ELF_SECHEADERS}" | ${GREP} -q '\.debug_info' || die "CONFIG_DEBUG_INFO not enabled"
|
||||
|
||||
init_addr2line $objfile
|
||||
|
||||
DIR_PREFIX=supercalifragilisticexpialidocious
|
||||
find_dir_prefix $objfile
|
||||
find_dir_prefix
|
||||
|
||||
FIRST=1
|
||||
while [[ $# -gt 0 ]]; do
|
||||
|
@ -125,8 +125,14 @@ bool arch_pc_relative_reloc(struct reloc *reloc)
|
||||
#define is_RIP() ((modrm_rm & 7) == CFI_BP && modrm_mod == 0)
|
||||
#define have_SIB() ((modrm_rm & 7) == CFI_SP && mod_is_mem())
|
||||
|
||||
/*
|
||||
* Check the ModRM register. If there is a SIB byte then check with
|
||||
* the SIB base register. But if the SIB base is 5 (i.e. CFI_BP) and
|
||||
* ModRM mod is 0 then there is no base register.
|
||||
*/
|
||||
#define rm_is(reg) (have_SIB() ? \
|
||||
sib_base == (reg) && sib_index == CFI_SP : \
|
||||
sib_base == (reg) && sib_index == CFI_SP && \
|
||||
(sib_base != CFI_BP || modrm_mod != 0) : \
|
||||
modrm_rm == (reg))
|
||||
|
||||
#define rm_is_mem(reg) (mod_is_mem() && !is_RIP() && rm_is(reg))
|
||||
|
@ -144,7 +144,7 @@ static bool opts_valid(void)
|
||||
opts.static_call ||
|
||||
opts.uaccess) {
|
||||
if (opts.dump_orc) {
|
||||
ERROR("--dump can't be combined with other options");
|
||||
ERROR("--dump can't be combined with other actions");
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -159,7 +159,7 @@ static bool opts_valid(void)
|
||||
if (opts.dump_orc)
|
||||
return true;
|
||||
|
||||
ERROR("At least one command required");
|
||||
ERROR("At least one action required");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user