1#!/bin/bash 2 3# Copyright (c) 2012 The Chromium Authors. All rights reserved. 4# Use of this source code is governed by a BSD-style license that can be 5# found in the LICENSE file. 6 7# Copies a framework to its new home, "unversioning" it. 8# 9# Normally, frameworks are versioned bundles. The contents of a framework are 10# stored in a versioned directory within the bundle, and symbolic links 11# provide access to the actual code and resources. See 12# http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html 13# 14# The symbolic links usually found in frameworks create problems. Symbolic 15# links are excluded from code signatures. That means that it's possible to 16# remove or retarget a symbolic link within a framework without affecting the 17# seal. In Chrome's case, the outer .app bundle contains a framework where 18# all application code and resources live. In order for the signature on the 19# .app to be meaningful, it encompasses the framework. Because framework 20# resources are accessed through the framework's symbolic links, this 21# arrangement results in a case where the resources can be altered without 22# affecting the .app signature's validity. 23# 24# Indirection through symbolic links also carries a runtime performance 25# penalty on open() operations, although open() typically completes so quickly 26# that this is not considered a major performance problem. 27# 28# To resolve these problems, the frameworks that ship within Chrome's .app 29# bundle are unversioned. Unversioning is simple: instead of using the 30# original outer .framework directory as the framework that ships within the 31# .app, the inner versioned directory is used. Instead of accessing bundled 32# resources through symbolic links, they are accessed directly. In normal 33# situations, the only hard-coded use of the versioned directory is by dyld, 34# when loading the framework's code, but this is handled through a normal 35# Mach-O load command, and it is easy to adjust the load command to point to 36# the unversioned framework code rather than the versioned counterpart. 37# 38# The resulting framework bundles aren't strictly conforming, but they work 39# as well as normal versioned framework bundles. 40# 41# An option to skip running install_name_tool is available. By passing -I as 42# the first argument to this script, install_name_tool will be skipped. This 43# is only suitable for copied frameworks that will not be linked against, or 44# when install_name_tool will be run on any linker output when something is 45# linked against the copied framework. This option exists to allow signed 46# frameworks to pass through without subjecting them to any modifications that 47# would break their signatures. 48 49set -e 50 51RUN_INSTALL_NAME_TOOL=1 52if [ $# -eq 3 ] && [ "${1}" = "-I" ] ; then 53 shift 54 RUN_INSTALL_NAME_TOOL= 55fi 56 57if [ $# -ne 2 ] ; then 58 echo "usage: ${0} [-I] FRAMEWORK DESTINATION_DIR" >& 2 59 exit 1 60fi 61 62# FRAMEWORK should be a path to a versioned framework bundle, ending in 63# .framework. DESTINATION_DIR is the directory that the unversioned framework 64# bundle will be copied to. 65 66FRAMEWORK="${1}" 67DESTINATION_DIR="${2}" 68 69FRAMEWORK_NAME="$(basename "${FRAMEWORK}")" 70if [ "${FRAMEWORK_NAME: -10}" != ".framework" ] ; then 71 echo "${0}: ${FRAMEWORK_NAME} does not end in .framework" >& 2 72 exit 1 73fi 74FRAMEWORK_NAME_NOEXT="${FRAMEWORK_NAME:0:$((${#FRAMEWORK_NAME} - 10))}" 75 76# Find the current version. 77VERSIONS="${FRAMEWORK}/Versions" 78CURRENT_VERSION_LINK="${VERSIONS}/Current" 79CURRENT_VERSION_ID="$(readlink "${VERSIONS}/Current")" 80CURRENT_VERSION="${VERSIONS}/${CURRENT_VERSION_ID}" 81 82# Make sure that the framework's structure makes sense as a versioned bundle. 83if [ ! -e "${CURRENT_VERSION}/${FRAMEWORK_NAME_NOEXT}" ] ; then 84 echo "${0}: ${FRAMEWORK_NAME} does not contain a dylib" >& 2 85 exit 1 86fi 87 88DESTINATION="${DESTINATION_DIR}/${FRAMEWORK_NAME}" 89 90# Copy the versioned directory within the versioned framework to its 91# destination location. 92mkdir -p "${DESTINATION_DIR}" 93rsync -acC --delete --exclude Headers --exclude PrivateHeaders \ 94 --include '*.so' "${CURRENT_VERSION}/" "${DESTINATION}" 95 96if [[ -n "${RUN_INSTALL_NAME_TOOL}" ]]; then 97 # Adjust the Mach-O LC_ID_DYLIB load command in the framework. This does not 98 # change the LC_LOAD_DYLIB load commands in anything that may have already 99 # linked against the framework. Not all frameworks will actually need this 100 # to be changed. Some frameworks may already be built with the proper 101 # LC_ID_DYLIB for use as an unversioned framework. Xcode users can do this 102 # by setting LD_DYLIB_INSTALL_NAME to 103 # $(DYLIB_INSTALL_NAME_BASE:standardizepath)/$(WRAPPER_NAME)/$(PRODUCT_NAME) 104 # If invoking ld via gcc or g++, pass the desired path to -Wl,-install_name 105 # at link time. 106 FRAMEWORK_DYLIB="${DESTINATION}/${FRAMEWORK_NAME_NOEXT}" 107 LC_ID_DYLIB_OLD="$(otool -l "${FRAMEWORK_DYLIB}" | 108 grep -A10 "^ *cmd LC_ID_DYLIB$" | 109 grep -m1 "^ *name" | 110 sed -Ee 's/^ *name (.*) \(offset [0-9]+\)$/\1/')" 111 VERSION_PATH="/Versions/${CURRENT_VERSION_ID}/${FRAMEWORK_NAME_NOEXT}" 112 LC_ID_DYLIB_NEW="$(echo "${LC_ID_DYLIB_OLD}" | 113 sed -Ee "s%${VERSION_PATH}$%/${FRAMEWORK_NAME_NOEXT}%")" 114 115 if [ "${LC_ID_DYLIB_NEW}" != "${LC_ID_DYLIB_OLD}" ] ; then 116 install_name_tool -id "${LC_ID_DYLIB_NEW}" "${FRAMEWORK_DYLIB}" 117 fi 118fi 119