RT-Thread的Finsh实现学习

news2024/7/6 18:52:46

学习原因

        工作中,使用同事开发的调试软件,输入参数打印的函数名就可以打印参数,但看不到代码实现,只能用自己微薄的知识积累去猜一下,之前尝试过,专门写一个函数,去解析编译生成的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);
#else
            rt_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.coloradmin.cn/o/1858780.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【漏洞复现】脸爱云一脸通智慧管理平台 SystemMng 管理用户信息泄露漏洞(XVE-2024-9382)

0x01 产品简介 脸爱云一脸通智慧管理平台是一套功能强大&#xff0c;运行稳定&#xff0c;操作简单方便&#xff0c;用户界面美观&#xff0c;轻松统计数据的一脸通系统。无需安装&#xff0c;只需在后台配置即可在浏览器登录。 功能包括:系统管理中心、人员信息管理中心、设备…

【TensorRT】TensorRT C# API 项目更新 (2):优化安装方式和代码

1. 项目介绍 NVIDIA TensorRT™ 是一款用于高性能深度学习推理的 SDK&#xff0c;包括深度学习推理优化器和运行时&#xff0c;可为推理应用程序提供低延迟和高吞吐量。基于 NVIDIA TensorRT 的应用程序在推理过程中的执行速度比纯 CPU 平台快 36 倍&#xff0c;使您能够优化在…

【MySQL】数据库——事务

一.事务概念 事务是一种机制、一个操作序列&#xff0c;包含了一组数据库操作命令&#xff0c;并且把所有的命令作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这一组数据库命令要么都执行&#xff0c;要么都不执行事务是一个不可分割的工作逻辑单元&#xff0c;在数…

小白学python(第一天)

在有了C语言的基础后&#xff0c;我们学python会变得相当容易&#xff0c;毕竟c生万物&#xff0c;废话不多说&#xff0c;直接进入我们的正题 课前准备 Python环境的搭建以及Pycharm的安装 python环境安装 Download Python | Python.org 因为我的电脑是windows&#xff0c;…

Idea启动服务报 Command line is too long

一、背景 合不同分支代码后&#xff0c;启动服务报 Error running Application, Command line is too long, Shorten the command line via JAR manifest or via a classpath file and rerun. 没有在意&#xff0c;然后点击了manifest 来进行 二、问题 然后自己在重新启动&…

终于有人说清楚CIF术语了‼️

什么是CIF&#xff1f; CIF&#xff0c;全称Cost, Insurance, and Freight (成本、保险费加运费)&#xff0c;是一种国际贸易术语&#xff0c;用于海运和内河运输。按照CIF条款&#xff0c;卖方的交货义务在装运港当货物越过船舷时完成&#xff0c;但卖方需承担将货物运至指定…

Hive基础知识(十八):Hive 函数的使用

1. 系统内置函数 1&#xff09;查看系统自带的函数 hive (hive3)> show functions; Time taken: 0.085 seconds, Fetched: 289 row(s) 2&#xff09;显示自带的函数的用法 hive (hive3)> desc function upper; OK tab_name upper(str)- Returns str with all characters…

this.$prompt 提示框增加文本域并修改文本域高度

2024.06.24今天我学习了如何对提示框增加文本域的方法&#xff0c;效果如下&#xff1a; 代码如下&#xff1a; <script>methods:{reject_event(){this.$prompt(驳回内容, 提示, {confirmButtonText: 确定,cancelButtonText: 取消,inputType: textarea,inputPlaceholder…

手工微调embedding模型,让RAG应用检索能力更强

BAAI/bge-small-en 目前HuggingFace的MTEB(海量文本Embedding基准)排行榜上排名第一的Embedding模型是big-large-en&#xff0c;它由北京人工智能研究院(BAAI&#xff0c;智源)开发。它是一种预训练的transformer模型&#xff0c;可用于各种自然语言处理任务&#xff0c;如文本…

Elasticsearch 索引与文档操作实践指南

上节我们部署了elasticsearch8.4.1和kibana8.4.1以及ik分词器&#xff0c;本节我们来学习下elasticsearch的相关索引&#xff0c;文档操作。 首先我们kinana的主界面找到开发者工具在里面我们来编写相关操作。 标题查看集群情况 GET /_cluster/health详细解释&#xff1a; …

