summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTor Arne Vestbø <tor.arne.vestbo@digia.com>2013-09-06 23:16:28 +0200
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-09-11 21:50:55 +0200
commit8a425026820cab9ea4b4e8bb05afde9f1092edd3 (patch)
tree1bbb66887374135c94a8becd666c369efec217e7
parentfc2b36ca1def7301c4af8b3ad2dca2f5a1b0a568 (diff)
iOS: Change main-wrapper logic to not require changing the user's main
Instead of using a define to rename the user's main() function during compilation, we leave the user code alone, and inject our wrapper one step earlier in the process, at the application entry point 'start'. This entry point is provided by crt1.o, which is normally linked into the application automatically. The start() function sets up some state and then calls main(), but we change the start() function to instead call our main wrapper. Instead of shipping our own crt1 binary/sources, we make a copy of the appropriate crt1.o at build time, and modify its symbol table in place. This is unproblematic as long as we keep the same length for the wrapper function name, as the symbol names are just entries in the global string table of the object file. The result is that for the regular Qt use-case the user won't see any changes to their main function, and we have more control over the startup sequence. For the hybrid use-case, we no longer rely on the fragile solution of having our back-up 'main' symbol in a single translation unit, which would break eg with --load_all, and we don't need to provide a dummy 'qt_user_main' symbol. OSX 10.8 and iOS 6.0 introduced a new load command called LC_MAIN, which places the state setup in the shared dyld, and then just calls main() directly. Once we bump the minimum deployment target to iOS 6.0 we can start using this loader instead of LC_UNIXTHREAD, but for now we force the classic loader using the -no_new_main flag. There's also a bug in the ld64 linker provided by the current Xcode toolchains that results in the -e linker flag (to set the entry point) having no effect, but hopefully this bug has been fixed (or Apple has switched to the LLVM lld linker) by the time we bump our deployment target. Change-Id: Ie0ba869c13ddc5277dc95c539aebaeb60e949dc2 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@digia.com>
-rw-r--r--mkspecs/features/ios/qt.prf56
-rw-r--r--src/plugins/platforms/ios/ios.pro1
-rw-r--r--src/plugins/platforms/ios/qiosapplicationdelegate.mm4
-rw-r--r--src/plugins/platforms/ios/qiosintegration.mm5
-rw-r--r--src/plugins/platforms/ios/qiosmain_dummy.mm56
-rw-r--r--src/plugins/platforms/ios/qiosmain_wrapper.mm13
6 files changed, 58 insertions, 77 deletions
diff --git a/mkspecs/features/ios/qt.prf b/mkspecs/features/ios/qt.prf
index 80d4907acd..f89cb0c287 100644
--- a/mkspecs/features/ios/qt.prf
+++ b/mkspecs/features/ios/qt.prf
@@ -17,8 +17,60 @@ equals(TEMPLATE, app):contains(QT, gui(-private)?) {
# need to generate an import for it.
CONFIG -= import_qpa_plugin
- # FIXME: Solve using 'ld -r -alias -unexported_symbol' instead
- !no_main_wrapper: DEFINES += main=qt_user_main
+ !no_main_wrapper {
+ # Instead of messing with the user's main function we go the other
+ # way and change the application entry point to call our main wrapper.
+ # This entry point is the 'start' symbol, provided by crt1.o, so we
+ # make a copy of the file and rename the '_main' unresolved symbol
+ # to our wrapper function, '_qtmn', injecting ourselves into the app
+ # startup. Once Apple starts shipping the LLVM linker (lld) we may
+ # get rid of this step completely and just pass -e _qtmn to the
+ # linker, taking advantage of the new LC_MAIN load command.
+
+ # We use xcodebuild to resolve the location of the crt1 object file
+ # as we know that it lives in the same location as the c library.
+ c_library_path = $$system("/usr/bin/xcodebuild -sdk $$QMAKE_MAC_SDK -find-library c 2>/dev/null")
+
+ # We also know that iOS 3.1 and up uses crt1.3.1.o (technically not
+ # true for simulator, but the SDK has a symlink to the correct file).
+ original_crt_path = $$dirname(c_library_path)/crt1.3.1.o
+
+ xcode_objects_path = "$(OBJECT_FILE_DIR_$(CURRENT_VARIANT))/$(CURRENT_ARCH)"
+ custom_crt_filename = "crt1_qt.o"
+ custom_crt_path = "$$xcode_objects_path/$$custom_crt_filename"
+
+ EOC = $$escape_expand(\\n\\t)
+ create_custom_crt.commands = \
+ # Copy original crt1 to build directory
+ "$$QMAKE_COPY_FILE $$original_crt_path $$custom_crt_path $$EOC" \
+ # And rename all occurrences of _main to _qtmn
+ "strings -t d - $${custom_crt_path}" \
+ "| sed -n 's/^\\([0-9]\\{1,\\}\\) _main\$\$/\1/p'" \
+ "| while read offset; do" \
+ "printf '_qtmn'" \
+ "| dd of=$${custom_crt_path} bs=1 seek=\$\$offset conv=notrunc >/dev/null 2>&1" \
+ "; done"
+ create_custom_crt.depends = $$original_crt_path
+ create_custom_crt.target = $$custom_crt_path
+ preprocess.depends = create_custom_crt
+ QMAKE_EXTRA_TARGETS += create_custom_crt preprocess
+
+ clean_custom_crt.commands = "$$QMAKE_DEL_FILE $$custom_crt_path"
+ preprocess_clean.depends += clean_custom_crt
+ QMAKE_EXTRA_TARGETS += clean_custom_crt preprocess_clean
+
+ # Prevent usage of new LC_MAIN load command, which skips start/crt1
+ # and calls main from the loader. We rely on injecting into start.
+ QMAKE_LFLAGS += -Wl,-no_new_main
+
+ # Explicitly link against our modified crt1 object
+ QMAKE_LFLAGS += -nostartfiles -l$${custom_crt_filename}
+
+ # Workaround for QMAKE_PBX_LIBPATHS mangling the Xcode variables
+ lib_search_path.name = LIBRARY_SEARCH_PATHS
+ lib_search_path.value = $$xcode_objects_path
+ QMAKE_MAC_XCODE_SETTINGS += lib_search_path
+ }
}
load(qt)
diff --git a/src/plugins/platforms/ios/ios.pro b/src/plugins/platforms/ios/ios.pro
index 5a2129eb08..263577d43f 100644
--- a/src/plugins/platforms/ios/ios.pro
+++ b/src/plugins/platforms/ios/ios.pro
@@ -10,7 +10,6 @@ LIBS += -framework Foundation -framework UIKit -framework QuartzCore
OBJECTIVE_SOURCES = \
plugin.mm \
qiosmain_wrapper.mm \
- qiosmain_dummy.mm \
qiosintegration.mm \
qioswindow.mm \
qiosscreen.mm \
diff --git a/src/plugins/platforms/ios/qiosapplicationdelegate.mm b/src/plugins/platforms/ios/qiosapplicationdelegate.mm
index 52d94f38fb..d4fd613ae3 100644
--- a/src/plugins/platforms/ios/qiosapplicationdelegate.mm
+++ b/src/plugins/platforms/ios/qiosapplicationdelegate.mm
@@ -46,7 +46,7 @@
#include <QtCore/QtCore>
-extern int qt_user_main(int argc, char *argv[]);
+extern "C" int main(int argc, char *argv[]);
@implementation QIOSApplicationDelegate
@@ -87,7 +87,7 @@ extern int qt_user_main(int argc, char *argv[]);
strcpy(argv[i], [arg cStringUsingEncoding:[NSString defaultCStringEncoding]]);
}
- qt_user_main(argc, argv);
+ main(argc, argv);
delete[] argv;
}
diff --git a/src/plugins/platforms/ios/qiosintegration.mm b/src/plugins/platforms/ios/qiosintegration.mm
index 7fd6015a2f..cbf9dba862 100644
--- a/src/plugins/platforms/ios/qiosintegration.mm
+++ b/src/plugins/platforms/ios/qiosintegration.mm
@@ -65,10 +65,7 @@ QIOSIntegration::QIOSIntegration()
<< "Error: You are creating QApplication before calling UIApplicationMain.\n"
<< "If you are writing a native iOS application, and only want to use Qt for\n"
<< "parts of the application, a good place to create QApplication is from within\n"
- << "'applicationDidFinishLaunching' inside your UIApplication delegate.\n"
- << "If you instead create a cross-platform Qt application and do not intend to call\n"
- << "UIApplicationMain, you need to link in libqtmain.a, and substitute main with qt_main.\n"
- << "This is normally done automatically by qmake.\n";
+ << "'applicationDidFinishLaunching' inside your UIApplication delegate.\n";
exit(-1);
}
diff --git a/src/plugins/platforms/ios/qiosmain_dummy.mm b/src/plugins/platforms/ios/qiosmain_dummy.mm
deleted file mode 100644
index 28d7e59381..0000000000
--- a/src/plugins/platforms/ios/qiosmain_dummy.mm
+++ /dev/null
@@ -1,56 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
-** Contact: http://www.qt-project.org/legal
-**
-** This file is part of the plugins 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 Digia. For licensing terms and
-** conditions see http://qt.digia.com/licensing. For further information
-** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 2.1 requirements
-** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
-**
-** In addition, as a special exception, Digia gives you certain additional
-** rights. These rights are described in the Digia Qt LGPL Exception
-** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3.0 as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU General Public License version 3.0 requirements will be
-** met: http://www.gnu.org/copyleft/gpl.html.
-**
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QtCore/qglobal.h>
-
-/*
- This file provides a dummy implementation of qt_user_main, so that
- we don't get an undefined symbol in the hybrid use-case, where we
- don't rename main() to qt_user_main(). As long as the linker is not
- passed -all_load, this translation unit is only picked up and used
- if qt_user_main is not defined by the user's code.
-*/
-
-int qt_user_main(int, char **)
-{
- qFatal("Hit dummy qt_user_main, this should never happen!");
- return 0;
-}
diff --git a/src/plugins/platforms/ios/qiosmain_wrapper.mm b/src/plugins/platforms/ios/qiosmain_wrapper.mm
index d9b8c7311e..cb9a2c161e 100644
--- a/src/plugins/platforms/ios/qiosmain_wrapper.mm
+++ b/src/plugins/platforms/ios/qiosmain_wrapper.mm
@@ -41,18 +41,7 @@
#include "qiosapplicationdelegate.h"
-/*
- This file provides a wrapper implementation of main() for the non-
- hybrid use-case. The user's main is renamed to qt_user_main by the
- build rules, and we'll call out to that main at the appropriate time.
-
- This file purposly only exports a single symbol, _main, so that
- when the linker considers the translation unit for inclusion it
- will discard it when main has already been defined in the user's
- application for the hybrid use-case.
-*/
-
-int main(int argc, char *argv[])
+extern "C" int qtmn(int argc, char *argv[])
{
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([QIOSApplicationDelegate class]));