X-Git-Url: https://git.saurik.com/apple/ld64.git/blobdiff_plain/eaf282aaf65b222563e6b3db98e12d720fb161bf..7f09b9353af9897bf18933788d6a59c152c29edd:/src/ld/passes/bitcode_bundle.cpp diff --git a/src/ld/passes/bitcode_bundle.cpp b/src/ld/passes/bitcode_bundle.cpp index bafad0b..527869a 100644 --- a/src/ld/passes/bitcode_bundle.cpp +++ b/src/ld/passes/bitcode_bundle.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #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 § : _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(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(<oBitcode, _state.ltoBitcodePath.c_str(), ltoTempFile); + obfuscator->bitcodeHideSymbols(<oBitcode, 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 exportedSymbols; + for ( auto § : _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 exports = _options.exportsData(); std::string exps; - for (std::vector::iterator it = exports.begin(); - it != exports.end(); ++ it) { + for (std::vector::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(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 unexportedSymbols; + for ( auto § : _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::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(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(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 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