]> git.saurik.com Git - apple/ld64.git/blobdiff - src/ld/passes/bitcode_bundle.cpp
ld64-274.2.tar.gz
[apple/ld64.git] / src / ld / passes / bitcode_bundle.cpp
index bafad0b52a0f460b6e1ef2b5d610d3106b57d21c..527869a839632191f537f1f2df96c7fe907cb445 100644 (file)
@@ -31,6 +31,7 @@
 #include <unistd.h>
 #include <time.h>
 #include <unordered_map>
+#include <sstream>
 
 #include "llvm-c/lto.h"
 // c header
@@ -93,23 +94,32 @@ public:
     ~BitcodeObfuscator();
 
     void addMustPreserveSymbols(const char* name);
+    void addAsmSymbolsToMustPreserve(lto_module_t module);
     void bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath, const char* outputPath);
     void writeSymbolMap(const char* outputPath);
+    const char* lookupHiddenName(const char* symbol);
 private:
     typedef void (*lto_codegen_func_t) (lto_code_gen_t);
     typedef void (*lto_codegen_output_t) (lto_code_gen_t, const char*);
+    typedef const char* (*lto_codegen_lookup_t) (lto_code_gen_t, const char*);
+    typedef unsigned int (*lto_module_num_symbols) (lto_module_t);
+    typedef const char* (*lto_module_symbol_name) (lto_module_t, unsigned int);
 
     lto_code_gen_t                          _obfuscator;
     lto_codegen_func_t                      _lto_hide_symbols;
     lto_codegen_func_t                      _lto_reset_context;
     lto_codegen_output_t                    _lto_write_reverse_map;
+    lto_codegen_lookup_t                    _lto_lookup_hidden_name;
+    lto_module_num_symbols                  _lto_get_asm_symbol_num;
+    lto_module_symbol_name                  _lto_get_asm_symbol_name;
 };
 
 class FileHandler {
     // generic handler for files in a bundle
 public:
     virtual void populateMustPreserveSymbols(BitcodeObfuscator* _obfuscator)    { }
-    virtual void obfuscateAndWriteToPath(BitcodeObfuscator* _obfuscator, const char* path) { };
+    virtual void obfuscateAndWriteToPath(BitcodeObfuscator* _obfuscator, const char* path);
+    virtual const char* compressionMethod() { return XAR_OPT_VAL_NONE; }    // no compression by default
     xar_file_t getXARFile()                 { return _xar_file; }
 
     FileHandler(char* content, size_t size) :
@@ -165,7 +175,7 @@ public:
 
     ~BitcodeHandler();
 
-    virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override { } // Don't need to preserve symbols
+    virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override;
     virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override;
 };
 
@@ -178,9 +188,21 @@ public:
 
     ~ObjectHandler();
 
-    void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override;
-    void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override;
+    virtual void populateMustPreserveSymbols(BitcodeObfuscator* obfuscator) override;
+
+};
+
+class SymbolListHandler : public FileHandler {
+public:
+    SymbolListHandler(char* content, size_t size) :
+        FileHandler(content, size)                      { }
+    SymbolListHandler(xar_t parent, xar_file_t xar_file) :
+        FileHandler(parent, xar_file)                   { }
+
+    ~SymbolListHandler();
 
+    virtual void obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path) override;
+    virtual const char* compressionMethod() override { return XAR_OPT_VAL_GZIP; }
 };
 
 
@@ -246,21 +268,30 @@ BitcodeTempFile::~BitcodeTempFile()
 
 BitcodeObfuscator::BitcodeObfuscator()
 {
+#if LTO_API_VERSION < 11
+    throwf("compile-time libLTO (%d) didn't support -bitcode_hide_symbols", LTO_API_VERSION);
+#else
     // check if apple internal libLTO is used
     if ( ::lto_get_version() == NULL )
         throwf("libLTO is not loaded");
     _lto_hide_symbols = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_hide_symbols");
     _lto_write_reverse_map = (lto_codegen_output_t) dlsym(RTLD_DEFAULT, "lto_codegen_write_symbol_reverse_map");
     _lto_reset_context = (lto_codegen_func_t) dlsym(RTLD_DEFAULT, "lto_codegen_reset_context");
+    _lto_lookup_hidden_name = (lto_codegen_lookup_t) dlsym(RTLD_DEFAULT, "lto_codegen_lookup_hidden_name");
+    _lto_get_asm_symbol_num = (lto_module_num_symbols) dlsym(RTLD_DEFAULT, "lto_module_get_num_asm_symbols");
+    _lto_get_asm_symbol_name = (lto_module_symbol_name) dlsym(RTLD_DEFAULT, "lto_module_get_asm_symbol_name");
     if ( _lto_hide_symbols == NULL || _lto_write_reverse_map == NULL ||
-        _lto_reset_context == NULL || ::lto_api_version() < 14 )
+        _lto_reset_context == NULL || _lto_lookup_hidden_name == NULL ||
+        _lto_get_asm_symbol_num == NULL || _lto_get_asm_symbol_name == NULL || ::lto_api_version() < 14 )
         throwf("loaded libLTO doesn't support -bitcode_hide_symbols: %s", ::lto_get_version());
     _obfuscator = ::lto_codegen_create_in_local_context();
-#if LTO_API_VERSION >= 14
+  #if LTO_API_VERSION >= 14
     lto_codegen_set_should_internalize(_obfuscator, false);
+  #endif
 #endif
 }
 
+
 BitcodeObfuscator::~BitcodeObfuscator()
 {
     ::lto_codegen_dispose(_obfuscator);
@@ -276,7 +307,8 @@ void BitcodeObfuscator::bitcodeHideSymbols(ld::Bitcode* bc, const char* filePath
 #if LTO_API_VERSION >= 13 && LTO_APPLE_INTERNAL
     lto_module_t module = ::lto_module_create_in_codegen_context(bc->getContent(), bc->getSize(), filePath, _obfuscator);
     if ( module == NULL )
-        throwf("object contains invalid bitcode: %s", filePath);
+        throwf("could not reparse object file %s in bitcode bundle: '%s', using libLTO version '%s'",
+               filePath, ::lto_get_error_message(), ::lto_get_version());
     ::lto_codegen_set_module(_obfuscator, module);
     (*_lto_hide_symbols)(_obfuscator);
 #if LTO_API_VERSION >= 15
@@ -293,6 +325,18 @@ void BitcodeObfuscator::writeSymbolMap(const char *outputPath)
     (*_lto_write_reverse_map)(_obfuscator, outputPath);
 }
 
+const char* BitcodeObfuscator::lookupHiddenName(const char *symbol)
+{
+    return (*_lto_lookup_hidden_name)(_obfuscator, symbol);
+}
+
+void BitcodeObfuscator::addAsmSymbolsToMustPreserve(lto_module_t module)
+{
+    for (unsigned int i = 0; i < _lto_get_asm_symbol_num(module); ++ i) {
+        addMustPreserveSymbols(_lto_get_asm_symbol_name(module, i));
+    }
+}
+
 BundleHandler::~BundleHandler()
 {
     // free buffers
@@ -326,6 +370,11 @@ ObjectHandler::~ObjectHandler()
     destroyFile();
 }
 
+SymbolListHandler::~SymbolListHandler()
+{
+    destroyFile();
+}
+
 void BundleHandler::init()
 {
     if ( _xar != NULL )
@@ -354,6 +403,8 @@ void BundleHandler::init()
 
     // read the xar file
     _xar = xar_open(oldXARPath.c_str(), READ);
+    if ( _xar == NULL )
+        throwf("malformed bundle format");
 
     // Init the vector of handler
     xar_iter_t iter = xar_iter_new();
@@ -369,8 +420,10 @@ void BundleHandler::init()
             _handlers.push_back(new ObjectHandler(_xar, f));
         else if ( strcmp(filetype, "Bitcode") == 0 || strcmp(filetype, "LTO") == 0 )
             _handlers.push_back(new BitcodeHandler(_xar, f));
+        else if ( strcmp(filetype, "Exports") == 0 || strcmp(filetype, "OrderFile") == 0)
+            _handlers.push_back(new SymbolListHandler(_xar, f));
         else
-            assert(0 && "Unknown file type");
+            _handlers.push_back(new FileHandler(_xar, f));
     }
     xar_iter_free(iter);
 }
