[CMake] Weirdness with shared library, RPATH policy on MacOS

Chris Wolf cw10025 at gmail.com
Sun Aug 15 01:25:49 EDT 2010



On 8/14/10 4:01 PM, Michael Wild wrote:
> 
> On 14. Aug, 2010, at 18:26 , Chris Wolf wrote:
> 
>>
>>
>> On 8/14/10 10:31 AM, Michael Wild wrote:
>>>
>>> On 14. Aug, 2010, at 15:13 , Chris Wolf wrote:
>>>
>>>>
>>>>
>>>> On 8/14/10 3:35 AM, Michael Wild wrote:
>>>>>
>>>>> On 13. Aug, 2010, at 20:58 , Michael Wild wrote:
>>>>> [...]
>>>>>>
>>>>>> Sure, http://repo.or.cz/w/freefoam.git/shortlog/refs/heads/pu, but it's pretty complex...
>>>>>>
>>>>>> Michael
>>>>>
>>>>> Attached is a tiny project which works for me on both Linux and Mac.
>>>>>
>>>>> Michael
>>>>>
>>>>
>>>> That's awesome! Thanks so much.  From your example, and looking at the documentation:
>>>> http://www.cmake.org/Wiki/CMake_RPATH_handling#Always_full_RPATH
>>>>
>>>> ...I can see the difference now - you are setting CMAKE_INSTALL_NAME_DIR. 
>>>> (which is not needed or used on Linux/ELF)
>>>>
>>>> What I had been trying to do along these lines was a per-target setting:
>>>>
>>>> set_target_properties(usbDynamic PROPERTIES INSTALL_NAME_DIR "/tmp/local/lib")
>>>>
>>>> -=or=-
>>>>
>>>> set_target_properties(usbDynamic PROPERTIES INSTALL_RPATH "/tmp/local/lib/libusb-1.dylib"")
>>>>
>>>> -=or=-
>>>>
>>>> set_target_properties(usbDynamic PROPERTIES
>>>>  INSTALL_RPATH "/tmp/local/lib/libusb-1.dylib"
>>>>  INSTALL_NAME_DIR "/tmp/local/lib")
>>>>
>>>> None of those were working for me.
>>>>
>>>> I will incorporate your setting in my project.
>>>>
>>>> Thanks,
>>>>
>>>>  -Chris
>>>
>>>
>>> Aaaah, I see. The INSTALL_RPATH should have been /tmp/local/lib. And RPATH is only a Linux thing, while install_name is a Mac OS X thing ;-)
>>>
>>> Michael
>>
>> Actually, "/tmp/local/lib" is just for testing - my intention is to use "/usr/local/lib".
>>
>> Also RPATH on Mac OSX is no different then Linux, SunOS, etc. - only the mechanism to change it is.
>>
>> For example, CMake appears to use perform direct modification of ELF-formatted binaries, 
>> apparently to avoid relinking upon installation (i.e. running the generated cmake_install.cmake)
>>
>> It does this via the undocumented FILE(RPATH_CHANGE,.., FILE(RPATH_REMOVE,... routines.
>> which are called indirectly via 
>> cmInstallTargetGenerator::PostReplacementTweaks/cmInstallTargetGenerator::AddChrpathPatchRule
>>
>> For some reason (maybe to accommodate frameworks) instead of polymorphically implementing
>> cmSystemTools::ChangeRPath and cmSystemTools::RemoveRPath to handle the ELF *or* Darwin
>> cases (which would have allowed AddChrpathPatchRule to work for ELF *and* Darwin) 
>> - a separate piece of logic was implemented for PostReplacementTweaks called,
>> cmInstallTargetGenerator::AddInstallNamePatchRule, which invokes the OSX utility
>> "install_name_tool" to change the RPATH in shared libraries and executables.
>>
>> The way I see it (and I could be wrong) there is divergence between the logic of
>> AddChrpathPatchRule (ELF RPATH logic) and AddInstallNamePatchRule (Darwin RPATH logic)
>>
>> Ideally, if cmSystemTools::ChangeRPath and cmSystemTools::RemoveRPath  were 
>> polymorphic for ELF and Darwin, you would only need AddChrpathPatchRule and not
>> AddInstallNamePatchRule, such that the FILE(RPATH_CHANGE,.., FILE(RPATH_REMOVE
>> routines would function the same for ELF and Darwin and the generated
>> "cmake_install.cmake" would look the same for both of these platforms - as a result,
>> the documented behavior (http://www.cmake.org/Wiki/CMake_RPATH_handling)
>> would be identical for ELF and Darwin cases.
>>
>> Well, that's my two cents...
>>
>> Thanks again for your help,
>>
>>  -Chris
> 
> No, the two mechanisms are fundamentally different.
> 
> On Linux the RPATH is a search path (think LD_LIBRARY_PATH) that is encoded into the binary. The linker 
> only embeds the library name, no directory information. The loader then first searches these directories 
> for the linked libraries, and then proceeds to search LD_LIBRARY_PATH and the directories defined in ld.conf.
> 
> On Mac OS X, the install_name is a file name encoded into the library itself. When another binary is linked 
> against this library, the linker hard-codes this file name into the binary, no matter what the actual location 
> of the library is. The loader then tries to load that library using the hard-coded install_name from the binary, 
> and only if that fails, uses DYLD_FALLBACK_LIBRARY_PATH.
> 
> I hope this clarifies things a bit.
> 
> Michael

Actually, I would say that the Darwin dynamic loader behavior is a super-set of the Linux dynamic loader.
You can set RPATHs in an executable via the linker option "-rpath /tmp/bar" and instead of single
colon-delimited path, you just specify multiple "-rpath /some/path" options.

Then you can set the install_name on the dependent shared libraries to "@rpath/mylib.dylib" which is used 
to locate the shared library via the rpath(s) in the executable.  This would be the same behavior
as Linux:

See:   http://bit.ly/bNert1
       http://developer.apple.com/mac/library/documentation/Darwin/Reference/ManPages/man1/dyld.1.html
       http://bit.ly/csQGqb

However, I am not necessarily advocating doing that, since that is not the convention used
to deliver simple (i.e. not framework or bundle or plugins) apps and libraries.  All I am
suggesting is that it might be helpful if CMake would present the same usage for specifying 
linkage behavior for Linux and Darwin, even if the underlying executable->library lookup 
mechanism is different.  Either that, or fix up the documentation to point out were things
diverge.

  -Chris






More information about the CMake mailing list