Bypass Loading of User Plugins
Summary: User plugins, which are plugins at `~/Library/Application Support/Developer/Shared/Xcode/Plug-ins`, can cause issues if linked into the same image as a process that already references the same symbols. In order to save time and prevent runtime linker issues it makes sense to bypass the loading of these symbols. The simplest way of doing this is swizzling the bundle loader method and log/no-op in the event we have one of these bundles. Reviewed By: mmmulani Differential Revision: D3392950 fbshipit-source-id: cf173c8bd548d2b26d8cbc4dbb27d226582bf036
This commit is contained in:
Родитель
f5b3f2d6a5
Коммит
7b359005b1
|
@ -20,6 +20,7 @@
|
|||
/**
|
||||
Loads a list of Frameworks.
|
||||
Will avoid re-loading already loaded Frameworks.
|
||||
Will also completely bypass loading of user plugins to prevent compatability issues.
|
||||
|
||||
@param weakFrameworks a list of frameworks to load
|
||||
@param logger a logger for logging framework loading activities.
|
||||
|
|
|
@ -9,14 +9,49 @@
|
|||
|
||||
#import "FBWeakFrameworkLoader.h"
|
||||
|
||||
#import <FBControlCore/FBControlCore.h>
|
||||
|
||||
#import <objc/runtime.h>
|
||||
|
||||
#import "FBCollectionInformation.h"
|
||||
#import "FBControlCoreError.h"
|
||||
#import "FBControlCoreGlobalConfiguration.h"
|
||||
#import "FBControlCoreLogger.h"
|
||||
#import "FBWeakFramework.h"
|
||||
|
||||
static BOOL (*originalNSBundleLoad)(NSBundle *, SEL, NSError **);
|
||||
static NSString *const ignoredPathSlice = @"Library/Application Support/Developer/Shared/Xcode/Plug-ins";
|
||||
|
||||
/**
|
||||
Loading xcplugins can pose an issue if the xcplugin statically links a symbol that the current process has linked dynamically.
|
||||
If we bypass the loading of these plugins, we can be more confident that there won't be ambiguous symbols at runtime.
|
||||
*/
|
||||
static BOOL FBUserPluginBypassingBundleLoad(NSBundle *bundle, SEL selector, NSError **error)
|
||||
{
|
||||
if (![bundle.bundlePath.pathExtension isEqualToString:@"xcplugin"]) {
|
||||
return originalNSBundleLoad(bundle, selector, error);
|
||||
}
|
||||
NSString *pluginPath = bundle.bundlePath.stringByDeletingLastPathComponent;
|
||||
if (![pluginPath hasSuffix:ignoredPathSlice]) {
|
||||
return originalNSBundleLoad(bundle, selector, error);
|
||||
}
|
||||
id<FBControlCoreLogger> logger = FBControlCoreGlobalConfiguration.defaultLogger;
|
||||
[logger.debug logFormat:@"Bypassing load of %@ as it is a User Plugin", bundle.bundlePath];
|
||||
return YES;
|
||||
}
|
||||
|
||||
@implementation FBWeakFrameworkLoader
|
||||
|
||||
+ (void)swizzleBundleLoader
|
||||
{
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
Method method = class_getInstanceMethod(NSBundle.class, @selector(loadAndReturnError:));
|
||||
originalNSBundleLoad = (BOOL(*)(NSBundle *, SEL, NSError **)) method_getImplementation(method);
|
||||
method_setImplementation(method, (IMP) FBUserPluginBypassingBundleLoad);
|
||||
});
|
||||
}
|
||||
|
||||
// A Mapping of Class Names to the Frameworks that they belong to. This serves to:
|
||||
// 1) Represent the Frameworks that FBControlCore is dependent on via their classes
|
||||
// 2) Provide a path to the relevant Framework.
|
||||
|
@ -26,6 +61,9 @@
|
|||
// 5) Provide a sanity check that any preloaded Private Frameworks match the current xcode-select version
|
||||
+ (BOOL)loadPrivateFrameworks:(NSArray<FBWeakFramework *> *)weakFrameworks logger:(id<FBControlCoreLogger>)logger error:(NSError **)error
|
||||
{
|
||||
// Swizzle the bundle loader to ensure that we don't load user plugins.
|
||||
[self swizzleBundleLoader];
|
||||
|
||||
for (FBWeakFramework *framework in weakFrameworks) {
|
||||
NSError *innerError = nil;
|
||||
if (![framework loadWithLogger:logger error:&innerError]) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче