1#!/bin/bash 2# Copyright 2020 The TensorFlow Authors. All Rights Reserved. 3# 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15# ============================================================================== 16# 17# A script to merge Mach-O object files into a single object file and hide 18# their internal symbols. Only allowed symbols will be visible in the 19# symbol table after this script. 20 21# To run this script, you must set several variables: 22# INPUT_FRAMEWORK: a zip file containing the iOS static framework. 23# BUNDLE_NAME: the pod/bundle name of the iOS static framework. 24# ALLOWLIST_FILE_PATH: contains the allowed symbols. 25# EXTRACT_SCRIPT_PATH: path to the extract_object_files script. 26# OUTPUT: the output zip file. 27 28# Halt on any error or any unknown variable. 29set -ue 30 31# mktemp from coreutils has different flags. Make sure we get the iOS one. 32MKTEMP=/usr/bin/mktemp 33 34LD_DEBUGGABLE_FLAGS="-x" 35# Uncomment the below to get debuggable output. This can only be done for one 36# library at a time. 37# LD_DEBUGGABLE_FLAGS="-d" 38 39# Exits if C++ symbols are found in the allowlist. 40if grep -q "^__Z" "${ALLOWLIST_FILE_PATH}"; then 41 echo "ERROR: Failed in symbol hiding. This rule does not permit hiding of" \ 42 "C++ symbols due to possible serious problems mixing symbol hiding," \ 43 "shared libraries and the C++ runtime." \ 44 "More info can be found in go/ios-symbols-hiding." \ 45 "Please recheck the allowlist and remove C++ symbols:" 46 echo "$(grep "^__Z" "${ALLOWLIST_FILE_PATH}")" 47 exit 1 # terminate and indicate error 48fi 49# Unzips the framework zip file into a temp workspace. 50framework=$($MKTEMP -t framework -d) 51unzip "${INPUT_FRAMEWORK}" -d "${framework}"/ 52 53# Executable file in the framework. 54executable_file="${BUNDLE_NAME}.framework/${BUNDLE_NAME}" 55 56# Extracts architectures from the framework binary. 57archs_str=$(xcrun lipo -info "${framework}/${executable_file}" | 58sed -En -e 's/^(Non-|Architectures in the )fat file: .+( is architecture| are): (.*)$/\3/p') 59 60IFS=' ' read -r -a archs <<< "${archs_str}" 61 62merge_cmd=(xcrun lipo) 63 64# Merges object files and hide symbols for each architecture. 65for arch in "${archs[@]}"; do 66 archdir=$($MKTEMP -t "${arch}" -d) 67 arch_file="${archdir}/${arch}" 68 69 # Handles the binary differently if they are fat or thin. 70 if [[ "${#archs[@]}" -gt 1 ]]; then 71 xcrun lipo "${framework}/${executable_file}" -thin "${arch}" -output "${arch_file}" 72 else 73 mv "${framework}/${executable_file}" "${arch_file}" 74 fi 75 if [[ "$arch" == "armv7" ]]; then 76 # Check that there are no thread local variables in the input, as they get broken. 77 # See b/124533863. 78 thread_locals=$(xcrun nm -m -g "${arch_file}" | awk '/__DATA,__thread_vars/ { print $5 }' | c++filt) 79 if [[ -n "${thread_locals}" ]]; then 80 echo 81 echo "WARNING: This symbol hiding script breaks thread local variables on 32-bit arm, you had:" 82 echo "${thread_locals}" 83 echo 84 echo "Your build will crash if these variables are actually used at runtime." 85 echo 86 fi 87 fi 88 if [[ ! -z "${EXTRACT_SCRIPT_PATH}" ]]; then 89 "${EXTRACT_SCRIPT_PATH}" "${arch_file}" "${archdir}" 90 else 91 # ar tool extracts the objects in the current working directory. Since the 92 # default working directory for a genrule is always the same, there can be 93 # a race condition when this script is called for multiple targets 94 # simultaneously. 95 pushd "${archdir}" > /dev/null 96 xcrun ar -x "${arch_file}" 97 popd > /dev/null 98 fi 99 100 objects_file_list=$($MKTEMP) 101 # Hides the symbols except the allowed ones. 102 find "${archdir}" -name "*.o" >> "${objects_file_list}" 103 104 # Checks whether bitcode is enabled in the framework. 105 all_objects_have_bitcode=true 106 for object_file in $(cat "$objects_file_list"); do 107 if otool -arch "${arch}" -l "${object_file}" | grep -q __LLVM; then 108 : # Do nothing 109 else 110 echo "The ${arch} in ${object_file} is NOT bitcode-enabled." 111 all_objects_have_bitcode=false 112 break 113 fi 114 done 115 if [[ "$all_objects_have_bitcode" = "true" ]]; then 116 echo "The ${arch} in ${executable_file} is fully bitcode-enabled." 117 xcrun ld -r -bitcode_bundle -exported_symbols_list \ 118 "${ALLOWLIST_FILE_PATH}" \ 119 $LD_DEBUGGABLE_FLAGS \ 120 -filelist "${objects_file_list}" -o "${arch_file}_processed.o" 121 else 122 echo "The ${arch} in ${executable_file} is NOT fully bitcode-enabled." 123 xcrun ld -r -exported_symbols_list \ 124 "${ALLOWLIST_FILE_PATH}" \ 125 $LD_DEBUGGABLE_FLAGS \ 126 -filelist "${objects_file_list}" -o "${arch_file}_processed.o" 127 fi 128 129 output_object="${framework}/${arch}" 130 131 mv "${arch_file}_processed.o" "${output_object}" 132 rm -rf "${archdir}" 133 rm "${objects_file_list}" 134 merge_cmd+=(-arch "${arch}" "${output_object}") 135done 136 137# Repackages the processed object files. 138unzip "${INPUT_FRAMEWORK}" 139merge_cmd+=(-create -output "${BUNDLE_NAME}") 140"${merge_cmd[@]}" 141 142chmod +x "${BUNDLE_NAME}" 143rm "${executable_file}" 144mv "${BUNDLE_NAME}" "${executable_file}" 145( TZ=UTC find "${BUNDLE_NAME}.framework/" -exec touch -h -t 198001010000 {} \+ ) 146zip --compression-method store --symlinks --recurse-paths --quiet "${OUTPUT}" "${BUNDLE_NAME}.framework/" 147