/proc/kallsyms 符号表说明

  • 内核符号可以通过/proc/kallsyms查看。
    在这里插入图片描述

  • 符号类型说明
    在Linux内核中,kallsyms是一个符号表,它包含了内核中所有的符号信息,包括函数、变量、常量等等。这些符号信息可以被内核模块和其他程序使用,这些符号类型可以帮助开发人员更好地理解内核中的符号信息,从而更好地进行内核开发和调试。

    在kallsyms中,每个符号都有一个类型,表示它的用途。下面是一些常见的符号类型:

    • T:表示该符号是一个函数,可以被其他代码调用。

    • t:表示该符号是一个局部函数,只能在当前文件中使用。

    • D:表示该符号是一个全局变量,可以被其他代码访问和修改。

    • d:表示该符号是一个局部变量,只能在当前文件中使用。

    • R:表示该符号是一个只读变量,不能被修改。

    • r:表示该符号是一个只读局部变量,只能在当前文件中使用。

    • A:表示该符号是一个可读写的变量,可以被其他代码访问和修改。

    • a:表示该符号是一个可读写的局部变量,只能在当前文件中使用。

    • B:表示该符号是一个未初始化的全局变量,它的值在程序启动时被初始化为0。

    • b:表示该符号是一个未初始化的局部变量,它的值在程序启动时被初始化为0。

    • G:表示该符号是一个全局变量,但是它的值在程序运行时可能会被修改。

    • C:表示该符号是一个常量,它的值在程序运行时不能被修改。

    • W:表示该符号是一个弱符号,

    • ?: 表示该符号的类型未知。

关于弱符号的一些说明:__weak 是一个关键字,用于声明一个弱符号。在 C 语言中,如果一个符号被定义了多次,编译器会报错。但是,如果一个符号被声明为弱符号,那么编译器就不会报错,而是在链接时选择其中一个符号作为最终的符号。这个特性在编写库时非常有用,因为它允许用户在链接时覆盖库中的函数。
如下为一个例子:

/**
 * probe_kernel_read(): safely attempt to read from a location
 * @dst: pointer to the buffer that shall take the data
 * @src: address to read from
 * @size: size of the data chunk
 *
 * Safely read from address @src to the buffer at @dst.  If a kernel fault
 * happens, handle that and return -EFAULT.
 *
 * We ensure that the copy_from_user is executed in atomic context so that
 * do_page_fault() doesn't attempt to take mmap_sem.  This makes
 * probe_kernel_read() suitable for use within regions where the caller
 * already holds mmap_sem, or other locks which nest inside mmap_sem.
 */

long __weak probe_kernel_read(void *dst, const void *src, size_t size)
    __attribute__((alias("__probe_kernel_read")));

long __probe_kernel_read(void *dst, const void *src, size_t size)
{
	long ret;
	mm_segment_t old_fs = get_fs();

	set_fs(KERNEL_DS);
	pagefault_disable();
	ret = __copy_from_user_inatomic(dst,
			(__force const void __user *)src, size);
	pagefault_enable();
	set_fs(old_fs);

	return ret ? -EFAULT : 0;
}
EXPORT_SYMBOL_GPL(probe_kernel_read);
  • 内核驱动获取符号地址

    /* 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);
    

    上述函数可以使用符号名找到在内核中的地址,使用该方法可以使用内核中未导出的内核函数。如下所示:

    /**
     * text_poke - Update instructions on a live kernel
     * @addr: address to modify
     * @opcode: source of the copy
     * @len: length to copy
     *
     * Only atomic text poke/set should be allowed when not doing early patching.
     * It means the size must be writable atomically and the address must be aligned
     * in a way that permits an atomic write. It also makes sure we fit on a single
     * page.
     */
    void *text_poke(void *addr, const void *opcode, size_t len)
    {
    	unsigned long flags;
    	char *vaddr;
    	struct page *pages[2];
    	int i;
    
    	/*
    	 * While boot memory allocator is runnig we cannot use struct
    	 * pages as they are not yet initialized.
    	 */
    	BUG_ON(!after_bootmem);
    
    	lockdep_assert_held(&text_mutex);
    
    	if (!core_kernel_text((unsigned long)addr)) {
    		pages[0] = vmalloc_to_page(addr);
    		pages[1] = vmalloc_to_page(addr + PAGE_SIZE);
    	} else {
    		pages[0] = virt_to_page(addr);
    		WARN_ON(!PageReserved(pages[0]));
    		pages[1] = virt_to_page(addr + PAGE_SIZE);
    	}
    	BUG_ON(!pages[0]);
    	local_irq_save(flags);
    	set_fixmap(FIX_TEXT_POKE0, page_to_phys(pages[0]));
    	if (pages[1])
    		set_fixmap(FIX_TEXT_POKE1, page_to_phys(pages[1]));
    	vaddr = (char *)fix_to_virt(FIX_TEXT_POKE0);
    	memcpy(&vaddr[(unsigned long)addr & ~PAGE_MASK], opcode, len);
    	clear_fixmap(FIX_TEXT_POKE0);
    	if (pages[1])
    		clear_fixmap(FIX_TEXT_POKE1);
    	local_flush_tlb();
    	sync_core();
    	/* Could also do a CLFLUSH here to speed up CPU recovery; but
    	   that causes hangs on some VIA CPUs. */
    	for (i = 0; i < len; i++)
    		BUG_ON(((char *)addr)[i] != ((char *)opcode)[i]);
    	local_irq_restore(flags);
    	return addr;
    }
    

    text_poke为非导出函数,在驱动中无法使用,可以使用kallsyms_lookup_name获取函数地址并赋值给函数指针,便可以使用该函数。

    void *(*text_poke_fn)(void *addr, const void *opcode, size_t len);
    text_poke_fn = (void *(*)(void *, const void *, size_t))kallsyms_lookup_nama("text_poke");
    
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值