Linux 内核获取函数size

方式一:通过objdump -t直接从目标文件中获取函数size

bash 复制代码
#objdump -t file_unread.o | grep hook
0000000000000030 l     F .text  000000000000012f hook_vfs_read

0000000000000030 l F .text 000000000000012f hook_vfs_read各个字段说明

  • 0000000000000030:符号的地址或值,这里是一个相对地址,表示该符号在.text节中的偏移量。
  • l:符号的绑定属性,这里是小写字母l,表示该符号是一个局部符号,只在当前目标文件中可见。
  • F:符号所在的节(section)的类型,这里是大写字母F,表示该符号所在的节是一个函数节(function section)。
  • .text:符号所在的节的名称,这里是.text,表示该符号所在的节是代码节。
  • 000000000000012f:符号的大小,以字节为单位,这里是0x12f,表示该函数的大小为303字节。
  • hook_vfs_read:符号的名称,这里是hook_vfs_read,表示该符号是一个名为hook_vfs_read的函数。

vfs_read+0xe6/0x2c0 表示什么意思呢?

其中vfs_read 是函数名,+0xe6/0x2c0 是函数在代码中的偏移量。具体来说,+0xe6 表示函数内部代码的偏移量,/ 后面的 0x2c0 表示函数的总大小(以字节为单位)。

hook_vfs_read+0xe4/0x130 [fi_file]为动态打印进程调用栈信息。

方式二:通过objdump -D反汇编后计算函数size

objdump对目标文件进行反汇编,从汇编代码中计算hook_vfs_read的函数size

bash 复制代码
0000000000000030 <hook_vfs_read>:
  30:   e8 00 00 00 00          callq  35 <hook_vfs_read+0x5>
  35:   41 57                   push   %r15
  37:   41 56                   push   %r14
  39:   31 c0                   xor    %eax,%eax
  ......
  14f:   eb c3                   jmp    114 <hook_vfs_read+0xe4>
  151:   48 c7 c7 00 00 00 00    mov    $0x0,%rdi
  158:   e8 00 00 00 00          callq  15d <hook_vfs_read+0x12d>
  15d:   eb a3                   jmp    102 <hook_vfs_read+0xd2>
  15f:   90                      nop

hook_vfs_read_size = 0x15f - 0x30 = 0x12f + 1,从计算的结果来看,加上偏移与调用栈打印的函数size是一致的。

bash 复制代码
-t, --syms               Display the contents of the symbol table(s)
-D, --disassemble-all    Display assembler contents of all sections

方式三:通过内核函数动态获取函数size

c 复制代码
/**
 * sprint_symbol - Look up a kernel symbol and return it in a text buffer
 * @buffer: buffer to be stored
 * @address: address to lookup
 *
 * This function looks up a kernel symbol with @address and stores its name,
 * offset, size and module name to @buffer if possible. If no symbol was found,
 * just saves its @address as is.
 *
 * This function returns the number of bytes stored in @buffer.
 */
int sprint_symbol(char *buffer, unsigned long address)
{
        return __sprint_symbol(buffer, address, 0, 1);
}
EXPORT_SYMBOL_GPL(sprint_symbol);

路径1:

获取到的字符串信息示例为hook_vfs_read+0x0/0x130 [fi_file],这种方式需要从数组str中解析出函数大小0x130

解析字符串中的函数size,还是要骚操作获取,在内核中没有看到直接获取函数size的函数。

c 复制代码
#include <linux/string.h>

static int __init my_init(void)
{
    const char *str = "hook_vfs_read+0x0/0x130 [fi_file]";
    const char *prefix = "/0x";
    const char *suffix = " [";
    char *start, *end;
    unsigned long value;

    // 查找前缀字符串
    start = strstr(str, prefix);
    if (!start) {
        printk(KERN_ERR "Failed to find prefix string\n");
        return -EINVAL;
    }
    start += strlen(prefix);

    // 查找后缀字符串
    end = strstr(start, suffix);
    if (!end) {
        printk(KERN_ERR "Failed to find suffix string\n");
        return -EINVAL;
    }

    // 截取字符串
    *end = '\0';
    value = simple_strtoul(start, NULL, 16);

    printk(KERN_INFO "Value: 0x%lx\n", value);

    return 0;
}

路径二:

内核代码v4.18.20sprint_symbol最终调用kallsyms_lookup获取函数size,因此也可以直接调用该函数获取函数size,免去上述复杂的字符串解析过程。

c 复制代码
/*
 * Lookup an address
 * - modname is set to NULL if it's in the kernel.
 * - We guarantee that the returned name is valid until we reschedule even if.
 *   It resides in a module.
 * - We also guarantee that modname will be valid until rescheduled.
 */
const char *kallsyms_lookup(unsigned long addr,
			    unsigned long *symbolsize,
			    unsigned long *offset,
			    char **modname, char *namebuf)
{
	const char *ret;

	namebuf[KSYM_NAME_LEN - 1] = 0;
	namebuf[0] = 0;

	if (is_ksym_addr(addr)) {
		unsigned long pos;

		pos = get_symbol_pos(addr, symbolsize, offset);
		/* Grab name */
		kallsyms_expand_symbol(get_symbol_offset(pos),
				       namebuf, KSYM_NAME_LEN);
		if (modname)
			*modname = NULL;
		return namebuf;
	}

	/* See if it's in a module or a BPF JITed image. */
	ret = module_address_lookup(addr, symbolsize, offset,
				    modname, namebuf);
	if (!ret)
		ret = bpf_address_lookup(addr, symbolsize,
					 offset, modname, namebuf);

	if (!ret)
		ret = ftrace_mod_address_lookup(addr, symbolsize,
						offset, modname, namebuf);
	return ret;
}
相关推荐
yuxb7314 分钟前
Linux 文本处理与 Shell 编程笔记:正则表达式、sed、awk 与变量脚本
linux·笔记·正则表达式
刘一说25 分钟前
CentOS 系统 Java 开发测试环境搭建手册
java·linux·运维·服务器·centos
wdxylb6 小时前
云原生俱乐部-shell知识点归纳(1)
linux·云原生
飞雪20077 小时前
Alibaba Cloud Linux 3 在 Apple M 芯片 Mac 的 VMware Fusion 上部署的完整密码重置教程(二)
linux·macos·阿里云·vmware·虚拟机·aliyun·alibaba cloud
路溪非溪7 小时前
关于Linux内核中头文件问题相关总结
linux
Lovyk10 小时前
Linux 正则表达式
linux·运维
Fireworkitte11 小时前
Ubuntu、CentOS、AlmaLinux 9.5的 rc.local实现 开机启动
linux·ubuntu·centos
sword devil90011 小时前
ubuntu常见问题汇总
linux·ubuntu
ac.char11 小时前
在CentOS系统中查询已删除但仍占用磁盘空间的文件
linux·运维·centos
淮北也生橘1213 小时前
Linux的ALSA音频框架学习笔记
linux·笔记·学习