]>
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 | { | |
ec29ba20 A |
271 | #if LTO_API_VERSION < 11 |
272 | throwf("compile-time libLTO (%d) didn't support -bitcode_hide_symbols", LTO_API_VERSION); | |
273 | #else | |
eaf282aa A |
274 | // check if apple internal libLTO is used |
275 | if ( ::lto_get_version() == NULL ) | |
276 | throwf("libLTO is not loaded"); | |
277 | _lto_hide_symbols = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_hide_symbols"); | |
278 | _lto_write_reverse_map = (lto_codegen_output_t) dlsym(RTLD_DEFAULT, "lto_codegen_write_symbol_reverse_map"); | |
279 | _lto_reset_context = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_reset_context"); | |
fb9a160c A |
280 | _lto_lookup_hidden_name = (lto_codegen_lookup_t) dlsym(RTLD_DEFAULT, "lto_codegen_lookup_hidden_name"); |
281 | _lto_get_asm_symbol_num = (lto_module_num_symbols) dlsym(RTLD_DEFAULT, "lto_module_get_num_asm_symbols"); | |
282 | _lto_get_asm_symbol_name = (lto_module_symbol_name) dlsym(RTLD_DEFAULT, "lto_module_get_asm_symbol_name"); | |
eaf282aa | 283 | if ( _lto_hide_symbols == NULL || _lto_write_reverse_map == NULL || |
fb9a160c A |
284 | _lto_reset_context == NULL || _lto_lookup_hidden_name == NULL || |
285 | _lto_get_asm_symbol_num == NULL || _lto_get_asm_symbol_name == NULL || ::lto_api_version() < 14 ) | |
eaf282aa A |
286 | throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version()); |
287 | _obfuscator = ::lto_codegen_create_in_local_context(); | |
ec29ba20 | 288 | #if LTO_API_VERSION >= 14 |
eaf282aa | 289 | lto_codegen_set_should_internalize(_obfuscator, false); |
ec29ba20 | 290 | #endif |
eaf282aa A |
291 | #endif |
292 | } | |
293 | ||
ec29ba20 | 294 | |
eaf282aa A |
295 | BitcodeObfuscator::~BitcodeObfuscator() |
296 | { | |
297 | ::lto_codegen_dispose(_obfuscator); | |
298 | } | |
299 | ||
300 | void BitcodeObfuscator::addMustPreserveSymbols(const char* name) | |
301 | { | |
302 | ::lto_codegen_add_must_preserve_symbol(_obfuscator, name); | |
303 | } | |
304 | ||
305 | void BitcodeObfuscator::bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath) | |
306 | { | |
307 | #if LTO_API_VERSION >= 13 && LTO_APPLE_INTERNAL | |
308 | lto_module_t module = ::lto_module_create_in_codegen_context(bc->getContent(), bc->getSize(), filePath, _obfuscator); | |
309 | if ( module == NULL ) | |
ec29ba20 A |
310 | throwf("could not reparse object file %s in bitcode bundle: '%s', using libLTO version '%s'", |
311 | filePath, ::lto_get_error_message(), ::lto_get_version()); | |
eaf282aa A |
312 | ::lto_codegen_set_module(_obfuscator, module); |
313 | (*_lto_hide_symbols)(_obfuscator); | |
314 | #if LTO_API_VERSION >= 15 | |
315 | ::lto_codegen_set_should_embed_uselists(_obfuscator, true); | |
316 | #endif | |
317 | ::lto_codegen_write_merged_modules(_obfuscator, outputPath); | |
318 | (*_lto_reset_context)(_obfuscator); | |
319 | #endif | |
320 | return; | |
321 | } | |
322 | ||
323 | void BitcodeObfuscator::writeSymbolMap(const char *outputPath) | |
324 | { | |
325 | (*_lto_write_reverse_map)(_obfuscator, outputPath); | |
326 | } | |
327 | ||
fb9a160c A |
328 | const char* BitcodeObfuscator::lookupHiddenName(const char *symbol) |
329 | { | |
330 | return (*_lto_lookup_hidden_name)(_obfuscator, symbol); | |
331 | } | |
332 | ||
333 | void BitcodeObfuscator::addAsmSymbolsToMustPreserve(lto_module_t module) | |
334 | { | |
335 | for (unsigned int i = 0; i < _lto_get_asm_symbol_num(module); ++ i) { | |
336 | addMustPreserveSymbols(_lto_get_asm_symbol_name(module, i)); | |
337 | } | |
338 | } | |
339 | ||
eaf282aa A |
340 | BundleHandler::~BundleHandler() |
341 | { | |
342 | // free buffers | |
343 | destroyFile(); | |
344 | // free handlers | |
345 | for (auto handler : _handlers) | |
346 | delete handler; | |
347 | ||
348 | // delete temp file if not -save-temps | |
349 | if ( _xar ) { | |
350 | xar_close(_xar); | |
351 | std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); | |
352 | if ( !_options.saveTempFiles() && ::unlink(oldXARPath.c_str()) != 0) | |
353 | warning("could not delete temp file: %s", oldXARPath.c_str()); | |
354 | } | |
355 | ||
356 | if ( _temp_dir ) { | |
357 | if ( !_options.saveTempFiles() && ::rmdir(_temp_dir) != 0 ) | |
358 | warning("could not delete temp directory: %s", _temp_dir); | |
359 | free(_temp_dir); | |
360 | } | |
361 | } | |
362 | ||
363 | BitcodeHandler::~BitcodeHandler() | |
364 | { | |
365 | destroyFile(); | |
366 | } | |
367 | ||
368 | ObjectHandler::~ObjectHandler() | |
369 | { | |
370 | destroyFile(); | |
371 | } | |
372 | ||
fb9a160c A |
373 | SymbolListHandler::~SymbolListHandler() |
374 | { | |
375 | destroyFile(); | |
376 | } | |
377 | ||
eaf282aa A |
378 | void BundleHandler::init() |
379 | { | |
380 | if ( _xar != NULL ) | |
381 | return; | |
382 | ||
383 | // make temp directory | |
384 | const char* finalOutput = _options.outputFilePath(); | |
385 | _temp_dir = (char*)malloc(PATH_MAX * sizeof(char)); | |
386 | // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX | |
387 | // If so, fall back to /tmp | |
388 | if ( strlen(finalOutput) + 30 >= PATH_MAX ) | |
389 | sprintf(_temp_dir, "/tmp/ld.bundle.XXXXXX"); | |
390 | else | |
391 | sprintf(_temp_dir, "%s.bundle.XXXXXX", finalOutput); | |
392 | ::mkdtemp(_temp_dir); | |
393 | ||
394 | // write the bundle to the temp_directory | |
395 | initFile(); | |
396 | std::string oldXARPath = std::string(_temp_dir) + std::string("/bundle.xar"); | |
397 | int f = ::open(oldXARPath.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | |
398 | if ( f == -1 ) | |
399 | throwf("could not write file to temp directory: %s", _temp_dir); | |
400 | if ( ::write(f, _file_buffer, _file_size) != (int)_file_size ) | |
401 | throwf("failed to write content to temp file: %s", oldXARPath.c_str()); | |
402 | ::close(f); | |
403 | ||
404 | // read the xar file | |
405 | _xar = xar_open(oldXARPath.c_str(), READ); | |
ec29ba20 A |
406 | if ( _xar == NULL ) |
407 | throwf("malformed bundle format"); | |
eaf282aa A |
408 | |
409 | // Init the vector of handler | |
410 | xar_iter_t iter = xar_iter_new(); | |
411 | if ( !iter ) | |
412 | throwf("could not aquire iterator for the bitcode bundle"); | |
413 | for ( xar_file_t f = xar_file_first(_xar, iter); f; f = xar_file_next(iter) ) { | |
414 | const char* filetype = NULL; | |
415 | if ( xar_prop_get(f, "file-type", &filetype) != 0 ) | |
416 | throwf("could not get the file type for the bitcode bundle"); | |
417 | if ( strcmp(filetype, "Bundle") == 0 ) | |
418 | _handlers.push_back(new BundleHandler(_xar, f, _options)); | |
419 | else if ( strcmp(filetype, "Object") == 0 ) | |
420 | _handlers.push_back(new ObjectHandler(_xar, f)); | |
421 | else if ( strcmp(filetype, "Bitcode") == 0 || strcmp(filetype, "LTO") == 0 ) | |
422 | _handlers.push_back(new BitcodeHandler(_xar, f)); | |
fb9a160c A |
423 | else if ( strcmp(filetype, "Exports") == 0 || strcmp(filetype, "OrderFile") == 0) |
424 | _handlers.push_back(new SymbolListHandler(_xar, f)); | |
eaf282aa | 425 | else |
fb9a160c | 426 | _handlers.push_back(new FileHandler(_xar, f)); |
eaf282aa A |
427 | } |
428 | xar_iter_free(iter); | |
429 | } | |
430 | ||
431 | void BundleHandler::copyXARProp(xar_file_t src, xar_file_t dst) | |
432 | { | |
433 | // copy the property in the XAR. | |
434 | // Since XAR API can only get the first value from the key, | |
435 | // Deleting the value after read. | |
436 | int i = 0; | |
437 | while (1) { | |
438 | xar_iter_t p = xar_iter_new(); | |
439 | const char* key = xar_prop_first(src, p); | |
440 | for (int x = 0; x < i; x++) | |
441 | key = xar_prop_next(p); | |
ec29ba20 A |
442 | if ( !key ) { |
443 | xar_iter_free(p); | |
eaf282aa | 444 | break; |
ec29ba20 | 445 | } |
eaf282aa A |
446 | const char* val = NULL; |
447 | xar_prop_get(src, key, &val); | |
448 | if ( // Info from bitcode files | |
449 | strcmp(key, "file-type") == 0 || | |
450 | strcmp(key, "clang/cmd") == 0 || | |
451 | strcmp(key, "swift/cmd") == 0 || | |
452 | // Info from linker subdoc | |
453 | strcmp(key, "version") == 0 || | |
454 | strcmp(key, "architecture") == 0 || | |
455 | strcmp(key, "hide-symbols") == 0 || | |
456 | strcmp(key, "platform") == 0 || | |
457 | strcmp(key, "sdkversion") == 0 || | |
458 | strcmp(key, "dylibs/lib") == 0 || | |
459 | strcmp(key, "link-options/option") == 0 ) { | |
460 | xar_prop_create(dst, key, val); | |
461 | xar_prop_unset(src, key); | |
462 | } else | |
463 | ++ i; | |
464 | xar_iter_free(p); | |
465 | } | |
466 | } | |
467 | ||
468 | void BundleHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) | |
469 | { | |
470 | // init the handler | |
471 | if ( _xar == NULL ) | |
472 | init(); | |
473 | ||
474 | // iterate through the XAR file and add symbols | |
475 | for ( auto handler : _handlers ) | |
476 | handler->populateMustPreserveSymbols(obfuscator); | |
477 | } | |
478 | ||
fb9a160c A |
479 | void BitcodeHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) |
480 | { | |
481 | initFile(); | |
482 | ||
483 | // init LTOModule and add asm labels | |
ec29ba20 | 484 | #if LTO_API_VERSION < 11 |
fb9a160c | 485 | lto_module_t module = lto_module_create_from_memory(_file_buffer, _file_size); |
ec29ba20 A |
486 | #else |
487 | lto_module_t module = lto_module_create_in_local_context(_file_buffer, _file_size, "bitcode bundle temp file"); | |
488 | #endif | |
489 | if ( module == NULL ) | |
490 | throwf("could not reparse object file in bitcode bundle: '%s', using libLTO version '%s'", | |
491 | ::lto_get_error_message(), ::lto_get_version()); | |
fb9a160c A |
492 | obfuscator->addAsmSymbolsToMustPreserve(module); |
493 | lto_module_dispose(module); | |
494 | } | |
495 | ||
496 | ||
eaf282aa A |
497 | void ObjectHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) |
498 | { | |
499 | initFile(); | |
500 | // Parse the object file and add the symbols | |
501 | std::vector<const char*> symbols; | |
502 | if ( mach_o::relocatable::getNonLocalSymbols((uint8_t*)_file_buffer, symbols) ) { | |
503 | for ( auto sym : symbols ) | |
504 | obfuscator->addMustPreserveSymbols(sym); | |
505 | } | |
506 | } | |
507 | ||
508 | void BundleHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) | |
509 | { | |
510 | // init the handler | |
511 | if ( _xar == NULL ) | |
512 | init(); | |
513 | ||
514 | // creating the new xar | |
515 | xar_t x = xar_open(path, WRITE); | |
516 | if (x == NULL) | |
517 | throwf("could not open output bundle to write %s", path); | |
518 | // Disable compression | |
519 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
520 | throwf("could not disable compression for bitcode bundle"); | |
521 | ||
522 | // iterate through the XAR file and obfuscate | |
523 | for ( auto handler : _handlers ) { | |
524 | const char* name = NULL; | |
525 | xar_file_t f = handler->getXARFile(); | |
526 | if ( xar_prop_get(f, "name", &name) != 0 ) | |
527 | throwf("could not get the name of the file from bitcode bundle"); | |
528 | char outputPath[PATH_MAX]; | |
529 | sprintf(outputPath, "%s/%s", _temp_dir, name); | |
530 | handler->obfuscateAndWriteToPath(obfuscator, outputPath); | |
531 | BitcodeTempFile* bcOut = new BitcodeTempFile(outputPath, !_options.saveTempFiles()); | |
fb9a160c A |
532 | if ( xar_opt_set(x, XAR_OPT_COMPRESSION, handler->compressionMethod()) != 0 ) |
533 | throwf("could not set compression type for exports list"); | |
eaf282aa | 534 | xar_file_t bcEntry = xar_add_frombuffer(x, NULL, name, (char*)bcOut->getContent(), bcOut->getSize()); |
fb9a160c A |
535 | if ( bcEntry == NULL ) |
536 | throwf("could not add file to the bundle"); | |
537 | if ( xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0 ) | |
538 | throwf("could not reset compression type for exports list"); | |
eaf282aa A |
539 | copyXARProp(f, bcEntry); |
540 | delete bcOut; | |
541 | } | |
542 | ||
543 | // copy the subdoc as well | |
544 | for ( xar_subdoc_t sub = xar_subdoc_first(_xar); sub; sub = xar_subdoc_next(sub) ) { | |
545 | const char *name = xar_subdoc_name(sub); | |
546 | xar_subdoc_t newDoc = xar_subdoc_new(x, name); | |
547 | copyXARProp((xar_file_t) sub, (xar_file_t) newDoc); | |
548 | } | |
549 | xar_close(x); | |
550 | } | |
551 | ||
552 | void BitcodeHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) | |
553 | { | |
554 | initFile(); | |
555 | ld::Bitcode bc((uint8_t*)_file_buffer, _file_size); | |
556 | obfuscator->bitcodeHideSymbols(&bc, path, path); | |
557 | } | |
558 | ||
fb9a160c A |
559 | void SymbolListHandler::obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) |
560 | { | |
561 | initFile(); | |
562 | // Obfuscate exported symbol list. | |
563 | std::string exports_list; | |
564 | for (size_t i = 0, start = 0; i < _file_size; ++i) { | |
565 | if ( _file_buffer[i] == '\n' ) { | |
566 | _file_buffer[i] = '\0'; | |
567 | const char* hiddenName = obfuscator->lookupHiddenName(_file_buffer + start); | |
568 | if ( hiddenName == NULL ) | |
569 | exports_list += _file_buffer + start; | |
570 | else | |
571 | exports_list += hiddenName; | |
572 | exports_list += "\n"; | |
573 | start = i + 1; | |
574 | } else if ( _file_buffer[i] == '*' ) { | |
575 | throwf("illegal export list found. Please rebuild your static library using -exported_symbol[s_list] with the newest Xcode"); | |
576 | } | |
577 | } | |
578 | exports_list += "\n"; | |
579 | int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | |
580 | if ( f == -1 || ::write(f, exports_list.data(), exports_list.size()) != (int)exports_list.size() ) | |
581 | throwf("failed to write content to temp file: %s", path); | |
582 | ::close(f); | |
583 | } | |
584 | ||
585 | void FileHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path) | |
eaf282aa A |
586 | { |
587 | initFile(); | |
588 | int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR); | |
589 | if ( f == -1 || ::write(f, _file_buffer, _file_size) != (int)_file_size ) | |
590 | throwf("failed to write content to temp file: %s", path); | |
591 | ::close(f); | |
592 | } | |
593 | ||
594 | void BitcodeBundle::doPass() | |
595 | { | |
dd9e569f A |
596 | if ( _options.bitcodeKind() == Options::kBitcodeStrip || |
597 | _options.bitcodeKind() == Options::kBitcodeAsData ) | |
598 | // if emit no bitcode or emit bitcode segment as data, no need to generate bundle. | |
599 | return; | |
600 | else if ( _state.embedMarkerOnly || _options.bitcodeKind() == Options::kBitcodeMarker ) { | |
601 | // if the bitcode is just a marker, | |
602 | // the executable will be created without bitcode section. | |
603 | // Otherwise, create a marker. | |
604 | if( _options.outputKind() != Options::kDynamicExecutable && | |
605 | _options.outputKind() != Options::kStaticExecutable ) { | |
606 | BitcodeAtom* marker = new BitcodeAtom(); | |
607 | _state.addAtom(*marker); | |
608 | } | |
eaf282aa A |
609 | return; |
610 | } | |
611 | ||
612 | if ( _state.filesWithBitcode.empty() && _state.ltoBitcodePath.empty() ) | |
613 | return; | |
614 | // Create tempdir, the temp directory should be OUTPUT/main.exe.bundle-XXXXXX | |
615 | char tempdir[PATH_MAX]; | |
616 | const char* finalOutput = _options.outputFilePath(); | |
617 | // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX | |
618 | // If so, fall back to /tmp | |
619 | if ( strlen(finalOutput) + 30 >= PATH_MAX ) | |
620 | sprintf(tempdir, "/tmp/ld.bundle.XXXXXX"); | |
621 | else | |
622 | sprintf(tempdir, "%s.bundle.XXXXXX", finalOutput); | |
623 | ::mkdtemp(tempdir); | |
624 | // A lookup map to look for BundlerHandler base on filename | |
625 | std::unordered_map<std::string, BundleHandler*> handlerMap; | |
626 | ||
627 | BitcodeObfuscator* obfuscator = _options.hideSymbols() ? new BitcodeObfuscator() : NULL; | |
628 | // Build must keep symbols if we need to hide all the symbols | |
629 | if ( _options.hideSymbols() ) { | |
630 | // Go through all the atoms and decide if it should be obfuscated. | |
631 | // The following symbols are kept: | |
632 | // 1. entry point | |
633 | // 2. undefined symbols | |
634 | // 3. symbols must not be stripped | |
635 | // 4. all the globals if the globals are dead_strip root (ex. dylibs) | |
636 | // 5. there is an exported symbol list suggests the symbol should be exported | |
ec29ba20 A |
637 | // 6. weak external symbols (not auto-hide) |
638 | // 7. the special symbols supplied by linker | |
eaf282aa A |
639 | for ( auto § : _state.sections ) { |
640 | for ( auto &atom : sect->atoms ) { | |
641 | if ( atom == _state.entryPoint || | |
642 | atom->definition() == ld::Atom::definitionProxy || | |
643 | atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip || | |
644 | ( _options.allGlobalsAreDeadStripRoots() && atom->scope() == ld::Atom::scopeGlobal ) || | |
ec29ba20 A |
645 | ( _options.hasExportRestrictList() && _options.shouldExport(atom->name()) ) || |
646 | ( atom->combine() == ld::Atom::combineByName && atom->scope() == ld::Atom::scopeGlobal && !atom->autoHide() ) ) | |
eaf282aa A |
647 | obfuscator->addMustPreserveSymbols(atom->name()); |
648 | } | |
649 | } | |
650 | // If there are assembly sources, add globals and undefined symbols from them as well | |
651 | for ( auto &f : _state.filesWithBitcode ) { | |
652 | if ( ld::AsmBitcode* ab = dynamic_cast<ld::AsmBitcode*>(f->getBitcode()) ) { | |
653 | ObjectHandler objHandler((char*)ab->getContent(), ab->getSize()); | |
654 | objHandler.populateMustPreserveSymbols(obfuscator); | |
655 | } else if ( ld::BundleBitcode* bb = dynamic_cast<ld::BundleBitcode*>(f->getBitcode()) ) { | |
656 | BundleHandler* bh = new BundleHandler((char*)bb->getContent(), bb->getSize(), _options); | |
657 | bh->populateMustPreserveSymbols(obfuscator); | |
658 | handlerMap.emplace(std::string(f->path()), bh); | |
fb9a160c | 659 | } else if ( ld::LLVMBitcode* bitcode = dynamic_cast<ld::LLVMBitcode*>(f->getBitcode()) ) { |
0a8dc3df A |
660 | BitcodeHandler bitcodeHandler((char*)bitcode->getContent(), bitcode->getSize()); |
661 | bitcodeHandler.populateMustPreserveSymbols(obfuscator); | |
eaf282aa A |
662 | } |
663 | } | |
0a8dc3df A |
664 | // add must preserve symbols from lto input. |
665 | for ( auto &f : _state.ltoBitcodePath ) { | |
666 | BitcodeTempFile ltoTemp(f.c_str(), false); // Keep the temp file because it needs to be read in later in the pass. | |
667 | BitcodeHandler bitcodeHandler((char*)ltoTemp.getContent(), ltoTemp.getSize()); | |
668 | bitcodeHandler.populateMustPreserveSymbols(obfuscator); | |
669 | } | |
670 | ||
eaf282aa A |
671 | // special symbols supplied by linker |
672 | obfuscator->addMustPreserveSymbols("___dso_handle"); | |
673 | obfuscator->addMustPreserveSymbols("__mh_execute_header"); | |
674 | obfuscator->addMustPreserveSymbols("__mh_dylib_header"); | |
675 | obfuscator->addMustPreserveSymbols("__mh_bundle_header"); | |
676 | obfuscator->addMustPreserveSymbols("__mh_dylinker_header"); | |
677 | obfuscator->addMustPreserveSymbols("__mh_object_header"); | |
678 | obfuscator->addMustPreserveSymbols("__mh_preload_header"); | |
ec29ba20 A |
679 | |
680 | // add all the Proxy Atom linker ever created and all the undefs that are possibily dead-stripped. | |
681 | for (auto sym : _state.allUndefProxies) | |
682 | obfuscator->addMustPreserveSymbols(sym); | |
683 | _state.allUndefProxies.clear(); | |
eaf282aa A |
684 | } |
685 | ||
686 | // Open XAR output | |
687 | xar_t x; | |
688 | char outFile[PATH_MAX]; | |
689 | sprintf(outFile, "%s/bundle.xar", tempdir); | |
690 | ||
691 | // By default, it uses gzip to compress and SHA1 as checksum | |
692 | x = xar_open(outFile, WRITE); | |
693 | if (x == NULL) | |
694 | throwf("could not open output bundle to write %s", outFile); | |
695 | // Disable compression | |
696 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
697 | throwf("could not disable compression for bitcode bundle"); | |
698 | ||
699 | // Sort all the object file according to oridnal order | |
700 | std::sort(_state.filesWithBitcode.begin(), _state.filesWithBitcode.end(), | |
701 | [](const ld::relocatable::File* a, const ld::relocatable::File* b) { | |
702 | return a->ordinal() < b->ordinal(); | |
703 | }); | |
704 | ||
705 | // Copy each bitcode file into archive | |
706 | int index = 1; | |
707 | char formatString[10]; | |
708 | sprintf(formatString, "%%0%ud", (unsigned int)log10(_state.filesWithBitcode.size()) + 1); | |
709 | for ( auto &obj : _state.filesWithBitcode ) { | |
710 | assert(obj->getBitcode() != NULL && "File should contain bitcode"); | |
711 | char outFilePath[16]; | |
712 | sprintf(outFilePath, formatString, index++); | |
713 | if ( ld::LLVMBitcode* llvmbc = dynamic_cast<ld::LLVMBitcode*>(obj->getBitcode()) ) { | |
714 | // Handle clang and swift bitcode | |
715 | xar_file_t bcFile = NULL; | |
716 | if ( _options.hideSymbols() && !llvmbc->isMarker() ) { // dont strip if it is just a marker | |
717 | char tempfile[PATH_MAX]; | |
718 | sprintf(tempfile, "%s/%s.bc", tempdir, outFilePath); | |
719 | obfuscator->bitcodeHideSymbols(llvmbc, obj->path(), tempfile); | |
720 | BitcodeTempFile* bcTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); | |
721 | bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bcTemp->getContent(), bcTemp->getSize()); | |
722 | delete bcTemp; | |
723 | } else { | |
724 | bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)const_cast<uint8_t*>(llvmbc->getContent()), llvmbc->getSize()); | |
725 | } | |
726 | if ( bcFile == NULL ) | |
727 | throwf("could not add bitcode from %s to bitcode bundle", obj->path()); | |
728 | if ( xar_prop_set(bcFile, "file-type", "Bitcode") != 0 ) | |
729 | throwf("could not set bitcode property for %s in bitcode bundle", obj->path()); | |
730 | // Write commandline options | |
731 | std::string tagName = std::string(llvmbc->getBitcodeName()) + std::string("/cmd"); | |
732 | for ( uint32_t i = 0; i < llvmbc->getCmdSize(); ++i ) { | |
733 | if ( i == 0 || llvmbc->getCmdline()[i-1] == '\0' ) { | |
734 | if ( xar_prop_create(bcFile, tagName.c_str(), (const char *)llvmbc->getCmdline() + i) ) | |
735 | throwf("could not set cmdline to XAR file"); | |
736 | } | |
737 | } | |
738 | } | |
739 | else if ( ld::BundleBitcode* bundlebc = dynamic_cast<ld::BundleBitcode*>(obj->getBitcode()) ) { | |
740 | xar_file_t bundleFile = NULL; | |
741 | if ( _options.hideSymbols() && !bundlebc->isMarker() ) { // dont strip if it is just a marker | |
742 | char tempfile[PATH_MAX]; | |
743 | sprintf(tempfile, "%s/%s.xar", tempdir, outFilePath); | |
744 | auto search = handlerMap.find(std::string(obj->path())); | |
745 | assert( search != handlerMap.end() && "Cannot find handler"); | |
746 | search->second->obfuscateAndWriteToPath(obfuscator, tempfile); | |
747 | BitcodeTempFile* bundleTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles()); | |
748 | bundleFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bundleTemp->getContent(), bundleTemp->getSize()); | |
749 | delete bundleTemp; | |
750 | } else { | |
751 | bundleFile = xar_add_frombuffer(x, NULL, outFilePath, | |
752 | (char*)const_cast<uint8_t*>(bundlebc->getContent()), | |
753 | bundlebc->getSize()); | |
754 | } | |
755 | if ( bundleFile == NULL ) | |
756 | throwf("could not add bitcode from the bundle %s to bitcode bundle", obj->path()); | |
757 | if ( xar_prop_set(bundleFile, "file-type", "Bundle") != 0 ) | |
758 | throwf("could not set bundle property for %s in bitcode bundle", obj->path()); | |
759 | } | |
760 | else if ( ld::AsmBitcode* asmbc = dynamic_cast<ld::AsmBitcode*>(obj->getBitcode()) ) { | |
761 | xar_file_t objFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)asmbc->getContent(), asmbc->getSize()); | |
762 | if ( objFile == NULL ) | |
763 | throwf("could not add obj file %s to bitcode bundle", obj->path()); | |
764 | if ( xar_prop_set(objFile, "file-type", "Object") != 0 ) | |
765 | throwf("could not set object property for %s in bitcode bundle", obj->path()); | |
766 | } | |
767 | else { | |
768 | assert(false && "Unknown bitcode"); | |
769 | } | |
770 | } | |
771 | ||
0a8dc3df | 772 | // Write merged LTO bitcode files |
eaf282aa | 773 | if ( !_state.ltoBitcodePath.empty() ) { |
0a8dc3df A |
774 | int count = 0; |
775 | for (auto &path : _state.ltoBitcodePath) { | |
776 | std::string xar_name = "lto.o." + std::to_string(count++); | |
777 | xar_file_t ltoFile = NULL; | |
778 | BitcodeTempFile* ltoTemp = new BitcodeTempFile(path.c_str(), !_options.saveTempFiles()); | |
779 | if ( _options.hideSymbols() ) { | |
eaf282aa A |
780 | ld::Bitcode ltoBitcode(ltoTemp->getContent(), ltoTemp->getSize()); |
781 | char ltoTempFile[PATH_MAX]; | |
782 | sprintf(ltoTempFile, "%s/lto.bc", tempdir); | |
0a8dc3df | 783 | obfuscator->bitcodeHideSymbols(<oBitcode, path.c_str(), ltoTempFile); |
eaf282aa | 784 | BitcodeTempFile* ltoStrip = new BitcodeTempFile(ltoTempFile, !_options.saveTempFiles()); |
0a8dc3df | 785 | ltoFile = xar_add_frombuffer(x, NULL, xar_name.c_str(), (char*)ltoStrip->getContent(), ltoStrip->getSize()); |
eaf282aa | 786 | delete ltoStrip; |
0a8dc3df A |
787 | } else { |
788 | ltoFile = xar_add_frombuffer(x, NULL, xar_name.c_str(), (char*)ltoTemp->getContent(), ltoTemp->getSize()); | |
789 | } | |
790 | if ( ltoFile == NULL ) | |
791 | throwf("could not add lto file %s to bitcode bundle", path.c_str()); | |
792 | if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 ) | |
793 | throwf("could not set bitcode property for %s in bitcode bundle", path.c_str()); | |
794 | delete ltoTemp; | |
eaf282aa | 795 | } |
eaf282aa A |
796 | } |
797 | ||
798 | // Common LinkOptions | |
799 | std::vector<std::string> linkCmd = _options.writeBitcodeLinkOptions(); | |
800 | ||
801 | // support -sectcreate option | |
802 | for ( auto extraSect = _options.extraSectionsBegin(); extraSect != _options.extraSectionsEnd(); ++ extraSect ) { | |
803 | std::string sectName = std::string(extraSect->segmentName) + std::string(",") + std::string(extraSect->sectionName); | |
804 | BitcodeTempFile* sectFile = new BitcodeTempFile(extraSect->path, false); | |
805 | xar_file_t sectXar = xar_add_frombuffer(x, NULL, sectName.c_str(), (char*)sectFile->getContent(), sectFile->getSize()); | |
806 | if ( sectXar == NULL ) | |
807 | throwf("could not encode sectcreate file %s into bitcode bundle", extraSect->path); | |
808 | if ( xar_prop_set(sectXar, "file-type", "Section") != 0 ) | |
809 | throwf("could not set bitcode property for %s", sectName.c_str()); | |
810 | delete sectFile; | |
811 | linkCmd.push_back("-sectcreate"); | |
812 | linkCmd.push_back(extraSect->segmentName); | |
813 | linkCmd.push_back(extraSect->sectionName); | |
814 | linkCmd.push_back(sectName); | |
815 | } | |
816 | ||
817 | // Write exports file | |
fb9a160c | 818 | // A vector of all the exported symbols. |
eaf282aa | 819 | if ( _options.hasExportMaskList() ) { |
fb9a160c A |
820 | std::vector<const char*> exportedSymbols; |
821 | for ( auto § : _state.sections ) { | |
822 | for ( auto &atom : sect->atoms ) { | |
823 | // The symbols should be added to the export list is the ones that are: | |
824 | // globalScope, in SymbolTable and should be exported suggested by export file. | |
825 | if ( atom->scope() == ld::Atom::scopeGlobal && | |
826 | atom->symbolTableInclusion() == ld::Atom::symbolTableIn && | |
827 | _options.shouldExport(atom->name()) ) | |
828 | exportedSymbols.push_back(atom->name()); | |
829 | } | |
830 | } | |
eaf282aa A |
831 | linkCmd.push_back("-exported_symbols_list"); |
832 | linkCmd.push_back("exports.exp"); | |
833 | const char* exportsPath = "exports.exp"; | |
eaf282aa | 834 | std::string exps; |
fb9a160c A |
835 | for (std::vector<const char*>::iterator it = exportedSymbols.begin(); |
836 | it != exportedSymbols.end(); ++ it) { | |
eaf282aa A |
837 | exps += *it; |
838 | exps += "\n"; | |
839 | } | |
840 | // always append an empty line so exps cannot be empty. rdar://problem/22404253 | |
841 | exps += "\n"; | |
fb9a160c A |
842 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0) |
843 | throwf("could not set compression type for exports list"); | |
eaf282aa A |
844 | xar_file_t exportsFile = xar_add_frombuffer(x, NULL, exportsPath, const_cast<char*>(exps.data()), exps.size()); |
845 | if (exportsFile == NULL) | |
846 | throwf("could not add exports list to bitcode bundle"); | |
847 | if (xar_prop_set(exportsFile, "file-type", "Exports") != 0) | |
848 | throwf("could not set exports property in bitcode bundle"); | |
fb9a160c A |
849 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) |
850 | throwf("could not reset compression type for exports list"); | |
851 | } else if ( _options.hasExportRestrictList() ) { | |
852 | // handle unexported list here | |
853 | std::vector<const char*> unexportedSymbols; | |
854 | for ( auto § : _state.sections ) { | |
855 | for ( auto &atom : sect->atoms ) { | |
856 | // The unexported symbols should not include anything that is in TranslationUnit scope (static) or | |
857 | // that cannot be in the SymbolTable | |
858 | if ( atom->scope() != ld::Atom::scopeTranslationUnit && | |
859 | atom->symbolTableInclusion() == ld::Atom::symbolTableIn && | |
860 | !_options.shouldExport(atom->name()) ) | |
861 | unexportedSymbols.push_back(atom->name()); | |
862 | } | |
863 | } | |
864 | linkCmd.push_back("-unexported_symbols_list"); | |
865 | linkCmd.push_back("unexports.exp"); | |
866 | const char* unexportsPath = "unexports.exp"; | |
867 | std::string unexps; | |
868 | for (std::vector<const char*>::iterator it = unexportedSymbols.begin(); | |
869 | it != unexportedSymbols.end(); ++ it) { | |
870 | // try obfuscate the name for symbols in unexported symbols list. They are likely to be obfsucated. | |
871 | const char* sym_name = NULL; | |
872 | if ( _options.hideSymbols() ) | |
873 | sym_name = obfuscator->lookupHiddenName(*it); | |
874 | if ( sym_name ) | |
875 | unexps += sym_name; | |
876 | else | |
877 | unexps += *it; | |
878 | unexps += "\n"; | |
879 | } | |
880 | unexps += "\n"; | |
881 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0) | |
882 | throwf("could not set compression type for exports list"); | |
883 | xar_file_t unexportsFile = xar_add_frombuffer(x, NULL, unexportsPath, const_cast<char*>(unexps.data()), unexps.size()); | |
884 | if (unexportsFile == NULL) | |
885 | throwf("could not add unexports list to bitcode bundle"); | |
886 | if (xar_prop_set(unexportsFile, "file-type", "Exports") != 0) | |
887 | throwf("could not set exports property in bitcode bundle"); | |
888 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
889 | throwf("could not reset compression type for exports list"); | |
890 | } | |
891 | ||
892 | // Handle order file. We need to obfuscate all the entries in the order file | |
893 | if ( _options.orderedSymbolsCount() > 0 ) { | |
894 | std::string orderFile; | |
895 | for ( auto entry = _options.orderedSymbolsBegin(); entry != _options.orderedSymbolsEnd(); ++ entry ) { | |
896 | std::stringstream line; | |
897 | if ( entry->objectFileName != NULL ) { | |
898 | unsigned index = 0; | |
899 | for ( auto &f : _state.filesWithBitcode ) { | |
900 | const char* atomFullPath = f->path(); | |
901 | const char* lastSlash = strrchr(atomFullPath, '/'); | |
902 | if ( (lastSlash != NULL && strcmp(&lastSlash[1], entry->objectFileName) == 0) || | |
903 | strcmp(atomFullPath, entry->objectFileName) == 0 ) | |
904 | break; | |
905 | ++ index; | |
906 | } | |
907 | if ( index >= _state.filesWithBitcode.size() ) | |
908 | continue; | |
909 | line << index << ".o:"; | |
910 | } | |
911 | const char* sym_name = NULL; | |
912 | if ( _options.hideSymbols() ) | |
913 | sym_name = obfuscator->lookupHiddenName(entry->symbolName); | |
914 | if ( sym_name ) | |
915 | line << sym_name; | |
916 | else | |
917 | line << entry->symbolName; | |
918 | line << "\n"; | |
919 | orderFile += line.str(); | |
920 | } | |
921 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0) | |
922 | throwf("could not set compression type for order file"); | |
923 | xar_file_t ordersFile = xar_add_frombuffer(x, NULL, "file.order", const_cast<char*>(orderFile.data()), orderFile.size()); | |
924 | if (ordersFile == NULL) | |
925 | throwf("could not add order file to bitcode bundle"); | |
926 | if (xar_prop_set(ordersFile, "file-type", "OrderFile") != 0) | |
927 | throwf("could not set order file property in bitcode bundle"); | |
928 | if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0) | |
929 | throwf("could not reset compression type for order file"); | |
930 | linkCmd.push_back("-order_file"); | |
931 | linkCmd.push_back("file.order"); | |
eaf282aa A |
932 | } |
933 | ||
934 | // Create subdoc to write link information | |
935 | xar_subdoc_t linkXML = xar_subdoc_new(x, "Ld"); | |
936 | if ( linkXML == NULL ) | |
937 | throwf("could not create XML in bitcode bundle"); | |
938 | ||
939 | // Write version number | |
940 | if ( xar_prop_create((xar_file_t)linkXML, "version", BITCODE_XAR_VERSION) != 0 ) | |
941 | throwf("could not add version number to bitcode bundle"); | |
942 | ||
943 | // Arch | |
944 | if ( xar_prop_create((xar_file_t)linkXML, "architecture", _options.architectureName()) != 0 ) | |
945 | throwf("could not add achitecture name to bitcode bundle"); | |
946 | ||
947 | // Opt-out symbols | |
948 | if ( _options.hideSymbols() ) { | |
949 | if ( xar_prop_create((xar_file_t)linkXML, "hide-symbols", "1") != 0 ) | |
950 | throwf("could not add property to bitcode bundle"); | |
951 | } | |
952 | ||
953 | // Write SDK version | |
954 | if ( _options.sdkPaths().size() > 1 ) | |
955 | throwf("only one -syslibroot is accepted for bitcode bundle"); | |
956 | if ( xar_prop_create((xar_file_t)linkXML, "platform", _options.getPlatformStr().c_str()) != 0 ) | |
957 | throwf("could not add platform name to bitcode bundle"); | |
958 | if ( xar_prop_create((xar_file_t)linkXML, "sdkversion", _options.getSDKVersionStr().c_str()) != 0 ) | |
959 | throwf("could not add SDK version to bitcode bundle"); | |
960 | ||
961 | // Write dylibs | |
dd9e569f A |
962 | char sdkRoot[PATH_MAX]; |
963 | if ( _options.sdkPaths().empty() || (realpath(_options.sdkPaths().front(), sdkRoot) == NULL) ) | |
964 | strcpy(sdkRoot, "/"); | |
eaf282aa | 965 | if ( !_state.dylibs.empty() ) { |
eaf282aa A |
966 | char dylibPath[PATH_MAX]; |
967 | for ( auto &dylib : _state.dylibs ) { | |
dd9e569f A |
968 | // For every dylib/framework, figure out if it is coming from a SDK. |
969 | // The dylib/framework from SDK must begin with '/' and user framework must begin with '@'. | |
970 | if (dylib->installPath()[0] == '/') { | |
971 | // Verify the path of the framework is within the SDK. | |
972 | char dylibRealPath[PATH_MAX]; | |
973 | if ( realpath(dylib->path(), dylibRealPath) != NULL && strncmp(sdkRoot, dylibRealPath, strlen(sdkRoot)) != 0 ) | |
974 | warning("%s has install name beginning with \"/\" but it is not from the specified SDK", dylib->path()); | |
eaf282aa | 975 | // The path start with a string template |
dd9e569f | 976 | strcpy(dylibPath, "{SDKPATH}"); |
eaf282aa | 977 | // append the path of dylib/frameowrk in the SDK |
dd9e569f | 978 | strcat(dylibPath, dylib->installPath()); |
eaf282aa A |
979 | } else { |
980 | // Not in any SDKs, then assume it is a user dylib/framework | |
981 | // strip off all the path in the front | |
982 | const char* dylib_name = strrchr(dylib->path(), '/'); | |
983 | dylib_name = (dylib_name == NULL) ? dylib->path() : dylib_name + 1; | |
984 | strcpy(dylibPath, dylib_name); | |
985 | } | |
986 | if ( dylib->forcedWeakLinked() ) { | |
987 | if ( xar_prop_create((xar_file_t)linkXML, "dylibs/weak", dylibPath) != 0) | |
988 | throwf("could not add dylib options to bitcode bundle"); | |
989 | } else { | |
990 | if ( xar_prop_create((xar_file_t)linkXML, "dylibs/lib", dylibPath) != 0) | |
991 | throwf("could not add dylib options to bitcode bundle"); | |
992 | } | |
993 | } | |
994 | } | |
995 | ||
996 | // Write link-line into archive | |
997 | for ( auto &it : linkCmd ) { | |
998 | if (xar_prop_create((xar_file_t)linkXML, "link-options/option", it.c_str()) != 0) | |
999 | throwf("could not add link options to bitcode bundle"); | |
1000 | } | |
1001 | // Finish writing | |
1002 | xar_close(x); | |
1003 | ||
1004 | // Read the file back | |
1005 | BitcodeTempFile* xarTemp = new BitcodeTempFile(outFile, !_options.saveTempFiles()); | |
1006 | ||
1007 | // Create an Atom and add to the list | |
1008 | BitcodeAtom* bundleAtom = new BitcodeAtom(*xarTemp); | |
1009 | _state.addAtom(*bundleAtom); | |
1010 | ||
1011 | // write the reverse mapping file if required | |
1012 | if ( _options.hideSymbols() && !_options.reverseMapTempPath().empty() ) | |
1013 | obfuscator->writeSymbolMap(_options.reverseMapTempPath().c_str()); | |
1014 | ||
1015 | // Clean up local variables | |
1016 | delete xarTemp; | |
1017 | delete obfuscator; | |
1018 | for ( auto &entry: handlerMap ) | |
1019 | delete entry.second; | |
1020 | // delete temp directory if not using -save-temps | |
1021 | // only do so after all the BitcodeTempFiles are deleted. | |
1022 | if ( !_options.saveTempFiles() ) { | |
1023 | if ( ::rmdir(tempdir) != 0 ) | |
1024 | warning("temp directory cannot be removed: %s", tempdir); | |
1025 | } | |
1026 | } | |
1027 | ||
1028 | ||
1029 | ||
1030 | // called by linker to write bitcode bundle into a mach-o section | |
1031 | void doPass(const Options& opts, ld::Internal& internal) { | |
1032 | BitcodeBundle BB(opts, internal); | |
1033 | BB.doPass(); | |
1034 | } | |
1035 | ||
1036 | ||
1037 | } // namespace bitcode_bundle | |
1038 | } // namespace passes | |
1039 | } // namespace ld |