@@ -386,8 +439,10 @@ void BundleHandler::copyXARProp(xar_file_t src, xar_file_t dst)
         const char* key = xar_prop_first(src, p);
         for (int x = 0; x < i; x++)
             key = xar_prop_next(p);
-        if ( !key )
+        if ( !key ) {
+            xar_iter_free(p);
             break;
+        }
         const char* val = NULL;
         xar_prop_get(src, key, &val);
         if ( // Info from bitcode files
@@ -421,6 +476,24 @@ void BundleHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator)
         handler->populateMustPreserveSymbols(obfuscator);
 }
 
+void BitcodeHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator)
+{
+    initFile();
+
+    // init LTOModule and add asm labels
+#if LTO_API_VERSION < 11
+    lto_module_t module = lto_module_create_from_memory(_file_buffer, _file_size);
+#else
+    lto_module_t module = lto_module_create_in_local_context(_file_buffer, _file_size, "bitcode bundle temp file");
+#endif
+    if ( module == NULL )
+        throwf("could not reparse object file in bitcode bundle: '%s', using libLTO version '%s'",
+               ::lto_get_error_message(), ::lto_get_version());
+    obfuscator->addAsmSymbolsToMustPreserve(module);
+    lto_module_dispose(module);
+}
+
+
 void ObjectHandler::populateMustPreserveSymbols(BitcodeObfuscator* obfuscator)
 {
     initFile();
@@ -456,7 +529,13 @@ void BundleHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const
         sprintf(outputPath, "%s/%s", _temp_dir, name);
         handler->obfuscateAndWriteToPath(obfuscator, outputPath);
         BitcodeTempFile* bcOut = new BitcodeTempFile(outputPath, !_options.saveTempFiles());
+        if ( xar_opt_set(x, XAR_OPT_COMPRESSION, handler->compressionMethod()) != 0 )
+            throwf("could not set compression type for exports list");
         xar_file_t bcEntry = xar_add_frombuffer(x, NULL, name, (char*)bcOut->getContent(), bcOut->getSize());
+        if ( bcEntry == NULL )
+            throwf("could not add file to the bundle");
+        if ( xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0 )
+            throwf("could not reset compression type for exports list");
         copyXARProp(f, bcEntry);
         delete bcOut;
     }
@@ -477,7 +556,33 @@ void BitcodeHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, cons
     obfuscator->bitcodeHideSymbols(&bc, path, path);
 }
 
