当前位置: 首页 > news >正文

Linux mod_sysfs_setup模块sysfs符号表暴露

Linux mod_sysfs_setup模块sysfs符号表暴露

mod_sysfs_setup()是Linux内核模块加载过程中负责在sysfs中创建模块相关目录和文件的关键函数。它在load_module()完成ELF重定位和符号解析之后被调用,向用户空间暴露模块的元数据、参数信息和导出的符号表。

mod_sysfs_setup()位于kernel/module/sysfs.c,其调用链为load_module() -> mod_sysfs_setup() -> add_module_usage()等辅助函数:

static int mod_sysfs_setup(struct module *mod,
const struct load_info *info,
struct kernel_param *kparam,
unsigned int num_params)
{
int err;

/* 第一步:创建模块在/sys/module/下的kobject */
mod->mkobj.kobj.kset = module_kset;
err = kobject_init_and_add(&mod->mkobj.kobj, &module_ktype,
NULL, "%s", mod->name);
if (err)
goto out;

/* 第二步:创建version info文件 */
err = mod_sysfs_setup_vermagic(mod, info);
if (err)
goto out_unreg;

/* 第三步:创建模块参数sysfs条目 */
err = module_param_sysfs_setup(mod, kparam, num_params);
if (err)
goto out_unreg;

/* 第四步:创建模块符号表的sysfs文件 */
err = module_sysfs_setup_holders(mod, info);
if (err)
goto out_unreg;

/* 第五步:创建notes section文件 */
err = module_notes_init(mod, info);
if (err)
goto out_unreg;

/* 第六步:创建模块依赖关系符号链接 */
add_module_usage(mod, info);

/* 第七步:暴露导出的内核符号到/sys/module//sections/ */
add_sect_attrs(mod, info);
add_notes_attrs(mod, info);

return 0;

out_unreg:
kobject_put(&mod->mkobj.kobj);
mod->mkobj.kobj.parent = NULL;
out:
return err;
}

module_kset是模块subsystem的全局kset,其父kobject为module_kobj(即/sys/module)。所有模块的kobject都在此之下形成/sys/module/目录。

符号表暴露的核心在add_sect_attrs()函数中,该函数遍历ELF节区(section),对其中包含符号的节创建sysfs属性文件:

static void add_sect_attrs(struct module *mod, const struct load_info *info)
{
struct module_sect_attrs *sattrs;
struct module_sect_attr *sattr;
unsigned int nsect;
int i;

/* 计算需要暴露的节区数量 */
nsect = 0;
for (i = 0; i < info->hdr->e_shnum; i++)
if (info->secstrings[info->sechdrs[i].sh_name][0] != '.')
nsect++;

sattrs = kzalloc(struct_size(sattrs, attrs, nsect), GFP_KERNEL);
sattr = &sattrs->attrs[0];

/* 为每个节区创建属性 */
for (i = 0; i < info->hdr->e_shnum; i++) {
if (info->secstrings[info->sechdrs[i].sh_name][0] == '.')
continue;

sattr->name = info->secstrings + info->sechdrs[i].sh_name;
sattr->address = info->sechdrs[i].sh_addr;
sysfs_attr_init(&sattr->attr);
sattr->attr.name = sattr->name;
sattr->attr.mode = 0400;
sattr->attr.show = module_sect_show;
sattr++;
}

/* 创建sysfs目录/sys/module//sections/ */
sattrs->kobj.kset = &mod->mkobj.kobj;
err = kobject_init_and_add(&sattrs->kobj, &module_sect_ktype, &mod->mkobj.kobj, "sections");

/* 为每个节区创建属性文件 */
for (i = 0; i < nsect; i++)
sysfs_create_file(&sattrs->kobj, &sattrs->attrs[i].attr);
}

module_sect_show()是属性文件的read回调,它返回对应节区的基地址:

static ssize_t module_sect_show(struct module_attribute *mattr,
struct module_kobject *mk, char *buf)
{
struct module_sect_attr *sattr = container_of(mattr, struct module_sect_attr, mattr);
return sprintf(buf, "0x%px\n", (void *)sattr->address);
}

通过/sys/module//sections/.text,用户空间可以读取模块代码段的加载地址,这对于调试和崩溃分析至关重要。

module_param_sysfs_setup()创建/sys/module//parameters/目录,并在其中为每个module_param()定义的参数创建属性文件。属性的读写回调指向param_ops中对应的get/set函数,使得用户空间可以通过写sysfs文件在运行时修改模块参数:

