Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf annotate: Fix jump target outside of function address range

If jump target is outside of function range, perf is not handling it
correctly. Especially when target address is lesser than function start
address, target offset will be negative. But, target address declared to
be unsigned, converts negative number into 2's complement. See below
example. Here target of 'jumpq' instruction at 34cf8 is 34ac0 which is
lesser than function start address(34cf0).

34ac0 - 34cf0 = -0x230 = 0xfffffffffffffdd0

Objdump output:

0000000000034cf0 <__sigaction>:
__GI___sigaction():
34cf0: lea -0x20(%rdi),%eax
34cf3: cmp -bashx1,%eax
34cf6: jbe 34d00 <__sigaction+0x10>
34cf8: jmpq 34ac0 <__GI___libc_sigaction>
34cfd: nopl (%rax)
34d00: mov 0x386161(%rip),%rax # 3bae68 <_DYNAMIC+0x2e8>
34d07: movl -bashx16,%fs:(%rax)
34d0e: mov -bashxffffffff,%eax
34d13: retq

perf annotate before applying patch:

__GI___sigaction /usr/lib64/libc-2.22.so
lea -0x20(%rdi),%eax
cmp -bashx1,%eax
v jbe 10
v jmpq fffffffffffffdd0
nop
10: mov _DYNAMIC+0x2e8,%rax
movl -bashx16,%fs:(%rax)
mov -bashxffffffff,%eax
retq

perf annotate after applying patch:

__GI___sigaction /usr/lib64/libc-2.22.so
lea -0x20(%rdi),%eax
cmp -bashx1,%eax
v jbe 10
^ jmpq 34ac0 <__GI___libc_sigaction>
nop
10: mov _DYNAMIC+0x2e8,%rax
movl -bashx16,%fs:(%rax)
mov -bashxffffffff,%eax
retq

Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Chris Riyder <chris.ryder@arm.com>
Cc: Kim Phillips <kim.phillips@arm.com>
Cc: Markus Trippelsdorf <markus@trippelsdorf.de>
Cc: Masami Hiramatsu <mhiramat@kernel.org>
Cc: Naveen N. Rao <naveen.n.rao@linux.vnet.ibm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Taeung Song <treeze.taeung@gmail.com>
Cc: linuxppc-dev@lists.ozlabs.org
Link: http://lkml.kernel.org/r/1480953407-7605-3-git-send-email-ravi.bangoria@linux.vnet.ibm.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Ravi Bangoria and committed by
Arnaldo Carvalho de Melo
e216874c 3ee2eb6d

+15 -9
+3 -2
tools/perf/ui/browsers/annotate.c
··· 215 215 ui_browser__set_color(browser, color); 216 216 if (dl->ins.ops && dl->ins.ops->scnprintf) { 217 217 if (ins__is_jump(&dl->ins)) { 218 - bool fwd = dl->ops.target.offset > (u64)dl->offset; 218 + bool fwd = dl->ops.target.offset > dl->offset; 219 219 220 220 ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR : 221 221 SLSMG_UARROW_CHAR); ··· 245 245 { 246 246 if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins) 247 247 || !disasm_line__has_offset(dl) 248 - || dl->ops.target.offset >= symbol__size(sym)) 248 + || dl->ops.target.offset < 0 249 + || dl->ops.target.offset >= (s64)symbol__size(sym)) 249 250 return false; 250 251 251 252 return true;
+9 -5
tools/perf/util/annotate.c
··· 230 230 else 231 231 ops->target.addr = strtoull(ops->raw, NULL, 16); 232 232 233 - if (s++ != NULL) 233 + if (s++ != NULL) { 234 234 ops->target.offset = strtoull(s, NULL, 16); 235 - else 236 - ops->target.offset = UINT64_MAX; 235 + ops->target.offset_avail = true; 236 + } else { 237 + ops->target.offset_avail = false; 238 + } 237 239 238 240 return 0; 239 241 } ··· 243 241 static int jump__scnprintf(struct ins *ins, char *bf, size_t size, 244 242 struct ins_operands *ops) 245 243 { 246 - if (!ops->target.addr) 244 + if (!ops->target.addr || ops->target.offset < 0) 247 245 return ins__raw_scnprintf(ins, bf, size, ops); 248 246 249 247 return scnprintf(bf, size, "%-6.6s %" PRIx64, ins->name, ops->target.offset); ··· 1211 1209 if (dl == NULL) 1212 1210 return -1; 1213 1211 1214 - if (dl->ops.target.offset == UINT64_MAX) 1212 + if (!disasm_line__has_offset(dl)) { 1215 1213 dl->ops.target.offset = dl->ops.target.addr - 1216 1214 map__rip_2objdump(map, sym->start); 1215 + dl->ops.target.offset_avail = true; 1216 + } 1217 1217 1218 1218 /* kcore has no symbols, so add the call target name */ 1219 1219 if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.name) {
+3 -2
tools/perf/util/annotate.h
··· 24 24 char *raw; 25 25 char *name; 26 26 u64 addr; 27 - u64 offset; 27 + s64 offset; 28 + bool offset_avail; 28 29 } target; 29 30 union { 30 31 struct { ··· 69 68 70 69 static inline bool disasm_line__has_offset(const struct disasm_line *dl) 71 70 { 72 - return dl->ops.target.offset != UINT64_MAX; 71 + return dl->ops.target.offset_avail; 73 72 } 74 73 75 74 void disasm_line__free(struct disasm_line *dl);