天天看點

RPATH與RUNPATH的差別RPATH與RUNPATH的差別

文章目錄

  • RPATH與RUNPATH的差別
    • 源檔案
    • 複現步驟
    • 問題原因
      • 排除共享庫本身問題
      • 分析庫查找過程
      • RPATH與RUNPATH的差別
    • 解決方案
      • LD_LIBRARY_PATH(不推薦)
      • --disable-new-dtags
    • 參考

本文從一個實際遇到的問題出發,分析

RPATH

RUNPATH

的差別,以及産生的原因。

RPATH與RUNPATH的差別

年前更新了作業系統後,同樣的代碼在新系統編譯後無法執行,提示找不到依賴庫,本文用來記錄一下是如何解決這個問題的。

源檔案

main.c

#include "a.h"

int main() {
	a();
	return 0;
}
           

libA.so

// a.c
#include "b.h"

void a() {
	b();
}
           
// a.h
void a();
           

ubuntu 16.04(gcc version 5.4.0)

$ gcc -fPIC -shared a.c -I. -L. -lB -o libA.so
           

libB.so

// b.c
#include <stdio.h>

void b() {
	printf("Hello, World\n");
}
           
// b.h
void b();
           

ubuntu 16.04(gcc version 5.4.0)

$ gcc -fPIC -shared -I. b.c -o libB.so
           
函數調用依賴關系:

main

->

a

->

b

複現步驟

  1. 分别在

    ubuntu 16.04(gcc version 5.4.0)

    ubuntu 20.04(gcc version 9.3.0)

    編譯可執行程式:
$ gcc -I. -o main main.c -Wl,--rpath,. -L. -lA -lB
           
  1. ubuntu 16.04(gcc version 5.4.0)

    運作成功:
$ ./main
Hello, World
           
  1. ubuntu 20.04(gcc version 9.3.0)

    運作失敗:
$ ./main 
./main: error while loading shared libraries: libB.so: cannot open shared object file: No such file or directory
           
錯誤提示為找不到可執行程式

./main

依賴的共享庫

libB.so

問題原因

排除共享庫本身問題

首先,由于

libA.so

libB.so

是在

ubuntu 16.04(gcc version 5.4.0)

上編譯的,是以重新在

ubuntu 20.04(gcc version 9.3.0)

上編譯

libA.so

libB.so

,并重複複現步驟,現象相同,說明不是

libA.so

libB.so

導緻的問題;

分析庫查找過程

使用

LD_DEBUG

,打開連結器的調試功能,分析共享庫的查找過程:

ubuntu 16.04(gcc version 5.4.0)

$ LD_DEBUG=libs ./main                                                        [16:37:15]
     22331:	find library=libA.so [0]; searching
     22331:	 search path=./tls/x86_64:./tls:./x86_64:.		(RPATH from file ./main)
......
     22331:	  trying file=./libA.so
......
     22331:	find library=libB.so [0]; searching
     22331:	 search path=./tls/x86_64:./tls:./x86_64:.		(RPATH from file ./main)
......
     22331:	  trying file=./libB.so
 ......
           

ubuntu 20.04(gcc version 9.3.0)

$ LD_DEBUG=libs ./main
     33218:	find library=libA.so [0]; searching
     33218:	 search path=./tls/haswell/x86_64:./tls/haswell:./tls/x86_64:./tls:./haswell/x86_64:./haswell:./x86_64:.		(RUNPATH from file ./main)
......
     33218:	  trying file=./libA.so
......
     33218:	find library=libB.so [0]; searching
     33218:	 search cache=/etc/ld.so.cache
     33218:	 search path=/lib/x86_64-linux-gnu/tls/haswell/x86_64:/lib/x86_64-linux-gnu/tls/haswell:/lib/x86_64-linux-gnu/tls/x86_64:/lib/x86_64-linux-gnu/tls:/lib/x86_64-linux-gnu/haswell/x86_64:/lib/x86_64-linux-gnu/haswell:/lib/x86_64-linux-gnu/x86_64:/lib/x86_64-linux-gnu:/usr/lib/x86_64-linux-gnu/tls/haswell/x86_64:/usr/lib/x86_64-linux-gnu/tls/haswell:/usr/lib/x86_64-linux-gnu/tls/x86_64:/usr/lib/x86_64-linux-gnu/tls:/usr/lib/x86_64-linux-gnu/haswell/x86_64:/usr/lib/x86_64-linux-gnu/haswell:/usr/lib/x86_64-linux-gnu/x86_64:/usr/lib/x86_64-linux-gnu:/lib/tls/haswell/x86_64:/lib/tls/haswell:/lib/tls/x86_64:/lib/tls:/lib/haswell/x86_64:/lib/haswell:/lib/x86_64:/lib:/usr/lib/tls/haswell/x86_64:/usr/lib/tls/haswell:/usr/lib/tls/x86_64:/usr/lib/tls:/usr/lib/haswell/x86_64:/usr/lib/haswell:/usr/lib/x86_64:/usr/lib		(system search path)
