Search code examples
objective-cxcodemacosopensslstatic-linking

Why do I clearly refer to the static library in xcode, but the program tries to load the dynamic library during execution?


I am testing openssl under macos. First, I installed openssl through brew install openssl, and the installation location is: /usr/local/Cellar/openssl@3/3.0.5/. Then I created a simple macos command line project, and then wrote some very simple code to reference the openssl encryption and decryption library. enter image description here

In order to pass the compilation, I added the include path of openssl in the header file search path. enter image description here

Then I added a reference to libcrypto.a in the Build Phase options. From what I understand, this library should be a static library, then when linking, the linker should copy all its code into my process without adding references to other dynamic libraries. But the strange thing is that when I try to debug and run the project, the process actually tries to load a dynamic library libcrypto.3.dylib that I have never referenced, and the loading fails because the dynamic library has no signature. enter image description here

dyld[4481]: Library not loaded: '/usr/local/opt/openssl@3/lib/libcrypto.3.dylib'
  Referenced from: '/Users/dongbo/Library/Developer/Xcode/DerivedData/TestOpenSSL-abmortoxmqaalbcuirkuraizktsa/Build/Products/Debug/TestOpenSSL'
  Reason: tried: '/Users/dongbo/Library/Developer/Xcode/DerivedData/TestOpenSSL-abmortoxmqaalbcuirkuraizktsa/Build/Products/Debug/libcrypto.3.dylib' (no such file), '/usr/lib/system/introspection/libcrypto.3.dylib' (no such file), '/usr/local/opt/openssl@3/lib/libcrypto.3.dylib' (code signature in <BF9EFA44-EE24-3AF6-B0D4-3DFC6E454288> '/usr/local/Cellar/openssl@3/3.0.5/lib/libcrypto.3.dylib' not valid for use in process: mapped file has no cdhash, completely unsigned? Code has to be at least ad-hoc signed.), '/usr/local/lib/libcrypto.3.dylib' (no such file), '/usr/lib/libcrypto.3.dylib' (no such file), '/Users/dongbo/Library/Developer/Xcode/DerivedData/TestOpenSSL-abmortoxmqaalbcuirkuraizktsa/Build/Products/Debug/libcrypto.3.dylib' (no such file), '/usr/lib/system/introspection/libcrypto.3.dylib' (no such file), '/usr/local/Cellar/openssl@3/3.0.5/lib/libcrypto.3.dylib' (code signature in <BF9EFA44-EE24-3AF6-B0D4-3DFC6E454288> '/usr/local/Cellar/openssl@3/3.0.5/lib/libcrypto.3.dylib' not valid for use in process: mapped file has no cdhash, completely unsigned? Code has to be at least ad-hoc signed.), '/usr/local/lib/libcrypto.3.dylib' (no such file), '/usr/lib/libcrypto.3.dylib' (no such file)
Program ended with exit code: 9

I'm getting this error while testing on an intel-architecture imac, but magically, I'm doing the same on an m1 with no issues, except my m1 pro has sip turned off. I don't know if everyone knows the reason for this?

all test codes:
#import <Foundation/Foundation.h>
#include <string>

#include <openssl/rsa.h>
#include <openssl/pem.h>

const std::string pk = std::string("-----BEGIN PUBLIC KEY-----\n") +
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDTlCZFucurE+QNupniPUXz5RwN\n" +
"dhRAplB+jd51U4NTcpDl4AL3LppKdRxEyt4FlvLiE66tmonEJTc4BcaRurxxXOuY\n" +
"+0IS4l28FynYT/yDpdoiop0Jf2NCa8V5nCBISKp1Lgvz7AbHBw+3KNCF1UdrOeRs\n" +
"r/GBOSXosmTzPMRUNwIDAQAB\n" +
"-----END PUBLIC KEY-----";

std::string decrypt_string(const void* data, size_t length)
{
    BIO* bio = BIO_new_mem_buf(pk.c_str(), (int)pk.size());
    
    RSA* rsa = NULL;
    PEM_read_bio_RSA_PUBKEY(bio, &rsa, 0, 0);
    
    char buf[1024] = {0};
    
    int ret = RSA_public_decrypt(0x80, (const unsigned char*)data, (unsigned char*)buf, rsa, RSA_PKCS1_PADDING);
    
    if(ret == -1)
    {
        BIO_free(bio);
        
        printf("decrypt error:%d\n", ret);
        return "Error";
    }
    
    BIO_free(bio);
    
    std::string str = buf;
    
    return buf;
}


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        // insert code here...
        NSLog(@"Hello, World!");
    }
    return 0;
}

Solution

  • First, openssl is in fact considered deprecated and the corresponding headers were removed from macOS SDK 10.11 (the replacement is Secure Transport API).

    Second, when linking a libName.a archive from Xcode settings, Xcode in fact transform this into -lName flag when linking. It means Xcode doesn't refer to any specific path you provided, but instead looks for appropriate libraries in the default search-libs paths (in addition to the provided ones).


    If for any reason you still need the library Apple recommends just build it locally and embed the archive in the project bundle:

    1. Add the library archive to the project:

    enter image description here

    1. Provide path to the system headers (it can be just headers paths, but i prefer system headers for 3-rd party libraries to avoid redundant warnings):

    enter image description here

    1. Add the archive to Embed Frameworks but not Link Binary build phases (we avoid using Link Binary step because if the library exists in the outdated macOS SDK, Xcode mail fail to disambiguate it with the embedded version):

    enter image description here

    1. Add linker flag to the Build Settings which points to your archive in the project directory (don't worry that such a directory doesn't exist for a product, the Embed Frameworks phase will resolve this path into the correct one):

    enter image description here

    At this point the library should link without errors.