1314d2688c
Xcode 10 ships version 921.0.1 of cctools, which otool is part of. The defaults have changed in that version to no longer print verbosely (symbolically), which we relied on. We now request it explicitly. Change-Id: Ifbe0c97462b9f78cf128c820847eff9c72f17065 Reviewed-by: Timur Pocheptsov <timur.pocheptsov@qt.io> Reviewed-by: Tim Blechmann <tim@klingt.org>
223 lines
7.2 KiB
Bash
Executable File
223 lines
7.2 KiB
Bash
Executable File
#!/bin/bash
|
|
|
|
#############################################################################
|
|
##
|
|
## Copyright (C) 2017 The Qt Company Ltd.
|
|
## Contact: https://www.qt.io/licensing/
|
|
##
|
|
## This file is the build configuration utility of the Qt Toolkit.
|
|
##
|
|
## $QT_BEGIN_LICENSE:LGPL$
|
|
## Commercial License Usage
|
|
## Licensees holding valid commercial Qt licenses may use this file in
|
|
## accordance with the commercial license agreement provided with the
|
|
## Software or, alternatively, in accordance with the terms contained in
|
|
## a written agreement between you and The Qt Company. For licensing terms
|
|
## and conditions see https://www.qt.io/terms-conditions. For further
|
|
## information use the contact form at https://www.qt.io/contact-us.
|
|
##
|
|
## GNU Lesser General Public License Usage
|
|
## Alternatively, this file may be used under the terms of the GNU Lesser
|
|
## General Public License version 3 as published by the Free Software
|
|
## Foundation and appearing in the file LICENSE.LGPL3 included in the
|
|
## packaging of this file. Please review the following information to
|
|
## ensure the GNU Lesser General Public License version 3 requirements
|
|
## will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
|
|
##
|
|
## GNU General Public License Usage
|
|
## Alternatively, this file may be used under the terms of the GNU
|
|
## General Public License version 2.0 or (at your option) the GNU General
|
|
## Public license version 3 or any later version approved by the KDE Free
|
|
## Qt Foundation. The licenses are as published by the Free Software
|
|
## Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
|
|
## included in the packaging of this file. Please review the following
|
|
## information to ensure the GNU General Public License requirements will
|
|
## be met: https://www.gnu.org/licenses/gpl-2.0.html and
|
|
## https://www.gnu.org/licenses/gpl-3.0.html.
|
|
##
|
|
## $QT_END_LICENSE$
|
|
##
|
|
#############################################################################
|
|
|
|
script_argument_prefix="-Wobjc_namespace,--"
|
|
|
|
required_arguments="target suffix original_ld"
|
|
optional_arguments="exclude_list exclude_regex slient"
|
|
|
|
for argument in $required_arguments $optional_arguments; do
|
|
declare "$argument="
|
|
done
|
|
|
|
declare -i silent=0
|
|
declare -a linker_arguments
|
|
|
|
for i in "$@"; do
|
|
case $1 in
|
|
$script_argument_prefix*)
|
|
declare "${1#$script_argument_prefix}"
|
|
;;
|
|
-o)
|
|
if [ -n "$2" ]; then
|
|
target="$2"
|
|
fi
|
|
linker_arguments+=("$1")
|
|
;;
|
|
*)
|
|
linker_arguments+=("$1")
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
get_entry() {
|
|
local map=$1 key=$2
|
|
local i="${map}_map_${2}"
|
|
printf '%s' "${!i}"
|
|
}
|
|
|
|
error() {
|
|
echo "$0: error: $*" >&2
|
|
exit 1
|
|
}
|
|
|
|
for argument in $required_arguments; do
|
|
if [ -z "${!argument}" ]; then
|
|
error "missing argument --${argument}"
|
|
fi
|
|
done
|
|
|
|
# Normalize suffix so we can use it as a bash variable
|
|
suffix=${suffix//[-. ]/_}
|
|
|
|
link_binary() {
|
|
(PS4=; test $silent -ne 1 && set -x; $original_ld "${linker_arguments[@]}" "$@") 2>&1 || exit 1
|
|
}
|
|
|
|
sanitize_address() {
|
|
local address="$1"
|
|
address=${address#0x} # Remove hex prefix
|
|
address=${address: ${#address} < 8 ? 0 : -8} # Limit to 32-bit
|
|
echo "0x$address"
|
|
}
|
|
|
|
read_binary() {
|
|
local address=$1
|
|
local length=$2
|
|
|
|
dd if="$target" bs=1 iseek=$address count=$length 2>|/dev/null
|
|
}
|
|
|
|
read_32bit_value() {
|
|
local address=$1
|
|
read_binary $address 4 | xxd -p | dd conv=swab 2>/dev/null | rev
|
|
}
|
|
|
|
inspect_binary() {
|
|
inspect_mode="$1"
|
|
|
|
echo -n "🔎 Inspecting binary '$target', "
|
|
if [ ! -f "$target" ]; then
|
|
echo "target does not exist!"
|
|
exit 1
|
|
fi
|
|
|
|
read -a mach_header <<< "$(otool -h "$target" -v | tail -n 1)"
|
|
if [ "${mach_header[1]}" != "X86_64" ]; then
|
|
echo "binary is not 64-bit, only 64-bit binaries are supported!"
|
|
exit 1
|
|
fi
|
|
|
|
classnames_section="__objc_classname"
|
|
classnames=$(otool -v -s __TEXT $classnames_section "$target" | tail -n +3)
|
|
while read -a classname; do
|
|
address=$(sanitize_address ${classname[0]})
|
|
name=${classname[1]}
|
|
|
|
declare "address_to_classname_map_$address=$name"
|
|
declare "classname_to_address_map_$name=$address"
|
|
done <<< "$classnames"
|
|
|
|
extra_classnames_file="$(mktemp -t ${classnames_section}_additions).S"
|
|
|
|
if [ "$inspect_mode" == "inject_classnames" ]; then
|
|
echo "class names have not been namespaced, adding suffix '$suffix'..."
|
|
printf ".section __TEXT,$classnames_section,cstring_literals,no_dead_strip\n" > $extra_classnames_file
|
|
elif [ "$inspect_mode" == "patch_classes" ]; then
|
|
echo "found namespaced class names, updating class entries..."
|
|
fi
|
|
|
|
classes=$(otool -o -v "$target" | grep class_ro_t)
|
|
while read -a class; do
|
|
address="$(sanitize_address ${class[1]})"
|
|
|
|
class_flags="0x$(read_32bit_value $address)"
|
|
if [ -z "$class_flags" ]; then
|
|
echo " 💥 failed to read class flags for class at $address"
|
|
continue
|
|
fi
|
|
|
|
is_metaclass=$(($class_flags & 0x1))
|
|
|
|
name_offset=$(($address + 24))
|
|
classname_address="0x$(read_32bit_value $name_offset)"
|
|
if [ -z "$classname_address" ]; then
|
|
echo " 💥 failed to read class name address for class at $address"
|
|
continue
|
|
fi
|
|
|
|
classname=$(get_entry address_to_classname $classname_address)
|
|
if [ -z "$classname" ]; then
|
|
echo " 💥 failed to resolve class name for address '$classname_address'"
|
|
continue
|
|
fi
|
|
|
|
if [[ $exclude_list =~ $classname || $classname =~ $exclude_regex ]]; then
|
|
if [ $is_metaclass -eq 1 ]; then
|
|
class_type="meta class"
|
|
else
|
|
class_type="class"
|
|
fi
|
|
echo " 🚽 skipping excluded $class_type '$classname'"
|
|
continue
|
|
fi
|
|
|
|
newclassname="${classname}_${suffix}"
|
|
|
|
if [ "$inspect_mode" == "inject_classnames" ]; then
|
|
if [ $is_metaclass -eq 1 ]; then
|
|
continue
|
|
fi
|
|
|
|
echo " 💉 injecting $classnames_section entry '$newclassname' for '$classname'"
|
|
printf ".asciz \"$newclassname\"\n" >> $extra_classnames_file
|
|
|
|
elif [ "$inspect_mode" == "patch_classes" ]; then
|
|
newclassname_address=$(get_entry classname_to_address ${newclassname})
|
|
if [ -z "$newclassname_address" ]; then
|
|
echo " 💥 failed to resolve class name address for class '$newclassname'"
|
|
continue
|
|
fi
|
|
|
|
if [ $is_metaclass -eq 1 ]; then
|
|
class_type="meta"
|
|
else
|
|
class_type="class"
|
|
fi
|
|
|
|
echo " 🔨 patching class_ro_t at $address ($class_type) from $classname_address ($classname) to $newclassname_address ($newclassname)"
|
|
echo ${newclassname_address: -8} | rev | dd conv=swab 2>/dev/null | xxd -p -r -seek $name_offset -l 4 - "$target"
|
|
fi
|
|
done <<< "$classes"
|
|
}
|
|
|
|
echo "🔩 Linking binary using '$original_ld'..."
|
|
link_binary
|
|
|
|
inspect_binary inject_classnames
|
|
|
|
echo "🔩 Re-linking binary with extra __objc_classname section..."
|
|
link_binary $extra_classnames_file
|
|
|
|
inspect_binary patch_classes
|
|
|