]>
Commit | Line | Data |
---|---|---|
1 | #! /bin/sh | |
2 | ||
3 | # Add a .gdb_index section to a file. | |
4 | ||
5 | # Copyright (C) 2010-2025 Free Software Foundation, Inc. | |
6 | # This program is free software; you can redistribute it and/or modify | |
7 | # it under the terms of the GNU General Public License as published by | |
8 | # the Free Software Foundation; either version 3 of the License, or | |
9 | # (at your option) any later version. | |
10 | # | |
11 | # This program is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | # GNU General Public License for more details. | |
15 | # | |
16 | # You should have received a copy of the GNU General Public License | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | ||
19 | # This program assumes gdb and objcopy are in $PATH. | |
20 | # If not, or you want others, pass the following in the environment | |
21 | GDB=${GDB:=gdb} | |
22 | OBJCOPY=${OBJCOPY:=objcopy} | |
23 | READELF=${READELF:=readelf} | |
24 | ||
25 | PKGVERSION="@PKGVERSION@" | |
26 | VERSION="@VERSION@" | |
27 | ||
28 | myname="${0##*/}" | |
29 | ||
30 | print_usage() { | |
31 | prefix="Usage: $myname" | |
32 | echo "$prefix [-h|--help] [-v|--version] [--dwarf-5] FILENAME" | |
33 | } | |
34 | ||
35 | print_try_help() { | |
36 | echo "Try '$myname --help' for more information." | |
37 | } | |
38 | ||
39 | print_help() { | |
40 | print_usage | |
41 | echo | |
42 | echo "Add a .gdb_index section to FILENAME to facilitate faster debug" | |
43 | echo "information loading by GDB." | |
44 | echo | |
45 | echo " -h, --help Print this message then exit." | |
46 | echo " -v, --version Print version information then exit." | |
47 | echo " --dwarf-5 Add the DWARF-5 style .debug_names section" | |
48 | echo " instead of .gdb_index." | |
49 | } | |
50 | ||
51 | print_version() { | |
52 | echo "GNU gdb-add-index (${PKGVERSION}) ${VERSION}" | |
53 | } | |
54 | ||
55 | dwarf5="" | |
56 | ||
57 | # Parse options. | |
58 | until | |
59 | opt=$1 | |
60 | case ${opt} in | |
61 | --dwarf-5 | -dwarf-5) | |
62 | dwarf5="-dwarf-5" | |
63 | ;; | |
64 | ||
65 | --help | -help | -h) | |
66 | print_help | |
67 | exit 0 | |
68 | ;; | |
69 | ||
70 | --version | -version | -v) | |
71 | print_version | |
72 | exit 0 | |
73 | ;; | |
74 | ||
75 | -?*) | |
76 | print_try_help 1>&2 | |
77 | exit 2 | |
78 | ;; | |
79 | ||
80 | *) | |
81 | # No arguments remaining. | |
82 | ;; | |
83 | esac | |
84 | # Break from loop if the first character of OPT is not '-'. | |
85 | [ "x$(printf %.1s "$opt")" != "x-" ] | |
86 | do | |
87 | shift | |
88 | done | |
89 | ||
90 | if test $# != 1; then | |
91 | print_try_help | |
92 | exit 1 | |
93 | fi | |
94 | ||
95 | file="$1" | |
96 | ||
97 | if test -L "$file"; then | |
98 | if ! command -v readlink >/dev/null 2>&1; then | |
99 | echo "$myname: 'readlink' missing. Failed to follow symlink $1." 1>&2 | |
100 | exit 1 | |
101 | fi | |
102 | ||
103 | # Count number of links followed in order to detect loops. | |
104 | count=0 | |
105 | while test -L "$file"; do | |
106 | target=$(readlink "$file") | |
107 | ||
108 | case "$target" in | |
109 | /*) | |
110 | file="$target" | |
111 | ;; | |
112 | *) | |
113 | file="$(dirname "$file")/$target" | |
114 | ;; | |
115 | esac | |
116 | ||
117 | count="$((count + 1))" | |
118 | if test "$count" -gt 10; then | |
119 | echo "$myname: Detected loop while following link $file" | |
120 | exit 1 | |
121 | fi | |
122 | done | |
123 | fi | |
124 | ||
125 | if test ! -r "$file"; then | |
126 | echo "$myname: unable to access: $file" 1>&2 | |
127 | exit 1 | |
128 | fi | |
129 | ||
130 | dir="${file%/*}" | |
131 | test "$dir" = "$file" && dir="." | |
132 | ||
133 | dwz_file="" | |
134 | if $READELF -S "$file" | grep -q " \.gnu_debugaltlink "; then | |
135 | dwz_file=$($READELF --string-dump=.gnu_debugaltlink "$file" \ | |
136 | | grep -A1 "'\.gnu_debugaltlink':" \ | |
137 | | tail -n +2 \ | |
138 | | sed 's/.*]//') | |
139 | dwz_file=$(echo $dwz_file) | |
140 | if $READELF -S "$dwz_file" | grep -E -q " \.(gdb_index|debug_names) "; then | |
141 | # Already has an index, skip it. | |
142 | dwz_file="" | |
143 | fi | |
144 | fi | |
145 | ||
146 | set_files () | |
147 | { | |
148 | fpath="$1" | |
149 | ||
150 | index4="${fpath}.gdb-index" | |
151 | index5="${fpath}.debug_names" | |
152 | debugstr="${fpath}.debug_str" | |
153 | debugstrmerge="${fpath}.debug_str.merge" | |
154 | debugstrerr="${fpath}.debug_str.err" | |
155 | } | |
156 | ||
157 | tmp_files= | |
158 | for f in "$file" "$dwz_file"; do | |
159 | if [ "$f" = "" ]; then | |
160 | continue | |
161 | fi | |
162 | set_files "$f" | |
163 | tmp_files="$tmp_files $index4 $index5 $debugstr $debugstrmerge $debugstrerr" | |
164 | done | |
165 | ||
166 | rm -f $tmp_files | |
167 | ||
168 | # Ensure intermediate index file is removed when we exit. | |
169 | trap "rm -f $tmp_files" 0 | |
170 | ||
171 | $GDB --batch -nx -iex 'set auto-load no' \ | |
172 | -iex 'set debuginfod enabled off' \ | |
173 | -ex "file '$file'" -ex "save gdb-index $dwarf5 '$dir'" || { | |
174 | # Just in case. | |
175 | status=$? | |
176 | echo "$myname: gdb error generating index for $file" 1>&2 | |
177 | exit $status | |
178 | } | |
179 | ||
180 | # In some situations gdb can exit without creating an index. This is | |
181 | # not an error. | |
182 | # E.g., if $file is stripped. This behavior is akin to stripping an | |
183 | # already stripped binary, it's a no-op. | |
184 | status=0 | |
185 | ||
186 | handle_file () | |
187 | { | |
188 | fpath="$1" | |
189 | ||
190 | set_files "$fpath" | |
191 | ||
192 | if test -f "$index4" -a -f "$index5"; then | |
193 | echo "$myname: Both index types were created for $fpath" 1>&2 | |
194 | status=1 | |
195 | elif test -f "$index4" -o -f "$index5"; then | |
196 | if test -f "$index4"; then | |
197 | index="$index4" | |
198 | section=".gdb_index" | |
199 | else | |
200 | index="$index5" | |
201 | section=".debug_names" | |
202 | fi | |
203 | if test -s "$debugstr"; then | |
204 | if ! $OBJCOPY --dump-section .debug_str="$debugstrmerge" "$fpath" \ | |
205 | /dev/null 2> "$debugstrerr"; then | |
206 | cat >&2 "$debugstrerr" | |
207 | exit 1 | |
208 | fi | |
209 | cat "$debugstr" >>"$debugstrmerge" | |
210 | if grep -q "can't dump section '.debug_str' - it does not exist" \ | |
211 | "$debugstrerr"; then | |
212 | $OBJCOPY --add-section $section="$index" \ | |
213 | --set-section-flags $section=readonly \ | |
214 | --add-section .debug_str="$debugstrmerge" \ | |
215 | --set-section-flags .debug_str=readonly \ | |
216 | "$fpath" "$fpath" | |
217 | else | |
218 | $OBJCOPY --add-section $section="$index" \ | |
219 | --set-section-flags $section=readonly \ | |
220 | --update-section .debug_str="$debugstrmerge" \ | |
221 | "$fpath" "$fpath" | |
222 | fi | |
223 | else | |
224 | $OBJCOPY --add-section $section="$index" \ | |
225 | --set-section-flags $section=readonly \ | |
226 | "$fpath" "$fpath" | |
227 | fi | |
228 | ||
229 | status=$? | |
230 | else | |
231 | echo "$myname: No index was created for $fpath" 1>&2 | |
232 | echo "$myname: [Was there no debuginfo? Was there already an index?]" \ | |
233 | 1>&2 | |
234 | fi | |
235 | } | |
236 | ||
237 | handle_file "$file" | |
238 | if [ "$dwz_file" != "" ]; then | |
239 | handle_file "$dwz_file" | |
240 | fi | |
241 | ||
242 | exit $status |