-void ObjectHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path)
+void SymbolListHandler::obfuscateAndWriteToPath(BitcodeObfuscator* obfuscator, const char* path)
+{
+    initFile();
+    // Obfuscate exported symbol list.
+    std::string exports_list;
+    for (size_t i = 0, start = 0; i < _file_size; ++i) {
+        if ( _file_buffer[i] == '\n' ) {
+            _file_buffer[i] = '\0';
+            const char* hiddenName = obfuscator->lookupHiddenName(_file_buffer + start);
+            if ( hiddenName == NULL )
+                exports_list += _file_buffer + start;
+            else
+                exports_list += hiddenName;
+            exports_list += "\n";
+            start = i + 1;
+        } else if ( _file_buffer[i] == '*' ) {
+            throwf("illegal export list found. Please rebuild your static library using -exported_symbol[s_list] with the newest Xcode");
+        }
+    }
+    exports_list += "\n";
+    int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
+    if ( f == -1 || ::write(f, exports_list.data(), exports_list.size()) != (int)exports_list.size() )
+        throwf("failed to write content to temp file: %s", path);
+    ::close(f);
+}
+
+void FileHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const char *path)
 {
     initFile();
     int f = ::open(path, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
@@ -488,12 +593,19 @@ void ObjectHandler::obfuscateAndWriteToPath(BitcodeObfuscator *obfuscator, const
 
 void BitcodeBundle::doPass()
 {
-    if ( _state.embedMarkerOnly ) {
-        assert( _options.outputKind() != Options::kDynamicExecutable &&
-                _options.outputKind() != Options::kStaticExecutable &&
-                "Don't emit marker for executables");
-        BitcodeAtom* marker = new BitcodeAtom();
-        _state.addAtom(*marker);
+    if ( _options.bitcodeKind() == Options::kBitcodeStrip ||
+         _options.bitcodeKind() == Options::kBitcodeAsData )
+        // if emit no bitcode or emit bitcode segment as data, no need to generate bundle.
+        return;
+    else if ( _state.embedMarkerOnly || _options.bitcodeKind() == Options::kBitcodeMarker ) {
+        // if the bitcode is just a marker,
+        // the executable will be created without bitcode section.
+        // Otherwise, create a marker.
+        if( _options.outputKind() != Options::kDynamicExecutable &&
+            _options.outputKind() != Options::kStaticExecutable ) {
+            BitcodeAtom* marker = new BitcodeAtom();
+            _state.addAtom(*marker);
+        }
         return;
     }
 
@@ -522,14 +634,16 @@ void BitcodeBundle::doPass()
         // 3. symbols must not be stripped
         // 4. all the globals if the globals are dead_strip root (ex. dylibs)
         // 5. there is an exported symbol list suggests the symbol should be exported
-        // 6. the special symbols supplied by linker
+        // 6. weak external symbols (not auto-hide)
+        // 7. the special symbols supplied by linker
         for ( auto &sect : _state.sections ) {
             for ( auto &atom : sect->atoms ) {
                 if ( atom == _state.entryPoint ||
                      atom->definition() == ld::Atom::definitionProxy ||
                      atom->symbolTableInclusion() == ld::Atom::symbolTableInAndNeverStrip ||
                      ( _options.allGlobalsAreDeadStripRoots() && atom->scope() == ld::Atom::scopeGlobal ) ||
-                     ( _options.hasExportRestrictList() && _options.shouldExport(atom->name())) )
+                     ( _options.hasExportRestrictList() && _options.shouldExport(atom->name()) ) ||
+                     ( atom->combine() == ld::Atom::combineByName && atom->scope() == ld::Atom::scopeGlobal && !atom->autoHide() ) )
                     obfuscator->addMustPreserveSymbols(atom->name());
             }
         }
@@ -542,8 +656,18 @@ void BitcodeBundle::doPass()
                 BundleHandler* bh = new BundleHandler((char*)bb->getContent(), bb->getSize(), _options);
                 bh->populateMustPreserveSymbols(obfuscator);
                 handlerMap.emplace(std::string(f->path()), bh);
+            } else if ( ld::LLVMBitcode* bitcode = dynamic_cast<ld::LLVMBitcode*>(f->getBitcode()) ) {
+                BitcodeHandler bitcodeHandler((char*)bitcode->getContent(), bitcode->getSize());
+                bitcodeHandler.populateMustPreserveSymbols(obfuscator);
             }
         }
+        // add must preserve symbols from lto input.
+        for ( auto &f : _state.ltoBitcodePath ) {
+            BitcodeTempFile ltoTemp(f.c_str(), false); // Keep the temp file because it needs to be read in later in the pass.
+            BitcodeHandler bitcodeHandler((char*)ltoTemp.getContent(), ltoTemp.getSize());
+            bitcodeHandler.populateMustPreserveSymbols(obfuscator);
+        }
+
         // special symbols supplied by linker
         obfuscator->addMustPreserveSymbols("___dso_handle");
         obfuscator->addMustPreserveSymbols("__mh_execute_header");
@@ -552,6 +676,11 @@ void BitcodeBundle::doPass()
         obfuscator->addMustPreserveSymbols("__mh_dylinker_header");
         obfuscator->addMustPreserveSymbols("__mh_object_header");
         obfuscator->addMustPreserveSymbols("__mh_preload_header");
+
+        // add all the Proxy Atom linker ever created and all the undefs that are possibily dead-stripped.
+        for (auto sym : _state.allUndefProxies)
+            obfuscator->addMustPreserveSymbols(sym);
+        _state.allUndefProxies.clear();
     }
 
     // Open XAR output
@@ -640,26 +769,30 @@ void BitcodeBundle::doPass()
         }
     }
 
