硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1292|回复: 17
收起左侧

[MDK] 关于keil中linker symbol的问题

[复制链接]

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
发表于 2022-2-26 16:51:58 | 显示全部楼层 |阅读模式
硬汉哥,网上我看到了一片文章嵌入式开发--STM32上实现驱动注册initcall机制(类linux)_SunnyBit的博客-CSDN博客
也有源码 cola_os-master.zip (17.07 MB, 下载次数: 11)
,源码中我一直对下面的代码有些疑问


  1.     extern initcall_t initcall0init$$Base[];
  2.     extern initcall_t initcall0init$$Limit[];
  3.     extern initcall_t initcall1init$$Base[];
  4.     extern initcall_t initcall1init$$Limit[];
  5.     extern initcall_t initcall2init$$Base[];
  6.     extern initcall_t initcall2init$$Limit[];
  7.     extern initcall_t initcall3init$$Base[];
  8.     extern initcall_t initcall3init$$Limit[];


复制代码
这个变量是哪里定义的(比如initcall0init$$Base)?其中,下面的宏定义很像你《第7期BSP驱动教程:MDK专题高级进阶,重要的分散加载使用。。》视频中的提到的用法,但是实际上分散加载文件里面没看到这些,

  1. #define __define_initcall(fn, id) \
  2.     static const initcall_t __initcall_##fn##id __used \
  3.     __attribute__((__section__("initcall" #id "init"))) = fn;
复制代码

但是map文件的确看到了,能否帮忙解释下,谢谢。



回复

使用道具 举报

5

主题

196

回帖

211

积分

高级会员

积分
211
发表于 2022-2-26 23:10:38 | 显示全部楼层
initcall0init$$Base, initcall0init$$Limit这类属于keil工具链的扩展语法。
对于sct文件里定义的一个段(segment), Keil会根据其属性自动生成name$$Base, name$$Limit, name$$ZI$$Limit等符号, 用来标记对应的数据段物理地址, 数据段数据长度, 数据段加载长度(如果该段具备load属性)。
这些符号是在sct文件里隐式定义的, c文件通过extern symbol的方式引用其地址, 获取感兴趣的段的加载信息。

Keil提供了很多有趣的扩展语法, 比如当你实现了带$sub$$和$supper$$前缀的符号, 外部模块对原始符号的引用,会被转化为对带有$sub$$前缀的符号的引用。而如果你想引用原始符号, 可以通过引用带$supper$$前缀的符号实现。 这个技巧可以劫持函数调用, 一种典型应用是插桩。

比如定义一组形如$sub$$main, $super$$main的函数, 可以实现对main函数调用控制流的劫持, 在调用原始main符号之前插入你自己的代码。

    外部模块调用main -> $sub$$main -> $super$$main -> 原始的main
回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-2-27 09:46:41 | 显示全部楼层
alexyzhov 发表于 2022-2-26 23:10
initcall0init$$Base, initcall0init$$Limit这类属于keil工具链的扩展语法。
对于sct文件里定义的一个段(s ...

非常感谢你的回答,请问你是怎么做到这么精通的呢??
回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-2-27 09:50:11 | 显示全部楼层
alexyzhov 发表于 2022-2-26 23:10
initcall0init$$Base, initcall0init$$Limit这类属于keil工具链的扩展语法。
对于sct文件里定义的一个段(s ...

这些符号是在sct文件里隐式定义的, c文件通过extern symbol的方式引用其地址, 获取感兴趣的段的加载信息。
是否我可以定义一个
int variable4 __attribute__((__used__)) __attribute__((section("foo"))) = 10;
但是我定义以后,可以编译,但是在map文件里面没有找到foo&&Base,直接extern foo&&Base也是报错,是undefined foo&&Base
回复

使用道具 举报

5

主题

196

回帖

211

积分

高级会员

积分
211
发表于 2022-2-27 13:00:01 | 显示全部楼层
本帖最后由 alexyzhov 于 2022-2-27 13:05 编辑
wdliming 发表于 2022-2-27 09:50
这些符号是在sct文件里隐式定义的, c文件通过extern symbol的方式引用其地址, 获取感兴趣的段的加载信息 ...

隐式定义的意思是,keil会为sct里已有的数据段自动展开这并定义些符号,不是凭空造出来的。

具体来说,就是你在sct里定义一个 foo 段,然后不需要做任何事,直接用extern char Image$$foo$$Base 就能引用到这个用来标记 foo 段起始地址的符号。

你在C语言里把variable4丢进foo段,但如果sct文件里并没有显式定义foo段,variable4在链接时会被通配进 .ANY(+RW)。你可以在编译输出的map文件里,看到变量具体被分配到的地址。

关于这部分的扩展语法内容,可以参考Keil的文档:
https://www.keil.com/support/man ... ge1362065952432.htm
回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-2-27 14:33:51 | 显示全部楼层
alexyzhov 发表于 2022-2-27 13:00
隐式定义的意思是,keil会为sct里已有的数据段自动展开这并定义些符号,不是凭空造出来的。

具体来说 ...

非常感谢~~~果然是大佬啊
回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-2-27 14:42:38 | 显示全部楼层
alexyzhov 发表于 2022-2-27 13:00
隐式定义的意思是,keil会为sct里已有的数据段自动展开这并定义些符号,不是凭空造出来的。

具体来说 ...

我编译后
  1.     variable4                                0x2000004c   Data           4  main.o(foo)
复制代码
回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-2-27 14:44:10 | 显示全部楼层
alexyzhov 发表于 2022-2-27 13:00
隐式定义的意思是,keil会为sct里已有的数据段自动展开这并定义些符号,不是凭空造出来的。

具体来说 ...

如果定义全局
int variable2;

  1.     variable2                                0x20000018   Data           4  main.o(.data)
复制代码
回复

使用道具 举报

5

主题

196

回帖

211

积分

高级会员

积分
211
发表于 2022-2-27 15:07:47 | 显示全部楼层
section 和 segment 都可以翻译成"段",网络上包括我自己也经常混用,但作为链接加载中的术语是有区别的,简单地说 section 是数据段,segment 是加载段。延伸知识可以看下 gabi 手册里对 elf 数据格式的定义和解释

类似容易混淆的术语还有 exception 和 interrupt,都可以翻译成"中断",大家也经常混用。但这俩作为术语使用的时候,exception (异常) 指 (一般由处理器自己发起的) 同步的内核中断事件,interrupt (中断) 指 (一般由处理器外部模块发起的) 异步的内核中断事件

gabi41.pdf

825.97 KB, 下载次数: 10

回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106978
QQ
发表于 2022-3-2 10:41:20 | 显示全部楼层
在MDK里面,initcall0init$$Base表示段(section) initcall0init的首地址,而initcall0init$$Limit表示段initcall0init的尾地址。

这个就是他的定义。

#define pure_initcall(fn)       __define_initcall(fn, 0) //可用作系统时钟初始化    -------------__attribute__((__section__("initcall0init)))
#define fs_initcall(fn)         __define_initcall(fn, 1) //tick和调试接口初始化        -------------__attribute__((__section__("initcall1init)))
#define device_initcall(fn)     __define_initcall(fn, 2) //驱动初始化                  -------------__attribute__((__section__("initcall2init)))
#define late_initcall(fn)       __define_initcall(fn, 3) //其他初始化                   -------------__attribute__((__section__("initcall3init)))


回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-3-2 20:34:29 | 显示全部楼层
alexyzhov 发表于 2022-2-27 15:07
section 和 segment 都可以翻译成"段",网络上包括我自己也经常混用,但作为链接加载中的术语是有区别的, ...

谢谢~~~
回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-3-3 08:33:11 | 显示全部楼层
eric2013 发表于 2022-3-2 10:41
在MDK里面,initcall0init$$Base表示段(section) initcall0init的首地址,而initcall0init$$Limit表示段ini ...


在MDK里面,initcall0init$$Base表示段(section) initcall0init的首地址,而initcall0init$$Limit表示段initcall0init的尾地址。
这句话我肯定是理解的,
  1. #define __define_initcall(fn, id) \
  2.     static const initcall_t __initcall_##fn##id __used \
  3.     __attribute__((__section__("initcall" #id "init"))) = fn

  4. #define pure_initcall(fn)       __define_initcall(fn, 0) //可用作系统时钟初始化  
  5. #define fs_initcall(fn)         __define_initcall(fn, 1) //tick和调试接口初始化
  6. #define device_initcall(fn)     __define_initcall(fn, 2) //驱动初始化
  7. #define late_initcall(fn)       __define_initcall(fn, 3) //其他初始化
  8. #define later_initcall(fn)       __define_initcall(fn, 4) //其他初始化

复制代码


硬汉哥你说的是这个就是定义了??
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106978
QQ
发表于 2022-3-3 09:28:12 | 显示全部楼层
wdliming 发表于 2022-3-3 08:33
在MDK里面,initcall0init$$Base表示段(section) initcall0init的首地址,而initcall0init$$Limit表示 ...

是的,然后他程序的相关驱动文件里面做了。

比如fs_initcall(usart_init);,device_initcall(led_register);等。
回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-3-3 10:33:41 | 显示全部楼层
eric2013 发表于 2022-3-3 09:28
是的,然后他程序的相关驱动文件里面做了。

比如fs_initcall(usart_init);,device_initcall(led_regi ...

好的,谢谢,这种方式,我看了也是挺好的,类似与rtthread的
回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-3-3 10:35:26 | 显示全部楼层
alexyzhov 发表于 2022-2-27 15:07
section 和 segment 都可以翻译成"段",网络上包括我自己也经常混用,但作为链接加载中的术语是有区别的, ...

你好,你的文档是哪里搞来的,这么专业。
回复

使用道具 举报

6

主题

640

回帖

658

积分

金牌会员

积分
658
QQ
发表于 2022-3-3 22:20:36 | 显示全部楼层
感觉就是骚操作。。。。
回复

使用道具 举报

5

主题

196

回帖

211

积分

高级会员

积分
211
发表于 2022-3-3 23:19:33 | 显示全部楼层
wdliming 发表于 2022-3-3 10:35
你好,你的文档是哪里搞来的,这么专业。

这个是ELF文件的格式规约,了解背景的话很容易搜到的。
回复

使用道具 举报

73

主题

1198

回帖

1417

积分

至尊会员

积分
1417
 楼主| 发表于 2022-3-4 08:39:49 | 显示全部楼层
yklstudent 发表于 2022-3-3 22:20
感觉就是骚操作。。。。

rtthread的就是类似这个骚操作的。。
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-13 05:57 , Processed in 0.317418 second(s), 28 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表