成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Linux從源碼分析ldconfig命令對可執行文件緩存信息的讀取原理(緩存文件的讀)

系統 Linux
本文主要通過解讀Linux的ldconfig命令的關鍵代碼,分析了ldconfig命令是如何實現讀取緩存文件 /etc/ld.so.cache 的內容的。

今日問題:Linux的ldconfig -p命令可打印出系統緩存已記錄的所有動態庫的信息。那么這個功能是如何實現的?

本文主要通過解讀Linux的ldconfig命令的關鍵代碼,分析了ldconfig命令是如何實現讀取緩存文件 /etc/ld.so.cache 的內容的。本文涉及到的ldconfig的cache.c 代碼文件網址[1],在參考資料里。

ldconfig 使用的 /etc/ld.so.cache 文件,曾出現過兩個版本:

1.老版本的緩存文件格式 老版本指libc5 格式的動態庫,在glibc 2.0/2.1版本時采用的格式。緩存文件內容由cache_file類型的數據結構填充,其定義為

struct cache_file
{
  char magic[sizeof CACHEMAGIC - 1];
  unsigned int nlibs; /* 記錄的條數*/
  struct file_entry libs[0];
};

2.新版本的的緩存文件格式 新版本指glibc 2.2及之后版本的。緩存文件內容由cache_file_new數據結構填充。定義為:

struct cache_file_new
{
  char magic[sizeof CACHEMAGIC_NEW - 1];
  char version[sizeof CACHE_VERSION - 1];
  uint32_t nlibs;  /* 記錄的條數 */
  uint32_t len_strings;  /* Size of string table. */

  /* flags & cache_file_new_flags_endian_mask is one of the values
     cache_file_new_flags_endian_unset, cache_file_new_flags_endian_invalid,
     cache_file_new_flags_endian_little, cache_file_new_flags_endian_big.

     The remaining bits are unused and should be generated as zero and
     ignored by readers.  */
  uint8_t flags;

  uint8_t padding_unsed[3]; /* Not used, for future extensions.  */

  /* File offset of the extension directory.  See struct
     cache_extension below.  Must be a multiple of four.  */
  uint32_t extension_offset;

  uint32_t unused[3];  /* Leave space for future extensions
       and align to 8 byte boundary.  */
  struct file_entry_new libs[0]; /* Entries describing libraries.  */
  /* After this the string table of size len_strings is found. */
};

glibc-ld.so.cache1.1??? 以上輸出信息確實以glibc-ld.so.cache開始,所以我用的Ubuntu22.04系統的ldconfig的緩存文件內容是新格式的。

ldconfig代碼的cache.c 文件里是這樣根據magic的不同用if(){} else{}處理的:

if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1)) {///當屬于老版本時,按這里的方式處理 /* This can only be the new format without the old one. */ cache_new = (struct cache_file_new *) cache;

if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)

在glibc-2.35的代碼中已用英文說明了,glibc2.2格式的,能兼容glibc2.2之前的緩存文件內容。這里說的兼容,是依賴于代碼檢測實現的:由于兩種結構體都以magic作為第一個項目,來識別緩存文件類型。再根據magic值的不同,對后續數據段采用不同的處理方式。老magic的定義為#define CACHEMAGIC "ld.so-1.7.0",新magic的定義為#define CACHEMAGIC_NEW "glibc-ld.so.cache"。也就是老版本 cache_file 的文件頭部以字符串ld.so-1.7.0開始,新版本cache_file_new 的文件頭部以字符串glibc-ld.so.cache開始。這點我們可以用head -c 命令查看下/etc/ld.so.cache文件的頭部30個字符串舊可以驗證了:

# head -c 30  /etc/ld.so.cache
glibc-ld.so.cache1.1???

以上輸出信息確實以glibc-ld.so.cache開始,所以我用的Ubuntu22.04系統的ldconfig的緩存文件內容是新格式的。

ldconfig代碼的cache.c 文件里是這樣根據magic的不同用if(){} else{}處理的:

if (memcmp (cache->magic, CACHEMAGIC, sizeof CACHEMAGIC - 1))
    {///當屬于老版本時,按這里的方式處理
      /* This can only be the new format without the old one.  */
      cache_new = (struct cache_file_new *) cache;

      if (memcmp (cache_new->magic, CACHEMAGIC_NEW, sizeof CACHEMAGIC_NEW - 1)
   || memcmp (cache_new->version, CACHE_VERSION,
        sizeof CACHE_VERSION - 1))
 error (EXIT_FAILURE, 0, _("File is not a cache file.\n"));
      check_new_cache (cache_new);
      format = 1;
      /* This is where the strings start.  */
      cache_data = (const char *) cache_new;
    }
  else
    {//當屬于新版本緩存文件的時候,按下面內容處理
      ……省略
    }

在知道了 緩存文件類型(magic標記)后,就可以開始根據格式標準,逐條讀/寫每條記錄了,這是ldconfig的重頭戲。

先看對cache文件的讀取效果,以 ldconfig -p命令打印出緩存文件的所有記錄的結果為例:

# ldconfig -p
1525 libs found in cache `/etc/ld.so.cache
……
  libGLESv1_CM.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libGLESv1_CM.so
  libGL.so.1 (libc6,x86-64) => /lib/x86_64-linux-gnu/libGL.so.1
  libGL.so (libc6,x86-64) => /lib/x86_64-linux-gnu/libGL.so
……

這里每條都是一個動態庫的名稱、格式(libc6等格式)、CPU架構、所在路徑的記錄。

緩存文件中的這么一條記錄,對應的結構體,舊版本的為file_entry,新版本的為file_entry_new。它們的定義分別為:

struct file_entry
{
  int32_t flags;  /* This is 1 for an ELF library.  */
  uint32_t key, value;  /* String table indices.  */
};

以及新版本的 file_entry格式:

struct file_entry_new ///文件記錄的新格式,增加了OS版本、硬件信息
{
  union
  {
    /* Fields shared with struct file_entry.  */
    struct file_entry entry;
    /* Also expose these fields directly.  */
    struct
    {
      int32_t flags;  /* This is 1 for an ELF library.  */
      uint32_t key, value; /* String table indices.  */
    };
  };
  uint32_t osversion;  /* Required OS version.  */
  uint64_t hwcap;  /* Hwcap entry.  */
};

繼續分析【讀緩存文件】的簡要流程:

使用了 mmap() 函數,將 /etc/ld.so.cache 緩存文件整體讀入內存:

struct cache_file *cache
    = mmap (NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);

這是通過mmap()函數,將打開的緩存文件(open(/etc/ld.so.cache)的句柄fd)的數據映射到內存,由于文件數據就是按struct cache_file_new結構體格式填充的,所以mmap()后,就可以按這個結構體去解析各個條目。2. 判斷magic,新老magic分流處理。3. 如果是新的magic,則按struct cache_file_new數據結構解析。4. 對于新格式,遍歷讀取數據、打印:

……
else{
      struct cache_extension_all_loaded ext;
      if (……)錯誤處理;

      /* Print everything.  */
      for (unsigned int i = 0; i < cache_new->nlibs; i++)
 {
   const char *hwcaps_string
     = glibc_hwcaps_string (&ext, cache, cache_size,
       &cache_new->libs[i]);
    
   print_entry (cache_data + cache_new->libs[i].key,
         cache_new->libs[i].flags,
         cache_new->libs[i].osversion,
         cache_new->libs[i].hwcap, hwcaps_string,
         cache_data + cache_new->libs[i].value);
 }
      print_extensions (&ext);
}

這里關鍵內容是:

  • cache_data,代表了mmap()讀取到的緩存文件內容;以cache_data的地址為初始地址,按偏移量cache_new->libs[i].key 相加后,可得到每條file_entry_new的入口,然后分別打印出記錄內容,就實現了 ldconfig -p 的代碼功能。
  • 動態庫的條數,等于 cache_new->nlibs 這個變量的值。作為for循環遍歷時的條件。
  • cache_new->libs[i].key 這里的key,在struct file_entry_new中的定義是:
uint32_t key, value;  /* String table indices.  */

key相當于第i條動態庫記錄的目錄索引。通過索引可以查到value。在實現時,key和value都是數字,這個數字代表字符串相對于cache_data這個首地址的字節偏移量,例如key->value 即 cache_new->libs[i].key, cache_new->libs[i].value 43256 -> 43234

總之,通過對結構體的合理使用,將緩存文件內容解析后,可打印出緩存文件中記錄的所有已知動態庫文件的信息。

void print_cache (const char *cache_name) 的函數代碼結束之前,還做了一下內存回收工作:

  /* Cleanup.  */
  munmap (cache, cache_size);
  close (fd);

首先使用munmap()函數,將之前已映射內存數據做一下清除;然后關閉打開的cache緩存文件描述符。

本文主要通過解讀Linux的ldconfig命令的關鍵代碼,分析了ldconfig命令是如何實現讀取緩存文件 /etc/ld.so.cache 的內容的。本文涉及到的ldconfig的cache.c 代碼文件網址[1],在參考資料里。

參考資料

[1]ldconfig的cache.c 代碼文件網址: https://sourceware.org/git/?p=glibc.git;a=blob;f=elf/cache.c;h=8149f889bab9f9cb32a50e349991ba821e4db0dd;hb=HEAD

責任編輯:趙寧寧 來源: 深入理解Linux
相關推薦

2009-06-20 09:21:37

UNIXLINUX

2012-01-05 10:37:40

Java

2021-01-06 05:29:57

虛擬內存文件

2023-03-31 23:31:06

.go文本文件

2023-12-27 08:27:03

Linuxldconfig命令

2015-02-02 11:03:12

2010-02-22 18:04:27

CentOS mpla

2011-08-09 10:24:19

可執行文件病毒病毒

2021-01-08 08:06:19

腳本Shell文件

2022-05-11 14:50:34

Python解包執行文件

2021-01-12 10:10:41

shell腳本Linux命令

2017-02-07 10:22:53

2024-08-12 16:42:50

二進制工具系統

2024-05-06 00:00:00

Go文件瘦身代碼

2009-04-16 10:37:17

Javaexejar

2024-05-21 12:01:39

.NET 6開發

2021-01-14 22:17:09

PythonLinux工具

2022-05-20 08:55:02

py文件exepython

2023-12-18 09:21:22

開發靜態編譯Linux

2023-09-04 07:14:36

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: av大全在线观看 | 国产精品免费一区二区 | 中文字幕在线一 | 牛牛热在线视频 | 精品国产伦一区二区三区观看体验 | 天天干天天操天天射 | 黄色一级在线播放 | 色视频网站在线观看 | 精品国产乱码久久久久久中文 | 国产女人精品视频 | 国产网站在线播放 | 欧美性久久 | 久草99| 日韩在线视频观看 | 拍戏被cao翻了h承欢 | 国产精品性做久久久久久 | 亚洲成人福利在线观看 | 欧美激情在线播放 | 中文字幕一区二区三区精彩视频 | 亚洲毛片在线观看 | 黄色大片免费网站 | 久久免费视频1 | 午夜免费网站 | 精品一区av | 中国黄色在线视频 | 天天澡天天狠天天天做 | 成人精品影院 | 色综网 | 欧美日韩精品中文字幕 | 日日干天天操 | 国产亚洲人成a在线v网站 | 午夜视频在线免费观看 | 久久久久亚洲av毛片大全 | 午夜精品久久久久99蜜 | 精品一区在线免费观看 | 色婷婷精品久久二区二区蜜臂av | 91在线导航 | 欧美一区二区在线播放 | 日韩中文字幕视频 | 中文精品视频 | 91精品久久久久久久久中文字幕 |