-    // Write merged LTO bitcode
+    // Write merged LTO bitcode files
     if ( !_state.ltoBitcodePath.empty() ) {
-        xar_file_t ltoFile = NULL;
-        BitcodeTempFile* ltoTemp = new BitcodeTempFile(_state.ltoBitcodePath.c_str(), !_options.saveTempFiles());
-        if ( _options.hideSymbols() ) {
+        int count = 0;
+        for (auto &path : _state.ltoBitcodePath) {
+          std::string xar_name = "lto.o." + std::to_string(count++);
+          xar_file_t ltoFile = NULL;
+          BitcodeTempFile* ltoTemp = new BitcodeTempFile(path.c_str(), !_options.saveTempFiles());
+          if ( _options.hideSymbols() ) {
             ld::Bitcode ltoBitcode(ltoTemp->getContent(), ltoTemp->getSize());
             char ltoTempFile[PATH_MAX];
             sprintf(ltoTempFile, "%s/lto.bc", tempdir);
-            obfuscator->bitcodeHideSymbols(&ltoBitcode, _state.ltoBitcodePath.c_str(), ltoTempFile);
+            obfuscator->bitcodeHideSymbols(&ltoBitcode, path.c_str(), ltoTempFile);
             BitcodeTempFile* ltoStrip = new BitcodeTempFile(ltoTempFile, !_options.saveTempFiles());
-            ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoStrip->getContent(), ltoStrip->getSize());
+            ltoFile = xar_add_frombuffer(x, NULL, xar_name.c_str(), (char*)ltoStrip->getContent(), ltoStrip->getSize());
             delete ltoStrip;
-        } else {
-            ltoFile = xar_add_frombuffer(x, NULL, "lto.o", (char*)ltoTemp->getContent(), ltoTemp->getSize());
+          } else {
+            ltoFile = xar_add_frombuffer(x, NULL, xar_name.c_str(), (char*)ltoTemp->getContent(), ltoTemp->getSize());
+          }
+          if ( ltoFile == NULL )
+            throwf("could not add lto file %s to bitcode bundle", path.c_str());
+          if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 )
+            throwf("could not set bitcode property for %s in bitcode bundle", path.c_str());
+          delete ltoTemp;
         }
-        if ( ltoFile == NULL )
-            throwf("could not add lto file %s to bitcode bundle", _state.ltoBitcodePath.c_str());
-        if ( xar_prop_set(ltoFile, "file-type", "LTO") != 0 )
-            throwf("could not set bitcode property for %s in bitcode bundle", _state.ltoBitcodePath.c_str());
-        delete ltoTemp;
     }
 
     // Common LinkOptions
@@ -682,24 +815,120 @@ void BitcodeBundle::doPass()
     }
 
     // Write exports file