一切前端概念,都是纸老虎

4、listener可以通过 store.getState() 得到当前状态。如果使用的是 React&#xff0c;这时可以触发重新渲染 View。 function listerner() { let newState store.getState(); component.setState(newState); } 对比 Flux 和 Flux 比较一下&#xff1a;Flux 中 Store 是…

机器人控制系列教程之串联机器人工作空间

简介 在机器人学领域&#xff0c;对机器人工作空间的精确分析是实现高效、安全操作的关键。工作空间是指机器人末端执行器在不与环境或其他物体发生碰撞的情况下能够到达的所有位置的集合。为了求解这一空间&#xff0c;研究者们发展了多种方法&#xff0c;包括作图法、蒙特卡…

【因果推断python】53_效应异质性和非线性带来的挑战1

目录 Treatment Effects on Binary Outcomes 合成一些数据 由于缺乏基本事实&#xff0c;在单位层面预测治疗效果极为困难。因为我们只能观察到一个潜在结果 T(t) &#xff0c;我们无法直接估计它。相反&#xff0c;我们必须依靠目标变换&#xff08;也可以看作是设计巧妙的损…

基于Pytorch框架构建ResNet模型

Pytorch 一、训练模型1.导入资源包2.定义数据预处理3.读取数据 二、定义卷积神经网络1.导入必要的库2.定义名为convolutional_block的卷积块类3.定义了一个名为identity_block的恒等块类4.定义了一个名为Resnet的深度卷积神经网络类 三、创建模型1. 检查GPU设备2. 训练过程 四、…

编译原理大题自解(活前缀DFA、LR(0)分析表)

目录 4. (简答题) &#xff08;1&#xff09;给出识别活前缀的DFA &#xff08;2&#xff09;设计此文法的 LR(0)分析表 第一种解法 第二种解放 首先声明这是作者的写法&#xff08;不保证正确&#xff01;&#xff09;仅供参考。本题因为可能存在冲突的原因&#xff0c;所…

【C语言小例程26/100】

题目&#xff1a;有一分数序列&#xff1a;2/1&#xff0c;3/2&#xff0c;5/3&#xff0c;8/5&#xff0c;13/8&#xff0c;21/13...求出这个数列的前20项之和。 程序分析&#xff1a;请抓住分子与分母的变化规律。 程序源代码&#xff1a; #include <stdio.h>int ma…

windows上运行arm32架构的安卓模拟器

说明 主要功能&#xff1a;在win10上研究和学习32位arm汇编指令的执行 环境如下 主机环境: windows10 目标模拟器环境:armeabi-v7a调试环境搭建 1、下载android studio 下载地址&#xff1a;https://developer.android.com/studio?hlzh-cn 2、安装android studio 直接下…

互联网应用主流框架整合之Spring Boot开发

Spring Boot数据库开发 通常SpringBoot数据库开发&#xff0c;会引入spring-boot-starter-jdbc&#xff0c;而如果引入了spring-boot-starter-jdbc&#xff0c;但没有可用的数据源或者没有配置&#xff0c;那么在运行Spring Boot时会出现异常&#xff0c;因为spring-boot-star…

思考-生涯思考-GPT-5对人们的影响

GPT-5 一年半后发布&#xff1f;对此你有何期待&#xff1f; IT之家6月22日消息&#xff0c;在美国达特茅斯工程学院周四公布的采访中&#xff0c;OpenAI首席技术官米拉穆拉蒂被问及GPT-5是否会在明年发布&#xff0c;给出了肯定答案并表示将在一年半后发布。此外&#xff0c;…

Java知识点整理 11— 后端 Spring Boot 万用初始化模板使用

一. 模块简介 annotation&#xff1a;自定义注解aop&#xff1a;请求日志和权限校验common&#xff1a;通用类config&#xff1a;配置类constant&#xff1a;常量 controller&#xff1a;控制层esdao&#xff1a;方便操作ESexception&#xff1a;异常类job&#xff1a;定时任务…