scripts/faddr2line: Fix "size mismatch" error
authorJosh Poimboeuf <jpoimboe@redhat.com>
Tue, 25 Oct 2016 14:51:11 +0000 (09:51 -0500)
committerIngo Molnar <mingo@kernel.org>
Tue, 25 Oct 2016 16:40:37 +0000 (18:40 +0200)
I'm not sure how we missed this problem before.  When I take a function
address and size from an oops and give it to faddr2line, it usually
complains about a size mismatch:

  $ scripts/faddr2line ~/k/vmlinux write_sysrq_trigger+0x51/0x60
  skipping write_sysrq_trigger address at 0xffffffff815731a1 due to size mismatch (0x60 != 83)
  no match for write_sysrq_trigger+0x51/0x60

The problem is caused by differences in how kallsyms and faddr2line
determine a function's size.

kallsyms calculates a function's size by parsing the output of 'nm -n'
and subtracting the next function's address from the current function's
address.  This means that nop instructions after the end of the function
are included in the size.

In contrast, faddr2line reads the size from the symbol table, which does
*not* include the ending nops in the function's size.

Change faddr2line to calculate the size from the output of 'nm -n' to be
consistent with kallsyms and oops outputs.

Signed-off-by: Josh Poimboeuf <jpoimboe@redhat.com>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Brian Gerst <brgerst@gmail.com>
Cc: Denys Vlasenko <dvlasenk@redhat.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Link: http://lkml.kernel.org/r/bd313ed7c4003f6b1fda63e825325c44a9d837de.1477405374.git.jpoimboe@redhat.com
Signed-off-by: Ingo Molnar <mingo@kernel.org>
scripts/faddr2line

index 450b332573397ee16ba343673f014ef954b37193..29df825d375c6eb4b5f2ac4d15b5f3df90ae38c4 100755 (executable)
@@ -105,9 +105,18 @@ __faddr2line() {
        # In rare cases there might be duplicates.
        while read symbol; do
                local fields=($symbol)
-               local sym_base=0x${fields[1]}
-               local sym_size=${fields[2]}
-               local sym_type=${fields[3]}
+               local sym_base=0x${fields[0]}
+               local sym_type=${fields[1]}
+               local sym_end=0x${fields[3]}
+
+               # calculate the size
+               local sym_size=$(($sym_end - $sym_base))
+               if [[ -z $sym_size ]] || [[ $sym_size -le 0 ]]; then
+                       warn "bad symbol size: base: $sym_base end: $sym_end"
+                       DONE=1
+                       return
+               fi
+               sym_size=0x$(printf %x $sym_size)
 
                # calculate the address
                local addr=$(($sym_base + $offset))
@@ -116,26 +125,26 @@ __faddr2line() {
                        DONE=1
                        return
                fi
-               local hexaddr=0x$(printf %x $addr)
+               addr=0x$(printf %x $addr)
 
                # weed out non-function symbols
-               if [[ $sym_type != "FUNC" ]]; then
+               if [[ $sym_type != t ]] && [[ $sym_type != T ]]; then
                        [[ $print_warnings = 1 ]] &&
-                               echo "skipping $func address at $hexaddr due to non-function symbol"
+                               echo "skipping $func address at $addr due to non-function symbol of type '$sym_type'"
                        continue
                fi
 
                # if the user provided a size, make sure it matches the symbol's size
                if [[ -n $size ]] && [[ $size -ne $sym_size ]]; then
                        [[ $print_warnings = 1 ]] &&
-                               echo "skipping $func address at $hexaddr due to size mismatch ($size != $sym_size)"
+                               echo "skipping $func address at $addr due to size mismatch ($size != $sym_size)"
                        continue;
                fi
 
                # make sure the provided offset is within the symbol's range
                if [[ $offset -gt $sym_size ]]; then
                        [[ $print_warnings = 1 ]] &&
-                               echo "skipping $func address at $hexaddr due to size mismatch ($offset > $sym_size)"
+                               echo "skipping $func address at $addr due to size mismatch ($offset > $sym_size)"
                        continue
                fi
 
@@ -143,12 +152,12 @@ __faddr2line() {
                [[ $FIRST = 0 ]] && echo
                FIRST=0
 
-               local hexsize=0x$(printf %x $sym_size)
-               echo "$func+$offset/$hexsize:"
-               addr2line -fpie $objfile $hexaddr | sed "s; $dir_prefix\(\./\)*; ;"
+               # pass real address to addr2line
+               echo "$func+$offset/$sym_size:"
+               addr2line -fpie $objfile $addr | sed "s; $dir_prefix\(\./\)*; ;"
                DONE=1
 
-       done < <(readelf -sW $objfile | awk -v f=$func '$8 == f {print}')
+       done < <(nm -n $objfile | awk -v fn=$func '$3 == fn { found=1; line=$0; start=$1; next } found == 1 { found=0; print line, $1 }')
 }
 
 [[ $# -lt 2 ]] && usage