+    // A vector of all the exported symbols.
     if ( _options.hasExportMaskList() ) {
+        std::vector<const char*> exportedSymbols;
+        for ( auto &sect : _state.sections ) {
+            for ( auto &atom : sect->atoms ) {
+                // The symbols should be added to the export list is the ones that are:
+                // globalScope, in SymbolTable and should be exported suggested by export file.
+                if ( atom->scope() == ld::Atom::scopeGlobal &&
+                     atom->symbolTableInclusion() == ld::Atom::symbolTableIn &&
+                     _options.shouldExport(atom->name()) )
+                    exportedSymbols.push_back(atom->name());
+            }
+        }
         linkCmd.push_back("-exported_symbols_list");
         linkCmd.push_back("exports.exp");
         const char* exportsPath = "exports.exp";
-        std::vector<const char*> exports = _options.exportsData();
         std::string exps;
-        for (std::vector<const char*>::iterator it = exports.begin();
-             it != exports.end(); ++ it) {
+        for (std::vector<const char*>::iterator it = exportedSymbols.begin();
+             it != exportedSymbols.end(); ++ it) {
             exps += *it;
             exps += "\n";
         }
         // always append an empty line so exps cannot be empty. rdar://problem/22404253
         exps += "\n";
+        if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0)
+            throwf("could not set compression type for exports list");
         xar_file_t exportsFile = xar_add_frombuffer(x, NULL, exportsPath, const_cast<char*>(exps.data()), exps.size());
         if (exportsFile == NULL)
             throwf("could not add exports list to bitcode bundle");
         if (xar_prop_set(exportsFile, "file-type", "Exports") != 0)
             throwf("could not set exports property in bitcode bundle");
+        if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0)
+            throwf("could not reset compression type for exports list");
+    } else if ( _options.hasExportRestrictList() ) {
+        // handle unexported list here
+        std::vector<const char*> unexportedSymbols;
+        for ( auto &sect : _state.sections ) {
+            for ( auto &atom : sect->atoms ) {
+                // The unexported symbols should not include anything that is in TranslationUnit scope (static) or
+                // that cannot be in the SymbolTable
+                if ( atom->scope() != ld::Atom::scopeTranslationUnit &&
+                     atom->symbolTableInclusion() == ld::Atom::symbolTableIn &&
+                     !_options.shouldExport(atom->name()) )
+                    unexportedSymbols.push_back(atom->name());
+            }
+        }
+        linkCmd.push_back("-unexported_symbols_list");
+        linkCmd.push_back("unexports.exp");
+        const char* unexportsPath = "unexports.exp";
+        std::string unexps;
+        for (std::vector<const char*>::iterator it = unexportedSymbols.begin();
+             it != unexportedSymbols.end(); ++ it) {
+            // try obfuscate the name for symbols in unexported symbols list. They are likely to be obfsucated.
+            const char* sym_name = NULL;
+            if ( _options.hideSymbols() )
+                sym_name = obfuscator->lookupHiddenName(*it);
+            if ( sym_name )
+                unexps += sym_name;
+            else
+                unexps += *it;
+            unexps += "\n";
+        }
+        unexps += "\n";
+        if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0)
+            throwf("could not set compression type for exports list");
+        xar_file_t unexportsFile = xar_add_frombuffer(x, NULL, unexportsPath, const_cast<char*>(unexps.data()), unexps.size());
+        if (unexportsFile == NULL)
+            throwf("could not add unexports list to bitcode bundle");
+        if (xar_prop_set(unexportsFile, "file-type", "Exports") != 0)
+            throwf("could not set exports property in bitcode bundle");
+        if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0)
+            throwf("could not reset compression type for exports list");
+    }
+
+    // Handle order file. We need to obfuscate all the entries in the order file
+    if ( _options.orderedSymbolsCount() > 0 ) {
+        std::string orderFile;
+        for ( auto entry = _options.orderedSymbolsBegin(); entry != _options.orderedSymbolsEnd(); ++ entry ) {
+            std::stringstream line;
+            if ( entry->objectFileName != NULL ) {
+                unsigned index = 0;
+                for ( auto &f : _state.filesWithBitcode ) {
+                    const char* atomFullPath = f->path();
+                    const char* lastSlash = strrchr(atomFullPath, '/');
+                    if ( (lastSlash != NULL && strcmp(&lastSlash[1], entry->objectFileName) == 0) ||
+                         strcmp(atomFullPath, entry->objectFileName) == 0 )
+                        break;
+                    ++ index;
+                }
+                if ( index >= _state.filesWithBitcode.size() )
+                    continue;
+                line << index << ".o:";
+            }
+            const char* sym_name = NULL;
+            if ( _options.hideSymbols() )
+                sym_name = obfuscator->lookupHiddenName(entry->symbolName);
+            if ( sym_name )
+                line << sym_name;
+            else
+                line << entry->symbolName;
+            line << "\n";
+            orderFile += line.str();
+        }
+        if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_GZIP) != 0)
+            throwf("could not set compression type for order file");
+        xar_file_t ordersFile = xar_add_frombuffer(x, NULL, "file.order", const_cast<char*>(orderFile.data()), orderFile.size());
+        if (ordersFile == NULL)
+            throwf("could not add order file to bitcode bundle");
+        if (xar_prop_set(ordersFile, "file-type", "OrderFile") != 0)
+            throwf("could not set order file property in bitcode bundle");
+        if (xar_opt_set(x, XAR_OPT_COMPRESSION, XAR_OPT_VAL_NONE) != 0)
+            throwf("could not reset compression type for order file");
+        linkCmd.push_back("-order_file");
+        linkCmd.push_back("file.order");
     }
 
     // Create subdoc to write link information
