본문 바로가기

Mac

dylib파일을 RTLD_LAZY 모드로 dlopen 할 때 주의점

만약 dylib파일을 웹 다운로드 하여 해당 dylib의 함수를 사용하려면, 


동적으로 로딩해서 사용해야 한다.


그때 void * dlopen(const char * __path, int __mode); 함수로 dylib를 오픈해서 사용했었다. 



typedef const char* Interface_GetVersionDylib(void);

typedef void Interface_DetachDylib(void);


- (BOOL)versionTest:(NSString*)dylibPath    

void* openedDylib = dlopen(dylibPath, RTLD_LAZY);

    if (openedDylib == nil) {

        LogError(@"openedDylib Open Failed(%s)", dlerror());

        return NO;

    }

    LogDebug(@"loaded %@", dylibPath);


    Interface_GetVersionDylib* getVersionDylib = (Interface_GetVersionDylib*)dlsym(openedDylib, "_GetVersionDylib");

    if (getVersionDylib == nil) {

        LogError(@"get version dylib failed(%s)", dlerror());

        

        Interface_DetachDylib* detachDylib = (Interface_DetachDylib*)dlsym(openedDylib, "_DetachDylib");

        if (detachDylib == nil) {

            LogError(@"detach dylib failed(%s)", dlerror());

        }

        else {

            (*detachDylib)();

            LogDebug(@"detach dylib %@", dylibPath);

        }

        

        dlclose(openedDylib);

        openedDylib = NULL;

        LogDebug(@"Unloaded dylib");

        

        return NO;

    }

    LogDebug(@"get version dylib : %s", (*getVersionDylib)());

    return YES;

}


위의 예제는 버전 출력과 dylib를 detach 하는 함수를 제공하는 dylib 주소가 dylibPath에 위치하는 경우이다.


일반적인 경우에는 크게 문제가 없다.


하지만 dlopen 하는 파일이 이름만 똑같은 다른 파일을 다시 dlopen할 경우에 문제가 된다.


예를 들어 버전이 1.0인 A.dylib 파일이 dylib 경로에 있었다고 하자.


[self versionTest:ADylibPath]; 를 호출했을 경우 정상적으로 버전이 1.0이 찍힐 것이다.


해당 프로그램를 종료하지 않고 버전이 2.0인 A.dylib인 파일을 똑같은 경로에 넣고 다시 호출을 한다. 


[self versionTest:ADylibPath];


이 경우 버전이 2.0이 찍혀야 정상이지만 1.0이 여전히 찍히는 문제가 발생한다.


만약 해당 프로그램을 종료하고 다시 실행하면 2.0이 찍힐 것이다.



테스트를 해본 결과 dlopen시 dylib 파일명(경로)이 다를 경우는 문제가 발생하지 않는다.


파일명(경로)이 같은 경우라도 dlopen시 RTLD_LAZY가 아닌 RTLD_NOW 모드를 주고 실행하면 문제가 발생하지 않는다.


즉, dlopen시 RTLD_LAZY 모드를 사용하면 같은 파일명(경로)의 dylib를 새로 로드시에도, 이전의 dylib가 메모리 어딘가에 계속 존재하여 해당 dylib가 호출 되는 것으로 추측된다.


여튼 이런 문제를 피하려면 dylib 파일명을 다르게 주던지, 경로를 조금 다르게 주던지, dlopen 시 RTLD_NOW 모드를 사용하면 된다.