Skip to content

Commit 292502c

Browse files
committed
Add scripts/dwarf2h
1 parent 4448654 commit 292502c

File tree

1 file changed

+281
-0
lines changed

1 file changed

+281
-0
lines changed

scripts/dwarf2h

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,281 @@
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

Comments
 (0)