static int module_param_sysfs_setup(struct module *mod,
struct kernel_param *kparam,
unsigned int num_params)
{
int i;

for (i = 0; i < num_params; i++) {
struct param_attribute *param_attr = kzalloc(sizeof(*param_attr), GFP_KERNEL);

sysfs_attr_init(¶m_attr->mattr.attr);
param_attr->mattr.attr.name = kparam[i].name;
param_attr->mattr.attr.mode = kparam[i].perm;
param_attr->mattr.show = param_attr_show;
param_attr->mattr.store = param_attr_store;
param_attr->mattr.attr.owner = mod;
param_attr->param = &kparam[i];

sysfs_create_file(&mod->mkobj.kobj, ¶m_attr->mattr.attr);
}
return 0;
}

add_module_usage()分析模块的符号依赖关系,在/sys/module//holders/目录下创建指向被依赖模块的符号链接。当模块B使用了模块A导出的符号时,/sys/module/A/holders/B符号链接出现,反映了模块间的依赖关系。

mod_sysfs_teardown()是逆操作,在模块卸载时被调用,依次移除所有sysfs节点并释放相关内存。如果某个模块的sysfs节点在用户空间仍有打开的文件描述符,kobject的引用计数机制会延迟实际的内存释放直到所有引用关闭。

通过mod_sysfs_setup()暴露的sysfs接口为系统管理员和调试工具提供了无需专用工具的模块信息查看能力。用户可以通过cat /sys/module//version、cat /sys/module//sections/.text等命令获取模块的加载信息。

http://www.rkmt.cn/news/1529181.html

相关文章:

  • 终极暗黑破坏神2现代化指南:D2DX如何让经典游戏在4K显示器上完美运行
  • 机器学习五大实战领域:新手从业务问题出发的进阶地图
  • 智能驱鸟器技术:驱鸟器分类、场景应用与选型策略深度解析
  • 怎样5分钟搞定你的第一个AI应用:Streamsync框架完整实战指南
  • NXP Quad Timer高级应用:单次触发、级联计数与PWM模式深度解析
  • 从靶场到实战:手把手教你用Python Flask复现SSTI漏洞(附完整Payload库)
  • 2026年6月济南黄金回收门店大盘点 正规靠谱不踩雷 - 开心测评
  • 以色列驾照翻译怎么办理?2026最新办理指南 - 资讯纵览
  • MuleSoft企业级AI编排:让大模型真正懂ERP、CRM和合规规则
  • 告别公式乱码!DeepSeek公式导出Word三步搞定 插件版零配置
  • NXP WCT1011B DAC配置实战:从5位VREF到12位通用DAC详解
  • 如何快速掌握ggplot2:R数据可视化终极教程
  • 2026 成都持证黄金回收门店汇总,仪器鉴定当场结算安心变现 - 奢侈品回收评测
  • 为什么你的小程序图片裁剪功能需要we-cropper这个终极方案?
  • 【本地 AI 智能体】 OpenClaw 零基础 Windows 安装配置全流程(包含安装包)
  • 重庆二手钻石回收口碑榜,正规实体店权威排序 - 讯息早知道
  • 如何快速掌握Upkie:开源双足轮式机器人的完整实践指南
  • 5步搭建智能微信群消息转发系统:告别重复劳动,实现信息同步自动化
  • 2026推荐:肇庆井水检测单位,农村饮用水井水检测中心 - 公共场所卫生检测
  • 2026靠谱的四川定制旅游服务公司选择攻略 8步走 - 资讯纵览
  • MPC866内存同步与异常处理:嵌入式系统稳定性的核心机制
  • Beyond All Reason:开源RTS游戏的终极魅力与完整入门指南
  • 5分钟掌握UV Squares:Blender UV编辑的智能网格转换革命
  • (二十三)信捷PLC Modbus通讯功能介绍
  • D2DX终极指南:三步让暗黑破坏神2在现代电脑上焕发新生
  • 3个常见性能陷阱与突破方案:打造流畅的微信小程序数据可视化
  • Linux——MySQL
  • 数据预处理实战:从缺失值到漂移监控的七道生死关
  • URL 编码解码工具怎么选?2026 年前端开发与接口调试方案对比
  • 从Putty报错‘Software caused connection abort’深挖:你的云服务器SSH配置可能埋了这些坑