RT-Thread的Finsh实现学习

学习原因

        工作中,使用同事开发的调试软件,输入参数打印的函数名就可以打印参数,但看不到代码实现,只能用自己微薄的知识积累去猜一下,之前尝试过,专门写一个函数,去解析编译生成的map文件,就可以通过函数名去找到函数的地址,然后用函数指针去运行就可以了,最近工作之余在学习RT-Thread的内核源码,刚好看到了Finsh组件,可以在命令行输入函数名运行,所以就简单学习了下;

自定义MSH命令

举例引入

        如下是最简单的“hello world"函数, 在函数定义下面有一行宏,纸面意思是Finsh函数导出,下面对宏进行分析:

void hello(void)
{printf("hello RT-Thread!\n");
}
FINSH_FUNCTION_EXPORT(hello, say hello world);

宏定义说明

        先看下RT-Thread官方文档对如下宏定义的说明:

FINSH_FUNCTION_EXPORT(hello, say hello world);

 

宏定义源码分析

#define FINSH_FUNCTION_EXPORT(name, desc)   \FINSH_FUNCTION_EXPORT_CMD(name, name, desc)/* else分支下的宏定义 */
#define FINSH_FUNCTION_EXPORT_CMD(name, cmd, desc)                      \const char __fsym_##cmd##_name[] SECTION(".rodata.name") = #cmd;    \const char __fsym_##cmd##_desc[] SECTION(".rodata.name") = #desc;   \RT_USED const struct finsh_syscall __fsym_##cmd SECTION("FSymTab")= \{                           \__fsym_##cmd##_name,    \__fsym_##cmd##_desc,    \(syscall_func)&name     \};/* 上面宏定义中调用的宏的定义 */
#define SECTION(x)    __attribute__((section(x)))
#define RT_USED       __attribute__((used))/* 上面宏定义中使用的结构体定义 */
struct finsh_syscall
{const char*     name;       /* the name of system call */const char*     desc;       /* description of system call */syscall_func    func;       /* the function address of system call */
};/* 上面宏定义中使用的函数指针定义 */
typedef long (*syscall_func)(void);

宏展开后的真相

        可以看到展开之后,是一个finsh_syscall结构体类型的变量"__fsym_hello",在后续章节可以得到证实;

FINSH_FUNCTION_EXPORT(hello, say hello world);/* 展开之后 */
const char __fsym_hello_name[] __attribute__((section(".rodata.name"))) = "hello";              \
const char __fsym_hello_desc[] __attribute__((section(".rodata.name"))) = "say hello world";    \
__attribute__((used)) const struct finsh_syscall __fsym_hello __attribute__((section("FSymTab"))) = \
{                            \__fsym_hello_name,       \__fsym_hello_desc,       \(syscall_func)&hello     \
};

代码编译

        代码编译是在Ubuntu系统下,如下主要说明链接脚本文件生成和代码编译

链接文件生成

         指令如下:

ld --verbose

生成信息如下,红框中的内容需要删除掉,最后一行红框中的内容也需要删除,否则编译出错

代码编译

        代码编译是在Ubuntu系统下编译,编译指令如下:

gcc main.c -T link.lds -o main -Wl,-Map,test.map

其中"link.lds"链接时指定的链接文件,在上一个小节中生成,"test.map"是编译生成的map文件;

MAP文件分析

        分析编译生成的map文件,如下是”__fsym_hello_name“和”__fsym_hello_desc“的段属性:

        如下是"__fsym_hello"的段属性:

通过如上的分析,说明前面的宏定义分析是合理的,展开后的变量在编译生成的map文件中存在;

链接文件分析

        本章节第一小节说明了链接文件的生成过程,”.rodata“段是链接脚本生成的时候就有的,需要在".text"段添加"FSymTab"段,需要添加的代码如下:

. = ALIGN(8);__fsymtab_start = .;KEEP(*(FSymTab))__fsymtab_end = .; 
. = ALIGN(8);

添加完成后的".text" 段如下:

主要关注下:"__fsymtab_start"和"__fsymtab_end",是"FSymTab"段的首地址变量和尾地址变量,可以在代码中使用;如果不添加如上代码段,在代码中就无法使用这2个变量,同时也无法编译通过;

代码实现分析

        在板级代码中分析,在初始化任务中会调用finsh_system_init()函数,其注释是:初始化Finsh,所以就从这个代码入手分析,摘取了主要代码如下:

初始化关键变量

