|
| 1 | +#!/bin/bash |
| 2 | + |
| 3 | +PROG=${0##*/} |
| 4 | + |
| 5 | +function usage { |
| 6 | + ((${1:-1})) && exec 1>&2 |
| 7 | + cat <<EOF |
| 8 | +Usage: $PROG [options] OBJECT... |
| 9 | + $PROG [options] -i DWARF_DUMP_FILE |
| 10 | + Prints C prototypes and other information about functions exported |
| 11 | + from OBJECT. The OBJECT files must be compiled to contain DWARF |
| 12 | + sections -- i.e., use -ggdb3. |
| 13 | +
|
| 14 | + (The second usage is intended for debugging.) |
| 15 | +
|
| 16 | + This is intended to be used for generating v-tables for plugins. |
| 17 | +
|
| 18 | + Output options: |
| 19 | + |
| 20 | + -B Output function stubs for exported functions |
| 21 | + -I Generate code to initialize a v-table |
| 22 | + -P Output prototypes for exported functions |
| 23 | + -n Include argument names in prototypes |
| 24 | + -T Output function pointer typedefs for exported functions |
| 25 | +
|
| 26 | + -s NAME Name of v-table struct |
| 27 | + -S Output struct definition (requires -s NAME) |
| 28 | + -M Output macros that indirect through jq_state v-table |
| 29 | + (requires -s NAME) |
| 30 | +
|
| 31 | + Function symbol filter options: |
| 32 | +
|
| 33 | + -p GLOB Only include functions whose names match GLOB |
| 34 | + (may be given multiple times) |
| 35 | + -x GLOB Exclude functions matching GLOB |
| 36 | + (may be given multiple times) |
| 37 | + -f FILE Exclude functions that appear to be in a v-table in FILE |
| 38 | + (useful when updating a v-table so as to generate additions |
| 39 | + to the v-table that go at the end) |
| 40 | + (may be given multiple times) |
| 41 | +
|
| 42 | + Debug options: |
| 43 | +
|
| 44 | + -i FILE Use the dump of DWARF data in FILE, made with llvm-dwarfdump |
| 45 | + -v Trace this script |
| 46 | +
|
| 47 | + At least one of -P, -T, -S, or -s must be given. |
| 48 | +EOF |
| 49 | + exit ${1:-1} |
| 50 | +} |
| 51 | + |
| 52 | +(($#==0)) && usage |
| 53 | +declare -A exclude |
| 54 | +pats=() |
| 55 | +xpats=() |
| 56 | +dwarf_in= |
| 57 | +struct_name= |
| 58 | +stubs=false |
| 59 | +debug=false |
| 60 | +struct=false |
| 61 | +macros=false |
| 62 | +typedefs=false |
| 63 | +prototypes=false |
| 64 | +initializers=false |
| 65 | +include_arg_names=false |
| 66 | +while getopts +:IMPSTf:bdhi:np:s:vx: opt; do |
| 67 | + case "$opt" in |
| 68 | + I) initializers=true;; |
| 69 | + M) macros=true;; |
| 70 | + P) prototypes=true;; |
| 71 | + S) struct=true;; |
| 72 | + T) typedefs=true;; |
| 73 | + f) for fname in $( |
| 74 | + grep '^ \([^ ]*\)_f \1;' "$OPTARG" | cut -d' ' -f4 | cut -d';' -f1; |
| 75 | + grep ')(' "$OPTARG" | cut -d'(' -f2 | cut -d'*' -f2 | cut -d')' -f1 |
| 76 | + ); do |
| 77 | + exclude[$fname]=true |
| 78 | + done ;; |
| 79 | + b) stubs=true;; |
| 80 | + d) debug=true;; |
| 81 | + h) usage 0;; |
| 82 | + i) dwarf_in=$OPTARG;; |
| 83 | + n) include_arg_names=true;; |
| 84 | + p) pats+=("$OPTARG");; |
| 85 | + s) struct_name=$OPTARG;; |
| 86 | + v) set -vx;; |
| 87 | + x) xpats+=("$OPTARG");; |
| 88 | + *) usage;; |
| 89 | + esac |
| 90 | +done |
| 91 | +shift $((OPTIND - 1)) |
| 92 | +(($#==0)) && [[ -z $dwarf_in ]] && usage |
| 93 | +(($#)) && [[ -n $dwarf_in ]] && usage |
| 94 | + |
| 95 | +! $stubs && ! $typedefs && ! $prototypes && [[ -z $struct_name ]] && usage |
| 96 | + |
| 97 | +$struct && [[ -z $struct_name ]] && usage |
| 98 | +$macros && [[ -z $struct_name ]] && usage |
| 99 | + |
| 100 | +eof=false |
| 101 | +function read1 { |
| 102 | + if ! read a b; then |
| 103 | + $debug && printf 1>&2 'EOF\n' |
| 104 | + eof=true |
| 105 | + return 1 |
| 106 | + fi |
| 107 | + $debug && printf 2>&2 'READ: a=%s, b=%s\n' "$a" "$b" |
| 108 | + return 0 |
| 109 | +} |
| 110 | + |
| 111 | +function finish_function { |
| 112 | + if $skip; then |
| 113 | + printf 'WARNING: Excluding function with function-valued return type or argument, %s()\n' "$fname" 1>&2 |
| 114 | + return 0 |
| 115 | + fi |
| 116 | + [[ -n ${fnames[$fname]} ]] && return 0 # probably static inline |
| 117 | + $exported || return 0 # static |
| 118 | + if ((${#pats[@]})); then |
| 119 | + match=false |
| 120 | + for pat in "${pats[@]}"; do |
| 121 | + if [[ $fname = $pat ]]; then |
| 122 | + match=true |
| 123 | + break |
| 124 | + fi |
| 125 | + done |
| 126 | + $match || return 0 |
| 127 | + fi |
| 128 | + if ((${#xpats[@]})); then |
| 129 | + match=false |
| 130 | + for pat in "${xpats[@]}"; do |
| 131 | + if [[ $fname = $pat ]]; then |
| 132 | + match=true |
| 133 | + break |
| 134 | + fi |
| 135 | + done |
| 136 | + $match && return 0 |
| 137 | + fi |
| 138 | + [[ -z $fname ]] && return 0 |
| 139 | + [[ -n ${exclude[$fname]} ]] && return 0 |
| 140 | + fnames[$fname]=$fname |
| 141 | + IFS=, |
| 142 | + $typedefs && printf 'typedef %s (*%s_f)(%s);\n' "$rettype" "$fname" "${argtypes[*]}" |
| 143 | + $prototypes && $include_arg_names && |
| 144 | + printf '%s %s(%s);\n' "$rettype" "$fname" "${args[*]}" |
| 145 | + $prototypes && ! $include_arg_names && |
| 146 | + printf '%s %s(%s);\n' "$rettype" "$fname" "${argtypes[*]}" |
| 147 | + $stubs && printf '%s\n%s(%s)\n{\n}\n' "$rettype" "$fname" "${args[*]}" |
| 148 | + if $typedefs; then |
| 149 | + fields+=("${fname}_f ${fname}") |
| 150 | + else |
| 151 | + fields+=("$rettype (*${fname})(${argtypes[*]})") |
| 152 | + fi |
| 153 | + IFS=$OIFS |
| 154 | +} |
| 155 | + |
| 156 | +function parse_function { |
| 157 | + local skip=$skip |
| 158 | + |
| 159 | + args=() |
| 160 | + argnames=() |
| 161 | + argtypes=() |
| 162 | + |
| 163 | + [[ $a = 0x* && $b = DW_TAG_subprogram ]] || exit 5 |
| 164 | + fname= |
| 165 | + rettype=void |
| 166 | + exported=false |
| 167 | + |
| 168 | + # Extract function name and type |
| 169 | + while read1; do |
| 170 | + # Functions with no arguments do not get a NULL to terminate their |
| 171 | + # DWARF dump. In this case we will recurse. Hope we don't blow |
| 172 | + # the stack. |
| 173 | + if [[ $a = 0x* && $b = DW_TAG_subprogram ]]; then |
| 174 | + $skip || finish_function |
| 175 | + parse_function |
| 176 | + $skip || finish_function |
| 177 | + return $? |
| 178 | + fi |
| 179 | + |
| 180 | + [[ $a = 0x* ]] && break |
| 181 | + case "$a" in |
| 182 | + DW_AT_external) [[ $b = *true* ]] && exported=true;; |
| 183 | + DW_AT_name) |
| 184 | + b=${b%\"*} |
| 185 | + fname=${b#*\"};; |
| 186 | + DW_AT_type) |
| 187 | + b=${b%\"*} |
| 188 | + rettype=${b#*\"} |
| 189 | + [[ $rettype = [*]* ]] && rettype="void $rettype";; |
| 190 | + *) true;; |
| 191 | + esac |
| 192 | + done |
| 193 | + skip=false |
| 194 | + [[ $rettype = *subroutine* ]] && skip=true |
| 195 | + if [[ $b = DW_TAG_unspecified_parameters ]]; then |
| 196 | + argtypes+=(...) |
| 197 | + continue |
| 198 | + fi |
| 199 | + while [[ $b = DW_TAG_formal_parameter || $b = DW_TAG_unspecified_parameters ]]; do |
| 200 | + argname= |
| 201 | + argtype=void |
| 202 | + while read1; do |
| 203 | + if [[ $a = 0x* && $b = DW_TAG_subprogram ]]; then |
| 204 | + $skip || finish_function |
| 205 | + parse_function |
| 206 | + $skip || finish_function |
| 207 | + return $? |
| 208 | + fi |
| 209 | + [[ $a = 0x* ]] && break |
| 210 | + case "$a" in |
| 211 | + DW_AT_name) |
| 212 | + b=${b%\"*} |
| 213 | + argname=${b#*\"} |
| 214 | + argnames+=("$argname");; |
| 215 | + DW_AT_type) |
| 216 | + b=${b%\"*} |
| 217 | + argtype=${b#*\"} |
| 218 | + [[ $argtype = [*]* ]] && argtype="void $argtype" |
| 219 | + [[ $argtype = va_list ]] && argtype=... |
| 220 | + [[ $argtype = __va_list_tag\* ]] && argtype=va_list |
| 221 | + [[ $argtype = *subroutine* ]] && skip=true |
| 222 | + argtypes+=("$argtype");; |
| 223 | + *) true;; |
| 224 | + esac |
| 225 | + done |
| 226 | + args+=("$argtype $argname") |
| 227 | + done |
| 228 | + if ! $eof; then |
| 229 | + while [[ $a != 0x* || $b != NULL || $b != DW_TAG_subprogram ]]; do |
| 230 | + read1 || break |
| 231 | + if [[ $a = 0x* && $b = DW_TAG_subprogram ]]; then |
| 232 | + finish_function |
| 233 | + parse_function |
| 234 | + return $? |
| 235 | + fi |
| 236 | + done |
| 237 | + fi |
| 238 | + finish_function |
| 239 | +} |
| 240 | + |
| 241 | +declare -A fnames |
| 242 | +fields=() |
| 243 | +OIFS=$IFS |
| 244 | +a= |
| 245 | +b= |
| 246 | +while read1; do |
| 247 | + [[ $a = 0x* && $b = DW_TAG_subprogram ]] || continue |
| 248 | + parse_function |
| 249 | +done < <(if [[ -n $dwarf_in ]]; then cat "$dwarf_in"; else llvm-dwarfdump-6.0 "$@"; fi) |
| 250 | + |
| 251 | +if $struct; then |
| 252 | + printf 'struct %s {\n' "$struct_name" |
| 253 | + if ((${#exclude[@]} == 0)); then |
| 254 | + printf ' %s;\n' "${fields[@]}" |
| 255 | + else |
| 256 | + # Print only fields for functions not listed in the FILE given via -f FILE. |
| 257 | + # |
| 258 | + # The developer will have to edit that file and add the new fields (and |
| 259 | + # typedefs) themselves. |
| 260 | + for field in "${fields[@]}"; do |
| 261 | + fname=$(echo "$field" | grep '^\([^ ]*\)_f \1' | cut -d' ' -f2 | cut -d';' -f1; |
| 262 | + echo "$field" | grep ')(' | cut -d'(' -f2 | cut -d'*' -f2 | cut -d')' -f1) |
| 263 | + [[ -n ${exclude[$fname]} ]] && continue |
| 264 | + printf ' %s;\n' "$field" |
| 265 | + done |
| 266 | + fi |
| 267 | + printf '};\n' |
| 268 | +fi |
| 269 | + |
| 270 | +if $macros; then |
| 271 | + for fname in "${!fnames[@]}"; do |
| 272 | + [[ -n ${exclude[$fname]} ]] && continue |
| 273 | + printf '#define %s ((*(struct %s **)jq)->%s)\n' "$fname" "$struct_name" "$fname" |
| 274 | + done |
| 275 | +fi |
| 276 | + |
| 277 | +if $initializers; then |
| 278 | + for fname in "${!fnames[@]}"; do |
| 279 | + printf 'vtable->%s = %s;\n' "$fname" "$fname" |
| 280 | + done |
| 281 | +fi |
0 commit comments