@@ -730,22 +959,23 @@ void BitcodeBundle::doPass()
         throwf("could not add SDK version to bitcode bundle");
 
     // Write dylibs
-    const char* sdkRoot = NULL;
-    if ( !_options.sdkPaths().empty() )
-        sdkRoot = _options.sdkPaths().front();
+    char sdkRoot[PATH_MAX];
+    if ( _options.sdkPaths().empty() || (realpath(_options.sdkPaths().front(), sdkRoot) == NULL) )
+        strcpy(sdkRoot, "/");
     if ( !_state.dylibs.empty() ) {
-        std::vector<const char*> SDKPaths = _options.sdkPaths();
         char dylibPath[PATH_MAX];
         for ( auto &dylib : _state.dylibs ) {
-            // For every dylib/framework, figure out if it is coming from a SDK
-            // if it is coming from some SDK, we parse the path to figure out which SDK
-            // If -syslibroot is pointing to a SDK, it should end with PlatformX.Y.sdk/
-            if (sdkRoot && strncmp(dylib->path(), sdkRoot, strlen(sdkRoot)) == 0) {
-                // dylib/framework from one of the -syslibroot
+            // For every dylib/framework, figure out if it is coming from a SDK.
+            // The dylib/framework from SDK must begin with '/' and user framework must begin with '@'.
+            if (dylib->installPath()[0] == '/') {
+                // Verify the path of the framework is within the SDK.
+                char dylibRealPath[PATH_MAX];
+                if ( realpath(dylib->path(), dylibRealPath) != NULL && strncmp(sdkRoot, dylibRealPath, strlen(sdkRoot)) != 0 )
+                    warning("%s has install name beginning with \"/\" but it is not from the specified SDK", dylib->path());
                 // The path start with a string template
-                strcpy(dylibPath, "{SDKPATH}/");
+                strcpy(dylibPath, "{SDKPATH}");
                 // append the path of dylib/frameowrk in the SDK
-                strcat(dylibPath, dylib->path() + strlen(sdkRoot));
+                strcat(dylibPath, dylib->installPath());
             } else {
                 // Not in any SDKs, then assume it is a user dylib/framework
                 // strip off all the path in the front