WebKitTools:
[WebKit-https.git] / WebKitTools / WebKitLauncher / WebKitNightlyEnabler.m
1 /*
2  * Copyright (C) 2006 Apple Computer, Inc.  All rights reserved.
3  * Copyright (C) 2006 Graham Dennis.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1.  Redistributions of source code must retain the above copyright
10  *     notice, this list of conditions and the following disclaimer. 
11  * 2.  Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution. 
14  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
15  *     its contributors may be used to endorse or promote products derived
16  *     from this software without specific prior written permission. 
17  *
18  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
19  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #import <Cocoa/Cocoa.h>
31 #include <mach-o/dyld.h>
32 #include <dlfcn.h>
33 #include <mach-o/loader.h>
34 #include <mach-o/nlist.h>
35 #include <string.h>
36
37 static void cleanUpAfterOurselves(void) __attribute__ ((constructor));
38 static void *symbol_lookup(char *symbol);
39
40 static bool extensionBundlesWereLoaded = NO;
41 static NSSet *extensionPaths = nil;
42
43 static void myBundleDidLoad(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
44 {
45     // Break out early if we have already detected an extension
46     if (extensionBundlesWereLoaded)
47         return;
48
49     NSBundle *bundle = (NSBundle *)object;
50     NSString *bundlePath = [[bundle bundlePath] stringByAbbreviatingWithTildeInPath];
51     NSString *bundleFileName = [bundlePath lastPathComponent];
52
53     // Explicitly ignore SIMBL.bundle, as its only purpose is to load extensions
54     // on a per-application basis.  It's presence indicates a user has application
55     // extensions, but not that any will be loaded into Safari
56     if ([bundleFileName isEqualToString:@"SIMBL.bundle"])
57         return;
58
59     // If the bundle lives inside a known extension path, flag it as an extension
60     NSEnumerator *e = [extensionPaths objectEnumerator];
61     NSString *path = nil;
62     while (path = [e nextObject]) {
63         if ([bundlePath length] < [path length])
64             continue;
65
66         if ([[bundlePath substringToIndex:[path length]] isEqualToString:path]) {
67             extensionBundlesWereLoaded = YES;
68             break;
69         }
70     }
71 }
72
73 static void myApplicationWillFinishLaunching(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
74 {
75     CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), &myApplicationWillFinishLaunching, NULL, NULL);
76     CFNotificationCenterRemoveObserver(CFNotificationCenterGetLocalCenter(), &myBundleDidLoad, NULL, NULL);
77     [extensionPaths release];
78     extensionPaths = nil;
79
80     if (extensionBundlesWereLoaded)
81         NSRunInformationalAlertPanel(@"Safari extensions detected",
82                                      @"Safari extensions were detected on your system. They are incompatible with nightly builds of WebKit, and may cause crashes or incorrect behavior.  Please disable them if you experience such behavior.", @"Continue", nil, nil);
83 }
84
85 void cleanUpAfterOurselves(void)
86 {
87     char **args = *(char***)_NSGetArgv();
88     char **procPath = symbol_lookup("___CFProcessPath");
89     char *procPathBackup = *procPath;
90     *procPath = args[0];
91     CFBundleGetMainBundle();
92     *procPath = procPathBackup;
93     unsetenv("DYLD_INSERT_LIBRARIES");
94     unsetenv("CFProcessPath");
95
96     extensionPaths = [[NSSet alloc] initWithObjects:@"~/Library/InputManagers/", @"/Library/InputManagers/",
97                                                     @"~/Library/Application Support/SIMBL/Plugins/", @"/Library/Application Support/SIMBL/Plugins/",
98                                                     @"~/Library/Application Enhancers/", @"/Library/Application Enhancers/",
99                                                     nil];
100
101     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), &myBundleDidLoad,
102                                     myBundleDidLoad, (CFStringRef) NSBundleDidLoadNotification,
103                                     NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
104     CFNotificationCenterAddObserver(CFNotificationCenterGetLocalCenter(), &myApplicationWillFinishLaunching,
105                                     myApplicationWillFinishLaunching, (CFStringRef) NSApplicationWillFinishLaunchingNotification,
106                                     NULL, CFNotificationSuspensionBehaviorDeliverImmediately);
107 }
108
109 #if __LP64__
110 #define LC_SEGMENT_COMMAND LC_SEGMENT_64
111 #define macho_header mach_header_64
112 #define macho_segment_command segment_command_64
113 #define macho_section section_64
114 #define getsectdatafromheader getsectdatafromheader_64
115 #define macho_nlist nlist_64
116 #else
117 #define LC_SEGMENT_COMMAND LC_SEGMENT
118 #define macho_header mach_header
119 #define macho_segment_command segment_command
120 #define macho_section section
121 #define macho_nlist nlist
122 #endif
123
124 void *GDSymbolLookup(const struct macho_header *header, const char *symbol);
125
126 void *symbol_lookup(char *symbol)
127 {
128     int i;
129     for(i=0;i<_dyld_image_count();i++)
130     {    
131         void *symbolResult = GDSymbolLookup((const struct macho_header*)_dyld_get_image_header(i), symbol);
132         if (symbolResult)
133             return symbolResult;
134     }
135     return NULL;
136 }
137
138 void *GDSymbolLookup(const struct macho_header *header, const char *symbol)
139 {
140     if (!header || !symbol)
141         return NULL;
142     if ((header->magic != MH_MAGIC) && (header->magic != MH_MAGIC_64))
143         return NULL;
144     
145     uint32_t currCommand;
146     const struct load_command *loadCommand = (const struct load_command *)( ((void *)header) + sizeof(struct macho_header));
147     const struct macho_segment_command *segCommand;
148     
149     const struct symtab_command *symtabCommand = NULL;
150     const struct dysymtab_command *dysymtabCommand = NULL;
151     const struct macho_segment_command *textSegment = NULL;
152     const struct macho_segment_command *linkEditSegment = NULL;
153     
154     for (currCommand = 0; currCommand < header->ncmds; currCommand++)
155     {
156         switch (loadCommand->cmd)
157         {
158             case LC_SEGMENT_COMMAND:
159                 segCommand = (const struct macho_segment_command *)loadCommand;
160                 if (strcmp(segCommand->segname, "__TEXT")==0)
161                     textSegment = segCommand;
162                 else if (strcmp(segCommand->segname, "__LINKEDIT")==0)
163                     linkEditSegment = segCommand;
164                     break;
165                 
166             case LC_SYMTAB:
167                 symtabCommand = (const struct symtab_command *)loadCommand;
168                 break;
169                 
170             case LC_DYSYMTAB:
171                 dysymtabCommand = (const struct dysymtab_command *)loadCommand;
172                 break;
173         }
174         
175         loadCommand = (const struct load_command *)(((void*)loadCommand) + loadCommand->cmdsize);
176     }
177     if (textSegment==NULL || linkEditSegment==NULL || symtabCommand==NULL || dysymtabCommand==NULL) {
178         return NULL;
179     }
180     
181     typedef enum { Start = 0, LocalSymbols, ExternalSymbols, Done } SymbolSearchState;
182     uint32_t currentSymbolIndex;
183     uint32_t maximumSymbolIndex;
184     SymbolSearchState state;
185     
186     for (state = Start + 1; state < Done; state++)
187     {
188         switch(state) {
189             case LocalSymbols:
190                 currentSymbolIndex = dysymtabCommand->ilocalsym;
191                 maximumSymbolIndex = dysymtabCommand->ilocalsym + dysymtabCommand->nlocalsym;
192                 break;
193                 
194             case ExternalSymbols:
195                 currentSymbolIndex = dysymtabCommand->iextdefsym;
196                 maximumSymbolIndex = dysymtabCommand->nextdefsym;
197                 break;
198                 
199             default:
200                 return NULL;
201         }
202         for (; currentSymbolIndex < maximumSymbolIndex; currentSymbolIndex++)
203         {
204             const struct macho_nlist *symbolTableEntry;
205             symbolTableEntry = (const struct macho_nlist *)(currentSymbolIndex*sizeof(struct macho_nlist)
206                                                             + (ptrdiff_t)header + symtabCommand->symoff
207                                                             + linkEditSegment->vmaddr - linkEditSegment->fileoff
208                                                             - textSegment->vmaddr);
209             int32_t stringTableIndex = symbolTableEntry->n_un.n_strx;
210             if (stringTableIndex<0)
211                 continue;
212             const char *stringTableEntry = (const char*)(stringTableIndex + (ptrdiff_t)header + symtabCommand->stroff
213                                                          + linkEditSegment->vmaddr - linkEditSegment->fileoff
214                                                          - textSegment->vmaddr);
215             if (strcmp(symbol, stringTableEntry)==0) {
216                 return ((void*)header) - textSegment->vmaddr + symbolTableEntry->n_value;
217             }
218         }
219         state++;
220     }
221     return NULL;
222 }
223
224