前言
內(nèi)核模塊開發(fā)的基本規(guī)則之一是,模塊只能訪問已顯式導(dǎo)出的符號(函數(shù)和數(shù)據(jù)結(jié)構(gòu)),內(nèi)核使用EXPORT_SYMBOL宏和EXPORT_SYMBOL_GPL宏來導(dǎo)出符號。
關(guān)于EXPORT_SYMBOL宏和EXPORT_SYMBOL_GPL宏請參考:Linux EXPORT_SYMBOL宏詳解
即便如此,許多符號也受到限制,因此只有具有GPL兼容許可證的模塊才能訪問它們。然而,事實證明,有一種現(xiàn)成的解決方法,可以讓模塊輕松訪問它想要的任何符號,那就是使用內(nèi)核函數(shù)kallsyms_lookup_name來獲取沒有使用EXPORT_SYMBOL宏和EXPORT_SYMBOL_GPL宏來導(dǎo)出符號。
kallsyms_lookup_name它將返回與內(nèi)核符號表中任何符號相關(guān)的地址。
EXPORT_SYMBOL_GPL(kallsyms_lookup_name);
但使用kallsyms_lookup_name方法來獲取沒有導(dǎo)出內(nèi)核符號的地址在內(nèi)核版本5.7被刪除,即在內(nèi)核版本5.7即以上該函數(shù)沒有被導(dǎo)出。
一、API使用
kallsyms_lookup_name 是一個內(nèi)核函數(shù),用于通過符號名稱查找相應(yīng)的符號地址。它是內(nèi)核符號查找機(jī)制的一部分,允許內(nèi)核代碼和模塊在運(yùn)行時動態(tài)地查找和訪問內(nèi)核符號。
只要符號存在于/proc/kallsyms 文件中,都可以通過kallsyms_lookup_name獲取其符號的地址。
內(nèi)核版本 2.6.33 - 5.7.0 該符號都是導(dǎo)出的:
# cat /proc/kallsyms | grep '\<kallsyms_lookup_name\>'
ffffffffb8558e90 T kallsyms_lookup_name
// linux-4.19.90/kernel/kallsyms.c
/* Lookup the address for this symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name)
{
char namebuf[KSYM_NAME_LEN];
unsigned long i;
unsigned int off;
for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
if (strcmp(namebuf, name) == 0)
return kallsyms_sym_address(i);
}
return module_kallsyms_lookup_name(name);
}
EXPORT_SYMBOL_GPL(kallsyms_lookup_name);
使用EXPORT_SYMBOL_GPL宏導(dǎo)出kallsyms_lookup_name函數(shù)。
然而在 2.6.33 以下和 5.7.0 以上該函數(shù)沒有使用EXPORT_SYMBOL_GPL導(dǎo)出,比如5.19:
// linux-5.19/include/linux/kallsyms.h
/* Lookup the address for a symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name);
// linux-5.19/kernel/kallsyms.c
/* Lookup the address for this symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name)
{
char namebuf[KSYM_NAME_LEN];
unsigned long i;
unsigned int off;
/* Skip the search for empty string. */
if (!*name)
return 0;
for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
if (strcmp(namebuf, name) == 0)
return kallsyms_sym_address(i);
if (cleanup_symbol_name(namebuf) && strcmp(namebuf, name) == 0)
return kallsyms_sym_address(i);
}
return module_kallsyms_lookup_name(name);
}
對于在高版本內(nèi)核kallsyms_lookup_name不在導(dǎo)出的原因請參考:https://lwn.net/Articles/813350/
雖然沒有導(dǎo)出,但還是可以在/proc/kallsym 獲取其地址,如下:
# uname -r
5.19.0-46-generic
# cat /proc/kallsyms | grep "\<kallsyms_lookup_name\>"
ffffffffb9bbba60 T kallsyms_lookup_name
因此在 2.6.33 以下和 5.7.0 以上可以用 kprobe 來獲取該函數(shù)的地址:
#include <linux/version.h>
#include <linux/kallsyms.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 0) || LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 33)
#include <linux/kprobes.h>
static unsigned long (*kallsyms_lookup_name_sym)(const char *name);
static int _kallsyms_lookup_kprobe(struct kprobe *p, struct pt_regs *regs)
{
return 0;
}
unsigned long get_kallsyms_func(void)
{
struct kprobe probe;
int ret;
unsigned long addr;
memset(&probe, 0, sizeof(probe));
probe.pre_handler = _kallsyms_lookup_kprobe;
probe.symbol_name = "kallsyms_lookup_name";
ret = register_kprobe(&probe);
if (ret)
return 0;
addr = (unsigned long)probe.addr;
unregister_kprobe(&probe);
return addr;
}
unsigned long generic_kallsyms_lookup_name(const char *name)
{
/* singleton */
if (!kallsyms_lookup_name_sym) {
kallsyms_lookup_name_sym = (void *)get_kallsyms_func();
if(!kallsyms_lookup_name_sym)
return 0;
}
return kallsyms_lookup_name_sym(name);
}
#else
unsigned long generic_kallsyms_lookup_name(const char *name)
{
return kallsyms_lookup_name(name);
}
#endif
這樣generic_kallsyms_lookup_name在所有內(nèi)核版本都可以完成kallsyms_lookup_name的功能。
二、源碼解析
2.1 kallsyms_lookup_name
extern const unsigned long kallsyms_num_syms
__attribute__((weak, section(".rodata")));
/* Lookup the address for this symbol. Returns 0 if not found. */
unsigned long kallsyms_lookup_name(const char *name)
{
char namebuf[KSYM_NAME_LEN];
unsigned long i;
unsigned int off;
for (i = 0, off = 0; i < kallsyms_num_syms; i++) {
off = kallsyms_expand_symbol(off, namebuf, ARRAY_SIZE(namebuf));
if (strcmp(namebuf, name) == 0)
return kallsyms_sym_address(i);
}
return module_kallsyms_lookup_name(name);
}
EXPORT_SYMBOL_GPL(kallsyms_lookup_name);
首先定義了一個名為 namebuf 的字符數(shù)組,用于存儲擴(kuò)展后的符號名稱。KSYM_NAME_LEN 是一個常量,表示符號名稱的最大長度。
接下來,函數(shù)通過一個循環(huán)來遍歷符號表中的每個符號。kallsyms_num_syms 是一個表示符號表中符號數(shù)量的變量。
在循環(huán)中,使用 kallsyms_expand_symbol 函數(shù)對符號進(jìn)行解壓縮,將解壓后的符號名稱存儲在 namebuf 中。
然后,函數(shù)將解壓后的符號名稱與傳入的 name 進(jìn)行比較。如果找到匹配的符號名稱,函數(shù)返回相應(yīng)符號的地址,即通過 kallsyms_sym_address 函數(shù)獲取的地址。
如果循環(huán)結(jié)束后仍然沒有找到匹配的符號,函數(shù)調(diào)用 module_kallsyms_lookup_name 函數(shù),用于在內(nèi)核模塊中查找符號。
最后,通過 EXPORT_SYMBOL_GPL 宏,將 kallsyms_lookup_name 函數(shù)導(dǎo)出為內(nèi)核符號,以便其他模塊或代碼可以引用和使用該函數(shù)。
2.2 kallsyms_expand_symbol
(1)
extern const u8 kallsyms_names[] __weak;
extern const u8 kallsyms_token_table[] __weak;
extern const u16 kallsyms_token_index[] __weak;
/*
* Expand a compressed symbol data into the resulting uncompressed string,
* if uncompressed string is too long (>= maxlen), it will be truncated,
* given the offset to where the symbol is in the compressed stream.
*/
static unsigned int kallsyms_expand_symbol(unsigned int off,
char *result, size_t maxlen)
{
int len, skipped_first = 0;
const u8 *tptr, *data;
/* Get the compressed symbol length from the first symbol byte. */
data = &kallsyms_names[off];
len = *data;
data++;
/*
* Update the offset to return the offset for the next symbol on
* the compressed stream.
*/
off += len + 1;
/*
* For every byte on the compressed symbol data, copy the table
* entry for that byte.
*/
while (len) {
tptr = &kallsyms_token_table[kallsyms_token_index[*data]];
data++;
len--;
while (*tptr) {
if (skipped_first) {
if (maxlen <= 1)
goto tail;
*result = *tptr;
result++;
maxlen--;
} else
skipped_first = 1;
tptr++;
}
}
tail:
if (maxlen)
*result = '\0';
/* Return to offset to the next symbol. */
return off;
}
用于將壓縮的符號數(shù)據(jù)展開為未壓縮的字符串形式。
kallsyms_names數(shù)組保存每一個內(nèi)核符號名字被壓縮后的字節(jié)編碼。存儲的格式是 len + data,data是壓縮后的字符編碼。
函數(shù)的參數(shù)和功能如下:
off:指定符號在壓縮流中的偏移量。
result:存儲展開后的符號字符串的緩沖區(qū)。
maxlen:緩沖區(qū)的最大長度,用于避免字符串溢出。
函數(shù)首先從壓縮流中獲取符號的壓縮長度,并將其存儲在變量 len 中。然后,更新偏移量 off,使其指向下一個符號在壓縮流中的位置。
接下來,函數(shù)開始解壓縮符號數(shù)據(jù)。它逐字節(jié)遍歷壓縮的符號數(shù)據(jù),根據(jù)每個字節(jié)在符號表中的索引,從符號表中獲取相應(yīng)的字符,并將其復(fù)制到結(jié)果緩沖區(qū)中。
在解壓縮過程中,函數(shù)會跳過第一個字符,并將 skipped_first 設(shè)置為 1,這是為了處理一種特殊情況,即如果結(jié)果緩沖區(qū)的長度不足以容納完整的符號,則只復(fù)制符號的一部分。
當(dāng)解壓縮完成后,函數(shù)檢查剩余的緩沖區(qū)空間。如果緩沖區(qū)還有剩余空間,將在末尾添加空字符 ‘\0’,以確保結(jié)果字符串以 null 結(jié)尾。
最后,函數(shù)返回下一個符號在壓縮流中的偏移量,以便在下次解壓縮時使用。
(2)
extern const u8 kallsyms_token_table[] __weak;
extern const u16 kallsyms_token_index[] __weak;
kallsyms_token_table 和 kallsyms_token_index 數(shù)組在 Linux 內(nèi)核的符號查找機(jī)制中用于符號展開過程。
這些數(shù)組的作用是提供壓縮的符號數(shù)據(jù)與對應(yīng)的未壓縮字符之間的映射關(guān)系,以高效地將壓縮的符號展開為原始的字符串形式。
以下是對這兩個數(shù)組的簡要說明:
kallsyms_token_table:該數(shù)組包含了用作壓縮符號表示中的標(biāo)記(token)的字符表。每個標(biāo)記表示一系列在符號名稱中常見的字符序列。數(shù)組中的條目被組織成一種能夠高效查找標(biāo)記索引的方式。
kallsyms_token_index:該數(shù)組用作對 kallsyms_token_table 的索引。它將壓縮符號數(shù)據(jù)中的每個字節(jié)值映射到對應(yīng)的標(biāo)記表條目。在符號展開過程中,通過這個索引可以快速獲取壓縮數(shù)據(jù)中的字節(jié)對應(yīng)的字符序列。
在符號展開過程中,kallsyms_expand_symbol 函數(shù)逐字節(jié)遍歷壓縮的符號數(shù)據(jù)。對于每個字節(jié),它使用 kallsyms_token_index 數(shù)組獲取對應(yīng)的 kallsyms_token_table 中的索引。然后,從標(biāo)記表中獲取相應(yīng)的字符序列,并將其復(fù)制到結(jié)果緩沖區(qū)中。
通過使用標(biāo)記和索引,符號展開過程避免了在壓縮的符號數(shù)據(jù)中多次存儲重復(fù)的字符。相反,它使用較短的標(biāo)記表示常見字符序列,從而實現(xiàn)了對符號名稱的更高效壓縮和解壓縮。
kallsyms_token_table 和 kallsyms_token_index 數(shù)組在 Linux 內(nèi)核中的符號壓縮和展開機(jī)制中起著關(guān)鍵作用,通過優(yōu)化符號名稱的存儲和查找,提高了效率。
(3)
符號數(shù)據(jù)通常是通過編譯器和鏈接器生成的。這些符號數(shù)據(jù)包括函數(shù)名、變量名以及其他在編譯和鏈接過程中產(chǎn)生的符號,然后保存在內(nèi)核符號表(kernel symbol table)。
壓縮的符號數(shù)據(jù)來源通常是內(nèi)核符號表(kernel symbol table)。內(nèi)核符號表包含了各種內(nèi)核函數(shù)、變量和其他符號的信息,例如函數(shù)名、變量名以及其對應(yīng)的地址等。
內(nèi)核符號表中的符號信息通過特定的壓縮算法進(jìn)行壓縮,以減小其大小并節(jié)省存儲空間。
在編譯內(nèi)核時,可以選擇啟用內(nèi)核符號表的生成。通過特定的編譯選項,例如 CONFIG_KALLSYMS,可以將內(nèi)核符號信息保留在編譯后的內(nèi)核鏡像中。這樣,在運(yùn)行時,可以通過相應(yīng)的機(jī)制(例如 /proc/kallsyms 文件)讀取內(nèi)核符號表,并將符號信息保存在內(nèi)存中。
# cat /boot/config-4.19.90-23.8.v2101.ky10.x86_64 | grep CONFIG_KALLSYMS
CONFIG_KALLSYMS=y
壓縮的符號數(shù)據(jù)是通過對內(nèi)核符號表中的符號信息進(jìn)行壓縮算法處理而得到的。這樣做的目的是減小符號數(shù)據(jù)的大小,節(jié)省內(nèi)存空間和存儲空間。在壓縮過程中,符號的名稱和其他相關(guān)信息被編碼為壓縮格式,以便在需要時進(jìn)行解壓縮并恢復(fù)出原始的符號信息。
關(guān)于符號數(shù)據(jù)的壓縮,Linux內(nèi)核中使用了一種簡單的壓縮算法,將符號數(shù)據(jù)進(jìn)行壓縮以節(jié)省內(nèi)存空間。這種壓縮算法使用了基于標(biāo)記的方法,其中kallsyms_token_table和kallsyms_token_index數(shù)組用于解壓縮過程。
在內(nèi)核構(gòu)建過程中,符號數(shù)據(jù)首先被收集,并將其壓縮為一個壓縮流。壓縮的符號數(shù)據(jù)流中的每個字節(jié)都對應(yīng)于kallsyms_token_table中的一個標(biāo)記,或者表示一個特定的字符。通過解壓縮過程,這些壓縮的符號數(shù)據(jù)將被展開為原始的符號字符串。
kallsyms_expand_symbol 函數(shù)中的代碼片段處理的就是這樣的壓縮的符號數(shù)據(jù),通過解壓縮算法和相關(guān)的表格,將其還原為未壓縮的字符串形式,以便進(jìn)行進(jìn)一步的處理和使用。
備注:
具體的壓縮過程在內(nèi)核的構(gòu)建腳本中實現(xiàn),而不是在內(nèi)核源碼中:
// linux-4.19.90/scripts/kallsyms.c
/* replace a given token in all the valid symbols. Use the sampled symbols
* to update the counts */
static void compress_symbols(unsigned char *str, int idx)
{
unsigned int i, len, size;
unsigned char *p1, *p2;
for (i = 0; i < table_cnt; i++) {
len = table[i].len;
p1 = table[i].sym;
/* find the token on the symbol */
p2 = find_token(p1, len, str);
if (!p2) continue;
/* decrease the counts for this symbol's tokens */
forget_symbol(table[i].sym, len);
size = len;
do {
*p2 = idx;
p2++;
size -= (p2 - p1);
memmove(p2, p2 + 1, size);
p1 = p2;
len--;
if (size < 2) break;
/* find the token on the symbol */
p2 = find_token(p1, size, str);
} while (p2);
table[i].len = len;
/* increase the counts for this symbol's new tokens */
learn_symbol(table[i].sym, len);
}
}
隨著內(nèi)核的發(fā)展,可能會采用更為復(fù)雜的壓縮算法,以提高壓縮率和解壓縮效率。例如,使用字典壓縮算法,如LZ77或LZ78變種,通過建立符號字典并對重復(fù)的符號序列進(jìn)行替換來實現(xiàn)壓縮。這些算法通常會結(jié)合其他技術(shù),如霍夫曼編碼或算術(shù)編碼,進(jìn)一步提高壓縮效果。
2.3 kallsyms_sym_address
extern const int kallsyms_offsets[] __weak;
extern const unsigned long kallsyms_relative_base
__attribute__((weak, section(".rodata")));
static unsigned long kallsyms_sym_address(int idx)
{
if (!IS_ENABLED(CONFIG_KALLSYMS_BASE_RELATIVE))
return kallsyms_addresses[idx];
/* values are unsigned offsets if --absolute-percpu is not in effect */
if (!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU))
return kallsyms_relative_base + (u32)kallsyms_offsets[idx];
/* ...otherwise, positive offsets are absolute values */
if (kallsyms_offsets[idx] >= 0)
return kallsyms_offsets[idx];
/* ...and negative offsets are relative to kallsyms_relative_base - 1 */
return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
}
用于在內(nèi)核中獲取符號地址的函數(shù)kallsyms_sym_address的實現(xiàn)。根據(jù)給定的索引idx,該函數(shù)返回對應(yīng)符號的地址。
該函數(shù)的實現(xiàn)基于一些條件編譯選項,包括CONFIG_KALLSYMS_BASE_RELATIVE和CONFIG_KALLSYMS_ABSOLUTE_PERCPU。這些選項根據(jù)內(nèi)核的配置狀態(tài)來確定符號地址的計算方式。
目前我接觸的x86_64和arm64 都配置了CONFIG_KALLSYMS_BASE_RELATIVE編譯選項。在啟用CONFIG_KALLSYMS_BASE_RELATIVE選項時,符號地址以相對于基地址的偏移量形式存儲,而不是絕對地址。kallsyms_relative_base表示這個基地址:
kallsyms_relative_base變量作為基地址,用于計算相對符號地址。
kallsyms_offsets 是一個數(shù)組,記錄著每一個符號相對于kallsyms_relative_base變量的偏移量。
但是x86_64配置了CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項,arm64沒有配置CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項。
CONFIG_KALLSYMS_ABSOLUTE_PERCPU 選項與 kallsyms 特性中的 per-CPU 符號處理相關(guān)。Per-CPU 符號是針對系統(tǒng)中每個 CPU 單獨存在的特殊符號。這些符號通常用于存儲特定于每個 CPU 的數(shù)據(jù)或狀態(tài)。
當(dāng)啟用 CONFIG_KALLSYMS_ABSOLUTE_PERCPU 時,意味著在 kallsyms 機(jī)制中將 per-CPU 符號視為絕對值。正的 per-CPU 符號的偏移量被視為絕對地址,而負(fù)的偏移量被視為相對于特定基地址(kallsyms_relative_base - 1)的相對地址。
另一方面,當(dāng)未啟用 CONFIG_KALLSYMS_ABSOLUTE_PERCPU 時,per-CPU 符號被視為相對于基地址(kallsyms_relative_base)的無符號偏移量?;刂废鄬Φ姆椒梢愿o湊地存儲 per-CPU 符號在符號表中的表示。
2.3.1 x86_64
# cat /etc/os-release
NAME="Kylin Linux Advanced Server"
# uname -r
4.19.90-23.8.v2101.ky10.x86_64
# lscpu
架構(gòu): x86_64
# cat /boot/config-4.19.90-23.8.v2101.ky10.x86_64 | grep CONFIG_KALLSYMS_BASE_RELATIVE
CONFIG_KALLSYMS_BASE_RELATIVE=y
# cat /boot/config-4.19.90-23.8.v2101.ky10.x86_64 | grep CONFIG_KALLSYMS_ABSOLUTE_PERCPU
CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y
static unsigned long kallsyms_sym_address(int idx)
{
/* ...otherwise, positive offsets are absolute values */
if (kallsyms_offsets[idx] >= 0)
return kallsyms_offsets[idx];
/* ...and negative offsets are relative to kallsyms_relative_base - 1 */
return kallsyms_relative_base - 1 - kallsyms_offsets[idx];
}
CONFIG_KALLSYMS_ABSOLUTE_PERCPU啟用,表示偏移量包含正負(fù)值,函數(shù)根據(jù)正負(fù)值決定地址的計算方式。
如果偏移量kallsyms_offsets[idx]大于等于0,表示偏移量是絕對值,函數(shù)返回kallsyms_offsets[idx]作為地址。
正數(shù):表示符號的地址是絕對地址,可以直接作為符號的地址使用。
如果偏移量kallsyms_offsets[idx]小于0,表示偏移量是相對于kallsyms_relative_base - 1的負(fù)值,函數(shù)返回kallsyms_relative_base - 1 - kallsyms_offsets[idx]作為地址。
負(fù)數(shù):表示符號的地址是相對于 kallsyms_relative_base - 1 地址的偏移量。通過將偏移量從 kallsyms_relative_base - 1 中減去,可以計算出符號的實際地址。
CONFIG_KALLSYMS_ABSOLUTE_PERCPU用于控制符號地址在處理器特定數(shù)據(jù)區(qū)(per-CPU)時的解釋方式。
在內(nèi)核中,有一些符號(如變量或函數(shù))可以在每個處理器的特定數(shù)據(jù)區(qū)中有不同的副本。這是為了提高性能和并發(fā)性。這些符號在每個處理器的數(shù)據(jù)區(qū)中的地址可能是相對于某個基地址而不是絕對地址。
當(dāng)啟用了CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項時,表示偏移值中的正數(shù)偏移是絕對地址,而負(fù)數(shù)偏移是相對于基地址的偏移量。這意味著符號在每個處理器的數(shù)據(jù)區(qū)中具有不同的絕對地址。
當(dāng)啟用CONFIG_KALLSYMS_ABSOLUTE_PERCPU配置選項時,表示每個處理器特定數(shù)據(jù)區(qū)(per-CPU)的符號具有獨立的絕對地址。這意味著每個處理器的符號地址不是相對于共享基地址的,而是每個處理器都有自己獨特的絕對地址。
2.3.2 arm64
# cat /etc/os-release
NAME="Kylin Linux Advanced Server"
# uname -r
4.19.90-24.4.v2101.ky10.aarch64
# lscpu
架構(gòu): aarch64
# cat /boot/config-4.19.90-24.4.v2101.ky10.aarch64 | grep CONFIG_KALLSYMS_BASE_RELATIVE
CONFIG_KALLSYMS_BASE_RELATIVE=y
# cat /boot/config-4.19.90-24.4.v2101.ky10.aarch64 | grep CONFIG_KALLSYMS_ABSOLUTE_PERCPU
沒有CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項。
當(dāng)未啟用CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項時,每個處理器特定數(shù)據(jù)區(qū)的符號通常表示為相對于共享基地址的偏移量。通過偏移量與基地址相加,內(nèi)核可以計算出實際的每個處理器特定數(shù)據(jù)區(qū)中的符號地址。這種方法通過避免為每個處理器存儲單獨的絕對地址,節(jié)省了內(nèi)存。
static unsigned long kallsyms_sym_address(int idx)
{
/* values are unsigned offsets if --absolute-percpu is not in effect */
if (!IS_ENABLED(CONFIG_KALLSYMS_ABSOLUTE_PERCPU))
return kallsyms_relative_base + (u32)kallsyms_offsets[idx];
如果CONFIG_KALLSYMS_ABSOLUTE_PERCPU未啟用,表示偏移量是無符號的,函數(shù)返回kallsyms_relative_base + (u32)kallsyms_offsets[idx],其中kallsyms_relative_base是一個基地址,kallsyms_offsets是一個無符號偏移量數(shù)組。
函數(shù)通過將基地址(kallsyms_relative_base)與無符號偏移值(kallsyms_offsets[idx])相加來計算相對地址。
2.3.3 CONFIG_KALLSYMS_ABSOLUTE_PERCPU
在內(nèi)核中啟用CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項對性能的影響:
符號查找效率提高: 啟用CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項后,每個處理器特定數(shù)據(jù)區(qū)的符號都有自己的絕對地址,而不是相對于基地址的偏移量。這意味著符號查找可以更快速和直接地進(jìn)行,因為不再需要計算相對地址。這可能會提高符號查找的效率,特別是在需要頻繁訪問處理器特定數(shù)據(jù)區(qū)的情況下。
內(nèi)存占用增加: 啟用CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項會導(dǎo)致每個處理器特定數(shù)據(jù)區(qū)的符號都需要獨立的絕對地址。相對于使用相對地址來表示,這可能會增加內(nèi)存的占用量,因為需要為每個處理器存儲獨立的地址。如果系統(tǒng)中有大量的處理器或大量的處理器特定數(shù)據(jù)區(qū)符號,這種額外的內(nèi)存開銷可能是顯著的。
啟動時間延長: 內(nèi)核在啟動時需要解析符號表并建立符號地址的映射關(guān)系。啟用CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項后,需要額外的工作來處理每個處理器特定數(shù)據(jù)區(qū)的符號地址。這可能會導(dǎo)致啟動時間延長,特別是在有大量處理器或處理器特定數(shù)據(jù)區(qū)符號的系統(tǒng)中。
代碼復(fù)雜性增加: 啟用CONFIG_KALLSYMS_ABSOLUTE_PERCPU選項會引入對處理器特定數(shù)據(jù)區(qū)符號的維護(hù)和處理的復(fù)雜性。需要確保每個處理器的符號地址在正確的位置,并且符號查找和解釋邏輯需要處理符號地址的絕對性和相對性。這可能增加內(nèi)核代碼的復(fù)雜性和維護(hù)成本。
參考資料
Linux 4.19.90文章來源:http://www.zghlxwxcb.cn/news/detail-674000.html
https://lwn.net/Articles/813350/
https://blog.csdn.net/weixin_38878510/article/details/113264807文章來源地址http://www.zghlxwxcb.cn/news/detail-674000.html
到了這里,關(guān)于Linux 內(nèi)核函數(shù)kallsyms_lookup_name的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!