]>
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> | |
fb9a160c | 34 | #include <sstream> |
eaf282aa A |
35 | |
36 | #include "llvm-c/lto.h" | |
37 | // c header | |
38 | extern "C" { | |
39 | #include <xar/xar.h> | |
40 | } | |
41 | ||
42 | #include "bitcode_bundle.h" | |
43 | ||
44 | #include "Options.h" | |
45 | #include "ld.hpp" | |
46 | #include "Bitcode.hpp" | |
47 | #include "macho_relocatable_file.h" | |
48 | ||
49 | ||
50 | namespace ld { | |
51 | namespace passes { | |
52 | namespace bitcode_bundle { | |
53 | ||
54 | class BitcodeTempFile; | |
55 | ||
56 | class BitcodeAtom : public ld::Atom { | |
57 | static ld::Section bitcodeBundleSection; | |
58 | public: | |
59 | BitcodeAtom(); | |
60 | BitcodeAtom(BitcodeTempFile& tempfile); | |
61 | ~BitcodeAtom() { free(_content); } | |
62 | virtual ld::File* file() const { return NULL; } | |
63 | virtual const char* name() const { return "bitcode bundle"; } | |
64 | virtual uint64_t size() const { return _size; } | |
65 | virtual uint64_t objectAddress() const { return 0; } | |
66 | virtual void copyRawContent(uint8_t buffer[]) const | |
67 | { memcpy(buffer, _content, _size); } | |
68 | virtual void setScope(Scope) { } | |
69 | ||
70 | private: | |
71 | uint8_t* _content; | |
72 | uint64_t _size; | |
73 | }; | |
74 | ||
75 | ld::Section BitcodeAtom::bitcodeBundleSection("__LLVM", "__bundle", ld::Section::typeSectCreate); | |
76 | ||
77 | class BitcodeTempFile { | |
78 | public: | |
79 | BitcodeTempFile(const char* path, bool deleteAfterRead); | |
80 | ~BitcodeTempFile(); | |
81 | uint8_t* getContent() const { return _content; } | |
82 | uint64_t getSize() const { return _size; } | |
83 | private: | |
84 | friend class BitcodeAtom; | |
85 | const char* _path; | |
86 | uint8_t* _content; | |
87 | uint64_t _size; | |
88 | bool _deleteAfterRead; | |
89 | }; | |
90 | ||
91 | class BitcodeObfuscator { | |
92 | public: | |
93 | BitcodeObfuscator(); | |
94 | ~BitcodeObfuscator(); | |
95 | ||
96 | void addMustPreserveSymbols(const char* name); | |
fb9a160c | 97 | void addAsmSymbolsToMustPreserve(lto_module_t module); |
eaf282aa A |
98 | void bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath); |
99 | void writeSymbolMap(const char* outputPath); | |
fb9a160c | 100 | const char* lookupHiddenName(const char* symbol); |
eaf282aa A |
101 | private: |
102 | typedef void (*lto_codegen_func_t) (lto_code_gen_t); | |
103 | typedef void (*lto_codegen_output_t) (lto_code_gen_t, const char*); | |
fb9a160c A |
104 | typedef const char* (*lto_codegen_lookup_t) (lto_code_gen_t, const char*); |
105 | typedef unsigned int (*lto_module_num_symbols) (lto_module_t); | |
106 | typedef const char* (*lto_module_symbol_name) (lto_module_t, unsigned int); | |
eaf282aa A |
107 | |
108 | lto_code_gen_t _obfuscator; | |
109 | lto_codegen_func_t _lto_hide_symbols; | |
110 | lto_codegen_func_t _lto_reset_context; | |
111 | lto_codegen_output_t _lto_write_reverse_map; | |
fb9a160c A |
112 | lto_codegen_lookup_t _lto_lookup_hidden_name; |
113 | lto_module_num_symbols _lto_get_asm_symbol_num; | |
114 | lto_module_symbol_name _lto_get_asm_symbol_name; | |
eaf282aa A |
115 | }; |
116 | ||
117 | class FileHandler { | |
118 | // generic handler for files in a bundle | |
119 | public: | |
120 | virtual void populateMustPreserveSymbols(BitcodeObfuscator* _obfuscator) { } | |
fb9a160c A |
121 | virtual void obfuscateAndWriteToPath(BitcodeObfuscator* _obfuscator, const char* path); |
122 | virtual const char* compressionMethod() { return XAR_OPT_VAL_NONE; } // no compression by default | |
eaf282aa A |
123 | xar_file_t getXARFile() { return _xar_file; } |
124 | ||
125 | FileHandler(char* content, size_t size) : | |
126 | _parent(NULL), _xar_file(NULL), _file_buffer(content), _file_size(size) { } // eager construct | |
127 | FileHandler(xar_t parent, xar_file_t xar_file) : | |
128 | _parent(parent), _xar_file(xar_file), _file_buffer(NULL), _file_size(0) { } // lazy construct | |
129 | virtual ~FileHandler() { } | |
130 | ||
131 | protected: | |
132 | void initFile() { | |
133 | if (!_file_buffer) { | |
134 | if (xar_extract_tobuffersz(_parent, _xar_file, &_file_buffer, &_file_size) != 0) | |
135 | throwf("could not extract files from bitcode bundle"); | |
136 | } | |
137 | } | |
138 | void destroyFile() { | |
139 | if (_parent) | |
140 | free(_file_buffer); | |
141 | } | |
142 | ||
143 | xar_t _parent; | |
144 | xar_file_t _xar_file; | |
145 | char* _file_buffer; | |
146 | size_t _file_size; | |
147 | }; | |
148 | ||
149 | class BundleHandler : public FileHandler { | |
150 | public: | |
151 | BundleHandler(char* bundleContent, size_t bundleSize, const Options& options) : | |
152 | FileHandler(bundleContent, bundleSize), _xar(NULL), _temp_dir(NULL), _options(options) { } | |
153 | BundleHandler(xar_t parent, xar_file_t xar_file, const Options& options) : | |
154 | FileHandler(parent, xar_file), _xar(NULL), _temp_dir(NULL), _options(options) { } | |
155 | ||
156 | ~BundleHandler(); | |
157 | ||
158 | virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; | |
159 | virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; | |
160 | ||
161 | private: | |
162 | void init(); | |
163 | void copyXARProp(xar_file_t src, xar_file_t dst); | |
164 | ||
165 | xar_t _xar; | |
166 | char* _temp_dir; | |
167 | const Options& _options; | |
168 | std::vector<FileHandler*> _handlers; | |
169 | }; | |
170 | ||
171 | class BitcodeHandler : public FileHandler { | |
172 | public: | |
173 | BitcodeHandler(char* content, size_t size) : FileHandler(content, size) { } | |
174 | BitcodeHandler(xar_t parent, xar_file_t xar_file) : FileHandler(parent, xar_file) { } | |
175 | ||
176 | ~BitcodeHandler(); | |
177 | ||
fb9a160c | 178 | virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; |
eaf282aa A |
179 | virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; |
180 | }; | |
181 | ||
182 | class ObjectHandler : public FileHandler { | |
183 | public: | |
184 | ObjectHandler(char* content, size_t size) : | |
185 | FileHandler(content, size) { } | |
186 | ObjectHandler(xar_t parent, xar_file_t xar_file) : | |
187 | FileHandler(parent, xar_file) { } | |
188 | ||
189 | ~ObjectHandler(); | |
190 | ||
fb9a160c | 191 | virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override; |
eaf282aa A |
192 | |
193 | }; | |
194 | ||
fb9a160c A |
195 | class SymbolListHandler : public FileHandler { |
196 | public: | |
197 | SymbolListHandler(char* content, size_t size) : | |
198 | FileHandler(content, size) { } | |
199 | SymbolListHandler(xar_t parent, xar_file_t xar_file) : | |
200 | FileHandler(parent, xar_file) { } | |
201 | ||
202 | ~SymbolListHandler(); | |
203 | ||
204 | virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override; | |
205 | virtual const char* compressionMethod() override { return XAR_OPT_VAL_GZIP; } | |
206 | }; | |
207 | ||
eaf282aa A |
208 | |
209 | class BitcodeBundle { | |
210 | public: | |
211 | BitcodeBundle(const Options& opts, ld::Internal& internal) : | |
212 | _options(opts), _state(internal) { } | |
213 | ~BitcodeBundle() { } | |
214 | void doPass(); | |
215 | ||
216 | private: | |
217 | const Options& _options; | |
218 | ld::Internal& _state; | |
219 | }; | |
220 | ||
221 | BitcodeAtom::BitcodeAtom() | |
222 | : ld::Atom(bitcodeBundleSection, | |
223 | ld::Atom::definitionRegular, ld::Atom::combineNever, | |
224 | ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, | |
225 | ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), | |
226 | _size(1) | |
227 | { | |
228 | // initialize a marker of 1 byte | |
229 | _content = (uint8_t*)calloc(1,1); | |
230 | } | |
231 | ||
232 | BitcodeAtom::BitcodeAtom(BitcodeTempFile& tempfile) | |
233 | : ld::Atom(bitcodeBundleSection, | |
234 | ld::Atom::definitionRegular, ld::Atom::combineNever, | |
235 | ld::Atom::scopeTranslationUnit, ld::Atom::typeUnclassified, | |
236 | ld::Atom::symbolTableNotIn, true, false, false, ld::Atom::Alignment(0)), | |
237 | _content(tempfile._content), _size(tempfile._size) | |
238 | { | |
239 | // Creating the Atom will transfer the ownership of the buffer from Tempfile to Atom | |
240 | tempfile._content = NULL; | |
241 | } | |
242 | ||
243 | BitcodeTempFile::BitcodeTempFile(const char* path, bool deleteAfterRead = true) | |
244 | : _path(path), _deleteAfterRead(deleteAfterRead) | |
245 | { | |
246 | int fd = ::open(path, O_RDONLY, 0); | |
247 | if ( fd == -1 ) | |
248 | throwf("could not open bitcode temp file: %s", path); | |
249 | struct stat stat_buf; | |
250 | ::fstat(fd, &stat_buf); | |
251 | _content = (uint8_t*)malloc(stat_buf.st_size); | |
252 | if ( _content == NULL ) | |
253 | throwf("could not process bitcode temp file: %s", path); | |
254 | if ( read(fd, _content, stat_buf.st_size) != stat_buf.st_size ) | |
255 | throwf("could not read bitcode temp file: %s", path); | |
256 | ::close(fd); | |
257 | _size = stat_buf.st_size; | |
258 | } | |
259 | ||
260 | BitcodeTempFile::~BitcodeTempFile() | |
261 | { | |
262 | free(_content); | |
263 | if ( _deleteAfterRead ) { | |
264 | if ( ::unlink(_path) != 0 ) | |
265 | throwf("could not remove temp file: %s", _path); | |
266 | } | |
267 | } | |
268 | ||
269 | BitcodeObfuscator::BitcodeObfuscator() | |
270 | { | |
271 | // check if apple internal libLTO is used | |
272 | if ( ::lto_get_version() == NULL ) | |
273 | throwf("libLTO is not loaded"); | |
274 | _lto_hide_symbols = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_hide_symbols"); | |
275 | _lto_write_reverse_map = (lto_codegen_output_t) dlsym(RTLD_DEFAULT, "lto_codegen_write_symbol_reverse_map"); | |
276 | _lto_reset_context = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_reset_context"); | |
fb9a160c A |
277 | _lto_lookup_hidden_name = (lto_codegen_lookup_t) dlsym(RTLD_DEFAULT, "lto_codegen_lookup_hidden_name"); |
278 | _lto_get_asm_symbol_num = (lto_module_num_symbols) dlsym(RTLD_DEFAULT, "lto_module_get_num_asm_symbols"); | |
279 | _lto_get_asm_symbol_name = (lto_module_symbol_name) dlsym(RTLD_DEFAULT, "lto_module_get_asm_symbol_name"); | |
eaf282aa | 280 | if ( _lto_hide_symbols == NULL || _lto_write_reverse_map == NULL || |
fb9a160c A |
281 | _lto_reset_context == NULL || _lto_lookup_hidden_name == NULL || |
282 | _lto_get_asm_symbol_num == NULL || _lto_get_asm_symbol_name == NULL || ::lto_api_version() < 14 ) | |
eaf282aa A |
283 | throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version()); |
284 | _obfuscator = ::lto_codegen_create_in_local_context(); | |
285 | #if LTO_API_VERSION >= 14 | |
286 | lto_codegen_set_should_internalize(_obfuscator, false); | |
287 | #endif | |
288 | } | |
289 | ||
290 | BitcodeObfuscator::~BitcodeObfuscator() | |
291 | { | |
292 | ::lto_codegen_dispose(_obfuscator); | |
293 | } | |
294 | ||
295 | void BitcodeObfuscator::addMustPreserveSymbols(const char* name) | |
296 | { | |
297 | ::lto_codegen_add_must_preserve_symbol(_obfuscator, name); | |
298 | } | |
299 | ||
300 | void BitcodeObfuscator::bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath) | |
301 | { | |
302 | #if LTO_API_VERSION >= 13 && LTO_APPLE_INTERNAL | |
303 | lto_module_t module = ::lto_module_create_in_codegen_context(bc->getContent(), bc->getSize(), filePath, _obfuscator); | |
304 | if ( module == NULL ) | |
305 | throwf("object contains invalid bitcode: %s", filePath); | |
306 | ::lto_codegen_set_module(_obfuscator, module); | |
307 | (*_lto_hide_symbols)(_obfuscator); | |
308 | #if LTO_API_VERSION >= 15 | |
309 | ::lto_codegen_set_should_embed_uselists(_obfuscator, true); | |
310 | #endif | |
311 | ::lto_codegen_write_merged_modules(_obfuscator, outputPath); | |
312 | (*_lto_reset_context)(_obfuscator); | |
313 | #endif | |
314 | return; | |
315 | } | |
316 | ||
317 | void BitcodeObfuscator::writeSymbolMap(const char *outputPath) | |
318 | { | |
319 | (*_lto_write_reverse_map)(_obfuscator, outputPath); | |
320 | } | |
321 | ||
fb9a160c A |
322 | const char* BitcodeObfuscator::lookupHiddenName(const char *symbol) |
323 | { | |
324 | return (*_lto_lookup_hidden_name)(_obfuscator, symbol); | |
325 | } | |
326 | ||
327 | void BitcodeObfuscator::addAsmSymbolsToMustPreserve(lto_module_t module) | |
328 | { | |
329 | for (unsigned int i = 0; i < _lto_get_asm_symbol_num(module); ++ i) { | |
330 | addMustPreserveSymbols(_lto_get_asm_symbol_name(module, i)); | |
331 | } | |
332 | } | |
333 | ||
eaf282aa A |
334 | BundleHandler::~BundleHandler() |
335 | { | |
336 | // free buffers | |
337 | destroyFile(); | |
338 | // free handlers | |
339 | for (auto handler : _handlers) | |
340 | delete handler; | |
341 | ||
342 | // delete temp file if not -save-temps | |
343 | if ( _xar ) { | |
344 | xar_close(_xar); | |
345 | std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); | |
346 | if ( !_options.saveTempFiles() && ::unlink(oldXARPath.c_str()) != 0) | |
347 | warning("could not delete temp file: %s", oldXARPath.c_str()); | |
348 | } | |
349 | ||
350 | if ( _temp_dir ) { | |
351 | if ( !_options.saveTempFiles() && ::rmdir(_temp_dir) != 0 ) | |
352 | warning("could not delete temp directory: %s", _temp_dir); | |
353 | free(_temp_dir); | |
354 | } | |
355 | } | |
356 | ||
357 | BitcodeHandler::~BitcodeHandler() | |
358 | { | |
359 | destroyFile(); | |
360 | } | |
361 | ||
362 | ObjectHandler::~ObjectHandler() | |
363 | { | |
364 | destroyFile(); | |
365 | } | |
366 | ||
fb9a160c A |
367 | SymbolListHandler::~SymbolListHandler() |
368 | { | |
369 | destroyFile(); | |
370 | } | |
371 | ||
eaf282aa A |
372 | void BundleHandler::init() |
373 | { | |
374 | if ( _xar != NULL ) | |
375 | return; | |
376 | ||
377 | // make temp directory | |
378 | const char* finalOutput = _options.outputFilePath(); | |
379 | _temp_dir = (char*)malloc(PATH_MAX * sizeof(char)); | |
380 | // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX | |
381 | // If so, fall back to /tmp | |
382 | if ( strlen(finalOutput) + 30 >= PATH_MAX ) | |
383 | sprintf(_temp_dir, "/tmp/ld.bundle.XXXXXX"); | |
384 | else | |
385 | sprintf(_temp_dir, "%s.bundle.XXXXXX", finalOutput); | |
386 | ::mkdtemp(_temp_dir); | |
387 | ||
388 | // write the bundle to the temp_directory | |
389 | initFile(); | |
390 | std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); | |
391 | int f = ::open(oldXARPath.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | |
392 | if ( f == -1 ) | |
393 | throwf("could not write file to temp directory: %s", _temp_dir); | |
394 | if ( ::write(f, _file_buffer, _file_size) != (int)_file_size ) | |
395 | throwf("failed to write content to temp file: %s", oldXARPath.c_str()); | |
396 | ::close(f); | |
397 | ||
398 | // read the xar file | |
399 | _xar = xar_open(oldXARPath.c_str(), READ); | |
400 | ||
401 | // Init the vector of handler | |
402 | xar_iter_t iter = xar_iter_new(); | |
403 | if ( !iter ) | |
404 | throwf("could not aquire iterator for the bitcode bundle"); | |
405 | for ( xar_file_t f = xar_file_first(_xar, iter); f; f = xar_file_next(iter) ) { | |
406 | const char* filetype = NULL; | |
407 | if ( xar_prop_get(f, "file-type", &filetype) != 0 ) | |
408 | throwf("could not get the file type for the bitcode bundle"); | |
409 | if ( strcmp(filetype, "Bundle") == 0 ) | |
410 | _handlers.push_back(new BundleHandler(_xar, f, _options)); | |
411 | else if ( strcmp(filetype, "Object") == 0 ) | |
412 | _handlers.push_back(new ObjectHandler(_xar, f)); | |
413 | else if ( strcmp(filetype, "Bitcode") == 0 || strcmp(filetype, "LTO") == 0 ) | |
414 | _handlers.push_back(new BitcodeHandler(_xar, f)); | |
fb9a160c A |
415 | else if ( strcmp(filetype, "Exports") == 0 || strcmp(filetype, "OrderFile") == 0) |
416 | _handlers.push_back(new SymbolListHandler(_xar, f)); | |
eaf282aa | 417 | else |
fb9a160c | 418 | _handlers.push_back(new FileHandler(_xar, f)); |
eaf282aa A |
419 | } |
420 | xar_iter_free(iter); | |
421 | } | |
422 | ||
423 | void BundleHandler::copyXARProp(xar_file_t src, xar_file_t dst) | |
424 | { | |
425 | // copy the property in the XAR. | |
426 | // Since XAR API can only get the first value from the key, | |
427 | // Deleting the value after read. | |
428 | int i = 0; | |
429 | while (1) { | |
430 | xar_iter_t p = xar_iter_new(); | |
431 | const char* key = xar_prop_first(src, p); | |
432 | for (int x = 0; x < i; x++) | |
433 | key = xar_prop_next(p); | |
434 | if ( !key ) | |
435 | break; | |
436 | const char* val = NULL; | |
437 | xar_prop_get(src, key, &val); | |
438 | if ( // Info from bitcode files | |
439 | strcmp(key, "file-type") == 0 || | |
440 | strcmp(key, "clang/cmd") == 0 || | |
441 | strcmp(key, "swift/cmd") == 0 || | |
442 | // Info from linker subdoc | |
443 | strcmp(key, "version") == 0 || | |
444 | strcmp(key, "architecture") == 0 || | |
445 | strcmp(key, "hide-symbols") == 0 || | |
446 | strcmp(key, "platform") == 0 || | |
447 | strcmp(key, "sdkversion") == 0 || | |
448 | strcmp(key, "dylibs/lib") == 0 || | |
449 | strcmp(key, "link-options/option") == 0 ) { | |
450 | xar_prop_create(dst, key, val); | |
451 | xar_prop_unset(src, key); | |
452 | } else | |
453 | ++ i; | |
454 | xar_iter_free(p); | |
455 | } | |
456 | } | |
457 | ||
458 | void BundleHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) | |
459 | { | |
460 | // init the handler | |
461 | if ( _xar == NULL ) | |
462 | init(); | |
463 | ||
464 | // iterate through the XAR file and add symbols | |
465 | for ( auto handler : _handlers ) | |
466 | handler->populateMustPreserveSymbols(obfuscator); | |
467 | } | |
468 | ||
fb9a160c A |
469 | void BitcodeHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) |
470 | { | |
471 | initFile(); | |
472 | ||
473 | // init LTOModule and add asm labels | |
474 | lto_module_t module = lto_module_create_from_memory(_file_buffer, _file_size); | |
475 | obfuscator->addAsmSymbolsToMustPreserve(module); | |
476 | lto_module_dispose(module); | |
477 | } | |
478 | ||
479 | ||
eaf282aa A |
480 | void ObjectHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) |
481 | { | |
482 | initFile(); | |
483 | // Parse the object file and add the symbols | |
484 | std::vector<const char*> symbols; | |
485 | if ( mach_o::relocatable::getNonLocalSymbols((uint8_t*)_file_buffer, symbols) ) { | |
486 | for ( auto sym : symbols ) | |
487 | obfuscator->addMustPreserveSymbols(sym); | |
488 | } | |
489 | } | |
490 | ||
491 | void BundleHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) | |
492 | { | |
493 | // init the handler | |
494 | if ( _xar == NULL ) | |
495 | init(); | |
496 | ||
497 | // creating the new xar | |
498 | xar_t x = xar_open(path, WRITE); | |
499 | if (x == NULL) | |
500 | throwf("could not open output bundle to write %s", path); | |
501 | // Disable compression | |
502 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
503 | throwf("could not disable compression for bitcode bundle"); | |
504 | ||
505 | // iterate through the XAR file and obfuscate | |
506 | for ( auto handler : _handlers ) { | |
507 | const char* name = NULL; | |
508 | xar_file_t f = handler->getXARFile(); | |
509 | if ( xar_prop_get(f, "name", &name) != 0 ) | |
510 | throwf("could not get the name of the file from bitcode bundle"); | |
511 | char outputPath[PATH_MAX]; | |
512 | sprintf(outputPath, "%s/%s", _temp_dir, name); | |
513 | handler->obfuscateAndWriteToPath(obfuscator, outputPath); | |
514 | BitcodeTempFile* bcOut = new BitcodeTempFile(outputPath, !_options.saveTempFiles()); | |
fb9a160c A |
515 | if ( xar_opt_set(x, XAR_OPT_COMPRESSION, handler->compressionMethod()) != 0 ) |
516 | throwf("could not set compression type for exports list"); | |
eaf282aa | 517 | xar_file_t bcEntry = xar_add_frombuffer(x, NULL, name, (char*)bcOut->getContent(), bcOut->getSize()); |
fb9a160c A |
518 | if ( bcEntry == NULL ) |
519 | throwf("could not add file to the bundle"); | |
520 | if ( xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0 ) | |
521 | throwf("could not reset compression type for exports list"); | |
eaf282aa A |
522 | copyXARProp(f, bcEntry); |
523 | delete bcOut; | |
524 | } | |
525 | ||
526 | // copy the subdoc as well | |
527 | for ( xar_subdoc_t sub = xar_subdoc_first(_xar); sub; sub = xar_subdoc_next(sub) ) { | |
528 | const char *name = xar_subdoc_name(sub); | |
529 | xar_subdoc_t newDoc = xar_subdoc_new(x, name); | |
530 | copyXARProp((xar_file_t) sub, (xar_file_t) newDoc); | |
531 | } | |
532 | xar_close(x); | |
533 | } | |
534 | ||
535 | void BitcodeHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) | |
536 | { | |
537 | initFile(); | |
538 | ld::Bitcode bc((uint8_t*)_file_buffer, _file_size); | |
539 | obfuscator->bitcodeHideSymbols(&bc, path, path); | |
540 | } | |
541 | ||
fb9a160c A |
542 | void SymbolListHandler::obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) |
543 | { | |
544 | initFile(); | |
545 | // Obfuscate exported symbol list. | |
546 | std::string exports_list; | |
547 | for (size_t i = 0, start = 0; i < _file_size; ++i) { | |
548 | if ( _file_buffer[i] == '\n' ) { | |
549 | _file_buffer[i] = '\0'; | |
550 | const char* hiddenName = obfuscator->lookupHiddenName(_file_buffer + start); | |
551 | if ( hiddenName == NULL ) | |
552 | exports_list += _file_buffer + start; | |
553 | else | |
554 | exports_list += hiddenName; | |
555 | exports_list += "\n"; | |
556 | start = i + 1; | |
557 | } else if ( _file_buffer[i] == '*' ) { | |
558 | throwf("illegal export list found. Please rebuild your static library using -exported_symbol[s_list] with the newest Xcode"); | |
559 | } | |
560 | } | |
561 | exports_list += "\n"; | |
562 | int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | |
563 | if ( f == -1 || ::write(f, exports_list.data(), exports_list.size()) != (int)exports_list.size() ) | |
564 | throwf("failed to write content to temp file: %s", path); | |
565 | ::close(f); | |
566 | } | |
567 | ||
568 | void FileHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) | |
eaf282aa A |
569 | { |
570 | initFile(); | |
571 | int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | |
572 | if ( f == -1 || ::write(f, _file_buffer, _file_size) != (int)_file_size ) | |
573 | throwf("failed to write content to temp file: %s", path); | |
574 | ::close(f); | |
575 | } | |
576 | ||
577 | void BitcodeBundle::doPass() | |
578 | { | |
dd9e569f A |
579 | if ( _options.bitcodeKind() == Options::kBitcodeStrip || |
580 | _options.bitcodeKind() == Options::kBitcodeAsData ) | |
581 | // if emit no bitcode or emit bitcode segment as data, no need to generate bundle. | |
582 | return; | |
583 | else if ( _state.embedMarkerOnly || _options.bitcodeKind() == Options::kBitcodeMarker ) { | |
584 | // if the bitcode is just a marker, | |
585 | // the executable will be created without bitcode section. | |
586 | // Otherwise, create a marker. | |
587 | if( _options.outputKind() != Options::kDynamicExecutable && | |
588 | _options.outputKind() != Options::kStaticExecutable ) { | |
589 | BitcodeAtom* marker = new BitcodeAtom(); | |
590 | _state.addAtom(*marker); | |
591 | } | |
eaf282aa A |
592 | return; |
593 | } | |
594 | ||
595 | if ( _state.filesWithBitcode.empty() && _state.ltoBitcodePath.empty() ) | |
596 | return; | |
597 | // Create tempdir, the temp directory should be OUTPUT/main.exe.bundle-XXXXXX | |
598 | char tempdir[PATH_MAX]; | |
599 | const char* finalOutput = _options.outputFilePath(); | |
600 | // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX | |
601 | // If so, fall back to /tmp | |
602 | if ( strlen(finalOutput) + 30 >= PATH_MAX ) | |
603 | sprintf(tempdir, "/tmp/ld.bundle.XXXXXX"); | |
604 | else | |
605 | sprintf(tempdir, "%s.bundle.XXXXXX", finalOutput); | |
606 | ::mkdtemp(tempdir); | |
607 | // A lookup map to look for BundlerHandler base on filename | |
608 | std::unordered_map<std::string, BundleHandler*> handlerMap; | |
609 | ||
610 | BitcodeObfuscator* obfuscator = _options.hideSymbols() ? new BitcodeObfuscator() : NULL; | |
611 | // Build must keep symbols if we need to hide all the symbols | |
612 | if ( _options.hideSymbols() ) { | |
613 | // Go through all the atoms and decide if it should be obfuscated. | |
614 | // The following symbols are kept: | |
615 | // 1. entry point | |
616 | // 2. undefined symbols | |
617 | // 3. symbols must not be stripped | |
618 | // 4. all the globals if the globals are dead_strip root (ex. dylibs) | |
619 | // 5. there is an exported symbol list suggests the symbol should be exported | |
620 | // 6. the special symbols supplied by linker | |
621 | for ( auto § : _state.sections ) { | |
622 | for ( auto &atom : sect->atoms ) { | |
623 | if ( atom == _state.entryPoint || | |
624 | atom->definition() == ld::Atom::definitionProxy || | |
625 | atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip || | |
626 | ( _options.allGlobalsAreDeadStripRoots() && atom->scope() == ld::Atom::scopeGlobal ) || | |
fb9a160c | 627 | ( _options.hasExportRestrictList() && _options.shouldExport(atom->name()) ) ) |
eaf282aa A |
628 | obfuscator->addMustPreserveSymbols(atom->name()); |
629 | } | |
630 | } | |
631 | // If there are assembly sources, add globals and undefined symbols from them as well | |
632 | for ( auto &f : _state.filesWithBitcode ) { | |
633 | if ( ld::AsmBitcode* ab = dynamic_cast<ld::AsmBitcode*>(f->getBitcode()) ) { | |
634 | ObjectHandler objHandler((char*)ab->getContent(), ab->getSize()); | |
635 | objHandler.populateMustPreserveSymbols(obfuscator); | |
636 | } else if ( ld::BundleBitcode* bb = dynamic_cast<ld::BundleBitcode*>(f->getBitcode()) ) { | |
637 | BundleHandler* bh = new BundleHandler((char*)bb->getContent(), bb->getSize(), _options); | |
638 | bh->populateMustPreserveSymbols(obfuscator); | |
639 | handlerMap.emplace(std::string(f->path()), bh); | |
fb9a160c A |
640 | } else if ( ld::LLVMBitcode* bitcode = dynamic_cast<ld::LLVMBitcode*>(f->getBitcode()) ) { |
641 | BitcodeHandler* bitcodeHandler = new BitcodeHandler((char*)bitcode->getContent(), bitcode->getSize()); | |
642 | bitcodeHandler->populateMustPreserveSymbols(obfuscator); | |
eaf282aa A |
643 | } |
644 | } | |
645 | // special symbols supplied by linker | |
646 | obfuscator->addMustPreserveSymbols("___dso_handle"); | |
647 | obfuscator->addMustPreserveSymbols("__mh_execute_header"); | |
648 | obfuscator->addMustPreserveSymbols("__mh_dylib_header"); | |
649 | obfuscator->addMustPreserveSymbols("__mh_bundle_header"); | |
650 | obfuscator->addMustPreserveSymbols("__mh_dylinker_header"); | |
651 | obfuscator->addMustPreserveSymbols("__mh_object_header"); | |
652 | obfuscator->addMustPreserveSymbols("__mh_preload_header"); | |
653 | } | |
654 | ||
655 | // Open XAR output | |
656 | xar_t x; | |
657 | char outFile[PATH_MAX]; | |
658 | sprintf(outFile, "%s/bundle.xar", tempdir); | |
659 | ||
660 | // By default, it uses gzip to compress and SHA1 as checksum | |
661 | x = xar_open(outFile, WRITE); | |
662 | if (x == NULL) | |
663 | throwf("could not open output bundle to write %s", outFile); | |
664 | // Disable compression | |
665 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
666 | throwf("could not disable compression for bitcode bundle"); | |
667 | ||
668 | // Sort all the object file according to oridnal order | |
669 | std::sort(_state.filesWithBitcode.begin(), _state.filesWithBitcode.end(), | |
670 | [](const ld::relocatable::File* a, const ld::relocatable::File* b) { | |
671 | return a->ordinal() < b->ordinal(); | |
672 | }); | |
673 | ||
674 | // Copy each bitcode file into archive | |
675 | int index = 1; | |
676 | char formatString[10]; | |
677 | sprintf(formatString, "%%0%ud", (unsigned int)log10(_state.filesWithBitcode.size()) + 1); | |
678 | for ( auto &obj : _state.filesWithBitcode ) { | |
679 | assert(obj->getBitcode() != NULL && "File should contain bitcode"); | |
680 | char outFilePath[16]; | |
681 | sprintf(outFilePath, formatString, index++); | |
682 | if ( ld::LLVMBitcode* llvmbc = dynamic_cast<ld::LLVMBitcode*>(obj->getBitcode()) ) { | |
683 | // Handle clang and swift bitcode | |
684 | xar_file_t bcFile = NULL; | |
685 | if ( _options.hideSymbols() && !llvmbc->isMarker() ) { // dont strip if it is just a marker | |
686 | char tempfile[PATH_MAX]; | |
687 | sprintf(tempfile, "%s/%s.bc", tempdir, outFilePath); | |
688 | obfuscator->bitcodeHideSymbols(llvmbc, obj->path(), tempfile); | |
689 | BitcodeTempFile* bcTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); | |
690 | bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bcTemp->getContent(), bcTemp->getSize()); | |
691 | delete bcTemp; | |
692 | } else { | |
693 | bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)const_cast<uint8_t*>(llvmbc->getContent()), llvmbc->getSize()); | |
694 | } | |
695 | if ( bcFile == NULL ) | |
696 | throwf("could not add bitcode from %s to bitcode bundle", obj->path()); | |
697 | if ( xar_prop_set(bcFile, "file-type", "Bitcode") != 0 ) | |
698 | throwf("could not set bitcode property for %s in bitcode bundle", obj->path()); | |
699 | // Write commandline options | |
700 | std::string tagName = std::string(llvmbc->getBitcodeName()) + std::string("/cmd"); | |
701 | for ( uint32_t i = 0; i < llvmbc->getCmdSize(); ++i ) { | |
702 | if ( i == 0 || llvmbc->getCmdline()[i-1] == '\0' ) { | |
703 | if ( xar_prop_create(bcFile, tagName.c_str(), (const char *)llvmbc->getCmdline() + i) ) | |
704 | throwf("could not set cmdline to XAR file"); | |
705 | } | |
706 | } | |
707 | } | |
708 | else if ( ld::BundleBitcode* bundlebc = dynamic_cast<ld::BundleBitcode*>(obj->getBitcode()) ) { | |
709 | xar_file_t bundleFile = NULL; | |
710 | if ( _options.hideSymbols() && !bundlebc->isMarker() ) { // dont strip if it is just a marker | |
711 | char tempfile[PATH_MAX]; | |
712 | sprintf(tempfile, "%s/%s.xar", tempdir, outFilePath); | |
713 | auto search = handlerMap.find(std::string(obj->path())); | |
714 | assert( search != handlerMap.end() && "Cannot find handler"); | |
715 | search->second->obfuscateAndWriteToPath(obfuscator, tempfile); | |
716 | BitcodeTempFile* bundleTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); | |
717 | bundleFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bundleTemp->getContent(), bundleTemp->getSize()); | |
718 | delete bundleTemp; | |
719 | } else { | |
720 | bundleFile = xar_add_frombuffer(x, NULL, outFilePath, | |
721 | (char*)const_cast<uint8_t*>(bundlebc->getContent()), | |
722 | bundlebc->getSize()); | |
723 | } | |
724 | if ( bundleFile == NULL ) | |
725 | throwf("could not add bitcode from the bundle %s to bitcode bundle", obj->path()); | |
726 | if ( xar_prop_set(bundleFile, "file-type", "Bundle") != 0 ) | |
727 | throwf("could not set bundle property for %s in bitcode bundle", obj->path()); | |
728 | } | |
729 | else if ( ld::AsmBitcode* asmbc = dynamic_cast<ld::AsmBitcode*>(obj->getBitcode()) ) { | |
730 | xar_file_t objFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)asmbc->getContent(), asmbc->getSize()); | |
731 | if ( objFile == NULL ) | |
732 | throwf("could not add obj file %s to bitcode bundle", obj->path()); | |
733 | if ( xar_prop_set(objFile, "file-type", "Object") != 0 ) | |
734 | throwf("could not set object property for %s in bitcode bundle", obj->path()); | |
735 | } | |
736 | else { | |
737 | assert(false && "Unknown bitcode"); | |
738 | } | |
739 | } | |
740 | ||
741 | // Write merged LTO bitcode | |
742 | if ( !_state.ltoBitcodePath.empty() ) { | |
743 | xar_file_t ltoFile = NULL; | |
744 | BitcodeTempFile* ltoTemp = new BitcodeTempFile(_state.ltoBitcodePath.c_str(), !_options.saveTempFiles()); | |
745 | if ( _options.hideSymbols() ) { | |
746 | ld::Bitcode ltoBitcode(ltoTemp->getContent(), ltoTemp->getSize()); | |
747 | char ltoTempFile[PATH_MAX]; | |
748 | sprintf(ltoTempFile, "%s/lto.bc", tempdir); | |
749 | obfuscator->bitcodeHideSymbols(<oBitcode, _state.ltoBitcodePath.c_str(), ltoTempFile); | |
750 | BitcodeTempFile* ltoStrip = new BitcodeTempFile(ltoTempFile, !_options.saveTempFiles()); | |
751 | ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoStrip->getContent(), ltoStrip->getSize()); | |
752 | delete ltoStrip; | |
753 | } else { | |
754 | ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoTemp->getContent(), ltoTemp->getSize()); | |
755 | } | |
756 | if ( ltoFile == NULL ) | |
757 | throwf("could not add lto file %s to bitcode bundle", _state.ltoBitcodePath.c_str()); | |
758 | if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 ) | |
759 | throwf("could not set bitcode property for %s in bitcode bundle", _state.ltoBitcodePath.c_str()); | |
760 | delete ltoTemp; | |
761 | } | |
762 | ||
763 | // Common LinkOptions | |
764 | std::vector<std::string> linkCmd = _options.writeBitcodeLinkOptions(); | |
765 | ||
766 | // support -sectcreate option | |
767 | for ( auto extraSect = _options.extraSectionsBegin(); extraSect != _options.extraSectionsEnd(); ++ extraSect ) { | |
768 | std::string sectName = std::string(extraSect->segmentName) + std::string(",") + std::string(extraSect->sectionName); | |
769 | BitcodeTempFile* sectFile = new BitcodeTempFile(extraSect->path, false); | |
770 | xar_file_t sectXar = xar_add_frombuffer(x, NULL, sectName.c_str(), (char*)sectFile->getContent(), sectFile->getSize()); | |
771 | if ( sectXar == NULL ) | |
772 | throwf("could not encode sectcreate file %s into bitcode bundle", extraSect->path); | |
773 | if ( xar_prop_set(sectXar, "file-type", "Section") != 0 ) | |
774 | throwf("could not set bitcode property for %s", sectName.c_str()); | |
775 | delete sectFile; | |
776 | linkCmd.push_back("-sectcreate"); | |
777 | linkCmd.push_back(extraSect->segmentName); | |
778 | linkCmd.push_back(extraSect->sectionName); | |
779 | linkCmd.push_back(sectName); | |
780 | } | |
781 | ||
782 | // Write exports file | |
fb9a160c | 783 | // A vector of all the exported symbols. |
eaf282aa | 784 | if ( _options.hasExportMaskList() ) { |
fb9a160c A |
785 | std::vector<const char*> exportedSymbols; |
786 | for ( auto § : _state.sections ) { | |
787 | for ( auto &atom : sect->atoms ) { | |
788 | // The symbols should be added to the export list is the ones that are: | |
789 | // globalScope, in SymbolTable and should be exported suggested by export file. | |
790 | if ( atom->scope() == ld::Atom::scopeGlobal && | |
791 | atom->symbolTableInclusion() == ld::Atom::symbolTableIn && | |
792 | _options.shouldExport(atom->name()) ) | |
793 | exportedSymbols.push_back(atom->name()); | |
794 | } | |
795 | } | |
eaf282aa A |
796 | linkCmd.push_back("-exported_symbols_list"); |
797 | linkCmd.push_back("exports.exp"); | |
798 | const char* exportsPath = "exports.exp"; | |
eaf282aa | 799 | std::string exps; |
fb9a160c A |
800 | for (std::vector<const char*>::iterator it = exportedSymbols.begin(); |
801 | it != exportedSymbols.end(); ++ it) { | |
eaf282aa A |
802 | exps += *it; |
803 | exps += "\n"; | |
804 | } | |
805 | // always append an empty line so exps cannot be empty. rdar://problem/22404253 | |
806 | exps += "\n"; | |
fb9a160c A |
807 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0) |
808 | throwf("could not set compression type for exports list"); | |
eaf282aa A |
809 | xar_file_t exportsFile = xar_add_frombuffer(x, NULL, exportsPath, const_cast<char*>(exps.data()), exps.size()); |
810 | if (exportsFile == NULL) | |
811 | throwf("could not add exports list to bitcode bundle"); | |
812 | if (xar_prop_set(exportsFile, "file-type", "Exports") != 0) | |
813 | throwf("could not set exports property in bitcode bundle"); | |
fb9a160c A |
814 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) |
815 | throwf("could not reset compression type for exports list"); | |
816 | } else if ( _options.hasExportRestrictList() ) { | |
817 | // handle unexported list here | |
818 | std::vector<const char*> unexportedSymbols; | |
819 | for ( auto § : _state.sections ) { | |
820 | for ( auto &atom : sect->atoms ) { | |
821 | // The unexported symbols should not include anything that is in TranslationUnit scope (static) or | |
822 | // that cannot be in the SymbolTable | |
823 | if ( atom->scope() != ld::Atom::scopeTranslationUnit && | |
824 | atom->symbolTableInclusion() == ld::Atom::symbolTableIn && | |
825 | !_options.shouldExport(atom->name()) ) | |
826 | unexportedSymbols.push_back(atom->name()); | |
827 | } | |
828 | } | |
829 | linkCmd.push_back("-unexported_symbols_list"); | |
830 | linkCmd.push_back("unexports.exp"); | |
831 | const char* unexportsPath = "unexports.exp"; | |
832 | std::string unexps; | |
833 | for (std::vector<const char*>::iterator it = unexportedSymbols.begin(); | |
834 | it != unexportedSymbols.end(); ++ it) { | |
835 | // try obfuscate the name for symbols in unexported symbols list. They are likely to be obfsucated. | |
836 | const char* sym_name = NULL; | |
837 | if ( _options.hideSymbols() ) | |
838 | sym_name = obfuscator->lookupHiddenName(*it); | |
839 | if ( sym_name ) | |
840 | unexps += sym_name; | |
841 | else | |
842 | unexps += *it; | |
843 | unexps += "\n"; | |
844 | } | |
845 | unexps += "\n"; | |
846 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0) | |
847 | throwf("could not set compression type for exports list"); | |
848 | xar_file_t unexportsFile = xar_add_frombuffer(x, NULL, unexportsPath, const_cast<char*>(unexps.data()), unexps.size()); | |
849 | if (unexportsFile == NULL) | |
850 | throwf("could not add unexports list to bitcode bundle"); | |
851 | if (xar_prop_set(unexportsFile, "file-type", "Exports") != 0) | |
852 | throwf("could not set exports property in bitcode bundle"); | |
853 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
854 | throwf("could not reset compression type for exports list"); | |
855 | } | |
856 | ||
857 | // Handle order file. We need to obfuscate all the entries in the order file | |
858 | if ( _options.orderedSymbolsCount() > 0 ) { | |
859 | std::string orderFile; | |
860 | for ( auto entry = _options.orderedSymbolsBegin(); entry != _options.orderedSymbolsEnd(); ++ entry ) { | |
861 | std::stringstream line; | |
862 | if ( entry->objectFileName != NULL ) { | |
863 | unsigned index = 0; | |
864 | for ( auto &f : _state.filesWithBitcode ) { | |
865 | const char* atomFullPath = f->path(); | |
866 | const char* lastSlash = strrchr(atomFullPath, '/'); | |
867 | if ( (lastSlash != NULL && strcmp(&lastSlash[1], entry->objectFileName) == 0) || | |
868 | strcmp(atomFullPath, entry->objectFileName) == 0 ) | |
869 | break; | |
870 | ++ index; | |
871 | } | |
872 | if ( index >= _state.filesWithBitcode.size() ) | |
873 | continue; | |
874 | line << index << ".o:"; | |
875 | } | |
876 | const char* sym_name = NULL; | |
877 | if ( _options.hideSymbols() ) | |
878 | sym_name = obfuscator->lookupHiddenName(entry->symbolName); | |
879 | if ( sym_name ) | |
880 | line << sym_name; | |
881 | else | |
882 | line << entry->symbolName; | |
883 | line << "\n"; | |
884 | orderFile += line.str(); | |
885 | } | |
886 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0) | |
887 | throwf("could not set compression type for order file"); | |
888 | xar_file_t ordersFile = xar_add_frombuffer(x, NULL, "file.order", const_cast<char*>(orderFile.data()), orderFile.size()); | |
889 | if (ordersFile == NULL) | |
890 | throwf("could not add order file to bitcode bundle"); | |
891 | if (xar_prop_set(ordersFile, "file-type", "OrderFile") != 0) | |
892 | throwf("could not set order file property in bitcode bundle"); | |
893 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
894 | throwf("could not reset compression type for order file"); | |
895 | linkCmd.push_back("-order_file"); | |
896 | linkCmd.push_back("file.order"); | |
eaf282aa A |
897 | } |
898 | ||
899 | // Create subdoc to write link information | |
900 | xar_subdoc_t linkXML = xar_subdoc_new(x, "Ld"); | |
901 | if ( linkXML == NULL ) | |
902 | throwf("could not create XML in bitcode bundle"); | |
903 | ||
904 | // Write version number | |
905 | if ( xar_prop_create((xar_file_t)linkXML, "version", BITCODE_XAR_VERSION) != 0 ) | |
906 | throwf("could not add version number to bitcode bundle"); | |
907 | ||
908 | // Arch | |
909 | if ( xar_prop_create((xar_file_t)linkXML, "architecture", _options.architectureName()) != 0 ) | |
910 | throwf("could not add achitecture name to bitcode bundle"); | |
911 | ||
912 | // Opt-out symbols | |
913 | if ( _options.hideSymbols() ) { | |
914 | if ( xar_prop_create((xar_file_t)linkXML, "hide-symbols", "1") != 0 ) | |
915 | throwf("could not add property to bitcode bundle"); | |
916 | } | |
917 | ||
918 | // Write SDK version | |
919 | if ( _options.sdkPaths().size() > 1 ) | |
920 | throwf("only one -syslibroot is accepted for bitcode bundle"); | |
921 | if ( xar_prop_create((xar_file_t)linkXML, "platform", _options.getPlatformStr().c_str()) != 0 ) | |
922 | throwf("could not add platform name to bitcode bundle"); | |
923 | if ( xar_prop_create((xar_file_t)linkXML, "sdkversion", _options.getSDKVersionStr().c_str()) != 0 ) | |
924 | throwf("could not add SDK version to bitcode bundle"); | |
925 | ||
926 | // Write dylibs | |
dd9e569f A |
927 | char sdkRoot[PATH_MAX]; |
928 | if ( _options.sdkPaths().empty() || (realpath(_options.sdkPaths().front(), sdkRoot) == NULL) ) | |
929 | strcpy(sdkRoot, "/"); | |
eaf282aa | 930 | if ( !_state.dylibs.empty() ) { |
eaf282aa A |
931 | char dylibPath[PATH_MAX]; |
932 | for ( auto &dylib : _state.dylibs ) { | |
dd9e569f A |
933 | // For every dylib/framework, figure out if it is coming from a SDK. |
934 | // The dylib/framework from SDK must begin with '/' and user framework must begin with '@'. | |
935 | if (dylib->installPath()[0] == '/') { | |
936 | // Verify the path of the framework is within the SDK. | |
937 | char dylibRealPath[PATH_MAX]; | |
938 | if ( realpath(dylib->path(), dylibRealPath) != NULL && strncmp(sdkRoot, dylibRealPath, strlen(sdkRoot)) != 0 ) | |
939 | warning("%s has install name beginning with \"/\" but it is not from the specified SDK", dylib->path()); | |
eaf282aa | 940 | // The path start with a string template |
dd9e569f | 941 | strcpy(dylibPath, "{SDKPATH}"); |
eaf282aa | 942 | // append the path of dylib/frameowrk in the SDK |
dd9e569f | 943 | strcat(dylibPath, dylib->installPath()); |
eaf282aa A |
944 | } else { |
945 | // Not in any SDKs, then assume it is a user dylib/framework | |
946 | // strip off all the path in the front | |
947 | const char* dylib_name = strrchr(dylib->path(), '/'); | |
948 | dylib_name = (dylib_name == NULL) ? dylib->path() : dylib_name + 1; | |
949 | strcpy(dylibPath, dylib_name); | |
950 | } | |
951 | if ( dylib->forcedWeakLinked() ) { | |
952 | if ( xar_prop_create((xar_file_t)linkXML, "dylibs/weak", dylibPath) != 0) | |
953 | throwf("could not add dylib options to bitcode bundle"); | |
954 | } else { | |
955 | if ( xar_prop_create((xar_file_t)linkXML, "dylibs/lib", dylibPath) != 0) | |
956 | throwf("could not add dylib options to bitcode bundle"); | |
957 | } | |
958 | } | |
959 | } | |
960 | ||
961 | // Write link-line into archive | |
962 | for ( auto &it : linkCmd ) { | |
963 | if (xar_prop_create((xar_file_t)linkXML, "link-options/option", it.c_str()) != 0) | |
964 | throwf("could not add link options to bitcode bundle"); | |
965 | } | |
966 | // Finish writing | |
967 | xar_close(x); | |
968 | ||
969 | // Read the file back | |
970 | BitcodeTempFile* xarTemp = new BitcodeTempFile(outFile, !_options.saveTempFiles()); | |
971 | ||
972 | // Create an Atom and add to the list | |
973 | BitcodeAtom* bundleAtom = new BitcodeAtom(*xarTemp); | |
974 | _state.addAtom(*bundleAtom); | |
975 | ||
976 | // write the reverse mapping file if required | |
977 | if ( _options.hideSymbols() && !_options.reverseMapTempPath().empty() ) | |
978 | obfuscator->writeSymbolMap(_options.reverseMapTempPath().c_str()); | |
979 | ||
980 | // Clean up local variables | |
981 | delete xarTemp; | |
982 | delete obfuscator; | |
983 | for ( auto &entry: handlerMap ) | |
984 | delete entry.second; | |
985 | // delete temp directory if not using -save-temps | |
986 | // only do so after all the BitcodeTempFiles are deleted. | |
987 | if ( !_options.saveTempFiles() ) { | |
988 | if ( ::rmdir(tempdir) != 0 ) | |
989 | warning("temp directory cannot be removed: %s", tempdir); | |
990 | } | |
991 | } | |
992 | ||
993 | ||
994 | ||
995 | // called by linker to write bitcode bundle into a mach-o section | |
996 | void doPass(const Options& opts, ld::Internal& internal) { | |
997 | BitcodeBundle BB(opts, internal); | |
998 | BB.doPass(); | |
999 | } | |
1000 | ||
1001 | ||
1002 | } // namespace bitcode_bundle | |
1003 | } // namespace passes | |
1004 | } // namespace ld |