LD_LIBRARY_PATH
Linux 运行的时候,是如何管理共享库(*.so)的?在 Linux 下面,共享库的寻找和加载是由 /lib/ld.so 实现的。 ld.so 在标准路经(/lib, /usr/lib) 中寻找应用程序用到的共享库。
但是,如果需要用到的共享库在非标准路经,ld.so 怎么找到它呢?
目前,Linux 通用的做法是将非标准路经加入 /etc/ld.so.conf,然后运行 ldconfig 生成 /etc/ld.so.cache。 ld.so 加载共享库的时候,会从 ld.so.cache 查找。
传统上,Linux 的先辈 Unix 还有一个环境变量:LD_LIBRARY_PATH 来处理非标准路经的共享库。ld.so 加载共享库的时候,也会查找这个变量所设置的路经。
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./lib
export LD_LIBRARY_PATH
但是,有不少声音主张要避免使用 LD_LIBRARY_PATH 变量,尤其是作为全局变量。这些声音是:
* LD_LIBRARY_PATH is not the answer - http://prefetch.net/articles/linkers.badldlibrary.html
* Why LD_LIBRARY_PATH is bad - http://xahlee.org/UnixResource_dir/_/ldpath.html
* LD_LIBRARY_PATH - just say no - http://blogs.sun.com/rie/date/20040710
解决这一问题的另一方法是在编译的时候通过 -R<path> 选项指定 run-time path。
1. 往/lib和/usr/lib里面加东西,是不用修改/etc/ld.so.conf的,但是完了之后要调一下ldconfig,不然这个library 会找不到
2. 想往上面两个目录以外加东西的时候,一定要修改/etc/ld.so.conf,然后再调用ldconfig,不然也会找不到。
比如安装了一个mysql到/usr/local/mysql,mysql有一大堆library在/usr/local/mysql/lib下 面,这时就需要在/etc/ld.so.conf下面加一行/usr/local/mysql/lib,保存过后ldconfig一下,新的 library才能在程序运行时被找到。
3. 如果想在这两个目录以外放lib,但是又不想在/etc/ld.so.conf中加东西(或者是没有权限加东西)。那也可以,就是export一个全局变 量LD_LIBRARY_PATH,然后运行程序的时候就会去这个目录中找library。一般来讲这只是一种临时的解决方案,在没有权限或临时需要的时 候使用。
4. ldconfig做的这些东西都与运行程序时有关,跟编译时一点关系都没有。编译的时候还是该加-L就得加,不要混淆了。
5. 总之,就是不管做了什么关于library的变动后,最好都ldconfig一下,不然会出现一些意想不到的结果。不会花太多的时间,但是会省很多的事。
LD_LIBRARY_PATH 这个环境变量是大家最为熟悉的,它告诉loader:在哪些目录中可以找到共享库。可以设置多个搜索目录,这些目录之间用冒号分隔开。在linux下,还 提供了另外一种方式来完成同样的功能,你可以把这些目录加到/etc/ld.so.conf中,然后调用ldconfig。当然,这是系统范围内全局有效 的,而环境变量只对当前shell有效。按照惯例,除非你用上述方式指明,loader是不会在当前目录下去找共享库的,正如shell不会在当前目前找 可执行文件一样。
本人在/etc/environment中设置了环境变量:
JAVA_HOME=/opt/java ORACLE_HOME=/home/user2/ LD_LIBRARY_PATH=/usr/lib:/usr/local/lib
正常情况下执行没有问题,但使用sudo XXXXXXX的时候,其他变量都好使,除了LD_LIBRARY_PATH;
经探索,找到原因:
据说因为安全原因,Linux系统做了限制。 LD_LIBRARY_PATH not loading from .profile nor /etc/environment
The problem is simple: how to preserve PATH and LD_LIBRARY_PATH when running a program via sudo. But when I tried to find a workaround, I was totally confused by related stuff. So I keep a record as clear as possible to help myself.
1.
Topic: How to set environment variables
Source: man sudo, section OPTIONS
Details:
Environment variables to be set for the command may also be passed on the command line in the form of VAR=value,
e.g. LD_LIBRARY_PATH=/usr/local/pkg/lib. Variables passed on the command line are subject to the same restric‐
tions as normal environment variables with one important exception. If the setenv option is set in sudoers, the
command to be run has the SETENV tag set or the command matched is ALL, the user may set variables that would
overwise be forbidden. See sudoers(5) for more information.
2.
Topic: About LD_LIBRARY_PATH
Source: man sudo, section SECURITY NOTES
Details:
Note that the dynamic linker on most operating systems will remove variables that can control dynamic linking from
the environment of setuid executables, including sudo. Depending on the operating system this may include _RLD*,
DYLD_*, LD_*, LDR_*, LIBPATH, SHLIB_PATH, and others. These type of variables are removed from the environment
before sudo even begins execution and, as such, it is not possible for sudo to preserve them.
3.
Topic: env_reset
Source: man sudo, section SUDOERS OPTIONS
Details:
env_reset If set, sudo will reset the environment to only contain the LOGNAME, SHELL, USER, USERNAME and the
SUDO_* variables. Any variables in the caller’s environment that match the env_keep and env_check
lists are then added. The default contents of the env_keep and env_check lists are displayed when
sudo is run by root with the -V option. If sudo was compiled with the SECURE_PATH option, its
value will be used for the PATH environment variable. This flag is on by default.
4.
Topic: Handling environment variables against env_reset
Source: man sudo, section SECURITY NOTES
Details:
There are two distinct ways to deal with environment variables. By default, the env_reset sudoers option is
enabled. This causes commands to be executed with a minimal environment containing TERM, PATH, HOME, SHELL, LOG‐
NAME, USER and USERNAME in addition to variables from the invoking process permitted by the env_check and env_keep
sudoers options. There is effectively a whitelist for environment variables.
If, however, the env_reset option is disabled in sudoers, any variables not explicitly denied by the env_check and
env_delete options are inherited from the invoking process. In this case, env_check and env_delete behave like a
blacklist. Since it is not possible to blacklist all potentially dangerous environment variables, use of the
default env_reset behavior is encouraged.
My workaround is as follows:
cd /usr/local/bin; sudo ln -s <path_to_my_executable>
sudo LD_LIBRARY_PATH=<path_to_shared_libraries> my_executable
在 Linux 下,如果你写好了自己的动态链接库,需要在其它程序里调用,则需要让这些程序能找到这些动态链接库。如果设置不对,会出现类似如下的错误:
test: error while loading shared libraries: libexampleso.so.0: cannot open shared object file: No such file or directory
这是因为没有把动态链接库的安装路径(例如说是 /usr/local/lib )放到变量 LD_LIBRARY_PATH 里。
这时,可以用命令 export 来临时测试确认是不是这个问题:
export LD_LIBRARY_PATH=/usr/local/lib
在终端里运行上面这行命令,再运行这个可执行文件,如果运行正常就说明是这个问题。
接下来的问题是:以上做法,只是临时设置变量 LD_LIBRARY_PATH ,下次开机,一切设置将不复存在;如何把这个值持续写到 LD_LIBRARY_PATH 里呢?
我们可以在 ~/.bashrc 或者 ~/.bash_profile 中加入 export 语句,前者在每次登陆和每次打开 shell 都读取一次,后者只在登陆时读取一次。我的习惯是加到 ~/.bashrc 中,在该文件的未尾,可采用如下语句来使设置生效:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
修改完后,记得关掉当前终端并重新打开一个新的终端,从而使上面的配置生效。