解決 “dyld: Library not loaded: ” 錯誤

今天在Mac上搞了一個動態庫(開發環境Eclipse+sconsolidator), 生成dylib倒是簡單。隨後新建一個可執行的demo, 鏈接時在設置裏指定dylib的目錄和相應的庫名,但-L參數指定的絕對目錄死活都連不上,提示找不到此目錄,把它換成相對目錄就可以,非常詭異。當一切OK後,運行時出錯了。錯誤原因是dylib動態庫沒加載,我把動態庫放在/usr/local/lib和可執行程序目錄下依然無效。百思不得其解,逐Google相關資料,直到找到此篇文章,才茅塞頓開。


http://qin.laya.com/tech_coding_help/dylib_linking.html

Creating working dylibs

So creating a working dylib of one of your own libraries is not a very well documented process. So I've decided to write a short summary of some things that will aid other people in creating their own dylibs that they can just stick into their app package and have work.

I assume that you already have knowledge of how to add a framework and get it to show up in the frameworks folder in your app package. You can find out how to do this sort of stuff from other pages, so I won't take the time to cover it here.

Some tools that will come in handy:
otool -L
install_name_tool
libtool

So, chances probably are that you've figured out how to compile your library into a dylib already, but when you add it to your project builder project and compile your code, you find that the dylib is being properly put into the right folder (the Frameworks folder) in your bundle. But your application won't work because it can't find the dylib! What's up?

Well, the problem isn't actually with project builder (it's actually sort of a bug with OS X, since it all worked properly in OS 9), it's with the library. If you open up the terminal and cd into your app package and find the binary that's in the Contents/MacOS folder, you can run otool -L on the binary that you find there, and you might see something like the following:
% otool -L MyFunBinary
MyFunBinary
		/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 71.0.0)
		libbz2.1.0.2.dylib (compatibility version 1.0.0, current version 1.0.2)
In this example, I'm trying to link in my own copy of the bzip2 library dylib that was created for me with bzip2 and a patch that's available on the internet.

As it turns out, in OS X 10.0 - 10.2, you need the path to the library to show up in there -- what you need is for the second line to show something like this: 
		@executable_path/../Frameworks/libbz2.1.0.2.dylib (compatibility version 1.0.0, current version 1.0.2)
This tells OS X that it should look in the place where the binary is, move up a directory and into the Framworks folder, and the library will be sitting there. Which it is. Indeed, if you run otool -L on the binary of some application in your /Applications folder that has a working frameworks or dylibs in the app package Frameworks folder (try one of the applications from Omni), you can see that this is the case -- all of their binaries comtain the right path. But the compiler is not figuring things out right... In fact, as it is right now, you can place the dylib in the same folder that the .app package is in, and things will run properly. But that's not what we want.

Well, the first thing you can try to do is to use install_name_tool. If you read the man page for install_name_tool, you can actually change the information inside of the executable so that it has the right location. Depending on how the application was compiled, this may or may not work correctly. More specifically, you'll be able to change the location if there was extra space compiled into the binary to allow space for a longer pathname. Generally install_name_tool will work on binaries, but not libaries. So you can now do this:

% install_name_tool -change libbz2.1.0.2.dylib @executable_path/../Frameworks/libbz2.1.0.2.dylib MyFunBinary 

Now try to run the file. Voila! It should work.
However this is somewhat unacceptable, as whenever the binary gets changed -- taht is, whenever you recompile your code, this binary is going to get the short end of the shaft and get replaced by a new binary. Now, you could actually set up a shell script that runs install_name_tool every time you build your program, but that's somewhat of a hack. We might as well try to fix things at the origin of the problem.

As it turns out, the actual problem resides with the library itself. If you take your library, and run otool -L on it like this:

% otool -L libbz2.1.0.2.dylib

		/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 71.0.0)
		libbz2.1.0.2.dylib (compatibility version 1.0.0, current version 1.0.2)

Well, this looks familiar! Well, in fact the computer picks up the location of the library from the library itself -- what we need to do is to get @executable_path/../Frameworks/ to show up in front of the binary name. We can try using install_name_tool, but this time we find out that things don't work. Why is this? There's no extra room compiled into the library to allow for a longer pathname, so we'd have to compile the original library with an extra flag to allow for more room in the path. But even better, we might as well compile things correctly the firs time! Note that I can't give you exact directions from this point on, since things will differ slightly for different applications you are compiling. But in general, you should go back to the source code for your application, find the linker line in the Makefile that generates the dylib, and change it, adding the -install_name flag to the libtool line. You'll want to do something like:

libtool -dynamic -flat_namespace -install_name @executable_path/../Frameworks/libbz2.1.0.2.dylib \ -lSystem -compatibility_version 1.0 -current_version 1.0.2 \ -o libbz2.1.0.2.dylib -undefined suppress $(OBJS) 

Anyway, I hope that helps some people out. Good luck!
Also of interest may be the Apple Documentation on Shared Libraries and Frameworks.


簡單的說,可執行文件中要鏈接的動態庫在連接時已經寫死在可執行文件中了,這個路徑來自dylib內部。我使用otool -L 查看了dylib的路徑,是Debug/aaa.dylb,它等同於-o後面的參數。按照作者提供的方法,修改dylib的install_name即可,不過libtool命令行太複雜,加之蘋果又修改了其參數,以至於我想依葫蘆畫瓢在build完成後加個patch都不行。正要放棄之際, 發現gcc有一個install_name的選項。死馬當活馬醫,試試看。結果沒問題。

修改工程的Properties -> C/C++ Build -> Settings -> MacOS X C++ Linker->Miscellaneous, 加上-install_name @executable_path/aaa.dylib參數,這樣生成的動態庫拷貝到demo的目錄下就可以用了。


PS:

原文我沒仔細看,其實蘋果已經提供了一個修改可執行程序dylib的install_name的方法

install_name_tool -change /usr/local/lib/aaa.dylib @executable_path/aaa.dylib "$PRODUCT_NAME"


"$PRODUCT_NAME"就是需要修改的可執行程序路徑

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章