当前位置: 首页 > 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.jsqmd.com/news/1017357/

相关文章:

  • 瑞芯微RV1126B开发板(EASY-EAI-PI2) 音频输入
  • 2026年山西企业如何破解获客难题:手机号精准定向、短视频运营与AI搜索优化的完整实战方案 - 优质企业观察收录
  • 深入解析e300核心缓存架构:从寄存器控制到指令级优化
  • 仁泽区跑断腿总结:卖黄金遇到这三类店,赶紧走人 - 行行星
  • 中立测评 2026 番禺代账 TOP5,南村电商产业园服务商实地盘点 - 资讯综合站
  • 机器学习五大实战领域:新手从业务问题出发的进阶地图
  • Maccy:macOS剪贴板管理终极解决方案
  • 把 Claude Code 变成你的架构顾问:如何用“隐式重构模式”自动消除代码坏味道
  • 智能驱鸟器技术:驱鸟器分类、场景应用与选型策略深度解析
  • Redis - 主从同步与故障切换的常见坑
  • 怎样5分钟搞定你的第一个AI应用:Streamsync框架完整实战指南
  • 终极UEFI固件解析指南:5步掌握UEFITool 0.28完整使用教程
  • 视线估计数据集预处理避坑指南:MPIIFaceGaze、EyeDiap、Gaze360和ETH-Gaze的常见错误与解决
  • 产业园创业干货|2026 广州白云小微企业财税风控,代账筛选要点 - 资讯综合站
  • 深度解析:亨得利原厂配件保修全攻略——2026年最新官方售后网点实测,劳力士欧米茄卡地亚用户必看避坑指南 - 亨得利腕表维修中心
  • 苏州晟雅泰电子:GD25LQ128ESIGR物料的应用情况及替代型号参考
  • Intel oneAPI AI Toolkit:Python数据科学CPU加速实战指南
  • 浏览器视频下载难题终结者:猫抓扩展3分钟极速上手指南
  • NXP Quad Timer高级应用:单次触发、级联计数与PWM模式深度解析
  • 从靶场到实战:手把手教你用Python Flask复现SSTI漏洞(附完整Payload库)
  • ECharts辅助线踩坑实录:从‘画不出来’到‘精准控制’的5个常见问题解决
  • Hackintool终极指南:黑苹果系统配置的完整解决方案
  • C语言标准库跨平台编程:从历史函数到现代可移植性实践
  • 2026年6月济南黄金回收门店大盘点 正规靠谱不踩雷 - 开心测评
  • 2026广州LV回收避坑大全,新手闲置奢品变现不踩雷实操攻略 - 薛定谔的梨花猫
  • OurBoard.io高级功能探索:从白板工具到团队协作中枢
  • 以色列驾照翻译怎么办理?2026最新办理指南 - 资讯纵览
  • MuleSoft企业级AI编排:让大模型真正懂ERP、CRM和合规规则
  • HyprFlux与Hyprland完美融合:打造高效开发者工作流终极指南
  • MyBatis-Plus 源码分析-分页功能深度解析:从原理到实战,掌握高性能分页