faddr2line 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #!/bin/bash
  2. #
  3. # Translate stack dump function offsets.
  4. #
  5. # addr2line doesn't work with KASLR addresses. This works similarly to
  6. # addr2line, but instead takes the 'func+0x123' format as input:
  7. #
  8. # $ ./scripts/faddr2line ~/k/vmlinux meminfo_proc_show+0x5/0x568
  9. # meminfo_proc_show+0x5/0x568:
  10. # meminfo_proc_show at fs/proc/meminfo.c:27
  11. #
  12. # If the address is part of an inlined function, the full inline call chain is
  13. # printed:
  14. #
  15. # $ ./scripts/faddr2line ~/k/vmlinux native_write_msr+0x6/0x27
  16. # native_write_msr+0x6/0x27:
  17. # arch_static_branch at arch/x86/include/asm/msr.h:121
  18. # (inlined by) static_key_false at include/linux/jump_label.h:125
  19. # (inlined by) native_write_msr at arch/x86/include/asm/msr.h:125
  20. #
  21. # The function size after the '/' in the input is optional, but recommended.
  22. # It's used to help disambiguate any duplicate symbol names, which can occur
  23. # rarely. If the size is omitted for a duplicate symbol then it's possible for
  24. # multiple code sites to be printed:
  25. #
  26. # $ ./scripts/faddr2line ~/k/vmlinux raw_ioctl+0x5
  27. # raw_ioctl+0x5/0x20:
  28. # raw_ioctl at drivers/char/raw.c:122
  29. #
  30. # raw_ioctl+0x5/0xb1:
  31. # raw_ioctl at net/ipv4/raw.c:876
  32. #
  33. # Multiple addresses can be specified on a single command line:
  34. #
  35. # $ ./scripts/faddr2line ~/k/vmlinux type_show+0x10/45 free_reserved_area+0x90
  36. # type_show+0x10/0x2d:
  37. # type_show at drivers/video/backlight/backlight.c:213
  38. #
  39. # free_reserved_area+0x90/0x123:
  40. # free_reserved_area at mm/page_alloc.c:6429 (discriminator 2)
  41. set -o errexit
  42. set -o nounset
  43. command -v awk >/dev/null 2>&1 || die "awk isn't installed"
  44. command -v readelf >/dev/null 2>&1 || die "readelf isn't installed"
  45. command -v addr2line >/dev/null 2>&1 || die "addr2line isn't installed"
  46. usage() {
  47. echo "usage: faddr2line <object file> <func+offset> <func+offset>..." >&2
  48. exit 1
  49. }
  50. warn() {
  51. echo "$1" >&2
  52. }
  53. die() {
  54. echo "ERROR: $1" >&2
  55. exit 1
  56. }
  57. # Try to figure out the source directory prefix so we can remove it from the
  58. # addr2line output. HACK ALERT: This assumes that start_kernel() is in
  59. # kernel/init.c! This only works for vmlinux. Otherwise it falls back to
  60. # printing the absolute path.
  61. find_dir_prefix() {
  62. local objfile=$1
  63. local start_kernel_addr=$(readelf -sW $objfile | awk '$8 == "start_kernel" {printf "0x%s", $2}')
  64. [[ -z $start_kernel_addr ]] && return
  65. local file_line=$(addr2line -e $objfile $start_kernel_addr)
  66. [[ -z $file_line ]] && return
  67. local prefix=${file_line%init/main.c:*}
  68. if [[ -z $prefix ]] || [[ $prefix = $file_line ]]; then
  69. return
  70. fi
  71. DIR_PREFIX=$prefix
  72. return 0
  73. }
  74. __faddr2line() {
  75. local objfile=$1
  76. local func_addr=$2
  77. local dir_prefix=$3
  78. local print_warnings=$4
  79. local func=${func_addr%+*}
  80. local offset=${func_addr#*+}
  81. offset=${offset%/*}
  82. local size=
  83. [[ $func_addr =~ "/" ]] && size=${func_addr#*/}
  84. if [[ -z $func ]] || [[ -z $offset ]] || [[ $func = $func_addr ]]; then
  85. warn "bad func+offset $func_addr"
  86. DONE=1
  87. return
  88. fi
  89. # Go through each of the object's symbols which match the func name.
  90. # In rare cases there might be duplicates.
  91. while read symbol; do
  92. local fields=($symbol)
  93. local sym_base=0x${fields[1]}
  94. local sym_size=${fields[2]}
  95. local sym_type=${fields[3]}
  96. # calculate the address
  97. local addr=$(($sym_base + $offset))
  98. if [[ -z $addr ]] || [[ $addr = 0 ]]; then
  99. warn "bad address: $sym_base + $offset"
  100. DONE=1
  101. return
  102. fi
  103. local hexaddr=0x$(printf %x $addr)
  104. # weed out non-function symbols
  105. if [[ $sym_type != "FUNC" ]]; then
  106. [[ $print_warnings = 1 ]] &&
  107. echo "skipping $func address at $hexaddr due to non-function symbol"
  108. continue
  109. fi
  110. # if the user provided a size, make sure it matches the symbol's size
  111. if [[ -n $size ]] && [[ $size -ne $sym_size ]]; then
  112. [[ $print_warnings = 1 ]] &&
  113. echo "skipping $func address at $hexaddr due to size mismatch ($size != $sym_size)"
  114. continue;
  115. fi
  116. # make sure the provided offset is within the symbol's range
  117. if [[ $offset -gt $sym_size ]]; then
  118. [[ $print_warnings = 1 ]] &&
  119. echo "skipping $func address at $hexaddr due to size mismatch ($offset > $sym_size)"
  120. continue
  121. fi
  122. # separate multiple entries with a blank line
  123. [[ $FIRST = 0 ]] && echo
  124. FIRST=0
  125. local hexsize=0x$(printf %x $sym_size)
  126. echo "$func+$offset/$hexsize:"
  127. addr2line -fpie $objfile $hexaddr | sed "s; $dir_prefix\(\./\)*; ;"
  128. DONE=1
  129. done < <(readelf -sW $objfile | awk -v f=$func '$8 == f {print}')
  130. }
  131. [[ $# -lt 2 ]] && usage
  132. objfile=$1
  133. [[ ! -f $objfile ]] && die "can't find objfile $objfile"
  134. shift
  135. DIR_PREFIX=supercalifragilisticexpialidocious
  136. find_dir_prefix $objfile
  137. FIRST=1
  138. while [[ $# -gt 0 ]]; do
  139. func_addr=$1
  140. shift
  141. # print any matches found
  142. DONE=0
  143. __faddr2line $objfile $func_addr $DIR_PREFIX 0
  144. # if no match was found, print warnings
  145. if [[ $DONE = 0 ]]; then
  146. __faddr2line $objfile $func_addr $DIR_PREFIX 1
  147. warn "no match for $func_addr"
  148. fi
  149. done