-
Notifications
You must be signed in to change notification settings - Fork 1
/
sdk.write
executable file
·284 lines (245 loc) · 9.07 KB
/
sdk.write
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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
#!/bin/sh
# Copyright (C) 2014 Codethink Limited
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
# =*= License: GPL-2 =*=
set -eu
die(){
echo "$@" >&2
exit 1
}
shellescape(){
echo "'$(echo "$1" | sed -e "s/'/'\\''/g")'"
}
########################## END OF COMMON HEADER ###############################
#
# The above lines, as well as being part of this script, are copied into the
# self-installing SDK blob's header script, as a means of re-using content.
#
help(){
cat <<EOF
sdk.write: Write extension for making an SDK installer.
Description:
This is a write extension for producing a self-installing SDK blob
from a configured system.
It generates a shell script header and appends the rootfs as a tarball,
which the header later extracts, and performs various configuration
to have it useable as a relocatable toolchain.
This is similar to what the shar and makeself programs do, but we
need custom setup, so shar isn't appropriate, and makeself's api is
insufficiently flexible for our requirements.
The toolchain relocation is handled by sedding every text file in the
SDK directory, and using the patchelf from inside the SDK to change
every ELF binary in the toolchain to use the linker and libraries from
inside the SDK.
The ELF patching is required so that the SDK can work independently
of the versions of libraries installed on the host system.
Location: Path to create the script at
ENV VARS:
PREFIX (optional) The prefix the toolchain is built with
defaults to /usr
TARGET (mandatory) The gnu triplet the toolchain was built with
EOF
}
ROOTDIR="$1"
OUTPUT_SCRIPT="$2"
PREFIX=${PREFIX-/usr}
find_patchelf(){
# Look for patchelf in the usual places
for binpath in /bin "$PREFIX/bin"; do
if [ -x "$ROOTDIR$binpath/patchelf" ]; then
echo "$binpath/patchelf"
return
fi
done
die "patchelf not found in rootfs"
}
read_elf_interpreter(){
# Use readelf and sed to find the interpreter a binary uses this is
# required since we can't yet guarantee that the deploying system
# contains patchelf
readelf --wide --program-headers "$1" |
sed -nr -f /proc/self/fd/3 3<<'EOF'
/\s+INTERP/{
n # linker is on line after INTERP line
s/^\s*\[Requesting program interpreter: (.*)]$/\1/
p # in -n mode, so need to print our text
}
EOF
}
find_lib_paths(){
local found_first=false
for libpath in "$PREFIX/lib32" "$PREFIX/lib64" "$PREFIX/lib" \
/lib32 /lib64 /lib; do
if [ -e "$ROOTDIR$libpath" ]; then
if "$found_first"; then
printf ":%s" "$libpath"
else
printf "%s" "$libpath"
found_first=true
fi
fi
done
}
# Create script with common header
header_end=$(grep -En -m1 -e '^#+ END OF COMMON HEADER #+$' "$0" | cut -d: -f1)
head -n "$header_end" "$0" | install -m 755 -D /dev/stdin "$OUTPUT_SCRIPT"
# Determine any config
PATCHELF="$(find_patchelf)"
RTLD="$(read_elf_interpreter "$ROOTDIR$PATCHELF")"
LIB_PATH="${LIB_PATH-$(find_lib_paths)}"
# Append deploy-time config to header
cat >>"$OUTPUT_SCRIPT" <<EOF
#################### START OF DEPLOY TIME CONFIGURATION #######################
TARGET=$(shellescape "$TARGET")
PREFIX=$(shellescape "$PREFIX")
PATCHELF=$(shellescape "$PATCHELF")
RTLD=$(shellescape "$RTLD")
LIB_PATH=$(shellescape "$LIB_PATH")
##################### END OF DEPLOY TIME CONFIGURATION ########################
EOF
# Append deployment script
cat >>"$OUTPUT_SCRIPT" <<'EOF'
########################### START OF HEADER SCRIPT ############################
usage(){
cat <<USAGE
usage: $0 TOOLCHAIN_PATH
USAGE
}
if [ "$#" != 1 ]; then
echo TOOLCHAIN_PATH not given >&2
usage >&2
exit 1
fi
TOOLCHAIN_PATH="$1"
sedescape(){
# Escape the passed in string so it can be safely interpolated into
# a sed expression as a literal value.
echo "$1" | sed -e 's/[\/&]/\\&/g'
}
prepend_to_path_elements(){
# Prepend $1 to every entry in the : separated list specified as $2.
local prefix="$1"
(
# Split path into components
IFS=:
set -- $2
# Print path back out with new prefix
printf %s "$prefix/$1"
shift
for arg in "$@"; do
printf ":%s" "$prefix/$arg"
done
)
}
extract_rootfs(){
# Extract the bzipped tarball at the end of the script passed as $1
# to the path specified as $2
local selfextractor="$1"
local target="$2"
local script_end="$(($(\
grep -aEn -m1 -e '^#+ END OF HEADER SCRIPT #+$' "$selfextractor" |
cut -d: -f1) + 1 ))"
mkdir -p "$target"
tail -n +"$script_end" "$selfextractor" | tar -xj -C "$target" .
}
amend_text_file_paths(){
# Replace all instances of $3 with $4 in the directory specified by $1
# excluding the subdirectory $2
local root="$1"
local inner_sysroot="$2"
local old_prefix="$3"
local new_prefix="$4"
find "$root" \( -path "$inner_sysroot" -prune \) -o -type f \
-exec sh -c 'file "$1" | grep -q text' - {} \; \
-exec sed -i -e \
"s/$(sedescape "$old_prefix")/$(sedescape "$new_prefix")/g" {} +
}
filter_patchelf_errors(){
# Filter out warnings from patchelf that are acceptable
# The warning that it's making a file bigger is just noise
# The warning about not being an ELF executable just means we got a
# false positive from file that it was an ELF binary
# Failing to find .interp is because for convenience, we set the
# interpreter in the same command as setting the rpath, even though
# we give it both executables and libraries.
grep -v -e 'warning: working around a Linux kernel bug' \
-e 'not an ELF executable' \
-e 'cannot find section .interp'
}
patch_elves(){
# Set the interpreter and library paths of ELF binaries in $1,
# except for the $2 subdirectory, using the patchelf command in the
# toolchain specified as $3, so that it uses the linker specified
# as $4 as the interpreter, and the runtime path specified by $5.
#
# The patchelf inside the toolchain is used to ensure that it works
# independently of the availability of patchelf on the host.
#
# This is possible by invoking the linker directly and specifying
# --linker-path as the RPATH we want to set the binaries to use.
local root="$1"
local inner_sysroot="$2"
local patchelf="$3"
local linker="$4"
local lib_path="$5"
find "$root" \( -path "$inner_sysroot" -prune \) -o -type f \
-type f -perm +111 \
-exec sh -c 'file "$1" | grep -q "ELF"' - {} \; \
-exec "$linker" --library-path "$lib_path" \
"$patchelf" --set-interpreter "$linker" \
--set-rpath "$lib_path" {} \; 2>&1 \
| filter_patchelf_errors
}
generate_environment_setup(){
local target="$1"
install -m 644 -D /dev/stdin "$target" <<ENVSETUP
export PATH=$(shellescape "$TOOLCHAIN_PATH/usr/bin"):"\$PATH"
export TARGET_PREFIX=$(shellescape "$TARGET"-)
export CC=$(shellescape "$TARGET-gcc")
export CXX=$(shellescape "$TARGET-g++")
export CPP=$(shellescape "$TARGET-gcc -E")
export AS=$(shellescape "$TARGET-as")
export LD=$(shellescape "$TARGET-ld")
export STRIP=$(shellescape "$TARGET-strip")
export RANLIB=$(shellescape "$TARGET-ranlib")
export OBJCOPY=$(shellescape "$TARGET-objcopy")
export OBJDUMP=$(shellescape "$TARGET-objdump")
export AR=$(shellescape "$TARGET-ar")
export NM=$(shellescape "$TARGET-nm")
ENVSETUP
}
SYSROOT="$TOOLCHAIN_PATH$PREFIX/$TARGET/sys-root"
PATCHELF="$TOOLCHAIN_PATH$PATCHELF"
RTLD="$TOOLCHAIN_PATH$RTLD"
OLD_PREFIX="$PREFIX"
NEW_PREFIX="$TOOLCHAIN_PATH/$PREFIX"
RPATH="$(prepend_to_path_elements "$TOOLCHAIN_PATH" "$LIB_PATH")"
ENV_SETUP_FILE="$(dirname "$TOOLCHAIN_PATH")/environment-setup-$TARGET"
echo Writing environment setup script to "$ENV_SETUP_FILE"
generate_environment_setup "$ENV_SETUP_FILE"
echo Extracting rootfs
extract_rootfs "$0" "$TOOLCHAIN_PATH"
echo "Relocating prefix references of $OLD_PREFIX to $NEW_PREFIX in" \
"the toolchain's textual config files."
amend_text_file_paths "$TOOLCHAIN_PATH" "$SYSROOT" "$OLD_PREFIX" "$NEW_PREFIX"
echo "Patching ELF binary files' interpreter and runtime library paths" \
"to refer to libraries within the toolchain"
patch_elves "$TOOLCHAIN_PATH" "$SYSROOT" "$PATCHELF" "$RTLD" "$RPATH"
exit
############################ END OF HEADER SCRIPT #############################
EOF
# Append rootfs as tarball
tar -C "$1" -cj >>"$OUTPUT_SCRIPT" .