在设置动态链接的方法中,rpath 有其自身的问题1ld.so.conf 为 OS 全局配置,可能会因为单一服务的动态库版本而影响到其他服务,不是一个优雅的方法。

那如何才能不借助 rpathld.so.conf ,即能使目标服务找到对应的动态链接库,又能不影响其他服务呢?

LD_LIBRARY_PATH

对于动态链接库的路径配置而言,除了 rpathld.so.conf ,还有 LD_LIBRARY_PATH。只要在二进制启动的环境中设置 ``LD_LIBRARY_PATH变量,则glibc` 会将该变量中的路径配置作为动态链接库的查找路径之一,使得二进制可执行文件能够正常链接到其依赖的动态链接库。

LD_LIBRARY_PATH 会在当前 shell session 中“全局”生效,不过,由于笔者服务采用 systemd 来管理其生命周期,按照上述思路,应该只需要在服务启动前,将 LD_LIBRARY_PATH 注入启动环境即可,这样还能够借助 systemd 来实现一定程度的环境“隔离”。systemd 服务配置中的 Environment 字段是用于描述启动环境的环境变量的,我们先在这里加入 LD_LIBRARY_PATH (如下),重新加载 systemd 配置并重启服务,观察效果。

 [Unit]
 Description=Nginx
 After=syslog.target network.target remote-fs.target nss-lookup.target

 [Service]
 User=triplez
 Group=triplez
 Type=forking
+Environment="LD_LIBRARY_PATH=/your/path/to/openssl/lib:/your/path/to/jemalloc/lib:/your/path/to/luajit/lib"
 PIDFile=/run/nginx/nginx.pid
 RuntimeDirectory=nginx
 RuntimeDirectoryMode=0755
 ExecStartPre=/your/path/to/nginx -t
 ExecStart=/your/path/to/nginx
 ExecReload=/bin/kill -s HUP $MAINPID
 ExecStop=/bin/kill -s QUIT $MAINPID

 PrivateTmp=true
 Restart=always

 [Install]
 WantedBy=multi-user.target
$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx.service

事实上,对于大多数服务,只需要加入环境变量即可解决问题。但很不幸的是,由于我们的服务需要监听 1024 及以下端口(如 80、443),笔者对二进制可执行文件进行了 setcap 操作,赋予其监听低位端口的能力。

$ sudo getcap /your/path/to/nginx
/your/path/to/nginx = cap_net_bind_service+ep

Capability

由于该文件含有 capability,glibc 会将 LD_LIBRARY_PATH 忽略2 3,导致服务还是无法正确链接到动态库上。

了解原因之后,接下来要做的事情也很清晰了:一是先将二进制可执行文件上的 capability 移除(chown 就可以将其移除, setcap -ep 亦可),二是利用 systemd 的服务配置来实现配置 capability (如下)。

 [Unit]
 Description=Nginx
 After=syslog.target network.target remote-fs.target nss-lookup.target

 [Service]
 User=triplez
 Group=triplez
 Type=forking
 Environment="LD_LIBRARY_PATH=/your/path/to/openssl/lib:/your/path/to/jemalloc/lib:/your/path/to/luajit/lib"
 PIDFile=/run/nginx/nginx.pid
 RuntimeDirectory=nginx
 RuntimeDirectoryMode=0755
 ExecStartPre=/your/path/to/nginx -t
 ExecStart=/your/path/to/nginx
 ExecReload=/bin/kill -s HUP $MAINPID
 ExecStop=/bin/kill -s QUIT $MAINPID

+CapabilityBoundingSet=CAP_NET_BIND_SERVICE
+AmbientCapabilities=CAP_NET_BIND_SERVICE
 PrivateTmp=true
 Restart=always

 [Install]
 WantedBy=multi-user.target

再次重新加载 systemd 配置并重启服务,问题完美解决。


  1. RpathIssue
    https://wiki.debian.org/RpathIssue ↩︎

  2. Use of file capabilities disables LD_LIBRARY_PATH - Red Hat Bugzilla
    https://bugzilla.redhat.com/show_bug.cgi?id=448594 ↩︎

  3. shared libraries - Linux capabilities (setcap) seems to disable LD_LIBRARY_PATH - Stack Overflow
    https://stackoverflow.com/questions/9843178/linux-capabilities-setcap-seems-to-disable-ld-library-path ↩︎


知识共享许可协议
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。