blob: ee5bb5ce0c072fd183d0fd922094660a612dcdbd [file] [log] [blame]
Andrew Jefferyc7a446e2022-07-21 10:14:54 +09301#!/bin/sh
2
3set -eu
4
5: ${IPKDBG_OPKG_CACHE:=}
6: ${IPKDBG_CONF_HOST:=host.local}
7: ${IPKDBG_CONF_MNT:=mountpoint}
8: ${IPKDBG_CONF_LOC:=themoon}
9: ${IPKDBG_CONF_ROOT:=path}
10: ${IPKDBG_CONF_USER:=$USER}
11: ${IPKDBG_WGET_OPTS:="--quiet"}
12: ${IPKDBG_ZSTD:=zstd}
13
14ipkdbg_error() {
15 /bin/echo -e "$@" | fold >&2
16}
17
18ipkdbg_info() {
19 /bin/echo -e "$@" | fold
20}
21
22ipkdbg_help() {
23/bin/echo -e "\033[1mNAME\033[0m"
24/bin/echo -e "\tipkdbg - debug OpenBMC applications from an (internally) released firmware"
25/bin/echo -e
26/bin/echo -e "\033[1mSYNOPSIS\033[0m"
27/bin/echo -e "\tipkdbg [-q] RELEASE FILE CORE [PACKAGE...]"
28/bin/echo -e
29/bin/echo -e "\033[1mDESCRIPTION\033[0m"
30/bin/echo -e "\tRELEASE is the firmware release whose packages to install"
31/bin/echo -e "\tFILE is the absolute path to the binary of interest in the target environment"
32/bin/echo -e "\tCORE is an optional core file generated by FILE. Pass '-' for no core file"
33/bin/echo -e "\tPACKAGES will be used to populate a temporary rootfs for debugging FILE"
34/bin/echo -e
35/bin/echo -e "\033[1mOPTIONS\033[0m"
36/bin/echo -e "\t\033[1m-h\033[0m"
37/bin/echo -e "\tPrint this help."
38/bin/echo -e
39/bin/echo -e "\t\033[1m-q\033[0m"
40/bin/echo -e "\tQuit gdb once done. Intended for use in a scripting environment in combination"
41/bin/echo -e "\twith a core file, as the backtrace will be printed as an implicit first command."
42/bin/echo -e
43/bin/echo -e "\033[1mENVIRONMENT\033[0m"
44/bin/echo -e "\tThere are several important environment variables controlling the behaviour of"
45/bin/echo -e "\tthe script:"
46/bin/echo -e
47/bin/echo -e "\t\033[1mIPKDBG_OPKG_CACHE\033[0m"
48/bin/echo -e "\tA package cache directory for opkg. Defaults to empty, disabling the cache."
49/bin/echo -e
50/bin/echo -e "\t\033[1mIPKDBG_CONF_HOST\033[0m"
51/bin/echo -e "\tHostname for access to opkg.conf over the web interface"
52/bin/echo -e
53/bin/echo -e "\tDefaults to '${IPKDBG_CONF_HOST}'"
54/bin/echo -e
55/bin/echo -e "\t\033[1mIPKDBG_CONF_MNT\033[0m"
56/bin/echo -e "\tMount-point for access to opkg.conf"
57/bin/echo -e
58/bin/echo -e "\tDefaults to '${IPKDBG_CONF_MNT}'"
59/bin/echo -e
60/bin/echo -e "\t\033[1mIPKDBG_CONF_LOC\033[0m"
61/bin/echo -e "\tGeo-location for access to opkg.conf"
62/bin/echo -e
63/bin/echo -e "\tDefaults to '${IPKDBG_CONF_LOC}'"
64/bin/echo -e
65/bin/echo -e "\t\033[1mIPKDBG_CONF_ROOT\033[0m"
66/bin/echo -e "\tPath to the directory containing build artifacts, for access to opkg.conf"
67/bin/echo -e
68/bin/echo -e "\tDefaults to '${IPKDBG_CONF_ROOT}'"
69/bin/echo -e
70/bin/echo -e "\t\033[1mIPKDBG_CONF_USER\033[0m"
71/bin/echo -e "\tUsername for access to opkg.conf over the web interface"
72/bin/echo -e
73/bin/echo -e "\tDefaults to \$USER ($USER)"
74/bin/echo -e
75/bin/echo -e "\t\033[1mIPKDBG_GDB\033[0m"
76/bin/echo -e "\tThe gdb(1) binary to invoke. Automatically detected if unset."
77/bin/echo -e
78/bin/echo -e "\t\033[1mIPKDBG_WGET_OPTS\033[0m"
79/bin/echo -e "\tUser options to pass to wget(1) when fetching opkg.conf. Defaults to"
80/bin/echo -e "\t'$IPKDBG_WGET_OPTS'"
81/bin/echo -e
82/bin/echo -e "\t\033[1mIPKDBG_ZSTD\033[0m"
83/bin/echo -e "\tThe zstd(1) binary to extract the compressed core dump. Automatically"
84/bin/echo -e "\tdetected if unset."
85/bin/echo -e
86/bin/echo -e "\033[1mEXAMPLE\033[0m"
87/bin/echo -e "\tipkdbg 1020.2206.20220208a \\"
88/bin/echo -e "\t\t/usr/bin/nvmesensor - \\"
89/bin/echo -e "\t\tdbus-sensors dbus-sensors-dbg"
90}
91
92IPKDBG_OPT_QUIT=0
93
94while getopts hq f
95do
96 case $f in
97 q) IPKDBG_OPT_QUIT=1;;
98 h|\?) ipkdbg_help ; exit 1;;
99 esac
100done
101shift $(expr $OPTIND - 1)
102
103trap ipkdbg_help EXIT
104
105ipkdbg_core_extract()
106{
107 if [ "-" = "$1" ]
108 then
109 echo -
110 else
111 local src="$(realpath "$1")"
112 local dst="${src%.zst}"
113
114 command -v $IPKDBG_ZSTD > /dev/null
115 $IPKDBG_ZSTD --decompress --quiet --quiet --force -o "$dst" "$src" || true
116 echo "$dst"
117 fi
118}
119
120IPKDBG_BUILD=$1; shift
121IPKDBG_FILE=$1; shift
122IPKDBG_CORE=$(ipkdbg_core_extract "$1"); shift
123IPKDBG_PKGS=$@
124
125: ${IPKDBG_GDB:=}
126if [ -n "$IPKDBG_GDB" ]
127then
128 ipkdbg_info "Using provided gdb command '$IPKDBG_GDB'"
129else
130 os_id=$(. /etc/os-release; echo ${ID}-${VERSION_ID})
131 case $os_id in
132 rhel-8.6 | fedora*)
133 IPKDBG_GDB=gdb
134 if [ -z "$(command -v $IPKDBG_GDB)" ]
135 then
136 ipkdbg_error "Please install the gdb package:"
137 ipkdbg_error
138 ipkdbg_error "\tsudo dnf install gdb"
139 ipkdbg_error
140 exit 1
141 fi
142 ;;
143 rhel*)
144 IPKDBG_GDB=gdb-multiarch
145 if [ -z "$(command -v $IPKDBG_GDB)" ]
146 then
147 ipkdbg_error "Please install the gdb-multiarch package:"
148 ipkdbg_error
149 ipkdbg_error "\tsudo dnf install gdb-multiarch"
150 ipkdbg_error
151 exit 1
152 fi
153 ;;
154 ubuntu*)
155 IPKDBG_GDB=gdb-multiarch
156 if [ -z "$(command -v $IPKDBG_GDB)" ]
157 then
158 ipkdbg_error "Please Install the gdb-multiarch package"
159 ipkdbg_error
160 ipkdbg_error "\tsudo apt install gdb-multiarch"
161 ipkdbg_error
162 exit 1
163 fi
164 ;;
165 *)
166 ipkdbg_error "Unrecognised distribution $release_id. Please set IPKDBG_GDB or " \
167 "install an appropriate gdb binary to invoke"
168 exit 1
169 ;;
170 esac
171 ipkdbg_info "Using gdb command ${IPKDBG_GDB} ($(command -v $IPKDBG_GDB))"
172fi
173
174ipkdbg_archive_extract() {
175 local offset=$1
176 local work=$2
177 tail -n+$offset $0 | base64 --decode - | tar -xz -C $work
178}
179
180ipkdbg_opkg_path() {
181 local root=$1
182 local arch=$(uname -m)
183 local release_id=$(. /etc/os-release; echo $ID)
184 local release_version_id=$(. /etc/os-release; echo $VERSION_ID)
185 local p=${root}/bin/${arch}/${release_id}/${release_version_id}/opkg
186 if [ ! -x "$p" ]
187 then
188 ipkdbg_error "Unsupported environment:"
189 ipkdbg_error
190 ipkdbg_error "Architecture:\t$arch"
191 ipkdbg_error "Distribution ID:\t$release_id"
192 ipkdbg_error "Distribution Version:\t$release_version_id"
193 exit 1
194 fi
195 echo $p
196}
197
198if [ ! -f $0 ]
199then
200 ipkdbg_error "Please execute the script with a relative or absolute path"
201 exit 1
202fi
203
204IPKDBG_DATA=$(awk '/^__ARCHIVE_BEGIN__$/ { print NR + 1; exit 0 }' $0)
205IPKDBG_WORK=$(mktemp -t --directory ipkdbg.XXX)
206IPKDBG_BINS=${IPKDBG_WORK}/tools
207IPKDBG_ROOT=${IPKDBG_WORK}/root
208IPKDBG_CONF=${IPKDBG_WORK}/opkg.conf
209IPKDBG_DB=${IPKDBG_WORK}/database
210
211cleanup() {
212 rm -rf $IPKDBG_WORK
213}
214
215trap cleanup EXIT INT QUIT KILL
216
217mkdir $IPKDBG_BINS $IPKDBG_DB
218ipkdbg_archive_extract $IPKDBG_DATA $IPKDBG_BINS
219
220IPKDBG_OPKG_BIN=$(ipkdbg_opkg_path $IPKDBG_BINS)
221
222ipkdbg_build_gen_path() {
223 local build=$1
224 local component="$2"
225 echo /${IPKDBG_CONF_MNT}/${IPKDBG_CONF_LOC}/${IPKDBG_CONF_ROOT}/${build}/"$component"
226}
227
228ipkdbg_build_gen_url() {
229 local build=$1
230 local component="$2"
231 echo https://${IPKDBG_CONF_HOST}/${IPKDBG_CONF_MNT}/${IPKDBG_CONF_LOC}/${IPKDBG_CONF_ROOT}/${build}/${component}
232}
233
234ipkdbg_build_gen_cache() {
235 local build=$1
236 local component="$2"
237 echo "${HOME}/.cache/ipkdbg/builds/${build}/${component}"
238}
239
240ipkdbg_opkg_conf_gen_path() {
241 local build=$1
242 ipkdbg_build_gen_path $build bmc_ipk/opkg.conf
243}
244
245ipkdbg_opkg_fetch_path() {
246 local path=$1
247 local output=$2
248 cp "$path" "$output" > /dev/null 2>&1
249}
250
251ipkdbg_opkg_conf_gen_url() {
252 local build=$1
253 ipkdbg_build_gen_url $build bmc_ipk/opkg.conf
254}
255
256ipkdbg_opkg_fetch_url() {
257 local url=$1
258 local output=$2
259 # We don't want URL to wrap
260 ipkdbg_info "Authenticating as user $IPKDBG_CONF_USER"
261 if ! wget --http-user=$IPKDBG_CONF_USER \
262 --ask-password \
263 --output-document $output \
264 $IPKDBG_WGET_OPTS \
265 $url
266 then
267 ipkdbg_error "Failed to fetch resource"
268 exit 1
269 fi
270}
271
272ipkdbg_opkg_conf_gen_cache() {
273 local build=$1
274 ipkdbg_build_gen_cache $build opkg.conf
275}
276
277ipkdbg_opkg_conf_fetch_cache() {
278 local build=$1
279 local output=$2
280 local path="$(ipkdbg_opkg_conf_gen_cache $build)"
281 cp "$path" "$output" > /dev/null 2>&1
282}
283
284ipkdbg_opkg_conf_install() {
285 local build=$1
286 local output=$2
287 mkdir -p $(dirname $output)
288 if ! ipkdbg_opkg_conf_fetch_cache $build $output
289 then
290 local cache="$(ipkdbg_opkg_conf_gen_cache $build)"
291 mkdir -p $(dirname $cache)
292 url=
293 ipkdbg_opkg_fetch_path "$(ipkdbg_opkg_conf_gen_path $build)" $cache ||
294 (echo "Configuring opkg via $(ipkdbg_opkg_conf_gen_url $build)" &&
295 ipkdbg_opkg_fetch_url "$(ipkdbg_opkg_conf_gen_url $build)" $cache)
296 ipkdbg_opkg_conf_fetch_cache $build $output
297 fi
298}
299
300ipkdbg_opkg_db_gen_path() {
301 local build=$1
302 ipkdbg_build_gen_path $build bmc_ipk/opkg-database.tar.xz
303}
304
305ipkdbg_opkg_db_gen_url() {
306 local build=$1
307 ipkdbg_build_gen_url ${build} bmc_ipk/opkg-database.tar.xz
308}
309
310ipkdbg_opkg_db_gen_cache() {
311 local build=$1
312 ipkdbg_build_gen_cache $build opkg-database.tar.xz
313}
314
315ipkdbg_opkg_db_install() {
316 local build=$1
317 local root=$2
318 local state=${root}/var/lib/opkg
319 local cache="$(ipkdbg_opkg_db_gen_cache $build)"
320 mkdir -p $state
321 if ! [ -f $cache ]
322 then
323 mkdir -p $(dirname $cache)
324 ipkdbg_opkg_fetch_path "$(ipkdbg_opkg_db_gen_path $build)" $cache ||
325 ipkdbg_opkg_fetch_url "$(ipkdbg_opkg_db_gen_url $build)" $cache ||
326 rm -f $cache
327 fi
328 tar -xf $cache -C $state 2> /dev/null
329 mkdir -p ${root}/usr/local
330 ln -s ${root}/var ${root}/usr/local/var
331}
332
333ipkdbg_opkg() {
334 $IPKDBG_OPKG_BIN \
335 $([ -z "$IPKDBG_OPKG_CACHE" ] ||
336 echo --cache-dir $IPKDBG_OPKG_CACHE --host-cache-dir) \
337 -V1 -f $IPKDBG_CONF -o $IPKDBG_ROOT $@
338}
339
340ipkdbg_gdb_extract_bin() {
341 local core=$1
342 $IPKDBG_GDB --core $core -ex quit 2> /dev/null |
343 awk -F "[\`']" '/Core was generated by/ { print $2 }' |
344 awk -F " " '{ print $1 }' # Chop off the arguments, we only want the binary path
345}
346
347ipkdbg_opkg_find() {
348 ipkdbg_opkg find $@ | awk '{ print $1 }'
349}
350
351ipkdbg_opkg_find_extra() {
352 local pkg=$1
353
354 # Try appending -dbg and -src to the binary package name
355 extra_pkgs="$(ipkdbg_opkg_find ${pkg}-dbg) $(ipkdbg_opkg_find ${pkg}-src)"
356
357 # If that fails, we probably have a split binary package
358 if [ -z "$extra_pkgs" ]
359 then
360 # Strip the last component off as it's probably the split binary package name and
361 # try again
362 extra_pkgs="$(ipkdbg_opkg_find ${pkg%-*}-dbg) $(ipkdbg_opkg_find ${pkg%-*}-src)"
363 fi
364 echo $extra_pkgs
365}
366
367ipkdbg_opkg_conf_install $IPKDBG_BUILD $IPKDBG_CONF
368
369# Extract the binary path from the core
370if [ '-' = "$IPKDBG_FILE" -a '-' != "$IPKDBG_CORE" ]
371then
372 IPKDBG_FILE=$(ipkdbg_gdb_extract_bin $IPKDBG_CORE)
373fi
374
375# Update the package database before potentially looking up the debug packages
376ipkdbg_opkg update
377
378# Extract the package name for the binary
379if [ '-' != "$IPKDBG_CORE" ]
380then
381 if ipkdbg_opkg_db_install $IPKDBG_BUILD $IPKDBG_DB
382 then
383 # Look up the package for the binary
384 IPKDBG_CORE_PKG="$(IPKDBG_ROOT=$IPKDBG_DB ipkdbg_opkg search ${IPKDBG_DB}${IPKDBG_FILE} | awk '{ print $1 }')"
385 if [ -n "$IPKDBG_CORE_PKG" ]
386 then
387 # Look up the extra (debug, source) packages for the binary package
388 IPKDBG_PKGS="$IPKDBG_PKGS $IPKDBG_CORE_PKG"
389 IPKDBG_PKGS="$IPKDBG_PKGS $(ipkdbg_opkg_find_extra $IPKDBG_CORE_PKG)"
390 fi
391 fi
392
393 if [ -z "$IPKDBG_PKGS" ]
394 then
395 ipkdbg_error "Unable to determine package-set to install, please specify" \
396 "appropriate packages on the command line"
397 exit 1
398 fi
399fi
400
401# Force installation of gcc-runtime-dbg to give us debug symbols for libstdc++
402IPKDBG_PKGS="gcc-runtime-dbg $IPKDBG_PKGS"
403
404if [ -n "$IPKDBG_OPKG_CACHE" ]
405then
406 mkdir -p "$IPKDBG_OPKG_CACHE"
407 ipkdbg_opkg install --download-only $IPKDBG_PKGS
408fi
409
410ipkdbg_opkg install $IPKDBG_PKGS | grep -vF 'Warning when extracting archive entry'
411
412cat <<EOF > ${IPKDBG_BINS}/opkg
413#!/bin/sh
414exec $IPKDBG_OPKG_BIN -f $IPKDBG_CONF -o $IPKDBG_ROOT \$@
415EOF
416chmod +x ${IPKDBG_BINS}/opkg
417
418PATH=${IPKDBG_BINS}:${PATH} $IPKDBG_GDB -q \
419 -iex "set solib-absolute-prefix $IPKDBG_ROOT" \
420 -iex "add-auto-load-safe-path $IPKDBG_ROOT" \
421 -iex "set directories $IPKDBG_ROOT" \
422 -iex "cd $IPKDBG_ROOT" \
423 $([ '-' = "$IPKDBG_CORE" ] || echo -ex bt) \
424 $([ 0 -eq $IPKDBG_OPT_QUIT ] || echo -ex quit) \
425 ${IPKDBG_ROOT}${IPKDBG_FILE} \
426 $([ '-' = "$IPKDBG_CORE" ] || echo $IPKDBG_CORE)
427
428exit 0
429
430__ARCHIVE_BEGIN__