1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2010 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
33 #include <unordered_map>
36 #include "llvm-c/lto.h"
42 #include "bitcode_bundle.h"
46 #include "Bitcode.hpp"
47 #include "macho_relocatable_file.h"
52 namespace bitcode_bundle
{
54 class BitcodeTempFile
;
56 class BitcodeAtom
: public ld::Atom
{
57 static ld::Section bitcodeBundleSection
;
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
) { }
75 ld::Section
BitcodeAtom::bitcodeBundleSection("__LLVM", "__bundle", ld::Section::typeSectCreate
);
77 class BitcodeTempFile
{
79 BitcodeTempFile(const char* path
, bool deleteAfterRead
);
81 uint8_t* getContent() const { return _content
; }
82 uint64_t getSize() const { return _size
; }
84 friend class BitcodeAtom
;
88 bool _deleteAfterRead
;
91 class BitcodeObfuscator
{
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
);
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);
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
;
118 // generic handler for files in a bundle
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
; }
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() { }
134 if (xar_extract_tobuffersz(_parent
, _xar_file
, &_file_buffer
, &_file_size
) != 0)
135 throwf("could not extract files from bitcode bundle");
144 xar_file_t _xar_file
;
149 class BundleHandler
: public FileHandler
{
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
) { }
158 virtual void populateMustPreserveSymbols(BitcodeObfuscator
* obfuscator
) override
;
159 virtual void obfuscateAndWriteToPath(BitcodeObfuscator
* obfuscator
, const char* path
) override
;
163 void copyXARProp(xar_file_t src
, xar_file_t dst
);
167 const Options
& _options
;
168 std::vector
<FileHandler
*> _handlers
;
171 class BitcodeHandler
: public FileHandler
{
173 BitcodeHandler(char* content
, size_t size
) : FileHandler(content
, size
) { }
174 BitcodeHandler(xar_t parent
, xar_file_t xar_file
) : FileHandler(parent
, xar_file
) { }
178 virtual void populateMustPreserveSymbols(BitcodeObfuscator
* obfuscator
) override
;
179 virtual void obfuscateAndWriteToPath(BitcodeObfuscator
* obfuscator
, const char* path
) override
;
182 class ObjectHandler
: public FileHandler
{
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
) { }
191 virtual void populateMustPreserveSymbols(BitcodeObfuscator
* obfuscator
) override
;
195 class SymbolListHandler
: public FileHandler
{
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
) { }
202 ~SymbolListHandler();
204 virtual void obfuscateAndWriteToPath(BitcodeObfuscator
* obfuscator
, const char* path
) override
;
205 virtual const char* compressionMethod() override
{ return XAR_OPT_VAL_GZIP
; }
209 class BitcodeBundle
{
211 BitcodeBundle(const Options
& opts
, ld::Internal
& internal
) :
212 _options(opts
), _state(internal
) { }
217 const Options
& _options
;
218 ld::Internal
& _state
;
221 #if LTO_API_VERSION >= 7
222 static void ltoDiagnosticHandler(lto_codegen_diagnostic_severity_t severity
, const char* message
, void*)
224 switch ( severity
) {
225 #if LTO_API_VERSION >= 10
229 break; // ignore remarks and notes
231 warning("%s", message
);
234 throwf("%s", message
);
240 BitcodeAtom::BitcodeAtom()
241 : ld::Atom(bitcodeBundleSection
,
242 ld::Atom::definitionRegular
, ld::Atom::combineNever
,
243 ld::Atom::scopeTranslationUnit
, ld::Atom::typeUnclassified
,
244 ld::Atom::symbolTableNotIn
, true, false, false, ld::Atom::Alignment(0)),
247 // initialize a marker of 1 byte
248 _content
= (uint8_t*)calloc(1,1);
251 BitcodeAtom::BitcodeAtom(BitcodeTempFile
& tempfile
)
252 : ld::Atom(bitcodeBundleSection
,
253 ld::Atom::definitionRegular
, ld::Atom::combineNever
,
254 ld::Atom::scopeTranslationUnit
, ld::Atom::typeUnclassified
,
255 ld::Atom::symbolTableNotIn
, true, false, false, ld::Atom::Alignment(0)),
256 _content(tempfile
._content
), _size(tempfile
._size
)
258 // Creating the Atom will transfer the ownership of the buffer from Tempfile to Atom
259 tempfile
._content
= NULL
;
262 BitcodeTempFile::BitcodeTempFile(const char* path
, bool deleteAfterRead
= true)
263 : _path(path
), _deleteAfterRead(deleteAfterRead
)
265 int fd
= ::open(path
, O_RDONLY
, 0);
267 throwf("could not open bitcode temp file: %s", path
);
268 struct stat stat_buf
;
269 ::fstat(fd
, &stat_buf
);
270 _content
= (uint8_t*)malloc(stat_buf
.st_size
);
271 if ( _content
== NULL
)
272 throwf("could not process bitcode temp file: %s", path
);
273 if ( read(fd
, _content
, stat_buf
.st_size
) != stat_buf
.st_size
)
274 throwf("could not read bitcode temp file: %s", path
);
276 _size
= stat_buf
.st_size
;
279 BitcodeTempFile::~BitcodeTempFile()
282 if ( _deleteAfterRead
) {
283 if ( ::unlink(_path
) != 0 )
284 throwf("could not remove temp file: %s", _path
);
288 BitcodeObfuscator::BitcodeObfuscator()
290 #if LTO_API_VERSION < 11
291 throwf("compile-time libLTO (%d) didn't support -bitcode_hide_symbols", LTO_API_VERSION
);
293 // check if apple internal libLTO is used
294 if ( ::lto_get_version() == NULL
)
295 throwf("libLTO is not loaded");
296 _lto_hide_symbols
= (lto_codegen_func_t
) dlsym(RTLD_DEFAULT
, "lto_codegen_hide_symbols");
297 _lto_write_reverse_map
= (lto_codegen_output_t
) dlsym(RTLD_DEFAULT
, "lto_codegen_write_symbol_reverse_map");
298 _lto_reset_context
= (lto_codegen_func_t
) dlsym(RTLD_DEFAULT
, "lto_codegen_reset_context");
299 _lto_lookup_hidden_name
= (lto_codegen_lookup_t
) dlsym(RTLD_DEFAULT
, "lto_codegen_lookup_hidden_name");
300 _lto_get_asm_symbol_num
= (lto_module_num_symbols
) dlsym(RTLD_DEFAULT
, "lto_module_get_num_asm_symbols");
301 _lto_get_asm_symbol_name
= (lto_module_symbol_name
) dlsym(RTLD_DEFAULT
, "lto_module_get_asm_symbol_name");
302 if ( _lto_hide_symbols
== NULL
|| _lto_write_reverse_map
== NULL
||
303 _lto_reset_context
== NULL
|| _lto_lookup_hidden_name
== NULL
||
304 _lto_get_asm_symbol_num
== NULL
|| _lto_get_asm_symbol_name
== NULL
|| ::lto_api_version() < 14 )
305 throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version());
306 _obfuscator
= ::lto_codegen_create_in_local_context();
308 #if LTO_API_VERSION >= 7
309 lto_codegen_set_diagnostic_handler(_obfuscator
, ltoDiagnosticHandler
, NULL
);
312 #if LTO_API_VERSION >= 14
313 lto_codegen_set_should_internalize(_obfuscator
, false);
319 BitcodeObfuscator::~BitcodeObfuscator()
321 ::lto_codegen_dispose(_obfuscator
);
324 void BitcodeObfuscator::addMustPreserveSymbols(const char* name
)
326 ::lto_codegen_add_must_preserve_symbol(_obfuscator
, name
);
329 void BitcodeObfuscator::bitcodeHideSymbols(ld::Bitcode
* bc
, const char* filePath
, const char* outputPath
)
331 #if LTO_API_VERSION >= 13 && LTO_APPLE_INTERNAL
332 lto_module_t
module = ::lto_module_create_in_codegen_context(bc
->getContent(), bc
->getSize(), filePath
, _obfuscator
);
333 if ( module == NULL
)
334 throwf("could not reparse object file %s in bitcode bundle: '%s', using libLTO version '%s'",
335 filePath
, ::lto_get_error_message(), ::lto_get_version());
336 ::lto_codegen_set_module(_obfuscator
, module);
337 (*_lto_hide_symbols
)(_obfuscator
);
338 #if LTO_API_VERSION >= 15
339 ::lto_codegen_set_should_embed_uselists(_obfuscator
, true);
341 ::lto_codegen_write_merged_modules(_obfuscator
, outputPath
);
342 (*_lto_reset_context
)(_obfuscator
);
347 void BitcodeObfuscator::writeSymbolMap(const char *outputPath
)
349 (*_lto_write_reverse_map
)(_obfuscator
, outputPath
);
352 const char* BitcodeObfuscator::lookupHiddenName(const char *symbol
)
354 return (*_lto_lookup_hidden_name
)(_obfuscator
, symbol
);
357 void BitcodeObfuscator::addAsmSymbolsToMustPreserve(lto_module_t
module)
359 for (unsigned int i
= 0; i
< _lto_get_asm_symbol_num(module); ++ i
) {
360 addMustPreserveSymbols(_lto_get_asm_symbol_name(module, i
));
364 BundleHandler::~BundleHandler()
369 for (auto handler
: _handlers
)
372 // delete temp file if not -save-temps
375 std::string oldXARPath
= std::string(_temp_dir
) + std::string("/bundle.xar");
376 if ( !_options
.saveTempFiles() && ::unlink(oldXARPath
.c_str()) != 0)
377 warning("could not delete temp file: %s", oldXARPath
.c_str());
381 if ( !_options
.saveTempFiles() && ::rmdir(_temp_dir
) != 0 )
382 warning("could not delete temp directory: %s", _temp_dir
);
387 BitcodeHandler::~BitcodeHandler()
392 ObjectHandler::~ObjectHandler()
397 SymbolListHandler::~SymbolListHandler()
402 void BundleHandler::init()
407 // make temp directory
408 const char* finalOutput
= _options
.outputFilePath();
409 _temp_dir
= (char*)malloc(PATH_MAX
* sizeof(char));
410 // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX
411 // If so, fall back to /tmp
412 if ( strlen(finalOutput
) + 30 >= PATH_MAX
)
413 sprintf(_temp_dir
, "/tmp/ld.bundle.XXXXXX");
415 sprintf(_temp_dir
, "%s.bundle.XXXXXX", finalOutput
);
416 ::mkdtemp(_temp_dir
);
418 // write the bundle to the temp_directory
420 std::string oldXARPath
= std::string(_temp_dir
) + std::string("/bundle.xar");
421 int f
= ::open(oldXARPath
.c_str(), O_WRONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
);
423 throwf("could not write file to temp directory: %s", _temp_dir
);
424 if ( ::write(f
, _file_buffer
, _file_size
) != (int)_file_size
)
425 throwf("failed to write content to temp file: %s", oldXARPath
.c_str());
429 _xar
= xar_open(oldXARPath
.c_str(), READ
);
431 throwf("malformed bundle format");
433 // Init the vector of handler
434 xar_iter_t iter
= xar_iter_new();
436 throwf("could not aquire iterator for the bitcode bundle");
437 for ( xar_file_t f
= xar_file_first(_xar
, iter
); f
; f
= xar_file_next(iter
) ) {
438 const char* filetype
= NULL
;
439 if ( xar_prop_get(f
, "file-type", &filetype
) != 0 )
440 throwf("could not get the file type for the bitcode bundle");
441 if ( strcmp(filetype
, "Bundle") == 0 )
442 _handlers
.push_back(new BundleHandler(_xar
, f
, _options
));
443 else if ( strcmp(filetype
, "Object") == 0 )
444 _handlers
.push_back(new ObjectHandler(_xar
, f
));
445 else if ( strcmp(filetype
, "Bitcode") == 0 || strcmp(filetype
, "LTO") == 0 )
446 _handlers
.push_back(new BitcodeHandler(_xar
, f
));
447 else if ( strcmp(filetype
, "Exports") == 0 || strcmp(filetype
, "OrderFile") == 0)
448 _handlers
.push_back(new SymbolListHandler(_xar
, f
));
450 _handlers
.push_back(new FileHandler(_xar
, f
));
455 void BundleHandler::copyXARProp(xar_file_t src
, xar_file_t dst
)
457 // copy the property in the XAR.
458 // Since XAR API can only get the first value from the key,
459 // Deleting the value after read.
462 xar_iter_t p
= xar_iter_new();
463 const char* key
= xar_prop_first(src
, p
);
464 for (int x
= 0; x
< i
; x
++)
465 key
= xar_prop_next(p
);
470 const char* val
= NULL
;
471 xar_prop_get(src
, key
, &val
);
472 if ( // Info from bitcode files
473 strcmp(key
, "file-type") == 0 ||
474 strcmp(key
, "clang/cmd") == 0 ||
475 strcmp(key
, "swift/cmd") == 0 ||
476 // Info from linker subdoc
477 strcmp(key
, "version") == 0 ||
478 strcmp(key
, "architecture") == 0 ||
479 strcmp(key
, "hide-symbols") == 0 ||
480 strcmp(key
, "platform") == 0 ||
481 strcmp(key
, "sdkversion") == 0 ||
482 strcmp(key
, "swift-version") == 0 ||
483 strcmp(key
, "dylibs/lib") == 0 ||
484 strcmp(key
, "link-options/option") == 0 ) {
485 xar_prop_create(dst
, key
, val
);
486 xar_prop_unset(src
, key
);
493 void BundleHandler::populateMustPreserveSymbols(BitcodeObfuscator
* obfuscator
)
499 // iterate through the XAR file and add symbols
500 for ( auto handler
: _handlers
)
501 handler
->populateMustPreserveSymbols(obfuscator
);
504 void BitcodeHandler::populateMustPreserveSymbols(BitcodeObfuscator
* obfuscator
)
508 // init LTOModule and add asm labels
509 #if LTO_API_VERSION < 11
510 lto_module_t
module = lto_module_create_from_memory(_file_buffer
, _file_size
);
512 lto_module_t
module = lto_module_create_in_local_context(_file_buffer
, _file_size
, "bitcode bundle temp file");
514 if ( module == NULL
)
515 throwf("could not reparse object file in bitcode bundle: '%s', using libLTO version '%s'",
516 ::lto_get_error_message(), ::lto_get_version());
517 obfuscator
->addAsmSymbolsToMustPreserve(module);
518 lto_module_dispose(module);
522 void ObjectHandler::populateMustPreserveSymbols(BitcodeObfuscator
* obfuscator
)
525 // Parse the object file and add the symbols
526 std::vector
<const char*> symbols
;
527 if ( mach_o::relocatable::getNonLocalSymbols((uint8_t*)_file_buffer
, symbols
) ) {
528 for ( auto sym
: symbols
)
529 obfuscator
->addMustPreserveSymbols(sym
);
533 void BundleHandler::obfuscateAndWriteToPath(BitcodeObfuscator
*obfuscator
, const char *path
)
539 // creating the new xar
540 xar_t x
= xar_open(path
, WRITE
);
542 throwf("could not open output bundle to write %s", path
);
543 // Disable compression
544 if (xar_opt_set(x
, XAR_OPT_COMPRESSION
, XAR_OPT_VAL_NONE
) != 0)
545 throwf("could not disable compression for bitcode bundle");
547 // iterate through the XAR file and obfuscate
548 for ( auto handler
: _handlers
) {
549 const char* name
= NULL
;
550 xar_file_t f
= handler
->getXARFile();
551 if ( xar_prop_get(f
, "name", &name
) != 0 )
552 throwf("could not get the name of the file from bitcode bundle");
553 char outputPath
[PATH_MAX
];
554 sprintf(outputPath
, "%s/%s", _temp_dir
, name
);
555 handler
->obfuscateAndWriteToPath(obfuscator
, outputPath
);
556 BitcodeTempFile
* bcOut
= new BitcodeTempFile(outputPath
, !_options
.saveTempFiles());
557 if ( xar_opt_set(x
, XAR_OPT_COMPRESSION
, handler
->compressionMethod()) != 0 )
558 throwf("could not set compression type for exports list");
559 xar_file_t bcEntry
= xar_add_frombuffer(x
, NULL
, name
, (char*)bcOut
->getContent(), bcOut
->getSize());
560 if ( bcEntry
== NULL
)
561 throwf("could not add file to the bundle");
562 if ( xar_opt_set(x
, XAR_OPT_COMPRESSION
, XAR_OPT_VAL_NONE
) != 0 )
563 throwf("could not reset compression type for exports list");
564 copyXARProp(f
, bcEntry
);
568 // copy the subdoc as well
569 for ( xar_subdoc_t sub
= xar_subdoc_first(_xar
); sub
; sub
= xar_subdoc_next(sub
) ) {
570 const char *name
= xar_subdoc_name(sub
);
571 xar_subdoc_t newDoc
= xar_subdoc_new(x
, name
);
572 copyXARProp((xar_file_t
) sub
, (xar_file_t
) newDoc
);
577 void BitcodeHandler::obfuscateAndWriteToPath(BitcodeObfuscator
*obfuscator
, const char *path
)
580 ld::Bitcode
bc((uint8_t*)_file_buffer
, _file_size
);
581 obfuscator
->bitcodeHideSymbols(&bc
, path
, path
);
584 void SymbolListHandler::obfuscateAndWriteToPath(BitcodeObfuscator
* obfuscator
, const char* path
)
587 // Obfuscate exported symbol list.
588 std::string exports_list
;
589 for (size_t i
= 0, start
= 0; i
< _file_size
; ++i
) {
590 if ( _file_buffer
[i
] == '\n' ) {
591 _file_buffer
[i
] = '\0';
592 const char* hiddenName
= obfuscator
->lookupHiddenName(_file_buffer
+ start
);
593 if ( hiddenName
== NULL
)
594 exports_list
+= _file_buffer
+ start
;
596 exports_list
+= hiddenName
;
597 exports_list
+= "\n";
599 } else if ( _file_buffer
[i
] == '*' ) {
600 throwf("illegal export list found. Please rebuild your static library using -exported_symbol[s_list] with the newest Xcode");
603 exports_list
+= "\n";
604 int f
= ::open(path
, O_WRONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
);
605 if ( f
== -1 || ::write(f
, exports_list
.data(), exports_list
.size()) != (int)exports_list
.size() )
606 throwf("failed to write content to temp file: %s", path
);
610 void FileHandler::obfuscateAndWriteToPath(BitcodeObfuscator
*obfuscator
, const char *path
)
613 int f
= ::open(path
, O_WRONLY
| O_CREAT
, S_IRUSR
| S_IWUSR
);
614 if ( f
== -1 || ::write(f
, _file_buffer
, _file_size
) != (int)_file_size
)
615 throwf("failed to write content to temp file: %s", path
);
619 void BitcodeBundle::doPass()
621 if ( _options
.bitcodeKind() == Options::kBitcodeStrip
||
622 _options
.bitcodeKind() == Options::kBitcodeAsData
)
623 // if emit no bitcode or emit bitcode segment as data, no need to generate bundle.
625 else if ( _state
.embedMarkerOnly
|| _options
.bitcodeKind() == Options::kBitcodeMarker
) {
626 // if the bitcode is just a marker,
627 // the executable will be created without bitcode section.
628 // Otherwise, create a marker.
629 if( _options
.outputKind() != Options::kDynamicExecutable
&&
630 _options
.outputKind() != Options::kStaticExecutable
) {
631 BitcodeAtom
* marker
= new BitcodeAtom();
632 _state
.addAtom(*marker
);
637 if ( _state
.filesWithBitcode
.empty() && _state
.ltoBitcodePath
.empty() )
639 // Create tempdir, the temp directory should be OUTPUT/main.exe.bundle-XXXXXX
640 char tempdir
[PATH_MAX
];
641 const char* finalOutput
= _options
.outputFilePath();
642 // Check outputFilePath.bundle-XXXXXX/YYYYYYYYYY.bc will not over flow PATH_MAX
643 // If so, fall back to /tmp
644 if ( strlen(finalOutput
) + 30 >= PATH_MAX
)
645 sprintf(tempdir
, "/tmp/ld.bundle.XXXXXX");
647 sprintf(tempdir
, "%s.bundle.XXXXXX", finalOutput
);
649 // A lookup map to look for BundlerHandler base on filename
650 std::unordered_map
<std::string
, BundleHandler
*> handlerMap
;
652 BitcodeObfuscator
* obfuscator
= _options
.hideSymbols() ? new BitcodeObfuscator() : NULL
;
653 // Build must keep symbols if we need to hide all the symbols
654 if ( _options
.hideSymbols() ) {
655 // Go through all the atoms and decide if it should be obfuscated.
656 // The following symbols are kept:
658 // 2. undefined symbols
659 // 3. symbols must not be stripped
660 // 4. all the globals if the globals are dead_strip root (ex. dylibs)
661 // 5. there is an exported symbol list suggests the symbol should be exported
662 // 6. weak external symbols (not auto-hide)
663 // 7. the special symbols supplied by linker
664 for ( auto §
: _state
.sections
) {
665 for ( auto &atom
: sect
->atoms
) {
666 if ( atom
== _state
.entryPoint
||
667 atom
->definition() == ld::Atom::definitionProxy
||
668 atom
->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip
||
669 ( _options
.allGlobalsAreDeadStripRoots() && atom
->scope() == ld::Atom::scopeGlobal
) ||
670 ( _options
.hasExportRestrictList() && _options
.shouldExport(atom
->name()) ) ||
671 ( atom
->combine() == ld::Atom::combineByName
&& atom
->scope() == ld::Atom::scopeGlobal
&& !atom
->autoHide() ) )
672 obfuscator
->addMustPreserveSymbols(atom
->name());
675 // If there are assembly sources, add globals and undefined symbols from them as well
676 for ( auto &f
: _state
.filesWithBitcode
) {
677 if ( ld::AsmBitcode
* ab
= dynamic_cast<ld::AsmBitcode
*>(f
->getBitcode()) ) {
678 ObjectHandler
objHandler((char*)ab
->getContent(), ab
->getSize());
679 objHandler
.populateMustPreserveSymbols(obfuscator
);
680 } else if ( ld::BundleBitcode
* bb
= dynamic_cast<ld::BundleBitcode
*>(f
->getBitcode()) ) {
681 BundleHandler
* bh
= new BundleHandler((char*)bb
->getContent(), bb
->getSize(), _options
);
682 bh
->populateMustPreserveSymbols(obfuscator
);
683 handlerMap
.emplace(std::string(f
->path()), bh
);
684 } else if ( ld::LLVMBitcode
* bitcode
= dynamic_cast<ld::LLVMBitcode
*>(f
->getBitcode()) ) {
685 BitcodeHandler
bitcodeHandler((char*)bitcode
->getContent(), bitcode
->getSize());
686 bitcodeHandler
.populateMustPreserveSymbols(obfuscator
);
689 // add must preserve symbols from lto input.
690 for ( auto &f
: _state
.ltoBitcodePath
) {
691 BitcodeTempFile
ltoTemp(f
.c_str(), false); // Keep the temp file because it needs to be read in later in the pass.
692 BitcodeHandler
bitcodeHandler((char*)ltoTemp
.getContent(), ltoTemp
.getSize());
693 bitcodeHandler
.populateMustPreserveSymbols(obfuscator
);
695 // add must preserve symbols from compiler_rt input.
696 for ( auto &f
: _state
.filesFromCompilerRT
) {
697 std::vector
<const char*> symbols
;
698 if ( f
->fileContent() && mach_o::relocatable::getNonLocalSymbols(f
->fileContent(), symbols
) ) {
699 for ( auto &sym
: symbols
)
700 obfuscator
->addMustPreserveSymbols(sym
);
704 // special symbols supplied by linker
705 obfuscator
->addMustPreserveSymbols("___dso_handle");
706 obfuscator
->addMustPreserveSymbols("__mh_execute_header");
707 obfuscator
->addMustPreserveSymbols("__mh_dylib_header");
708 obfuscator
->addMustPreserveSymbols("__mh_bundle_header");
709 obfuscator
->addMustPreserveSymbols("__mh_dylinker_header");
710 obfuscator
->addMustPreserveSymbols("__mh_object_header");
711 obfuscator
->addMustPreserveSymbols("__mh_preload_header");
713 // add all the Proxy Atom linker ever created and all the undefs that are possibily dead-stripped.
714 for (auto sym
: _state
.allUndefProxies
)
715 obfuscator
->addMustPreserveSymbols(sym
);
716 _state
.allUndefProxies
.clear();
721 char outFile
[PATH_MAX
];
722 sprintf(outFile
, "%s/bundle.xar", tempdir
);
724 // By default, it uses gzip to compress and SHA1 as checksum
725 x
= xar_open(outFile
, WRITE
);
727 throwf("could not open output bundle to write %s", outFile
);
728 // Disable compression
729 if (xar_opt_set(x
, XAR_OPT_COMPRESSION
, XAR_OPT_VAL_NONE
) != 0)
730 throwf("could not disable compression for bitcode bundle");
732 // Sort all the object file according to oridnal order
733 std::sort(_state
.filesWithBitcode
.begin(), _state
.filesWithBitcode
.end(),
734 [](const ld::relocatable::File
* a
, const ld::relocatable::File
* b
) {
735 return a
->ordinal() < b
->ordinal();
738 // Copy each bitcode file into archive
740 char formatString
[10];
741 sprintf(formatString
, "%%0%ud", (unsigned int)log10(_state
.filesWithBitcode
.size()) + 1);
742 for ( auto &obj
: _state
.filesWithBitcode
) {
743 assert(obj
->getBitcode() != NULL
&& "File should contain bitcode");
744 char outFilePath
[16];
745 sprintf(outFilePath
, formatString
, index
++);
746 if ( ld::LLVMBitcode
* llvmbc
= dynamic_cast<ld::LLVMBitcode
*>(obj
->getBitcode()) ) {
747 // Handle clang and swift bitcode
748 xar_file_t bcFile
= NULL
;
749 if ( _options
.hideSymbols() && !llvmbc
->isMarker() ) { // dont strip if it is just a marker
750 char tempfile
[PATH_MAX
];
751 sprintf(tempfile
, "%s/%s.bc", tempdir
, outFilePath
);
752 obfuscator
->bitcodeHideSymbols(llvmbc
, obj
->path(), tempfile
);
753 BitcodeTempFile
* bcTemp
= new BitcodeTempFile(tempfile
, !_options
.saveTempFiles());
754 bcFile
= xar_add_frombuffer(x
, NULL
, outFilePath
, (char*)bcTemp
->getContent(), bcTemp
->getSize());
757 bcFile
= xar_add_frombuffer(x
, NULL
, outFilePath
, (char*)const_cast<uint8_t*>(llvmbc
->getContent()), llvmbc
->getSize());
759 if ( bcFile
== NULL
)
760 throwf("could not add bitcode from %s to bitcode bundle", obj
->path());
761 if ( xar_prop_set(bcFile
, "file-type", "Bitcode") != 0 )
762 throwf("could not set bitcode property for %s in bitcode bundle", obj
->path());
763 // Write commandline options
764 std::string tagName
= std::string(llvmbc
->getBitcodeName()) + std::string("/cmd");
765 for ( uint32_t i
= 0; i
< llvmbc
->getCmdSize(); ++i
) {
766 if ( i
== 0 || llvmbc
->getCmdline()[i
-1] == '\0' ) {
767 if ( xar_prop_create(bcFile
, tagName
.c_str(), (const char *)llvmbc
->getCmdline() + i
) )
768 throwf("could not set cmdline to XAR file");
772 else if ( ld::BundleBitcode
* bundlebc
= dynamic_cast<ld::BundleBitcode
*>(obj
->getBitcode()) ) {
773 xar_file_t bundleFile
= NULL
;
774 if ( _options
.hideSymbols() && !bundlebc
->isMarker() ) { // dont strip if it is just a marker
775 char tempfile
[PATH_MAX
];
776 sprintf(tempfile
, "%s/%s.xar", tempdir
, outFilePath
);
777 auto search
= handlerMap
.find(std::string(obj
->path()));
778 assert( search
!= handlerMap
.end() && "Cannot find handler");
779 search
->second
->obfuscateAndWriteToPath(obfuscator
, tempfile
);
780 BitcodeTempFile
* bundleTemp
= new BitcodeTempFile(tempfile
, !_options
.saveTempFiles());
781 bundleFile
= xar_add_frombuffer(x
, NULL
, outFilePath
, (char*)bundleTemp
->getContent(), bundleTemp
->getSize());
784 bundleFile
= xar_add_frombuffer(x
, NULL
, outFilePath
,
785 (char*)const_cast<uint8_t*>(bundlebc
->getContent()),
786 bundlebc
->getSize());
788 if ( bundleFile
== NULL
)
789 throwf("could not add bitcode from the bundle %s to bitcode bundle", obj
->path());
790 if ( xar_prop_set(bundleFile
, "file-type", "Bundle") != 0 )
791 throwf("could not set bundle property for %s in bitcode bundle", obj
->path());
793 else if ( ld::AsmBitcode
* asmbc
= dynamic_cast<ld::AsmBitcode
*>(obj
->getBitcode()) ) {
794 xar_file_t objFile
= xar_add_frombuffer(x
, NULL
, outFilePath
, (char*)asmbc
->getContent(), asmbc
->getSize());
795 if ( objFile
== NULL
)
796 throwf("could not add obj file %s to bitcode bundle", obj
->path());
797 if ( xar_prop_set(objFile
, "file-type", "Object") != 0 )
798 throwf("could not set object property for %s in bitcode bundle", obj
->path());
801 assert(false && "Unknown bitcode");
805 // Write merged LTO bitcode files
806 if ( !_state
.ltoBitcodePath
.empty() ) {
808 for (auto &path
: _state
.ltoBitcodePath
) {
809 std::string xar_name
= "lto.o." + std::to_string(count
++);
810 xar_file_t ltoFile
= NULL
;
811 BitcodeTempFile
* ltoTemp
= new BitcodeTempFile(path
.c_str(), !_options
.saveTempFiles());
812 if ( _options
.hideSymbols() ) {
813 ld::Bitcode
ltoBitcode(ltoTemp
->getContent(), ltoTemp
->getSize());
814 char ltoTempFile
[PATH_MAX
];
815 sprintf(ltoTempFile
, "%s/lto.bc", tempdir
);
816 obfuscator
->bitcodeHideSymbols(<oBitcode
, path
.c_str(), ltoTempFile
);
817 BitcodeTempFile
* ltoStrip
= new BitcodeTempFile(ltoTempFile
, !_options
.saveTempFiles());
818 ltoFile
= xar_add_frombuffer(x
, NULL
, xar_name
.c_str(), (char*)ltoStrip
->getContent(), ltoStrip
->getSize());
821 ltoFile
= xar_add_frombuffer(x
, NULL
, xar_name
.c_str(), (char*)ltoTemp
->getContent(), ltoTemp
->getSize());
823 if ( ltoFile
== NULL
)
824 throwf("could not add lto file %s to bitcode bundle", path
.c_str());
825 if ( xar_prop_set(ltoFile
, "file-type", "LTO") != 0 )
826 throwf("could not set bitcode property for %s in bitcode bundle", path
.c_str());
831 // Common LinkOptions
832 std::vector
<std::string
> linkCmd
= _options
.writeBitcodeLinkOptions();
834 // support -sectcreate option
835 for ( auto extraSect
= _options
.extraSectionsBegin(); extraSect
!= _options
.extraSectionsEnd(); ++ extraSect
) {
836 std::string sectName
= std::string(extraSect
->segmentName
) + std::string(",") + std::string(extraSect
->sectionName
);
837 BitcodeTempFile
* sectFile
= new BitcodeTempFile(extraSect
->path
, false);
838 xar_file_t sectXar
= xar_add_frombuffer(x
, NULL
, sectName
.c_str(), (char*)sectFile
->getContent(), sectFile
->getSize());
839 if ( sectXar
== NULL
)
840 throwf("could not encode sectcreate file %s into bitcode bundle", extraSect
->path
);
841 if ( xar_prop_set(sectXar
, "file-type", "Section") != 0 )
842 throwf("could not set bitcode property for %s", sectName
.c_str());
844 linkCmd
.push_back("-sectcreate");
845 linkCmd
.push_back(extraSect
->segmentName
);
846 linkCmd
.push_back(extraSect
->sectionName
);
847 linkCmd
.push_back(sectName
);
850 // Write exports file
851 // A vector of all the exported symbols.
852 if ( _options
.hasExportMaskList() ) {
853 std::vector
<const char*> exportedSymbols
;
854 for ( auto §
: _state
.sections
) {
855 for ( auto &atom
: sect
->atoms
) {
856 // The symbols should be added to the export list is the ones that are:
857 // globalScope, in SymbolTable and should be exported suggested by export file.
858 if ( atom
->scope() == ld::Atom::scopeGlobal
&&
859 atom
->symbolTableInclusion() == ld::Atom::symbolTableIn
&&
860 _options
.shouldExport(atom
->name()) )
861 exportedSymbols
.push_back(atom
->name());
864 linkCmd
.push_back("-exported_symbols_list");
865 linkCmd
.push_back("exports.exp");
866 const char* exportsPath
= "exports.exp";
868 for (std::vector
<const char*>::iterator it
= exportedSymbols
.begin();
869 it
!= exportedSymbols
.end(); ++ it
) {
873 // always append an empty line so exps cannot be empty. rdar://problem/22404253
875 if (xar_opt_set(x
, XAR_OPT_COMPRESSION
, XAR_OPT_VAL_GZIP
) != 0)
876 throwf("could not set compression type for exports list");
877 xar_file_t exportsFile
= xar_add_frombuffer(x
, NULL
, exportsPath
, const_cast<char*>(exps
.data()), exps
.size());
878 if (exportsFile
== NULL
)
879 throwf("could not add exports list to bitcode bundle");
880 if (xar_prop_set(exportsFile
, "file-type", "Exports") != 0)
881 throwf("could not set exports property in bitcode bundle");
882 if (xar_opt_set(x
, XAR_OPT_COMPRESSION
, XAR_OPT_VAL_NONE
) != 0)
883 throwf("could not reset compression type for exports list");
884 } else if ( _options
.hasExportRestrictList() ) {
885 // handle unexported list here
886 std::vector
<const char*> unexportedSymbols
;
887 for ( auto §
: _state
.sections
) {
888 for ( auto &atom
: sect
->atoms
) {
889 // The unexported symbols should not include anything that is in TranslationUnit scope (static) or
890 // that cannot be in the SymbolTable
891 if ( atom
->scope() != ld::Atom::scopeTranslationUnit
&&
892 atom
->symbolTableInclusion() == ld::Atom::symbolTableIn
&&
893 !_options
.shouldExport(atom
->name()) )
894 unexportedSymbols
.push_back(atom
->name());
897 linkCmd
.push_back("-unexported_symbols_list");
898 linkCmd
.push_back("unexports.exp");
899 const char* unexportsPath
= "unexports.exp";
901 for (std::vector
<const char*>::iterator it
= unexportedSymbols
.begin();
902 it
!= unexportedSymbols
.end(); ++ it
) {
903 // try obfuscate the name for symbols in unexported symbols list. They are likely to be obfsucated.
904 const char* sym_name
= NULL
;
905 if ( _options
.hideSymbols() )
906 sym_name
= obfuscator
->lookupHiddenName(*it
);
914 if (xar_opt_set(x
, XAR_OPT_COMPRESSION
, XAR_OPT_VAL_GZIP
) != 0)
915 throwf("could not set compression type for exports list");
916 xar_file_t unexportsFile
= xar_add_frombuffer(x
, NULL
, unexportsPath
, const_cast<char*>(unexps
.data()), unexps
.size());
917 if (unexportsFile
== NULL
)
918 throwf("could not add unexports list to bitcode bundle");
919 if (xar_prop_set(unexportsFile
, "file-type", "Exports") != 0)
920 throwf("could not set exports property in bitcode bundle");
921 if (xar_opt_set(x
, XAR_OPT_COMPRESSION
, XAR_OPT_VAL_NONE
) != 0)
922 throwf("could not reset compression type for exports list");
925 // Handle order file. We need to obfuscate all the entries in the order file
926 if ( _options
.orderedSymbolsCount() > 0 ) {
927 std::string orderFile
;
928 for ( auto entry
= _options
.orderedSymbolsBegin(); entry
!= _options
.orderedSymbolsEnd(); ++ entry
) {
929 std::stringstream line
;
930 if ( entry
->objectFileName
!= NULL
) {
932 for ( auto &f
: _state
.filesWithBitcode
) {
933 const char* atomFullPath
= f
->path();
934 const char* lastSlash
= strrchr(atomFullPath
, '/');
935 if ( (lastSlash
!= NULL
&& strcmp(&lastSlash
[1], entry
->objectFileName
) == 0) ||
936 strcmp(atomFullPath
, entry
->objectFileName
) == 0 )
940 if ( index
>= _state
.filesWithBitcode
.size() )
942 line
<< index
<< ".o:";
944 const char* sym_name
= NULL
;
945 if ( _options
.hideSymbols() )
946 sym_name
= obfuscator
->lookupHiddenName(entry
->symbolName
);
950 line
<< entry
->symbolName
;
952 orderFile
+= line
.str();
954 if (xar_opt_set(x
, XAR_OPT_COMPRESSION
, XAR_OPT_VAL_GZIP
) != 0)
955 throwf("could not set compression type for order file");
956 xar_file_t ordersFile
= xar_add_frombuffer(x
, NULL
, "file.order", const_cast<char*>(orderFile
.data()), orderFile
.size());
957 if (ordersFile
== NULL
)
958 throwf("could not add order file to bitcode bundle");
959 if (xar_prop_set(ordersFile
, "file-type", "OrderFile") != 0)
960 throwf("could not set order file property in bitcode bundle");
961 if (xar_opt_set(x
, XAR_OPT_COMPRESSION
, XAR_OPT_VAL_NONE
) != 0)
962 throwf("could not reset compression type for order file");
963 linkCmd
.push_back("-order_file");
964 linkCmd
.push_back("file.order");
967 // Create subdoc to write link information
968 xar_subdoc_t linkXML
= xar_subdoc_new(x
, "Ld");
969 if ( linkXML
== NULL
)
970 throwf("could not create XML in bitcode bundle");
972 // Write version number
973 if ( xar_prop_create((xar_file_t
)linkXML
, "version", BITCODE_XAR_VERSION
) != 0 )
974 throwf("could not add version number to bitcode bundle");
977 if ( xar_prop_create((xar_file_t
)linkXML
, "architecture", _options
.architectureName()) != 0 )
978 throwf("could not add achitecture name to bitcode bundle");
981 if ( _options
.hideSymbols() ) {
982 if ( xar_prop_create((xar_file_t
)linkXML
, "hide-symbols", "1") != 0 )
983 throwf("could not add property to bitcode bundle");
987 if ( _options
.sdkPaths().size() > 1 )
988 throwf("only one -syslibroot is accepted for bitcode bundle");
989 if ( xar_prop_create((xar_file_t
)linkXML
, "platform", _options
.getPlatformStr().c_str()) != 0 )
990 throwf("could not add platform name to bitcode bundle");
991 if ( xar_prop_create((xar_file_t
)linkXML
, "sdkversion", _options
.getSDKVersionStr().c_str()) != 0 )
992 throwf("could not add SDK version to bitcode bundle");
994 // Write swift version into header
995 if (_state
.swiftVersion
!= 0) {
996 char swiftVersion
[64];
997 Options::userReadableSwiftVersion(_state
.swiftVersion
, swiftVersion
);
998 if ( xar_prop_create((xar_file_t
)linkXML
, "swift-version", swiftVersion
) )
999 throwf("could not add swift version to bitcode bundle");
1003 char sdkRoot
[PATH_MAX
];
1004 if ( _options
.sdkPaths().empty() || (realpath(_options
.sdkPaths().front(), sdkRoot
) == NULL
) )
1005 strcpy(sdkRoot
, "/");
1006 if ( !_state
.dylibs
.empty() ) {
1007 char dylibPath
[PATH_MAX
];
1008 for ( auto &dylib
: _state
.dylibs
) {
1009 // For every dylib/framework, figure out if it is coming from a SDK.
1010 // The dylib/framework from SDK must begin with '/' and user framework must begin with '@'.
1011 if (dylib
->installPath()[0] == '/') {
1012 // Verify the path of the framework is within the SDK.
1013 char dylibRealPath
[PATH_MAX
];
1014 if ( realpath(dylib
->path(), dylibRealPath
) != NULL
&& strncmp(sdkRoot
, dylibRealPath
, strlen(sdkRoot
)) != 0 )
1015 warning("%s has install name beginning with \"/\" but it is not from the specified SDK", dylib
->path());
1016 // The path start with a string template
1017 strcpy(dylibPath
, "{SDKPATH}");
1018 // append the path of dylib/frameowrk in the SDK
1019 strcat(dylibPath
, dylib
->installPath());
1021 // Not in any SDKs, then assume it is a user dylib/framework
1022 // strip off all the path in the front
1023 const char* dylib_name
= strrchr(dylib
->path(), '/');
1024 dylib_name
= (dylib_name
== NULL
) ? dylib
->path() : dylib_name
+ 1;
1025 strcpy(dylibPath
, dylib_name
);
1027 if ( dylib
->forcedWeakLinked() ) {
1028 if ( xar_prop_create((xar_file_t
)linkXML
, "dylibs/weak", dylibPath
) != 0)
1029 throwf("could not add dylib options to bitcode bundle");
1031 if ( xar_prop_create((xar_file_t
)linkXML
, "dylibs/lib", dylibPath
) != 0)
1032 throwf("could not add dylib options to bitcode bundle");
1037 // Write if compiler_rt is force loaded
1038 if (_state
.forceLoadCompilerRT
) {
1039 if ( xar_prop_create((xar_file_t
)linkXML
, "rt-forceload", "1") != 0 )
1040 throwf("could not add compiler_rt force_load info to bitcode bundle");
1043 // Write link-line into archive
1044 for ( auto &it
: linkCmd
) {
1045 if (xar_prop_create((xar_file_t
)linkXML
, "link-options/option", it
.c_str()) != 0)
1046 throwf("could not add link options to bitcode bundle");
1051 // Read the file back
1052 BitcodeTempFile
* xarTemp
= new BitcodeTempFile(outFile
, !_options
.saveTempFiles());
1054 // Create an Atom and add to the list
1055 BitcodeAtom
* bundleAtom
= new BitcodeAtom(*xarTemp
);
1056 _state
.addAtom(*bundleAtom
);
1058 // write the reverse mapping file if required
1059 if ( _options
.hideSymbols() && !_options
.reverseMapTempPath().empty() )
1060 obfuscator
->writeSymbolMap(_options
.reverseMapTempPath().c_str());
1062 // Clean up local variables
1065 for ( auto &entry
: handlerMap
)
1066 delete entry
.second
;
1067 // delete temp directory if not using -save-temps
1068 // only do so after all the BitcodeTempFiles are deleted.
1069 if ( !_options
.saveTempFiles() ) {
1070 if ( ::rmdir(tempdir
) != 0 )
1071 warning("temp directory cannot be removed: %s", tempdir
);
1077 // called by linker to write bitcode bundle into a mach-o section
1078 void doPass(const Options
& opts
, ld::Internal
& internal
) {
1079 BitcodeBundle
BB(opts
, internal
);
1084 } // namespace bitcode_bundle
1085 } // namespace passes