void finsh_system_init(void)
{extern const int __fsymtab_start;extern const int __fsymtab_end;finsh_system_function_init(&__fsymtab_start, &__fsymtab_end);
}/* 全局变量声明 */
struct finsh_syscall *_syscall_table_begin  = NULL;
struct finsh_syscall *_syscall_table_end    = NULL;/* 函数调用 */
void finsh_system_function_init(const void *begin, const void *end)
{_syscall_table_begin = (struct finsh_syscall *) begin;_syscall_table_end = (struct finsh_syscall *) end;
}

帮助信息函数

 如上代码主要是设置了"_syscall_table_begin"和“_syscall_table_end”这2个全局变量,然后根据这2个全局变量找下是咋用的,找到了函数msh_help(),这个函数也导出了,在命令行输入也可以运行,并输出help帮助信息;

nt msh_help(int argc, char **argv)
{rt_kprintf("RT-Thread shell commands:\n");{struct finsh_syscall *index;for (index = _syscall_table_begin;index < _syscall_table_end;FINSH_NEXT_SYSCALL(index)){if (strncmp(index->name, "__cmd_", 6) != 0) continue;
#if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB)rt_kprintf("%-16s - %s\n", &index->name[6], index->desc);
#elsert_kprintf("%s ", &index->name[6]);
#endif}}rt_kprintf("\n");return 0;
}
FINSH_FUNCTION_EXPORT_ALIAS(msh_help, __cmd_help, RT-Thread shell help.);/* for循环里面用到的宏定义 */
#define FINSH_NEXT_SYSCALL(index)  index=finsh_syscall_next(index)/* 宏定义里面调用的函数 */
struct finsh_syscall* finsh_syscall_next(struct finsh_syscall* call)
{unsigned int *ptr;ptr = (unsigned int*) (call + 1);while ((*ptr == 0) && ((unsigned int*)ptr < (unsigned int*) _syscall_table_end))ptr ++;return (struct finsh_syscall*)ptr;
}

MSH命令执行过程

在finsh_system_init()函数里面创建了个任务,猜想分析应该是用来执行shell指令的,相关的代码如下,需要说明下,如下代码就大概看了下,可能有问题,请勿完全相信:

int finsh_system_init(void)
{rt_thread_t tid = &finsh_thread;result = rt_thread_init(&finsh_thread,FINSH_THREAD_NAME,finsh_thread_entry, RT_NULL,&finsh_thread_stack[0], sizeof(finsh_thread_stack),FINSH_THREAD_PRIORITY, 10);rt_thread_startup(tid);
}void finsh_thread_entry(void *parameter)
{while (1){ch = finsh_getchar();msh_exec(shell->line, shell->line_position);}}int msh_exec(char *cmd, rt_size_t length)
{if (_msh_exec_cmd(cmd, length, &cmd_ret) == 0){return cmd_ret;}
}static int _msh_exec_cmd(char *cmd, rt_size_t length, int *retp)
{cmd_function_t cmd_func;cmd_func = msh_get_cmd(cmd, cmd0_size);*retp = cmd_func(argc, argv);
}

简单代码实现

        MSH部分的源代码

        根据以上的分析,写了个简单的测试程序如下:

void finsh_system_init(void)
{extern const int __fsymtab_start;extern const int __fsymtab_end;unsigned int addr_offset = (char *)&__fsymtab_start + 8;//_syscall_table_begin = (struct finsh_syscall *) &__fsymtab_start;/* 看map表偏移了8byte,指向了FSymTab段第一个变量的首地址 */_syscall_table_begin = (struct finsh_syscall *) addr_offset;        _syscall_table_end = (struct finsh_syscall *) &__fsymtab_end;printf("addr_offset                   = %x\n",addr_offset);printf("&__fsymtab_start              = %x\n",&__fsymtab_start);printf("&__fsymtab_end                = %x\n",&__fsymtab_end);printf("_syscall_table_begin          = %x\n",_syscall_table_begin);printf("_syscall_table_end            = %x\n",_syscall_table_end);printf("syscall_table length          = %ld\n\n\n",((unsigned int)&__fsymtab_end) - ((unsigned int)&__fsymtab_start));
}void msh_help()
{struct finsh_syscall *index;for (index = _syscall_table_begin;index < _syscall_table_end; FINSH_NEXT_SYSCALL(index)){printf("%-8s \t - %s\n", &index->name[0], index->desc); index->func();printf("\n\n");}
}

代码分析

        在finsh_system_init()函数中,_syscall_table_begin指向的是"FSymTab"段第一个变量的首地址,而源代码中指向的是"FSymTab"段的首地址变量,这块应该是和地址对齐有关,因时间等原因没仔细分析,查看map文件后,简单的把地址做了个便宜;

        在源码的msh_help()函数中,遍历了"FSymTab"段中的各变量,打印函数名及函数描述信息,所以我增加了一行index->func(),这一行就可以调用导出的函数;

MAP文件分析

        如下是编译生成的map文件中,"FSymTab"段的地址信息,其中段的首地址变量和尾地址变量的地址用红框标注,首尾地址之间的是2个段变量,其实就是前面宏展开后的变量;可以看到段首地址变量的地址和绿框中第一个段变量的地址差了8,所以在代码里面强制偏移了8;同时注意到,绿框中2个变量的首地址相差32byte,因为”finsh_syscall“结构体的大小是32byte;

要导出的函数

        上一小节的map文件中,有2个变量,这2个变量就是测试代码中导出的2个函数:

void rt_show_version(void)
{printf("\n \\ | /\n");printf("- RT -     Thread Operating System\n");printf(" / | \\     %d.%d.%d build %s\n",3, 0, 05, __DATE__);printf(" 2006 - 2018 Copyright by rt-thread team\n");
}
FINSH_FUNCTION_EXPORT(rt_show_version, show rt_thread version);void hello(void)
{printf("hello RT-Thread!\n");
}
FINSH_FUNCTION_EXPORT(hello, say hello world);

同时在主函数里面只需要调用finsh_system_init()和 msh_help()即可

运行结果

        代码运行结果如下,”finsh_syscall“结构体的大小是32byte;导出的函数rt_show_version()和hello()也正常运行了;地址也和map文件完全一致;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/359674.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

一、Jquery入门(超详)

* [5.3 jQuery 对象和 DOM 对象之间的相互转换](about:blank#53_jQuery__DOM__271)* * [5.3.1 jQuery 对象转换为 DOM 对象](about:blank#531_jQuery__DOM__282)* [5.3.2 DOM 对象转换为 jQuery 对象](about:blank#532_DOM__jQuery__295)六、 解决 jQuery 和其他库的冲…

AI数据分析:集中度分析和离散度分析

在deepseek中输入提示词&#xff1a; 你是一个Python编程专家&#xff0c;要完成一个Python脚本编写的任务&#xff0c;具体步骤如下&#xff1a; 读取Excel表格&#xff1a;"F:\AI自媒体内容\AI行业数据分析\toolify月榜\toolify2023年-2024年月排行榜汇总数据.xlsx&qu…

Redis-事务-基本操作-在执行阶段出错不会回滚

文章目录 1、Redis事务控制命令2、Redis事务错误处理3、Redis事务错误处理&#xff0c;在执行阶段出错不会回滚 1、Redis事务控制命令 127.0.0.1:6379> keys * (empty array) 127.0.0.1:6379> multi OK 127.0.0.1:6379(TX)> set a1 v1 QUEUED 127.0.0.1:6379(TX)>…

深入研究websocket直播中signature这个参数怎么来的,模拟自己生成一个

上一节课我们已经找到了生成signature这个字段的代码位置&#xff0c;就是这个B函数&#xff0c;嗯......听起来好像有点奇怪&#xff0c;但是它确实叫B啊&#xff0c;笑死。不管了&#xff0c;看一下里面的逻辑是啥。 注意e参数的内容是&#xff1a; {"app_name":…

Flutter ffi Failed to lookup symbol

iOS release版本&#xff0c;解决方式参考官方文档&#xff1a;在 iOS 中使用 dart:ffi 调用本地代码 如果debug版本也报这个错误&#xff0c;很可能是有多个.c文件&#xff0c;编译的时候没带上&#xff01; 假设你的ffi模块名字是 c_lib 对于Android端&#xff0c;需要修改…

索引的分类和回表查询——Java全栈知识(29)

索引的分类和回表查询 Mysql 的索引按照类型可以分为以下几类&#xff0c;但是我们使用的 InnoDB 只支持主键索引&#xff0c;唯一索引&#xff0c;普通索引&#xff0c;并不支持全文索引。 1、聚集索引和二级索引 InnoDB 可以将索引分为两类分别是聚集索引和二级索引&…

java基于ssm+jsp 医院远程诊断系统

1前台首页功能模块 医院远程诊断系统&#xff0c;在系统首页可以查看首页、医生信息、论坛信息、我的、跳转到后台、客服等内容&#xff0c;如图1所示。 图1前台首页功能界面图 用户登录&#xff0c;在用户登录页面可以填写用户名、密码、等信息进行用户登录&#xff0c;如图2…

vue3滚动日历选择器

倒叙日历&#xff1a; <template><div class"date-picker"><div class"column" wheel"onYearScroll"><div v-for"(year, index) in displayedYears" :key"index" :class"{current: year current…

复分析——第7章——ζ 函数和素数定理(E.M. Stein R. Shakarchi)

第7章 ζ函数和素数定理 Bernhard Riemann, whose extraordinary intuitive powers we have already mentioned, has especially renovated our knowledge of the distribution of prime numbers, also one of the most mysterious questions in mathematics. He has tau…

uniapp实现路由拦截——遇到问题(三)

uniapp路由拦截开发过程中遇到问题 文章目录 uniapp路由拦截开发过程中遇到问题App 无法退出应用监听返回数据结构解决方式模拟原生物理返回键提示不提示&#xff0c;直接退出应用 微信小程序 登录成功返回页面报错效果图不同平台来源页面数据结构解决方式 App 无法退出应用 安…

WPF——属性

一、属性 类最初只有字段与函数&#xff0c;字段为一个变量&#xff0c;访问权限可以是private&#xff0c;protected&#xff0c;public。而将字段设为private&#xff0c;不方便外界对类数据的操作&#xff0c;但是将字段设为public又怕外界对数据进行非法操作&#xff0c;于…

SpringMVC系列十一: 文件上传与自定义拦截器

文章目录 SpringMVC文件上传基本介绍需求分析 / 图解应用实例-代码实现 自定义拦截器什么是拦截器自定义拦截器执行流程分析图自定义拦截器应用实例快速入门注意事项和细节Debug执行流程 多个拦截器多个拦截器执行流程示意图应用实例1代码实现注意事项和细节 应用实例2 作业布置…

怎么区分Boombap 制作Boombap曲子教学 boombap音乐出现的时间

Boombap音乐作为嘻哈音乐文化的重要组成部分&#xff0c;具有独特的音乐节奏、样式和情感。要理解和区分Boombap音乐&#xff0c;需要从其音乐的历史渊源、音乐特征和文化影响入手。接下来给大家介绍怎么区分Boombap&#xff0c;制作Boombap曲子教学的具体内容。 一、怎么区分B…

虚拟机配置桥接模式

背景 因为要打一些awd比赛,一些扫描工具什么的,要用到kali,就想着换成一个桥接模式 但是我看网上的一些文章任然没弄好,遇到了一些问题 前置小问题 每次点开虚拟网络编辑器的时候都没有vmnet0,但是点击更改的时候却有vmnet0 第一步: 点击更改设置 第二步: 把wmnet0删掉 …

【计算机视觉】人脸算法之图像处理基础知识(六)

图像直方图 图像直方图是描述图像中像素强度分布的一种统计图表&#xff0c;它是图像处理和计算机视觉领域中一个非常基础且重要的概念。图像直方图通常用于分析图像的亮度、对比度特性&#xff0c;以及在图像增强、阈值分割、特征提取等多种图像处理任务。 import cv2 impor…

高通安卓12-固件升级

下载步骤 第一步 格式化 「下载一次即可&#xff1b;能开机能下载的板子 忽略这一步&#xff0c;直接执行第二步即可」 QFIL工具配置为UFS类型&#xff0c;勾选Provision&#xff0c;如下图&#xff1a; Programmer选择prog_firehose_ddr.elf&#xff0c;Provision Xml选择prov…

ONLYOFFICE8.1版本桌面编辑器测评

https://www.onlyoffice.com/zh/ 随着工作方式的不断演变&#xff0c;文档编辑软件成为了我们日常工作中不可或缺的一部分。而ONLYOFFICE作为一款开源且功能丰富的办公套件&#xff0c;其最新推出的8.1版本在原有基础上进行了大量的优化与更新&#xff0c;旨在提供更流畅、更安…

无人机巡检小羊仿真

详细视频地址 仿真效果 可视化三维仿真 gazebo物理仿真 px4 飞控仿真 仿qgc简易地面站 详细视频地址

微信小程序之横向列表展示

效果图 参考微信小程序可看 代码&#xff1a; <view class"lbtClass"><view class"swiper-container"><scroll-view class"swiper" scroll-x"true" :scroll-left"scrollLeft"><block v-for"(six…

DDK电通拧紧MFC-S060控制器过流维修

一、DDK伺服拧紧轴控制器过流故障的成因 1. 电源电压过低&#xff1a;当电源电压过低时&#xff0c;控制器可能会出现过流现象。 2. 负载过大&#xff1a;当负载过大时&#xff0c;DDK电通拧紧机控制器MFC-S060的电流也会随之增大&#xff0c;可能导致过流故障。 3. 控制器内部…