Kernel. EXPORT_SYMBOL解析

本文详细解析了Linux内核中的EXPORT_SYMBOL宏定义及其作用。通过分析kernel_symbol结构体及EXPORT_SYMBOL(sym)、EXPORT_SYMBOL_GPL(sym)宏,揭示了如何在内核模块中导出函数符号,包括函数地址和名称的保存方式。

Kernel. EXPORT_SYMBOL解析
 
Code Segment:
 
include/module.h:
 
struct kernel_symbol
{
    unsigned long value;   
    const char *name;
};

 

/* For every exported symbol, place a struct in the __ksymtab section */
#define __EXPORT_SYMBOL(sym, sec)               /
    __CRC_SYMBOL(sym, sec)                  /
    static const char __kstrtab_##sym[]         /
    __attribute__((section("__ksymtab_strings")))       /
    = MODULE_SYMBOL_PREFIX #sym;                        /
    static const struct kernel_symbol __ksymtab_##sym   /
    __attribute_used__                  /
    __attribute__((section("__ksymtab" sec), unused))   /
    = { (unsigned long)&sym, __kstrtab_##sym }

#define EXPORT_SYMBOL(sym)                  /
    __EXPORT_SYMBOL(sym, "")

#define EXPORT_SYMBOL_GPL(sym)                  /
    __EXPORT_SYMBOL(sym, "_gpl")

#endif

 
Analysis:
 
1. kernel_symbol: 内核函数符号结构体
value: 记录使用EXPORT_SYMBOL(fun),函数fun的地址
name: 记录函数名称("fun"),在静态内存中
 
2. EXPORT_SYMBOL(sym) :导出函数符号,保存函数地址和名称
 
宏等价于:(去掉gcc的一些附加属性,MODULE_SYMBOL_PREFIX该宏一般是"")
 
static const char __kstrtab_sym[] = "sym";
static const struct kernel_symbol __ksymtab_sym =
    {(unsigned long)&sym, __kstrtab_sym }
 
 
3. gcc 附加属性

1>. __atrribute__ 指定变量或者函数属性。在此查看详细 http://gcc.gnu.org/onlinedocs/gcc-4.0.0/gcc/Variable-Attributes.html#Variable-Attributes
 
__attribute((section("section-name")) var : 编译器将变量var放在section-name所指定的data或者bss段里面。
 
很容易看出:EXPORT_SYMBOL(sym)将sym函数的名称__kstrtab_sym记录在,段名为"__kstrtab_strings"数据段中。 将sym所对应的kernel_symbol记录在名为__ksymtab段中。
EXPORT_SYMBOL_GPL(sym) 和EXPORT_SYMBOL不同之处在于sym对应的kenel_symbol记录在__ksymtab_gpl段中。

 

import os from struct import unpack_from from argparse import ArgumentParser import logging # 日志配置 logging.basicConfig(level=logging.INFO, format='%(message)s') def parse_module_symvers(file_path): """ 解析现有的 Module.symvers 文件。 """ symvers_data = {} if os.path.exists(file_path): with open(file_path, 'r') as f: for line in f: parts = line.strip().split('\t') if len(parts) == 4: crc, symbol, module, export_type = parts symvers_data[symbol] = { 'crc': crc, 'module': module, 'export_type': export_type } return symvers_data def extract_symbols_from_vmlinux(vmlinux_path, bit_size=None): """ 从 vmlinux 文件中提取符号及其 CRC 值。 """ from vmlinux_to_elf import kallsyms_finder symbols = {} try: with open(vmlinux_path, 'rb') as kernel_bin: raw_kernel = kernel_bin.read() kallsyms = kallsyms_finder.KallsymsFinder(kallsyms_finder.obtain_raw_kernel_from_file(raw_kernel), bit_size) # 提取 __ksymtab 符号 kcrctab_start = 0 for symbol in kallsyms.symbols: if symbol.name.startswith("__ksymtab_"): symbols[symbol.name.replace("__ksymtab_", "")] = {'crc': None} if symbol.name == "__start___kcrctab": kcrctab_start = symbol.virtual_address - kallsyms.relative_base_address # 解析 CRC 值 for sid, symbol_name in enumerate(symbols.keys()): crc, = unpack_from("<I", kallsyms.kernel_img, kcrctab_start + sid * 4) symbols[symbol_name]['crc'] = hex(crc)[2:] except Exception as e: logging.error(f"Error extracting symbols: {e}") return symbols def merge_symbols(existing_symbols, new_symbols): """ 合并现有符号与新提取的符号。 """ merged_symbols = existing_symbols.copy() for symbol, data in new_symbols.items(): if symbol in merged_symbols: merged_symbols[symbol]['crc'] = data['crc'] else: merged_symbols[symbol] = { 'crc': data['crc'], 'module': 'vmlinux', 'export_type': 'EXPORT_SYMBOL' } return merged_symbols def write_module_symvers(file_path, symbols): """ 将合并后的符号写入新的 Module.symvers 文件。 """ with open(file_path, 'w') as f: for symbol, data in symbols.items(): f.write(f"{data['crc']}\t{symbol}\t{data.get('module', 'unknown')}\t{data.get('export_type', 'EXPORT_SYMBOL')}\n") if __name__ == "__main__": parser = ArgumentParser(description="Update Module.symvers from a kernel file.") parser.add_argument('kernel_file', help="Path to the kernel file (vmlinux).") parser.add_argument('--bit-size', type=int, help="Force kernel bit size (32 or 64).") parser.add_argument('symvers_file', help="Path to the original Module.symvers file.") args = parser.parse_args() # 解析现有 Module.symvers 文件 existing_symbols = parse_module_symvers(args.symvers_file) # 从 vmlinux 提取符号信息 new_symbols = extract_symbols_from_vmlinux(args.kernel_file, args.bit_size) # 合并符号信息 merged_symbols = merge_symbols(existing_symbols, new_symbols) # 写入新的 Module.symvers 文件 output_file = args.symvers_file + ".new" write_module_symvers(output_file, merged_symbols) logging.info(f"Updated Module.symvers written to {output_file}.") 这个脚本
06-23
使用#!/usr/bin/env python3 # -*- encoding: Utf-8 -*- import logging from argparse import ArgumentParser from struct import unpack_from from vmlinux_to_elf import kallsyms_finder import sys def main(): logging.basicConfig(stream=sys.stdout, level=logging.INFO, format='%(message)s') parser = ArgumentParser(description="Find the kernel's Module.symvers from a raw or stripped ELF kernel file, and rebuild Module.symvers.") parser.add_argument('kernel_file', help="Path to the kernel file to extract symvers from") parser.add_argument('--bit-size', type=int, help='Force overriding the input kernel bit size, providing 32 or 64 bit (rather than auto-detect)') parser.add_argument('symvers_file', help="Path to the Module.symvers file") args = parser.parse_args() with open(args.kernel_file, 'rb') as kernel_bin: try: raw_kernel = kernel_bin.read() kallsyms = kallsyms_finder.KallsymsFinder(kallsyms_finder.obtain_raw_kernel_from_file(raw_kernel), args.bit_size) except kallsyms_finder.ArchitectureGuessError: logging.error('[!] The architecture of your kernel could not be guessed successfully. Please specify the --bit-size argument manually.') sys.exit(1) symbols = [] kcrctab_start = 0 for symbol in kallsyms.symbols: if symbol.name.startswith("__ksymtab_"): symbols.append(symbol) if symbol.name == "__start___kcrctab": kcrctab_start = symbol.virtual_address - kallsyms.relative_base_address if kcrctab_start == 0: logging.error('[!] Could not find __start___kcrctab symbol in the kernel file.') sys.exit(1) sym_dict = {} for sid, symbol in enumerate(symbols): crc, = unpack_from("<I", kallsyms.kernel_img, kcrctab_start + sid * 4) sym_name = symbol.name.replace("__ksymtab_", "") sym_dict[sym_name] = crc try: with open(args.symvers_file, "r") as symvers_in: with open(args.symvers_file + '.new', "w") as symvers_out: for line in symvers_in: tmp = line.strip().split('\t') if len(tmp) < 2: continue if tmp[1] in sym_dict: symvers_out.write(f"0x{sym_dict[tmp[1]]:08x}\t{tmp[1]}\t{tmp[2]}\t{tmp[3]}\n") else: logging.warning(f'''Error! Symbol {tmp[1]} not found in kernel file''') except FileNotFoundError: logging.error(f"[!] File {args.symvers_file} not found.") sys.exit(1) if __name__ == '__main__': main() 生成的文件文件丢失了一些符号
06-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值