this repo has no description
1#!/usr/bin/env python3
2
3import sys, os, subprocess, re, getpass, datetime
4
5# Data
6library = False
7iossupport_system = False
8framework = False
9private_framework = False
10
11# Constants
12library_prefix = "/usr/lib/"
13iossupport_system_prefix = "/System/iOSSupport"
14framework_prefix = "/System/Library/Frameworks/"
15private_framework_prefix = "/System/Library/PrivateFrameworks/"
16
17#######################################################
18##### SET THIS TO WHERE YOUR class-dump BINARY IS #####
19#######################################################
20username = getpass.getuser()
21class_dump = "/Users/" + username + "/bin/class-dump"
22
23copyright_template = """/*
24 This file is part of Darling.
25
26 Copyright (C) {} Darling Developers
27
28 Darling is free software: you can redistribute it and/or modify
29 it under the terms of the GNU General Public License as published by
30 the Free Software Foundation, either version 3 of the License, or
31 (at your option) any later version.
32
33 Darling is distributed in the hope that it will be useful,
34 but WITHOUT ANY WARRANTY; without even the implied warranty of
35 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
36 GNU General Public License for more details.
37
38 You should have received a copy of the GNU General Public License
39 along with Darling. If not, see <http://www.gnu.org/licenses/>.
40*/
41
42"""
43copyright = copyright_template.format(datetime.datetime.now(datetime.timezone.utc).year)
44
45c_func_impl_stub = """
46void* %s(void)
47{
48 if (verbose) puts("STUB: %s called");
49 return NULL;
50}
51"""
52
53msg_handling = """- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
54{
55 return [NSMethodSignature signatureWithObjCTypes: \"v@:\"];
56}
57
58- (void)forwardInvocation:(NSInvocation *)anInvocation
59{
60 NSLog(@\"Stub called: %@ in %@\", NSStringFromSelector([anInvocation selector]), [self class]);
61}
62
63"""
64
65# Utility functions
66def usage():
67 print("Usage: " + sys.argv[0] + " <Mach-O> <output directory>")
68 exit(1)
69
70
71def extract_library_name(name):
72 name = re.search("/lib([A-Za-z0-9]+)\.dylib$", name).group(1)
73 print(name)
74 return name
75
76def extract_framework_name(name):
77 return name[name.rfind("/") + 1:]
78
79def write_objc_source_file_locs(cmake_file, classes, num_spaces):
80 for cls in classes:
81 cmake_file.write(" " * num_spaces + "src/%s.m\n" % cls)
82
83
84# Main program start
85if len(sys.argv) != 3:
86 usage()
87
88full_path = sys.argv[1]
89output_dir = sys.argv[2]
90validate_path = full_path
91
92try:
93 os.makedirs(output_dir)
94except FileExistsError:
95 pass
96
97if full_path.startswith(iossupport_system_prefix):
98 iossupport_system = True
99 validate_path = full_path[len(iossupport_system_prefix):]
100
101if validate_path.endswith(".dylib"):
102 library = True
103 target_name = extract_library_name(validate_path)
104elif len(validate_path) > len(framework_prefix) and validate_path[:len(framework_prefix)] == framework_prefix:
105 framework = True
106 target_name = extract_framework_name(validate_path)
107elif len(validate_path) > len(private_framework_prefix) and validate_path[
108 :len(private_framework_prefix)] == private_framework_prefix:
109 private_framework = True
110 target_name = extract_framework_name(validate_path)
111else:
112 print("Failed to recognize Mach-O location")
113 target_name = None
114 exit(1)
115
116header_dir = output_dir + "/include/" + target_name + "/"
117source_dir = output_dir + "/src/"
118
119try:
120 os.makedirs(header_dir)
121except FileExistsError:
122 pass
123
124try:
125 os.makedirs(source_dir)
126except FileExistsError:
127 pass
128
129# Get C functions
130
131c_func_out = subprocess.check_output(["nm", "-Ug", full_path])
132c_func_out = c_func_out.decode('utf8').strip()
133
134functions = []
135for line in c_func_out.splitlines():
136
137 if line == "":
138 continue
139
140 print(line)
141 if len(line.split(" ")) == 3:
142 address, id, name = line.split(" ")
143 # Remove the underscore
144 name = name[1:]
145
146 if id == "T":
147 functions.append(name)
148 else:
149 print("Skipping c function output line")
150
151class_dump_output = subprocess.check_output([class_dump, full_path]).decode('utf8').strip()
152uses_objc = "This file does not contain any Objective-C runtime information." not in class_dump_output
153
154c_header = open(header_dir + target_name + ".h", "w")
155c_source = open(source_dir + target_name + ".m", "w") if uses_objc else open(source_dir + target_name + ".c", "w")
156
157c_header.write(copyright)
158c_source.write(copyright)
159
160c_source.write("""
161#include <%s/%s.h>
162#include <stdlib.h>
163#include <stdio.h>
164
165static int verbose = 0;
166
167__attribute__((constructor))
168static void initme(void) {
169 verbose = getenv("STUB_VERBOSE") != NULL;
170}
171""" % (target_name, target_name))
172
173cmake = open(output_dir + "/CMakeLists.txt", "w")
174
175cmake.write("project(%s)\n\n" % target_name)
176
177# Get current and compat versions
178
179otool_out = subprocess.check_output(["otool", "-L", full_path])
180otool_out = otool_out.decode('utf8').strip()
181version_line = otool_out.splitlines()[1]
182
183get_versions = re.compile("\\(compatibility version (.*?), current version (.*?)\\)")
184
185compat, current = get_versions.search(version_line).groups()
186
187if library:
188 cmake.write("set(DYLIB_INSTALL_NAME \"%s\")\n" % full_path)
189cmake.write("set(DYLIB_COMPAT_VERSION \"%s\")\n" % compat)
190cmake.write("set(DYLIB_CURRENT_VERSION \"%s\")\n" % current)
191cmake.write("set(FRAMEWORK_VERSION \"A\")\n\n")
192
193c_header.write("\n#ifndef _%s_H_\n#define _%s_H_\n\n" % (target_name, target_name))
194
195if uses_objc:
196 c_header.write("#import <Foundation/Foundation.h>\n\n")
197
198 # typedef_void_regex = re.compile("(typedef void.+?;)", re.DOTALL | re.MULTILINE)
199 # for typedef_void_def in typedef_void_regex.finditer(class_dump_output):
200 # c_header.write(typedef_void_def.groups()[0])
201 # c_header.write("\n\n")
202 #
203 # blacklisted_structs = ["_NSRange"]
204 # structs_regex = re.compile("(struct (.+?) {.+?};)", re.DOTALL | re.MULTILINE)
205 # for struct_def in structs_regex.finditer(class_dump_output):
206 # struct_contents, struct_name = struct_def.groups()
207 # if struct_name in blacklisted_structs:
208 # continue
209 # c_header.write(struct_contents)
210 # c_header.write("\n\n")
211 #
212 # typedef_struct_regex = re.compile("(typedef struct {.+?}.+?;)", re.DOTALL | re.MULTILINE)
213 # for typedef_struct_def in typedef_struct_regex.finditer(class_dump_output):
214 # c_header.write(typedef_struct_def.groups()[0])
215 # c_header.write("\n\n")
216
217 classes = []
218
219 protocols_regex = re.compile("(@protocol (.+?)[\n ].+?@end)", re.DOTALL | re.MULTILINE)
220 classes_regex = re.compile("(@interface (.+?) :.+?@end)", re.DOTALL | re.MULTILINE)
221
222 blacklisted_protocols = ["NSObject", "NSSecureCoding", "NSCoding", "NSCopying", "NSFastEnumeration", "NSMutableCopying",
223 "NSURLConnectionDataDelegate", "NSURLSessionTaskDelegate", "NSURLConnectionDelegate", "NSLocking"
224 ]
225 for protocol_def in protocols_regex.finditer(class_dump_output):
226 protocol_contents, protocol_name = protocol_def.groups()
227 if protocol_name in blacklisted_protocols:
228 continue
229 proto_header = open(header_dir + protocol_name + ".h", "w")
230 proto_header.write(copyright)
231 proto_header.write("#include <Foundation/Foundation.h>\n\n")
232 # proto_header.write(protocol_contents)
233 proto_header.write("@protocol %s\n\n@end\n" % protocol_name)
234 proto_header.close()
235 c_header.write("#import <%s/%s.h>\n" % (target_name, protocol_name))
236
237 for class_def in classes_regex.finditer(class_dump_output):
238 class_contents, class_name = class_def.groups()
239 classes.append(class_name)
240 class_header = open(header_dir + class_name + ".h", "w")
241 class_header.write(copyright)
242 class_header.write("#include <Foundation/Foundation.h>\n\n")
243 # class_header.write(class_contents)
244 class_header.write("@interface %s : NSObject\n\n@end\n" % class_name)
245 class_header.close()
246 c_header.write("#import <%s/%s.h>\n" % (target_name, class_name))
247
248 class_impl = open(source_dir + class_name + ".m", "w")
249 class_impl.write(copyright)
250 class_impl.write("#import <%s/%s.h>\n\n" % (target_name, class_name))
251 class_impl.write("@implementation " + class_name + "\n\n")
252 class_impl.write(msg_handling)
253 class_impl.write("@end\n")
254
255 c_header.write("\n")
256
257for funcname in functions:
258 c_header.write("void* %s(void);\n" % funcname)
259 c_source.write(c_func_impl_stub % (funcname, funcname))
260
261if library:
262 cmake.write("add_darling_library(%s SHARED\n" % target_name)
263 cmake.write(" src/%s." % target_name + ("m" if uses_objc else "c") + "\n")
264 if uses_objc:
265 write_objc_source_file_locs(cmake, classes, 4)
266 cmake.write(")\n")
267 cmake.write("make_fat(%s)\n" % target_name)
268 libraries = "system objc Foundation" if uses_objc else "system"
269 cmake.write("target_link_libraries(%s %s)\n" % (target_name, libraries))
270 cmake.write("install(TARGETS %s DESTINATION libexec/darling/usr/lib)\n" % target_name)
271else:
272 cmake.write("remove_sdk_framework(%s\n" % target_name)
273 if private_framework:
274 cmake.write(" PRIVATE\n")
275 cmake.write(")\n\n")
276 cmake.write("generate_sdk_framework(%s\n" % target_name)
277 cmake.write(" VERSION ${FRAMEWORK_VERSION}\n")
278 cmake.write(" HEADER \"include/%s\"\n" % target_name)
279 if private_framework:
280 cmake.write(" PRIVATE\n")
281 cmake.write(")\n\n")
282 cmake.write("add_framework(%s\n" % target_name)
283 cmake.write(" FAT\n CURRENT_VERSION\n")
284 if iossupport_system:
285 cmake.write(" IOSSUPPORT\n")
286 if private_framework:
287 cmake.write(" PRIVATE\n")
288 cmake.write(" VERSION ${FRAMEWORK_VERSION}\n\n")
289 cmake.write(" SOURCES\n")
290 cmake.write(" src/%s." % target_name + ("m" if uses_objc else "c") + "\n")
291 if uses_objc:
292 write_objc_source_file_locs(cmake, classes, 8)
293
294 cmake.write("\n")
295
296 cmake.write(" DEPENDENCIES\n")
297 cmake.write(" system\n")
298 if uses_objc:
299 cmake.write(" objc\n")
300 cmake.write(" Foundation\n")
301 cmake.write(")\n")
302
303c_header.write("\n#endif\n")