blob: 78ed19f0116a266d574ee56179ac4db7efea32a8 (
plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
|
#!/usr/bin/env bash
# build-aux/lint-bin - Lint final binary images
#
# Copyright (C) 2025 Luke T. Shumaker <lukeshu@lukeshu.com>
# SPDX-License-Identifier: AGPL-3.0-or-later
set -euE -o pipefail
shopt -s extglob
# There are several exisi files we can use:
#
# Binaries:
# - ${elf} : firmware image with debug symbols and relocationd data
# - ${elf%.elf}.bin : raw firmware image
# - ${elf%.elf}.hex : .bin as Intel HEX
# - ${elf%.elf}.uf2 : .bin as USB Flashing Format (UF2)
#
# Textual info:
# - ${elf%.elf}.dis : `objdump --section-headers ${elf}; objdump --disassemble ${elf}; picotool coprodis --quiet ${elf}`
# - ${elf}.map : `ld --print-map` info
# - ${elf%.elf}_stack.c : `stack.c.gen`
RED=$(tput setaf 1)
RESET=$(tput sgr0)
err() {
printf "${RED}%s${RESET}: %s\n" "$1" "$2" >&2
r=1
}
# Input is `ld --print-map` format.
#
# Output is a series of lines in the format "symbol location size
# source". Whitespace may seem silly.
objdump_globals() {
sed -E -n '/^ \.t?(data|bss)\./{ / 0x/{ p; D; }; N; s/\n/ /; p; }' <"$1"
}
readelf_funcs() {
local in_elffile
in_elffile=$1
readelf --syms --wide -- "$in_elffile" |
awk '$4 == "FUNC" { print $8 }'
}
lint_globals() {
local in_mapfile
in_mapfile=$1
local rel_base
rel_base=${in_mapfile#build/*/}
rel_base=${rel_base%/*}
local topdir
topdir=$PWD
{
echo 'Source Symbol Size'
objdump_globals "$in_mapfile" |
{
cd "$rel_base"
total=0
while read -r symbol addr size source; do
if (( addr == 0 )); then
continue
fi
case "$source" in
/*)
# libg.a(whatever.o) -> libg.a
source="${source%(*)}"
# resolve `..` components
source="$(realpath --canonicalize-missing --no-symlinks -- "$source")"
;;
CMakeFiles/*.dir/*.@(obj|o))
# CMakeFiles/sbc_harnes_objs.dir/...
source="${source#CMakeFiles/*.dir/}"
source="${source%.@(obj|o)}"
source="${source//__/..}"
source="$(realpath --canonicalize-missing --no-symlinks --relative-to="$topdir" -- "$source")"
;;
esac
printf "%s %s 0x%04x (%'d)\n" "$source" "$symbol" "$size" "$size"
total=$((total + size))
done
printf "~ Total 0x%04x (%'d)\n" "$total" "$total"
} |
LC_COLLATE=C sort
} | column -t
}
lint_stack() {
local in_elffile
in_elffile=$1
IFS=''
while read -r line; do
func=${line#$'\t'}
if [[ $line == $'\t'* ]]; then
err "$in_elffile" "function in binary but not _stack.c: ${func}"
else
err "$in_elffile" "function in _stack.c but not binary: ${func}"
fi
done < <(
comm -3 \
<(sed -En 's/^included: (.*:)?//p' "${in_elffile%.elf}_stack.c" | sort -u) \
<(readelf_funcs "$in_elffile" | sed -E -e 's/\.part\.[0-9]*$//' -e 's/^__(.*)_veneer$/\1/' | sort -u))
}
lint_func_blocklist() {
local in_elffile
in_elffile=$1
local blocklist=(
gpio_default_irq_handler
)
while read -r func; do
err "$in_elffile" "Contains blocklisted function: ${func}"
done < <(readelf --syms --wide -- "$in_elffile" |
awk '$4 == "FUNC" { print $8 }' |
grep -Fx "${blocklist[@]/#/-e}")
}
main() {
r=0
local elf
for elf in "$@"; do
{
echo 'Global variables:'
lint_globals "${elf}.map" | sed 's/^/ /'
} > "${elf%.elf}.lint.globals"
(lint_stack "$elf") &> "${elf%.elf}.lint.stack"
lint_func_blocklist "$elf"
done
return $r
}
main "$@"
|