iOS: Wrap user's main by renaming symbol and relying on weak linking
This approach is similar to the earlier apprach of defining main=qt_main when building the user's sources, but uses the linker to rename the symbol instead, which is less fragile than using the preprocessor. To keep the hybrid usecase unaffected by our wrapper logic we declare both our main wrapper and a fallback qt_main as weak symbols, which ensures that when the user's application links in our plugin the real main/qt_main provided by the user is preferred over our weak symbols. Change-Id: Ic76f3ba8932430c4b13a1d3a40b8ed2322fe5eea Reviewed-by: Simon Hausmann <simon.hausmann@digia.com>
This commit is contained in:
parent
b5fe1ed172
commit
32f34ddbe1
@ -1,8 +1,5 @@
|
||||
|
||||
equals(TEMPLATE, app):contains(QT, gui(-private)?) {
|
||||
!macx-xcode: \
|
||||
error("Linking the iOS platform plugin requires bulding through Xcode")
|
||||
|
||||
LIBS *= -L$$[QT_INSTALL_PLUGINS/get]/platforms
|
||||
|
||||
lib_name = qios
|
||||
@ -21,54 +18,31 @@ equals(TEMPLATE, app):contains(QT, gui(-private)?) {
|
||||
CONFIG -= import_qpa_plugin
|
||||
|
||||
!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 ld to rename the _main symbol to _qt_main, so that we don't get a symbol clash
|
||||
# with the _main we provide that calls UIApplicationMain. We need to make a copy of the
|
||||
# original object file, as ld will not copy over DWARF debug information to the output
|
||||
# file. Instead, it will inject a reference back to the original object file, so when
|
||||
# Xcode runs dsymutil to make the final dSYM file it will still find the debug info
|
||||
# for the object file that provided the original _main. This back-reference has the
|
||||
# interesting side-effect of the debug information still referring to the original
|
||||
# symbol name, so stack-traces will show both our wrapper main and the original
|
||||
# user main as 'main', and adding a symbolic breakpoint for 'main' will break on
|
||||
# both functions. Although a bit weird, it's a good thing, as the user will still be
|
||||
# able to add symbolic breakpoints for 'main', not caring that the symbol is actually
|
||||
# called 'qt_main' now.
|
||||
|
||||
# We 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 = "$(SDK_DIR)/usr/lib/crt1.3.1.o"
|
||||
isEmpty(OBJECTS_DIR): \
|
||||
OBJECTS_DIR = .
|
||||
|
||||
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"
|
||||
!isEmpty(QMAKE_PRE_LINK): \
|
||||
QMAKE_PRE_LINK += ";"
|
||||
|
||||
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" \
|
||||
QMAKE_PRE_LINK += \
|
||||
"for f in $(find $${OBJECTS_DIR} -name '*.o'); do" \
|
||||
"(nm $f | grep -q 'T _main' && cp $f $f.original" \
|
||||
"&& ld -r -alias _main _qt_main -unexported_symbol _main $f.original -o $f)" \
|
||||
"|| true" \
|
||||
"; 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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +213,7 @@ static int infoPlistValue(NSString* key, int defaultValue)
|
||||
return value ? [value intValue] : defaultValue;
|
||||
}
|
||||
|
||||
extern "C" int qtmn(int argc, char *argv[])
|
||||
extern "C" int __attribute__((weak)) main(int argc, char *argv[])
|
||||
{
|
||||
@autoreleasepool {
|
||||
size_t defaultStackSize = 512 * kBytesPerKiloByte; // Same as secondary threads
|
||||
@ -248,7 +248,18 @@ enum SetJumpResult
|
||||
kJumpedFromUserMainTrampoline,
|
||||
};
|
||||
|
||||
extern "C" int main(int argc, char *argv[]);
|
||||
// We define qt_main so that user_main_trampoline() will not cause
|
||||
// missing symbols in the case of hybrid applications that don't
|
||||
// user our main wrapper. Since the symbol is weak, it will not
|
||||
// get used or cause a clash in the normal Qt application usecase,
|
||||
// where we rename main to qt_main.
|
||||
extern "C" int __attribute__((weak)) qt_main(int argc, char *argv[])
|
||||
{
|
||||
Q_UNUSED(argc);
|
||||
Q_UNUSED(argv);
|
||||
|
||||
Q_UNREACHABLE();
|
||||
}
|
||||
|
||||
static void __attribute__((noinline, noreturn)) user_main_trampoline()
|
||||
{
|
||||
@ -261,7 +272,7 @@ static void __attribute__((noinline, noreturn)) user_main_trampoline()
|
||||
strcpy(argv[i], [arg cStringUsingEncoding:[NSString defaultCStringEncoding]]);
|
||||
}
|
||||
|
||||
int exitCode = main(argc, argv);
|
||||
int exitCode = qt_main(argc, argv);
|
||||
delete[] argv;
|
||||
|
||||
qEventDispatcherDebug() << "Returned from main with exit code " << exitCode;
|
||||
|
Loading…
Reference in New Issue
Block a user