]> git.saurik.com Git - apple/ld64.git/blob - src/ld/passes/bitcode_bundle.cpp
dce37836fb3187d496afe3dce909993d2ef731b5
[apple/ld64.git] / src / ld / passes / bitcode_bundle.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2010 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <fcntl.h>
28 #include <vector>
29 #include <dlfcn.h>
30 #include <math.h>
31 #include <unistd.h>
32 #include <time.h>
33 #include <unordered_map>
34 #include <sstream>
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);
97 void addAsmSymbolsToMustPreserve(lto_module_t module);
98 void bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath);
99 void writeSymbolMap(const char* outputPath);
100 const char* lookupHiddenName(const char* symbol);
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*);
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);
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;
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;
115 };
116
117 class FileHandler {
118 // generic handler for files in a bundle
119 public:
120 virtual void populateMustPreserveSymbols(BitcodeObfuscator* _obfuscator) { }
121 virtual void obfuscateAndWriteToPath(BitcodeObfuscator* _obfuscator, const char* path);
122 virtual const char* compressionMethod() { return XAR_OPT_VAL_NONE; } // no compression by default
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
178 virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override;
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
191 virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override;
192
193 };
194
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
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 #if LTO_API_VERSION < 11
272 throwf("compile-time libLTO (%d) didn't support -bitcode_hide_symbols", LTO_API_VERSION);
273 #else
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");
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");
283 if ( _lto_hide_symbols == NULL || _lto_write_reverse_map == NULL ||
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 )
286 throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version());
287 _obfuscator = ::lto_codegen_create_in_local_context();
288 #if LTO_API_VERSION >= 14
289 lto_codegen_set_should_internalize(_obfuscator, false);
290 #endif
291 #endif
292 }
293
294
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 )
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());
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
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
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
373 SymbolListHandler::~SymbolListHandler()
374 {
375 destroyFile();
376 }
377
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);
406 if ( _xar == NULL )
407 throwf("malformed bundle format");
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));
423 else if ( strcmp(filetype, "Exports") == 0 || strcmp(filetype, "OrderFile") == 0)
424 _handlers.push_back(new SymbolListHandler(_xar, f));
425 else
426 _handlers.push_back(new FileHandler(_xar, f));
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);
442 if ( !key ) {
443 xar_iter_free(p);
444 break;
445 }
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
479 void BitcodeHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator)
480 {
481 initFile();
482
483 // init LTOModule and add asm labels
484 #if LTO_API_VERSION < 11
485 lto_module_t module = lto_module_create_from_memory(_file_buffer, _file_size);
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());
492 obfuscator->addAsmSymbolsToMustPreserve(module);
493 lto_module_dispose(module);
494 }
495
496
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());
532 if ( xar_opt_set(x, XAR_OPT_COMPRESSION, handler->compressionMethod()) != 0 )
533 throwf("could not set compression type for exports list");
534 xar_file_t bcEntry = xar_add_frombuffer(x, NULL, name, (char*)bcOut->getContent(), bcOut->getSize());
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");
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
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)
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 {
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 }
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
637 // 6. weak external symbols (not auto-hide)
638 // 7. the special symbols supplied by linker
639 for ( auto &sect : _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 ) ||
645 ( _options.hasExportRestrictList() && _options.shouldExport(atom->name()) ) ||
646 ( atom->combine() == ld::Atom::combineByName && atom->scope() == ld::Atom::scopeGlobal && !atom->autoHide() ) )
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);
659 } else if ( ld::LLVMBitcode* bitcode = dynamic_cast<ld::LLVMBitcode*>(f->getBitcode()) ) {
660 BitcodeHandler* bitcodeHandler = new BitcodeHandler((char*)bitcode->getContent(), bitcode->getSize());
661 bitcodeHandler->populateMustPreserveSymbols(obfuscator);
662 }
663 }
664 // special symbols supplied by linker
665 obfuscator->addMustPreserveSymbols("___dso_handle");
666 obfuscator->addMustPreserveSymbols("__mh_execute_header");
667 obfuscator->addMustPreserveSymbols("__mh_dylib_header");
668 obfuscator->addMustPreserveSymbols("__mh_bundle_header");
669 obfuscator->addMustPreserveSymbols("__mh_dylinker_header");
670 obfuscator->addMustPreserveSymbols("__mh_object_header");
671 obfuscator->addMustPreserveSymbols("__mh_preload_header");
672
673 // add all the Proxy Atom linker ever created and all the undefs that are possibily dead-stripped.
674 for (auto sym : _state.allUndefProxies)
675 obfuscator->addMustPreserveSymbols(sym);
676 _state.allUndefProxies.clear();
677 }
678
679 // Open XAR output
680 xar_t x;
681 char outFile[PATH_MAX];
682 sprintf(outFile, "%s/bundle.xar", tempdir);
683
684 // By default, it uses gzip to compress and SHA1 as checksum
685 x = xar_open(outFile, WRITE);
686 if (x == NULL)
687 throwf("could not open output bundle to write %s", outFile);
688 // Disable compression
689 if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0)
690 throwf("could not disable compression for bitcode bundle");
691
692 // Sort all the object file according to oridnal order
693 std::sort(_state.filesWithBitcode.begin(), _state.filesWithBitcode.end(),
694 [](const ld::relocatable::File* a, const ld::relocatable::File* b) {
695 return a->ordinal() < b->ordinal();
696 });
697
698 // Copy each bitcode file into archive
699 int index = 1;
700 char formatString[10];
701 sprintf(formatString, "%%0%ud", (unsigned int)log10(_state.filesWithBitcode.size()) + 1);
702 for ( auto &obj : _state.filesWithBitcode ) {
703 assert(obj->getBitcode() != NULL && "File should contain bitcode");
704 char outFilePath[16];
705 sprintf(outFilePath, formatString, index++);
706 if ( ld::LLVMBitcode* llvmbc = dynamic_cast<ld::LLVMBitcode*>(obj->getBitcode()) ) {
707 // Handle clang and swift bitcode
708 xar_file_t bcFile = NULL;
709 if ( _options.hideSymbols() && !llvmbc->isMarker() ) { // dont strip if it is just a marker
710 char tempfile[PATH_MAX];
711 sprintf(tempfile, "%s/%s.bc", tempdir, outFilePath);
712 obfuscator->bitcodeHideSymbols(llvmbc, obj->path(), tempfile);
713 BitcodeTempFile* bcTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles());
714 bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bcTemp->getContent(), bcTemp->getSize());
715 delete bcTemp;
716 } else {
717 bcFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)const_cast<uint8_t*>(llvmbc->getContent()), llvmbc->getSize());
718 }
719 if ( bcFile == NULL )
720 throwf("could not add bitcode from %s to bitcode bundle", obj->path());
721 if ( xar_prop_set(bcFile, "file-type", "Bitcode") != 0 )
722 throwf("could not set bitcode property for %s in bitcode bundle", obj->path());
723 // Write commandline options
724 std::string tagName = std::string(llvmbc->getBitcodeName()) + std::string("/cmd");
725 for ( uint32_t i = 0; i < llvmbc->getCmdSize(); ++i ) {
726 if ( i == 0 || llvmbc->getCmdline()[i-1] == '\0' ) {
727 if ( xar_prop_create(bcFile, tagName.c_str(), (const char *)llvmbc->getCmdline() + i) )
728 throwf("could not set cmdline to XAR file");
729 }
730 }
731 }
732 else if ( ld::BundleBitcode* bundlebc = dynamic_cast<ld::BundleBitcode*>(obj->getBitcode()) ) {
733 xar_file_t bundleFile = NULL;
734 if ( _options.hideSymbols() && !bundlebc->isMarker() ) { // dont strip if it is just a marker
735 char tempfile[PATH_MAX];
736 sprintf(tempfile, "%s/%s.xar", tempdir, outFilePath);
737 auto search = handlerMap.find(std::string(obj->path()));
738 assert( search != handlerMap.end() && "Cannot find handler");
739 search->second->obfuscateAndWriteToPath(obfuscator, tempfile);
740 BitcodeTempFile* bundleTemp = new BitcodeTempFile(tempfile, !_options.saveTempFiles());
741 bundleFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)bundleTemp->getContent(), bundleTemp->getSize());
742 delete bundleTemp;
743 } else {
744 bundleFile = xar_add_frombuffer(x, NULL, outFilePath,
745 (char*)const_cast<uint8_t*>(bundlebc->getContent()),
746 bundlebc->getSize());
747 }
748 if ( bundleFile == NULL )
749 throwf("could not add bitcode from the bundle %s to bitcode bundle", obj->path());
750 if ( xar_prop_set(bundleFile, "file-type", "Bundle") != 0 )
751 throwf("could not set bundle property for %s in bitcode bundle", obj->path());
752 }
753 else if ( ld::AsmBitcode* asmbc = dynamic_cast<ld::AsmBitcode*>(obj->getBitcode()) ) {
754 xar_file_t objFile = xar_add_frombuffer(x, NULL, outFilePath, (char*)asmbc->getContent(), asmbc->getSize());
755 if ( objFile == NULL )
756 throwf("could not add obj file %s to bitcode bundle", obj->path());
757 if ( xar_prop_set(objFile, "file-type", "Object") != 0 )
758 throwf("could not set object property for %s in bitcode bundle", obj->path());
759 }
760 else {
761 assert(false && "Unknown bitcode");
762 }
763 }
764
765 // Write merged LTO bitcode
766 if ( !_state.ltoBitcodePath.empty() ) {
767 xar_file_t ltoFile = NULL;
768 BitcodeTempFile* ltoTemp = new BitcodeTempFile(_state.ltoBitcodePath.c_str(), !_options.saveTempFiles());
769 if ( _options.hideSymbols() ) {
770 ld::Bitcode ltoBitcode(ltoTemp->getContent(), ltoTemp->getSize());
771 char ltoTempFile[PATH_MAX];
772 sprintf(ltoTempFile, "%s/lto.bc", tempdir);
773 obfuscator->bitcodeHideSymbols(&ltoBitcode, _state.ltoBitcodePath.c_str(), ltoTempFile);
774 BitcodeTempFile* ltoStrip = new BitcodeTempFile(ltoTempFile, !_options.saveTempFiles());
775 ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoStrip->getContent(), ltoStrip->getSize());
776 delete ltoStrip;
777 } else {
778 ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoTemp->getContent(), ltoTemp->getSize());
779 }
780 if ( ltoFile == NULL )
781 throwf("could not add lto file %s to bitcode bundle", _state.ltoBitcodePath.c_str());
782 if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 )
783 throwf("could not set bitcode property for %s in bitcode bundle", _state.ltoBitcodePath.c_str());
784 delete ltoTemp;
785 }
786
787 // Common LinkOptions
788 std::vector<std::string> linkCmd = _options.writeBitcodeLinkOptions();
789
790 // support -sectcreate option
791 for ( auto extraSect = _options.extraSectionsBegin(); extraSect != _options.extraSectionsEnd(); ++ extraSect ) {
792 std::string sectName = std::string(extraSect->segmentName) + std::string(",") + std::string(extraSect->sectionName);
793 BitcodeTempFile* sectFile = new BitcodeTempFile(extraSect->path, false);
794 xar_file_t sectXar = xar_add_frombuffer(x, NULL, sectName.c_str(), (char*)sectFile->getContent(), sectFile->getSize());
795 if ( sectXar == NULL )
796 throwf("could not encode sectcreate file %s into bitcode bundle", extraSect->path);
797 if ( xar_prop_set(sectXar, "file-type", "Section") != 0 )
798 throwf("could not set bitcode property for %s", sectName.c_str());
799 delete sectFile;
800 linkCmd.push_back("-sectcreate");
801 linkCmd.push_back(extraSect->segmentName);
802 linkCmd.push_back(extraSect->sectionName);
803 linkCmd.push_back(sectName);
804 }
805
806 // Write exports file
807 // A vector of all the exported symbols.
808 if ( _options.hasExportMaskList() ) {
809 std::vector<const char*> exportedSymbols;
810 for ( auto &sect : _state.sections ) {
811 for ( auto &atom : sect->atoms ) {
812 // The symbols should be added to the export list is the ones that are:
813 // globalScope, in SymbolTable and should be exported suggested by export file.
814 if ( atom->scope() == ld::Atom::scopeGlobal &&
815 atom->symbolTableInclusion() == ld::Atom::symbolTableIn &&
816 _options.shouldExport(atom->name()) )
817 exportedSymbols.push_back(atom->name());
818 }
819 }
820 linkCmd.push_back("-exported_symbols_list");
821 linkCmd.push_back("exports.exp");
822 const char* exportsPath = "exports.exp";
823 std::string exps;
824 for (std::vector<const char*>::iterator it = exportedSymbols.begin();
825 it != exportedSymbols.end(); ++ it) {
826 exps += *it;
827 exps += "\n";
828 }
829 // always append an empty line so exps cannot be empty. rdar://problem/22404253
830 exps += "\n";
831 if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0)
832 throwf("could not set compression type for exports list");
833 xar_file_t exportsFile = xar_add_frombuffer(x, NULL, exportsPath, const_cast<char*>(exps.data()), exps.size());
834 if (exportsFile == NULL)
835 throwf("could not add exports list to bitcode bundle");
836 if (xar_prop_set(exportsFile, "file-type", "Exports") != 0)
837 throwf("could not set exports property in bitcode bundle");
838 if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0)
839 throwf("could not reset compression type for exports list");
840 } else if ( _options.hasExportRestrictList() ) {
841 // handle unexported list here
842 std::vector<const char*> unexportedSymbols;
843 for ( auto &sect : _state.sections ) {
844 for ( auto &atom : sect->atoms ) {
845 // The unexported symbols should not include anything that is in TranslationUnit scope (static) or
846 // that cannot be in the SymbolTable
847 if ( atom->scope() != ld::Atom::scopeTranslationUnit &&
848 atom->symbolTableInclusion() == ld::Atom::symbolTableIn &&
849 !_options.shouldExport(atom->name()) )
850 unexportedSymbols.push_back(atom->name());
851 }
852 }
853 linkCmd.push_back("-unexported_symbols_list");
854 linkCmd.push_back("unexports.exp");
855 const char* unexportsPath = "unexports.exp";
856 std::string unexps;
857 for (std::vector<const char*>::iterator it = unexportedSymbols.begin();
858 it != unexportedSymbols.end(); ++ it) {
859 // try obfuscate the name for symbols in unexported symbols list. They are likely to be obfsucated.
860 const char* sym_name = NULL;
861 if ( _options.hideSymbols() )
862 sym_name = obfuscator->lookupHiddenName(*it);
863 if ( sym_name )
864 unexps += sym_name;
865 else
866 unexps += *it;
867 unexps += "\n";
868 }
869 unexps += "\n";
870 if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0)
871 throwf("could not set compression type for exports list");
872 xar_file_t unexportsFile = xar_add_frombuffer(x, NULL, unexportsPath, const_cast<char*>(unexps.data()), unexps.size());
873 if (unexportsFile == NULL)
874 throwf("could not add unexports list to bitcode bundle");
875 if (xar_prop_set(unexportsFile, "file-type", "Exports") != 0)
876 throwf("could not set exports property in bitcode bundle");
877 if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0)
878 throwf("could not reset compression type for exports list");
879 }
880
881 // Handle order file. We need to obfuscate all the entries in the order file
882 if ( _options.orderedSymbolsCount() > 0 ) {
883 std::string orderFile;
884 for ( auto entry = _options.orderedSymbolsBegin(); entry != _options.orderedSymbolsEnd(); ++ entry ) {
885 std::stringstream line;
886 if ( entry->objectFileName != NULL ) {
887 unsigned index = 0;
888 for ( auto &f : _state.filesWithBitcode ) {
889 const char* atomFullPath = f->path();
890 const char* lastSlash = strrchr(atomFullPath, '/');
891 if ( (lastSlash != NULL && strcmp(&lastSlash[1], entry->objectFileName) == 0) ||
892 strcmp(atomFullPath, entry->objectFileName) == 0 )
893 break;
894 ++ index;
895 }
896 if ( index >= _state.filesWithBitcode.size() )
897 continue;
898 line << index << ".o:";
899 }
900 const char* sym_name = NULL;
901 if ( _options.hideSymbols() )
902 sym_name = obfuscator->lookupHiddenName(entry->symbolName);
903 if ( sym_name )
904 line << sym_name;
905 else
906 line << entry->symbolName;
907 line << "\n";
908 orderFile += line.str();
909 }
910 if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0)
911 throwf("could not set compression type for order file");
912 xar_file_t ordersFile = xar_add_frombuffer(x, NULL, "file.order", const_cast<char*>(orderFile.data()), orderFile.size());
913 if (ordersFile == NULL)
914 throwf("could not add order file to bitcode bundle");
915 if (xar_prop_set(ordersFile, "file-type", "OrderFile") != 0)
916 throwf("could not set order file property in bitcode bundle");
917 if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0)
918 throwf("could not reset compression type for order file");
919 linkCmd.push_back("-order_file");
920 linkCmd.push_back("file.order");
921 }
922
923 // Create subdoc to write link information
924 xar_subdoc_t linkXML = xar_subdoc_new(x, "Ld");
925 if ( linkXML == NULL )
926 throwf("could not create XML in bitcode bundle");
927
928 // Write version number
929 if ( xar_prop_create((xar_file_t)linkXML, "version", BITCODE_XAR_VERSION) != 0 )
930 throwf("could not add version number to bitcode bundle");
931
932 // Arch
933 if ( xar_prop_create((xar_file_t)linkXML, "architecture", _options.architectureName()) != 0 )
934 throwf("could not add achitecture name to bitcode bundle");
935
936 // Opt-out symbols
937 if ( _options.hideSymbols() ) {
938 if ( xar_prop_create((xar_file_t)linkXML, "hide-symbols", "1") != 0 )
939 throwf("could not add property to bitcode bundle");
940 }
941
942 // Write SDK version
943 if ( _options.sdkPaths().size() > 1 )
944 throwf("only one -syslibroot is accepted for bitcode bundle");
945 if ( xar_prop_create((xar_file_t)linkXML, "platform", _options.getPlatformStr().c_str()) != 0 )
946 throwf("could not add platform name to bitcode bundle");
947 if ( xar_prop_create((xar_file_t)linkXML, "sdkversion", _options.getSDKVersionStr().c_str()) != 0 )
948 throwf("could not add SDK version to bitcode bundle");
949
950 // Write dylibs
951 char sdkRoot[PATH_MAX];
952 if ( _options.sdkPaths().empty() || (realpath(_options.sdkPaths().front(), sdkRoot) == NULL) )
953 strcpy(sdkRoot, "/");
954 if ( !_state.dylibs.empty() ) {
955 char dylibPath[PATH_MAX];
956 for ( auto &dylib : _state.dylibs ) {
957 // For every dylib/framework, figure out if it is coming from a SDK.
958 // The dylib/framework from SDK must begin with '/' and user framework must begin with '@'.
959 if (dylib->installPath()[0] == '/') {
960 // Verify the path of the framework is within the SDK.
961 char dylibRealPath[PATH_MAX];
962 if ( realpath(dylib->path(), dylibRealPath) != NULL && strncmp(sdkRoot, dylibRealPath, strlen(sdkRoot)) != 0 )
963 warning("%s has install name beginning with \"/\" but it is not from the specified SDK", dylib->path());
964 // The path start with a string template
965 strcpy(dylibPath, "{SDKPATH}");
966 // append the path of dylib/frameowrk in the SDK
967 strcat(dylibPath, dylib->installPath());
968 } else {
969 // Not in any SDKs, then assume it is a user dylib/framework
970 // strip off all the path in the front
971 const char* dylib_name = strrchr(dylib->path(), '/');
972 dylib_name = (dylib_name == NULL) ? dylib->path() : dylib_name + 1;
973 strcpy(dylibPath, dylib_name);
974 }
975 if ( dylib->forcedWeakLinked() ) {
976 if ( xar_prop_create((xar_file_t)linkXML, "dylibs/weak", dylibPath) != 0)
977 throwf("could not add dylib options to bitcode bundle");
978 } else {
979 if ( xar_prop_create((xar_file_t)linkXML, "dylibs/lib", dylibPath) != 0)
980 throwf("could not add dylib options to bitcode bundle");
981 }
982 }
983 }
984
985 // Write link-line into archive
986 for ( auto &it : linkCmd ) {
987 if (xar_prop_create((xar_file_t)linkXML, "link-options/option", it.c_str()) != 0)
988 throwf("could not add link options to bitcode bundle");
989 }
990 // Finish writing
991 xar_close(x);
992
993 // Read the file back
994 BitcodeTempFile* xarTemp = new BitcodeTempFile(outFile, !_options.saveTempFiles());
995
996 // Create an Atom and add to the list
997 BitcodeAtom* bundleAtom = new BitcodeAtom(*xarTemp);
998 _state.addAtom(*bundleAtom);
999
1000 // write the reverse mapping file if required
1001 if ( _options.hideSymbols() && !_options.reverseMapTempPath().empty() )
1002 obfuscator->writeSymbolMap(_options.reverseMapTempPath().c_str());
1003
1004 // Clean up local variables
1005 delete xarTemp;
1006 delete obfuscator;
1007 for ( auto &entry: handlerMap )
1008 delete entry.second;
1009 // delete temp directory if not using -save-temps
1010 // only do so after all the BitcodeTempFiles are deleted.
1011 if ( !_options.saveTempFiles() ) {
1012 if ( ::rmdir(tempdir) != 0 )
1013 warning("temp directory cannot be removed: %s", tempdir);
1014 }
1015 }
1016
1017
1018
1019 // called by linker to write bitcode bundle into a mach-o section
1020 void doPass(const Options& opts, ld::Internal& internal) {
1021 BitcodeBundle BB(opts, internal);
1022 BB.doPass();
1023 }
1024
1025
1026 } // namespace bitcode_bundle
1027 } // namespace passes
1028 } // namespace ld