......
./main: error while loading shared libraries: libB.so: cannot open shared object file: No such file or directory

           

結果在

ubuntu 20.04(gcc version 9.3.0)

中, 可以正确查找到

libA.so

,但是無法正确查找到

libB.so

libB.so

的查找路徑是

system search path

,而非我們在編譯時設定的查找時路徑

./

,導緻可執行程式無法加載

libB.so

動态連結器對共享庫的查找順序:
  1. LD_LIBRARY_PATH

    -L

    -rpath

  2. /etc/ld.so.cache

  3. 預設共享庫目錄:

    /usr/lib

    /lib

RPATH與RUNPATH的差別

由上分析可以得出是共享庫運作時加載路徑非法導緻的問題,在排除設定

LD_LIBRARY_PATH

等環境變量的情況下,可以将焦點鎖定在使用的連結選項

-rpath

上,檢視源檔案依賴:

ubuntu 16.04(gcc version 5.4.0)

$ readelf -d main
Dynamic section at offset 0xe08 contains 26 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libA.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [.]
 
 $ readelf -d libA.so
Dynamic section at offset 0xe08 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libB.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 
 $ readelf -d libB.so
Dynamic section at offset 0xe18 contains 24 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
           

ubuntu 20.04(gcc version 9.3.0)

$ readelf -d main 
Dynamic section at offset 0x2da8 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libA.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000001d (RUNPATH)            Library runpath: [.]
 
 $ readelf -d libA.so 
Dynamic section at offset 0xe08 contains 25 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libB.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 
 $ readelf -d libB.so 
Dynamic section at offset 0xe18 contains 24 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
           

發現在

ubuntu 20.04(gcc version 9.3.0)

RPATH

變成了

RUNPATH

,說明連結器選項

-rpath

的行為發生了改變。

源檔案依賴關系:

main

->

libA.so

->

libB.so

綜上,問題的原因是

ubuntu 20.04(gcc version 9.3.0)

上,連結器選項

-rpath

的行為發生改變,預設配置為

RUNPATH

而不是

RPATH

;由于

RUNPATH

不适用于間接依賴的庫,是以導緻在

ubuntu 20.04(gcc version 9.3.0)

上隻能正确查找到

libA.so

,而無法正确查找到

libB.so

gcc version >= 7.5.0

時,

-rpath

預設行為即發生改變。

解決方案

LD_LIBRARY_PATH(不推薦)

LD_LIBRARY_PATH

是一個環境變量,作用是臨時改變連結器的加載路徑,可以存儲多個路徑,用冒号分隔:

$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./libs
           

不推薦的原因:

  1. 若全局設定

    LD_LIBRARY_PATH

    ,會影響其它應用程式的共享庫加載過程;
  2. 若隻在該應用程式啟動時局部設定

    LD_LIBRARY_PATH

    ,則每次啟動都需要設定,步驟過于繁瑣;

–disable-new-dtags

可以使用

-Wl,--disable-new-dtags

選項來使連結器保持舊行為,即在

ubuntu 20.04(gcc version 9.3.0)

使用如下指令編譯:

$ gcc -I. -o main main.c -Wl,--disable-new-dtags,--rpath,. -L. -lA -lB
           

重新運作并檢視依賴:

$ ./main 
Hello, World

$ readelf -d main
Dynamic section at offset 0x2da8 contains 29 entries:
  Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libA.so]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000f (RPATH)              Library rpath: [.]
           

可執行程式

main

可以正确運作,

RUNPATH

也變成了

RPATH

,連結器行為與

ubuntu 16.04(gcc version 5.4.0)

保持一緻了。

同理,也有

-Wl,--enable-new-dtags

選項來使連結器保持新行為

如下為官方解釋:

--enable-new-dtags
--disable-new-dtags
This linker can create the new dynamic tags in ELF. But the older ELF systems may not understand them. If you specify --enable-new-dtags, the new dynamic tags will be created as needed and older dynamic tags will be omitted. If you specify --disable-new-dtags, no new dynamic tags will be created. By default, the new dynamic tags are not created. Note that those options are only available for ELF systems.
           

參考

  • How to set RPATH and RUNPATH with GCC/LD?
  • use RPATH but not RUNPATH?

微信公衆号同步更新,微信搜尋"AnSwEr不是答案"或者掃描二維碼,即可訂閱。

RPATH與RUNPATH的差別RPATH與RUNPATH的差別
  • GitHub:AnSwErYWJ
  • Blog:http://www.answerywj.com
  • Email:[email protected]
  • Weibo:@AnSwEr不是答案

繼續閱讀