]>
Commit | Line | Data |
---|---|---|
eaf282aa A |
1 | /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*- |
2 | * | |
3 | * Copyright (c) 2010 Apple Inc. All rights reserved. | |
4 | * | |
5 | * @APPLE_LICENSE_HEADER_START@ | |
6 | * | |
7 | * This file contains Original Code and/or Modifications of Original Code | |
8 | * as defined in and that are subject to the Apple Public Source License | |
9 | * Version 2.0 (the 'License'). You may not use this file except in | |
10 | * compliance with the License. Please obtain a copy of the License at | |
11 | * http://www.opensource.apple.com/apsl/ and read it before using this | |
12 | * file. | |
13 | * | |
14 | * The Original Code and all software distributed under the License are | |
15 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
16 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
17 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. | |
19 | * Please see the License for the specific language governing rights and | |
20 | * limitations under the License. | |
21 | * | |
22 | * @APPLE_LICENSE_HEADER_END@ | |
23 | */ | |
24 | ||
25 | #include <stdio.h> | |
26 | #include <stdlib.h> | |
27 | #include <fcntl.h> | |
28 | #include <vector> | |
29 | #include <dlfcn.h> | |
30 | #include <math.h> | |
31 | #include <unistd.h> | |
32 | #include <time.h> | |
33 | #include <unordered_map> | |
34 | ||
35 | #include "llvm-c/lto.h" | |
36 | // c header | |
37 | extern "C" { | |
38 | #include <xar/xar.h> | |
39 | } | |
40 | ||
41 | #include "bitcode_bundle.h" | |
42 | ||
43 | #include "Options.h" | |
44 | #include "ld.hpp" | |
45 | #include "Bitcode.hpp" | |
46 | #include "macho_relocatable_file.h" | |
47 | ||
48 | ||
49 | namespace ld { | |
50 | namespace passes { | |
51 | namespace bitcode_bundle { | |
52 | ||
53 | class BitcodeTempFile; | |
54 | ||
55 | class BitcodeAtom : public ld::Atom { | |
56 | static ld::Section bitcodeBundleSection; | |
57 | public: | |
58 | BitcodeAtom(); | |
59 | BitcodeAtom(BitcodeTempFile& tempfile); | |
60 | ~BitcodeAtom() { free(_content); } | |
61 | virtual ld::File* file() const { return NULL; } | |
62 | virtual const char* name() const { return "bitcode bundle"; } | |
63 | virtual uint64_t size() const { return _size; } | |
64 | virtual uint64_t objectAddress() const { return 0; } | |
65 | virtual void copyRawContent(uint8_t buffer[]) const | |
66 | { memcpy(buffer, _content, _size); } | |
67 | virtual void setScope(Scope) { } | |
68 | ||
69 | private: | |
70 | uint8_t* _content; | |
71 | uint64_t _size; | |
72 | }; | |
73 | ||
74 | ld::Section BitcodeAtom::bitcodeBundleSection("__LLVM", "__bundle", ld::Section::typeSectCreate); | |
75 | ||
76 | class BitcodeTempFile { | |
77 | public: | |
78 | BitcodeTempFile(const char* path, bool deleteAfterRead); | |
79 | ~BitcodeTempFile(); | |
80 | uint8_t* getContent() const { return _content; } | |
81 | uint64_t getSize() const { return _size; } | |
82 | private: | |
83 | friend class BitcodeAtom; | |
84 | const char* _path; | |
85 | uint8_t* _content; | |
86 | uint64_t _size; | |
87 | bool _deleteAfterRead; | |
88 | }; | |
89 | ||
90 | class BitcodeObfuscator { | |
91 | public: | |
92 | BitcodeObfuscator(); | |
93 | ~BitcodeObfuscator(); | |
94 | ||
95 | void addMustPreserveSymbols(const char* name); | |
96 | void bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath); | |
97 | void writeSymbolMap(const char* outputPath); | |
98 | private: | |
99 | typedef void (*lto_codegen_func_t) (lto_code_gen_t); | |
100 | typedef void (*lto_codegen_output_t) (lto_code_gen_t, const char*); | |
101 | ||
102 | lto_code_gen_t _obfuscator; | |
103 | lto_codegen_func_t _lto_hide_symbols; | |
104 | lto_codegen_func_t _lto_reset_context; | |
105 | lto_codegen_output_t _lto_write_reverse_map; | |
106 | }; | |
107 | ||
108 | class FileHandler { | |
109 | // generic handler for files in a bundle | |
110 | public: | |
111 | virtual void populateMustPreserveSymbols(BitcodeObfuscator* _obfuscator) { } | |
112 | virtual void obfuscateAndWriteToPath(BitcodeObfuscator* _obfuscator, const char* path) { }; | |
113 | xar_file_t getXARFile() { return _xar_file; } | |
114 | ||
115 | FileHandler(char* content, size_t size) : | |
116 | _parent(NULL), _xar_file(NULL), _file_buffer(content), _file_size(size) { } // eager construct | |
117 | FileHandler(xar_t parent, xar_file_t xar_file) : | |
118 | _parent(parent), _xar_file(xar_file), _file_buffer(NULL), _file_size(0) { } // lazy construct | |
119 | virtual ~FileHandler() { } | |
120 | ||
121 | protected: | |
122 | void initFile() { | |
123 | if (!_file_buffer) { | |
124 | if (xar_extract_tobuffersz(_parent, _xar_file, &_file_buffer, &_file_size) != 0) | |
125 | throwf("could not extract files from bitcode bundle"); | |
126 | } | |
127 | } | |
128 | void destroyFile() { | |
129 | if (_parent) | |
130 | free(_file_buffer); | |
131 | } | |
132 | ||
133 | xar_t _parent; | |
134 | xar_file_t _xar_file; | |
135 | char* _file_buffer; | |
136 | size_t _file_size; | |
137 | }; | |
138 | ||
139 | class BundleHandler : public FileHandler { | |
140 | public: | |
141 | BundleHandler(char* bundleContent, size_t bundleSize, const Options& options) : | |
142 | FileHandler(bundleContent, bundleSize), _xar(NULL), _temp_dir(NULL), _options(options) { } | |
143 | BundleHandler(xar_t parent, xar_file_t xar_file, const Options& options) : | |
144 | FileHandler(parent, xar_file), _xar(NULL), _temp_dir(NULL), _options(options) { } | |
145 | ||
146 | ~BundleHandler(); | |
147 | ||
148 | virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; | |
149 | virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; | |
150 | ||
151 | private: | |
152 | void init(); | |
153 | void copyXARProp(xar_file_t src, xar_file_t dst); | |
154 | ||
155 | xar_t _xar; | |
156 | char* _temp_dir; | |
157 | const Options& _options; | |
158 | std::vector<FileHandler*> _handlers; | |
159 | }; | |
160 | ||
161 | class BitcodeHandler : public FileHandler { | |
162 | public: | |
163 | BitcodeHandler(char* content, size_t size) : FileHandler(content, size) { } | |
164 | BitcodeHandler(xar_t parent, xar_file_t xar_file) : FileHandler(parent, xar_file) { } | |
165 | ||
166 | ~BitcodeHandler(); | |
167 | ||
168 | virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override { } // Don't need to preserve symbols | |
169 | virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; | |
170 | }; | |
171 | ||
172 | class ObjectHandler : public FileHandler { | |
173 | public: | |
174 | ObjectHandler(char* content, size_t size) : | |
175 | FileHandler(content, size) { } | |
176 | ObjectHandler(xar_t parent, xar_file_t xar_file) : | |
177 | FileHandler(parent, xar_file) { } | |
178 | ||
179 | ~ObjectHandler(); | |
180 | ||
181 | void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; | |
182 | void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; | |
183 | ||
184 | }; | |
185 | ||
186 | ||
187 | class BitcodeBundle { | |
188 | public: | |
189 | BitcodeBundle(const Options& opts, ld::Internal& internal) : | |
190 | _options(opts), _state(internal) { } | |
191 | ~BitcodeBundle() { } | |
192 | void doPass(); | |
193 | ||
194 | private: | |
195 | const Options& _options; | |
196 | ld::Internal& _state; | |
197 | }; | |
198 | ||
199 | BitcodeAtom::BitcodeAtom() | |
200 | : ld::Atom(bitcodeBundleSection, | |
201 | ld::Atom::definitionRegular, ld::Atom::combineNever, | |
202 | ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, | |
203 | ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), | |
204 | _size(1) | |
205 | { | |
206 | // initialize a marker of 1 byte | |
207 | _content = (uint8_t*)calloc(1,1); | |
208 | } | |
209 | ||
210 | BitcodeAtom::BitcodeAtom(BitcodeTempFile& tempfile) | |
211 | : ld::Atom(bitcodeBundleSection, | |
212 | ld::Atom::definitionRegular, ld::Atom::combineNever, | |
213 | ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, | |
214 | ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), | |
215 | _content(tempfile._content), _size(tempfile._size) | |
216 | { | |
217 | // Creating the Atom will transfer the ownership of the buffer from Tempfile to Atom | |
218 | tempfile._content = NULL; | |
219 | } | |
220 | ||
221 | BitcodeTempFile::BitcodeTempFile(const char* path, bool deleteAfterRead = true) | |
222 | : _path(path), _deleteAfterRead(deleteAfterRead) | |
223 | { | |
224 | int fd = ::open(path, O_RDONLY, 0); | |
225 | if ( fd == -1 ) | |
226 | throwf("could not open bitcode temp file: %s", path); | |
227 | struct stat stat_buf; | |
228 | ::fstat(fd, &stat_buf); | |
229 | _content = (uint8_t*)malloc(stat_buf.st_size); | |
230 | if ( _content == NULL ) | |
231 | throwf("could not process bitcode temp file: %s", path); | |
232 | if ( read(fd, _content, stat_buf.st_size) != stat_buf.st_size ) | |
233 | throwf("could not read bitcode temp file: %s", path); | |
234 | ::close(fd); | |
235 | _size = stat_buf.st_size; | |
236 | } | |
237 | ||
238 | BitcodeTempFile::~BitcodeTempFile() | |
239 | { | |
240 | free(_content); | |
241 | if ( _deleteAfterRead ) { | |
242 | if ( ::unlink(_path) != 0 ) | |
243 | throwf("could not remove temp file: %s", _path); | |
244 | } | |
245 | } | |
246 | ||
247 | BitcodeObfuscator::BitcodeObfuscator() | |
248 | { | |
249 | // check if apple internal libLTO is used | |
250 | if ( ::lto_get_version() == NULL ) | |
251 | throwf("libLTO is not loaded"); | |
252 | _lto_hide_symbols = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_hide_symbols"); | |
253 | _lto_write_reverse_map = (lto_codegen_output_t) dlsym(RTLD_DEFAULT, "lto_codegen_write_symbol_reverse_map"); | |
254 | _lto_reset_context = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_reset_context"); | |
255 | if ( _lto_hide_symbols == NULL || _lto_write_reverse_map == NULL || | |
256 | _lto_reset_context == NULL || ::lto_api_version() < 14 ) | |
257 | throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version()); | |
258 | _obfuscator = ::lto_codegen_create_in_local_context(); | |
259 | #if LTO_API_VERSION >= 14 | |
260 | lto_codegen_set_should_internalize(_obfuscator, false); | |
261 | #endif | |
262 | } | |
263 | ||
264 | BitcodeObfuscator::~BitcodeObfuscator() | |
265 | { | |
266 | ::lto_codegen_dispose(_obfuscator); | |
267 | } | |
268 | ||
269 | void BitcodeObfuscator::addMustPreserveSymbols(const char* name) | |
270 | { | |
271 | ::lto_codegen_add_must_preserve_symbol(_obfuscator, name); | |
272 | } | |
273 | ||
274 | void BitcodeObfuscator::bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath) | |
275 | { | |
276 | #if LTO_API_VERSION >= 13 && LTO_APPLE_INTERNAL | |
277 | lto_module_t module = ::lto_module_create_in_codegen_context(bc->getContent(), bc->getSize(), filePath, _obfuscator); | |
278 | if ( module == NULL ) | |
279 | throwf("object contains invalid bitcode: %s", filePath); | |
280 | ::lto_codegen_set_module(_obfuscator, module); | |
281 | (*_lto_hide_symbols)(_obfuscator); | |
282 | #if LTO_API_VERSION >= 15 | |
283 | ::lto_codegen_set_should_embed_uselists(_obfuscator, true); | |
284 | #endif | |
285 | ::lto_codegen_write_merged_modules(_obfuscator, outputPath); | |
286 | (*_lto_reset_context)(_obfuscator); | |
287 | #endif | |
288 | return; | |
289 | } | |
290 | ||
291 | void BitcodeObfuscator::writeSymbolMap(const char *outputPath) | |
292 | { | |
293 | (*_lto_write_reverse_map)(_obfuscator, outputPath); | |
294 | } | |
295 | ||
296 | BundleHandler::~BundleHandler() | |
297 | { | |
298 | // free buffers | |
299 | destroyFile(); | |
300 | // free handlers | |
301 | for (auto handler : _handlers) | |
302 | delete handler; | |
303 | ||
304 | // delete temp file if not -save-temps | |
305 | if ( _xar ) { | |
306 | xar_close(_xar); | |
307 | std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); | |
308 | if ( !_options.saveTempFiles() && ::unlink(oldXARPath.c_str()) != 0) | |
309 | warning("could not delete temp file: %s", oldXARPath.c_str()); | |
310 | } | |
311 | ||
312 | if ( _temp_dir ) { | |
313 | if ( !_options.saveTempFiles() && ::rmdir(_temp_dir) != 0 ) | |
314 | warning("could not delete temp directory: %s", _temp_dir); | |
315 | free(_temp_dir); | |
316 | } | |
317 | } | |
318 | ||
319 | BitcodeHandler::~BitcodeHandler() | |
320 | { | |
321 | destroyFile(); | |
322 | } | |
323 | ||
324 | ObjectHandler::~ObjectHandler() | |
325 | { | |
326 | destroyFile(); | |
327 | } | |
328 | ||
329 | void BundleHandler::init() | |
330 | { | |
331 | if ( _xar != NULL ) | |
332 | return; | |
333 | ||
334 | // make temp directory | |
335 | const char* finalOutput = _options.outputFilePath(); | |
336 | _temp_dir = (char*)malloc(PATH_MAX * sizeof(char)); | |
337 | // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX | |
338 | // If so, fall back to /tmp | |
339 | if ( strlen(finalOutput) + 30 >= PATH_MAX ) | |
340 | sprintf(_temp_dir, "/tmp/ld.bundle.XXXXXX"); | |
341 | else | |
342 | sprintf(_temp_dir, "%s.bundle.XXXXXX", finalOutput); | |
343 | ::mkdtemp(_temp_dir); | |
344 | ||
345 | // write the bundle to the temp_directory | |
346 | initFile(); | |
347 | std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); | |
348 | int f = ::open(oldXARPath.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | |
349 | if ( f == -1 ) | |
350 | throwf("could not write file to temp directory: %s", _temp_dir); | |
351 | if ( ::write(f, _file_buffer, _file_size) != (int)_file_size ) | |
352 | throwf("failed to write content to temp file: %s", oldXARPath.c_str()); | |
353 | ::close(f); | |
354 | ||
355 | // read the xar file | |
356 | _xar = xar_open(oldXARPath.c_str(), READ); | |
357 | ||
358 | // Init the vector of handler | |
359 | xar_iter_t iter = xar_iter_new(); | |
360 | if ( !iter ) | |
361 | throwf("could not aquire iterator for the bitcode bundle"); | |
362 | for ( xar_file_t f = xar_file_first(_xar, iter); f; f = xar_file_next(iter) ) { | |
363 | const char* filetype = NULL; | |
364 | if ( xar_prop_get(f, "file-type", &filetype) != 0 ) | |
365 | throwf("could not get the file type for the bitcode bundle"); | |
366 | if ( strcmp(filetype, "Bundle") == 0 ) | |
367 | _handlers.push_back(new BundleHandler(_xar, f, _options)); | |
368 | else if ( strcmp(filetype, "Object") == 0 ) | |
369 | _handlers.push_back(new ObjectHandler(_xar, f)); | |
370 | else if ( strcmp(filetype, "Bitcode") == 0 || strcmp(filetype, "LTO") == 0 ) | |
371 | _handlers.push_back(new BitcodeHandler(_xar, f)); | |
372 | else | |
373 | assert(0 && "Unknown file type"); | |
374 | } | |
375 | xar_iter_free(iter); | |
376 | } | |
377 | ||
378 | void BundleHandler::copyXARProp(xar_file_t src, xar_file_t dst) | |
379 | { | |
380 | // copy the property in the XAR. | |
381 | // Since XAR API can only get the first value from the key, | |
382 | // Deleting the value after read. | |
383 | int i = 0; | |
384 | while (1) { | |
385 | xar_iter_t p = xar_iter_new(); | |
386 | const char* key = xar_prop_first(src, p); | |
387 | for (int x = 0; x < i; x++) | |
388 | key = xar_prop_next(p); | |
389 | if ( !key ) | |
390 | break; | |
391 | const char* val = NULL; | |
392 | xar_prop_get(src, key, &val); | |
393 | if ( // Info from bitcode files | |
394 | strcmp(key, "file-type") == 0 || | |
395 | strcmp(key, "clang/cmd") == 0 || | |
396 | strcmp(key, "swift/cmd") == 0 || | |
397 | // Info from linker subdoc | |
398 | strcmp(key, "version") == 0 || | |
399 | strcmp(key, "architecture") == 0 || | |
400 | strcmp(key, "hide-symbols") == 0 || | |
401 | strcmp(key, "platform") == 0 || | |
402 | strcmp(key, "sdkversion") == 0 || | |
403 | strcmp(key, "dylibs/lib") == 0 || | |
404 | strcmp(key, "link-options/option") == 0 ) { | |
405 | xar_prop_create(dst, key, val); | |
406 | xar_prop_unset(src, key); | |
407 | } else | |
408 | ++ i; | |
409 | xar_iter_free(p); | |
410 | } | |
411 | } | |
412 | ||
413 | void BundleHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) | |
414 | { | |
415 | // init the handler | |
416 | if ( _xar == NULL ) | |
417 | init(); | |
418 | ||
419 | // iterate through the XAR file and add symbols | |
420 | for ( auto handler : _handlers ) | |
421 | handler->populateMustPreserveSymbols(obfuscator); | |
422 | } | |
423 | ||
424 | void ObjectHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) | |
425 | { | |
426 | initFile(); | |
427 | // Parse the object file and add the symbols | |
428 | std::vector<const char*> symbols; | |
429 | if ( mach_o::relocatable::getNonLocalSymbols((uint8_t*)_file_buffer, symbols) ) { | |
430 | for ( auto sym : symbols ) | |
431 | obfuscator->addMustPreserveSymbols(sym); | |
432 | } | |
433 | } | |
434 | ||
435 | void BundleHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) | |
436 | { | |
437 | // init the handler | |
438 | if ( _xar == NULL ) | |
439 | init(); | |
440 | ||
441 | // creating the new xar | |
442 | xar_t x = xar_open(path, WRITE); | |
443 | if (x == NULL) | |
444 | throwf("could not open output bundle to write %s", path); | |
445 | // Disable compression | |
446 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
447 | throwf("could not disable compression for bitcode bundle"); | |
448 | ||
449 | // iterate through the XAR file and obfuscate | |
450 | for ( auto handler : _handlers ) { | |
451 | const char* name = NULL; | |
452 | xar_file_t f = handler->getXARFile(); | |
453 | if ( xar_prop_get(f, "name", &name) != 0 ) | |
454 | throwf("could not get the name of the file from bitcode bundle"); | |
455 | char outputPath[PATH_MAX]; | |
456 | sprintf(outputPath, "%s/%s", _temp_dir, name); | |
457 | handler->obfuscateAndWriteToPath(obfuscator, outputPath); | |
458 | BitcodeTempFile* bcOut = new BitcodeTempFile(outputPath, !_options.saveTempFiles()); | |
459 | xar_file_t bcEntry = xar_add_frombuffer(x, NULL, name, (char*)bcOut->getContent(), bcOut->getSize()); | |
460 | copyXARProp(f, bcEntry); | |
461 | delete bcOut; | |
462 | } | |
463 | ||
464 | // copy the subdoc as well | |
465 | for ( xar_subdoc_t sub = xar_subdoc_first(_xar); sub; sub = xar_subdoc_next(sub) ) { | |
466 | const char *name = xar_subdoc_name(sub); | |
467 | xar_subdoc_t newDoc = xar_subdoc_new(x, name); | |
468 | copyXARProp((xar_file_t) sub, (xar_file_t) newDoc); | |
469 | } | |
470 | xar_close(x); | |
471 | } | |
472 | ||
473 | void BitcodeHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) | |
474 | { | |
475 | initFile(); | |
476 | ld::Bitcode bc((uint8_t*)_file_buffer, _file_size); | |
477 | obfuscator->bitcodeHideSymbols(&bc, path, path); | |
478 | } | |
479 | ||
480 | void ObjectHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) | |
481 | { | |
482 | initFile(); | |
483 | int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | |
484 | if ( f == -1 || ::write(f, _file_buffer, _file_size) != (int)_file_size ) | |
485 | throwf("failed to write content to temp file: %s", path); | |
486 | ::close(f); | |
487 | } | |
488 | ||
489 | void BitcodeBundle::doPass() | |
490 | { | |
491 | if ( _state.embedMarkerOnly ) { | |
492 | assert( _options.outputKind() != Options::kDynamicExecutable && | |
493 | _options.outputKind() != Options::kStaticExecutable && | |
494 | "Don't emit marker for executables"); | |
495 | BitcodeAtom* marker = new BitcodeAtom(); | |
496 | _state.addAtom(*marker); | |
497 | return; | |
498 | } | |
499 | ||
500 | if ( _state.filesWithBitcode.empty() && _state.ltoBitcodePath.empty() ) | |
501 | return; | |
502 | // Create tempdir, the temp directory should be OUTPUT/main.exe.bundle-XXXXXX | |
503 | char tempdir[PATH_MAX]; | |
504 | const char* finalOutput = _options.outputFilePath(); | |
505 | // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX | |
506 | // If so, fall back to /tmp | |
507 | if ( strlen(finalOutput) + 30 >= PATH_MAX ) | |
508 | sprintf(tempdir, "/tmp/ld.bundle.XXXXXX"); | |
509 | else | |
510 | sprintf(tempdir, "%s.bundle.XXXXXX", finalOutput); | |
511 | ::mkdtemp(tempdir); | |
512 | // A lookup map to look for BundlerHandler base on filename | |
513 | std::unordered_map<std::string, BundleHandler*> handlerMap; | |
514 | ||
515 | BitcodeObfuscator* obfuscator = _options.hideSymbols() ? new BitcodeObfuscator() : NULL; | |
516 | // Build must keep symbols if we need to hide all the symbols | |
517 | if ( _options.hideSymbols() ) { | |
518 | // Go through all the atoms and decide if it should be obfuscated. | |
519 | // The following symbols are kept: | |
520 | // 1. entry point | |
521 | // 2. undefined symbols | |
522 | // 3. symbols must not be stripped | |
523 | // 4. all the globals if the globals are dead_strip root (ex. dylibs) | |
524 | // 5. there is an exported symbol list suggests the symbol should be exported | |
525 | // 6. the special symbols supplied by linker | |
526 | for ( auto § : _state.sections ) { | |
527 | for ( auto &atom : sect->atoms ) { | |
528 | if ( atom == _state.entryPoint || | |
529 | atom->definition() == ld::Atom::definitionProxy || | |
530 | atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip || | |
531 | ( _options.allGlobalsAreDeadStripRoots() && atom->scope() == ld::Atom::scopeGlobal ) || | |
532 | ( _options.hasExportRestrictList() && _options.shouldExport(atom->name())) ) | |
533 | obfuscator->addMustPreserveSymbols(atom->name()); | |
534 | } | |
535 | } | |
536 | // If there are assembly sources, add globals and undefined symbols from them as well | |
537 | for ( auto &f : _state.filesWithBitcode ) { | |
538 | if ( ld::AsmBitcode* ab = dynamic_cast<ld::AsmBitcode*>(f->getBitcode()) ) { | |
539 | ObjectHandler objHandler((char*)ab->getContent(), ab->getSize()); | |
540 | objHandler.populateMustPreserveSymbols(obfuscator); | |
541 | } else if ( ld::BundleBitcode* bb = dynamic_cast<ld::BundleBitcode*>(f->getBitcode()) ) { | |
542 | BundleHandler* bh = new BundleHandler((char*)bb->getContent(), bb->getSize(), _options); | |
543 | bh->populateMustPreserveSymbols(obfuscator); | |
544 | handlerMap.emplace(std::string(f->path()), bh); | |
545 | } | |
546 | } | |
547 | // special symbols supplied by linker | |
548 | obfuscator->addMustPreserveSymbols("___dso_handle"); | |
549 | obfuscator->addMustPreserveSymbols("__mh_execute_header"); | |
550 | obfuscator->addMustPreserveSymbols("__mh_dylib_header"); | |
551 | obfuscator->addMustPreserveSymbols("__mh_bundle_header"); | |
552 | obfuscator->addMustPreserveSymbols("__mh_dylinker_header"); | |
553 | obfuscator->addMustPreserveSymbols("__mh_object_header"); | |
554 | obfuscator->addMustPreserveSymbols("__mh_preload_header"); | |
555 | } | |
556 | ||
557 | // Open XAR output | |
558 | xar_t x; | |
559 | char outFile[PATH_MAX]; | |
560 | sprintf(outFile, "%s/bundle.xar", tempdir); | |
561 | ||
562 | // By default, it uses gzip to compress and SHA1 as checksum | |
563 | x = xar_open(outFile, WRITE); | |
564 | if (x == NULL) | |
565 | throwf("could not open output bundle to write %s", outFile); | |
566 | // Disable compression | |
567 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
568 | throwf("could not disable compression for bitcode bundle"); | |
569 | ||
570 | // Sort all the object file according to oridnal order | |
571 | std::sort(_state.filesWithBitcode.begin(), _state.filesWithBitcode.end(), | |
572 | [](const ld::relocatable::File* a, const ld::relocatable::File* b) { | |
573 | return a->ordinal() < b->ordinal(); | |
574 | }); | |
575 | ||
576 | // Copy each bitcode file into archive | |
577 | int index = 1; | |
578 | char formatString[10]; | |
579 | sprintf(formatString, "%%0%ud", (unsigned int)log10(_state.filesWithBitcode.size()) + 1); | |
580 | for ( auto &obj : _state.filesWithBitcode ) { | |
581 | assert(obj->getBitcode() != NULL && "File should contain bitcode"); | |
582 | char outFilePath[16]; | |
583 | sprintf(outFilePath, formatString, index++); | |
584 | if ( ld::LLVMBitcode* llvmbc = dynamic_cast<ld::LLVMBitcode*>(obj->getBitcode()) ) { | |
585 | // Handle clang and swift bitcode | |
586 | xar_file_t bcFile = NULL; | |
587 | if ( _options.hideSymbols() && !llvmbc->isMarker() ) { // dont strip if it is just a marker | |
588 | char tempfile[PATH_MAX]; | |
589 | sprintf(tempfile, "%s/%s.bc", tempdir, outFilePath); | |
590 | obfuscator->bitcodeHideSymbols(llvmbc, obj->path(), tempfile); | |
591 | BitcodeTempFile* bcTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); | |
592 | bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bcTemp->getContent(), bcTemp->getSize()); | |
593 | delete bcTemp; | |
594 | } else { | |
595 | bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)const_cast<uint8_t*>(llvmbc->getContent()), llvmbc->getSize()); | |
596 | } | |
597 | if ( bcFile == NULL ) | |
598 | throwf("could not add bitcode from %s to bitcode bundle", obj->path()); | |
599 | if ( xar_prop_set(bcFile, "file-type", "Bitcode") != 0 ) | |
600 | throwf("could not set bitcode property for %s in bitcode bundle", obj->path()); | |
601 | // Write commandline options | |
602 | std::string tagName = std::string(llvmbc->getBitcodeName()) + std::string("/cmd"); | |
603 | for ( uint32_t i = 0; i < llvmbc->getCmdSize(); ++i ) { | |
604 | if ( i == 0 || llvmbc->getCmdline()[i-1] == '\0' ) { | |
605 | if ( xar_prop_create(bcFile, tagName.c_str(), (const char *)llvmbc->getCmdline() + i) ) | |
606 | throwf("could not set cmdline to XAR file"); | |
607 | } | |
608 | } | |
609 | } | |
610 | else if ( ld::BundleBitcode* bundlebc = dynamic_cast<ld::BundleBitcode*>(obj->getBitcode()) ) { | |
611 | xar_file_t bundleFile = NULL; | |
612 | if ( _options.hideSymbols() && !bundlebc->isMarker() ) { // dont strip if it is just a marker | |
613 | char tempfile[PATH_MAX]; | |
614 | sprintf(tempfile, "%s/%s.xar", tempdir, outFilePath); | |
615 | auto search = handlerMap.find(std::string(obj->path())); | |
616 | assert( search != handlerMap.end() && "Cannot find handler"); | |
617 | search->second->obfuscateAndWriteToPath(obfuscator, tempfile); | |
618 | BitcodeTempFile* bundleTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); | |
619 | bundleFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bundleTemp->getContent(), bundleTemp->getSize()); | |
620 | delete bundleTemp; | |
621 | } else { | |
622 | bundleFile = xar_add_frombuffer(x, NULL, outFilePath, | |
623 | (char*)const_cast<uint8_t*>(bundlebc->getContent()), | |
624 | bundlebc->getSize()); | |
625 | } | |
626 | if ( bundleFile == NULL ) | |
627 | throwf("could not add bitcode from the bundle %s to bitcode bundle", obj->path()); | |
628 | if ( xar_prop_set(bundleFile, "file-type", "Bundle") != 0 ) | |
629 | throwf("could not set bundle property for %s in bitcode bundle", obj->path()); | |
630 | } | |
631 | else if ( ld::AsmBitcode* asmbc = dynamic_cast<ld::AsmBitcode*>(obj->getBitcode()) ) { | |
632 | xar_file_t objFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)asmbc->getContent(), asmbc->getSize()); | |
633 | if ( objFile == NULL ) | |
634 | throwf("could not add obj file %s to bitcode bundle", obj->path()); | |
635 | if ( xar_prop_set(objFile, "file-type", "Object") != 0 ) | |
636 | throwf("could not set object property for %s in bitcode bundle", obj->path()); | |
637 | } | |
638 | else { | |
639 | assert(false && "Unknown bitcode"); | |
640 | } | |
641 | } | |
642 | ||
643 | // Write merged LTO bitcode | |
644 | if ( !_state.ltoBitcodePath.empty() ) { | |
645 | xar_file_t ltoFile = NULL; | |
646 | BitcodeTempFile* ltoTemp = new BitcodeTempFile(_state.ltoBitcodePath.c_str(), !_options.saveTempFiles()); | |
647 | if ( _options.hideSymbols() ) { | |
648 | ld::Bitcode ltoBitcode(ltoTemp->getContent(), ltoTemp->getSize()); | |
649 | char ltoTempFile[PATH_MAX]; | |
650 | sprintf(ltoTempFile, "%s/lto.bc", tempdir); | |
651 | obfuscator->bitcodeHideSymbols(<oBitcode, _state.ltoBitcodePath.c_str(), ltoTempFile); | |
652 | BitcodeTempFile* ltoStrip = new BitcodeTempFile(ltoTempFile, !_options.saveTempFiles()); | |
653 | ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoStrip->getContent(), ltoStrip->getSize()); | |
654 | delete ltoStrip; | |
655 | } else { | |
656 | ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoTemp->getContent(), ltoTemp->getSize()); | |
657 | } | |
658 | if ( ltoFile == NULL ) | |
659 | throwf("could not add lto file %s to bitcode bundle", _state.ltoBitcodePath.c_str()); | |
660 | if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 ) | |
661 | throwf("could not set bitcode property for %s in bitcode bundle", _state.ltoBitcodePath.c_str()); | |
662 | delete ltoTemp; | |
663 | } | |
664 | ||
665 | // Common LinkOptions | |
666 | std::vector<std::string> linkCmd = _options.writeBitcodeLinkOptions(); | |
667 | ||
668 | // support -sectcreate option | |
669 | for ( auto extraSect = _options.extraSectionsBegin(); extraSect != _options.extraSectionsEnd(); ++ extraSect ) { | |
670 | std::string sectName = std::string(extraSect->segmentName) + std::string(",") + std::string(extraSect->sectionName); | |
671 | BitcodeTempFile* sectFile = new BitcodeTempFile(extraSect->path, false); | |
672 | xar_file_t sectXar = xar_add_frombuffer(x, NULL, sectName.c_str(), (char*)sectFile->getContent(), sectFile->getSize()); | |
673 | if ( sectXar == NULL ) | |
674 | throwf("could not encode sectcreate file %s into bitcode bundle", extraSect->path); | |
675 | if ( xar_prop_set(sectXar, "file-type", "Section") != 0 ) | |
676 | throwf("could not set bitcode property for %s", sectName.c_str()); | |
677 | delete sectFile; | |
678 | linkCmd.push_back("-sectcreate"); | |
679 | linkCmd.push_back(extraSect->segmentName); | |
680 | linkCmd.push_back(extraSect->sectionName); | |
681 | linkCmd.push_back(sectName); | |
682 | } | |
683 | ||
684 | // Write exports file | |
685 | if ( _options.hasExportMaskList() ) { | |
686 | linkCmd.push_back("-exported_symbols_list"); | |
687 | linkCmd.push_back("exports.exp"); | |
688 | const char* exportsPath = "exports.exp"; | |
689 | std::vector<const char*> exports = _options.exportsData(); | |
690 | std::string exps; | |
691 | for (std::vector<const char*>::iterator it = exports.begin(); | |
692 | it != exports.end(); ++ it) { | |
693 | exps += *it; | |
694 | exps += "\n"; | |
695 | } | |
696 | // always append an empty line so exps cannot be empty. rdar://problem/22404253 | |
697 | exps += "\n"; | |
698 | xar_file_t exportsFile = xar_add_frombuffer(x, NULL, exportsPath, const_cast<char*>(exps.data()), exps.size()); | |
699 | if (exportsFile == NULL) | |
700 | throwf("could not add exports list to bitcode bundle"); | |
701 | if (xar_prop_set(exportsFile, "file-type", "Exports") != 0) | |
702 | throwf("could not set exports property in bitcode bundle"); | |
703 | } | |
704 | ||
705 | // Create subdoc to write link information | |
706 | xar_subdoc_t linkXML = xar_subdoc_new(x, "Ld"); | |
707 | if ( linkXML == NULL ) | |
708 | throwf("could not create XML in bitcode bundle"); | |
709 | ||
710 | // Write version number | |
711 | if ( xar_prop_create((xar_file_t)linkXML, "version", BITCODE_XAR_VERSION) != 0 ) | |
712 | throwf("could not add version number to bitcode bundle"); | |
713 | ||
714 | // Arch | |
715 | if ( xar_prop_create((xar_file_t)linkXML, "architecture", _options.architectureName()) != 0 ) | |
716 | throwf("could not add achitecture name to bitcode bundle"); | |
717 | ||
718 | // Opt-out symbols | |
719 | if ( _options.hideSymbols() ) { | |
720 | if ( xar_prop_create((xar_file_t)linkXML, "hide-symbols", "1") != 0 ) | |
721 | throwf("could not add property to bitcode bundle"); | |
722 | } | |
723 | ||
724 | // Write SDK version | |
725 | if ( _options.sdkPaths().size() > 1 ) | |
726 | throwf("only one -syslibroot is accepted for bitcode bundle"); | |
727 | if ( xar_prop_create((xar_file_t)linkXML, "platform", _options.getPlatformStr().c_str()) != 0 ) | |
728 | throwf("could not add platform name to bitcode bundle"); | |
729 | if ( xar_prop_create((xar_file_t)linkXML, "sdkversion", _options.getSDKVersionStr().c_str()) != 0 ) | |
730 | throwf("could not add SDK version to bitcode bundle"); | |
731 | ||
732 | // Write dylibs | |
733 | const char* sdkRoot = NULL; | |
734 | if ( !_options.sdkPaths().empty() ) | |
735 | sdkRoot = _options.sdkPaths().front(); | |
736 | if ( !_state.dylibs.empty() ) { | |
737 | std::vector<const char*> SDKPaths = _options.sdkPaths(); | |
738 | char dylibPath[PATH_MAX]; | |
739 | for ( auto &dylib : _state.dylibs ) { | |
740 | // For every dylib/framework, figure out if it is coming from a SDK | |
741 | // if it is coming from some SDK, we parse the path to figure out which SDK | |
742 | // If -syslibroot is pointing to a SDK, it should end with PlatformX.Y.sdk/ | |
743 | if (sdkRoot && strncmp(dylib->path(), sdkRoot, strlen(sdkRoot)) == 0) { | |
744 | // dylib/framework from one of the -syslibroot | |
745 | // The path start with a string template | |
746 | strcpy(dylibPath, "{SDKPATH}/"); | |
747 | // append the path of dylib/frameowrk in the SDK | |
748 | strcat(dylibPath, dylib->path() + strlen(sdkRoot)); | |
749 | } else { | |
750 | // Not in any SDKs, then assume it is a user dylib/framework | |
751 | // strip off all the path in the front | |
752 | const char* dylib_name = strrchr(dylib->path(), '/'); | |
753 | dylib_name = (dylib_name == NULL) ? dylib->path() : dylib_name + 1; | |
754 | strcpy(dylibPath, dylib_name); | |
755 | } | |
756 | if ( dylib->forcedWeakLinked() ) { | |
757 | if ( xar_prop_create((xar_file_t)linkXML, "dylibs/weak", dylibPath) != 0) | |
758 | throwf("could not add dylib options to bitcode bundle"); | |
759 | } else { | |
760 | if ( xar_prop_create((xar_file_t)linkXML, "dylibs/lib", dylibPath) != 0) | |
761 | throwf("could not add dylib options to bitcode bundle"); | |
762 | } | |
763 | } | |
764 | } | |
765 | ||
766 | // Write link-line into archive | |
767 | for ( auto &it : linkCmd ) { | |
768 | if (xar_prop_create((xar_file_t)linkXML, "link-options/option", it.c_str()) != 0) | |
769 | throwf("could not add link options to bitcode bundle"); | |
770 | } | |
771 | // Finish writing | |
772 | xar_close(x); | |
773 | ||
774 | // Read the file back | |
775 | BitcodeTempFile* xarTemp = new BitcodeTempFile(outFile, !_options.saveTempFiles()); | |
776 | ||
777 | // Create an Atom and add to the list | |
778 | BitcodeAtom* bundleAtom = new BitcodeAtom(*xarTemp); | |
779 | _state.addAtom(*bundleAtom); | |
780 | ||
781 | // write the reverse mapping file if required | |
782 | if ( _options.hideSymbols() && !_options.reverseMapTempPath().empty() ) | |
783 | obfuscator->writeSymbolMap(_options.reverseMapTempPath().c_str()); | |
784 | ||
785 | // Clean up local variables | |
786 | delete xarTemp; | |
787 | delete obfuscator; | |
788 | for ( auto &entry: handlerMap ) | |
789 | delete entry.second; | |
790 | // delete temp directory if not using -save-temps | |
791 | // only do so after all the BitcodeTempFiles are deleted. | |
792 | if ( !_options.saveTempFiles() ) { | |
793 | if ( ::rmdir(tempdir) != 0 ) | |
794 | warning("temp directory cannot be removed: %s", tempdir); | |
795 | } | |
796 | } | |
797 | ||
798 | ||
799 | ||
800 | // called by linker to write bitcode bundle into a mach-o section | |
801 | void doPass(const Options& opts, ld::Internal& internal) { | |
802 | BitcodeBundle BB(opts, internal); | |
803 | BB.doPass(); | |
804 | } | |
805 | ||
806 | ||
807 | } // namespace bitcode_bundle | |
808 | } // namespace passes | |
809 | } // namespace ld |