是否可以将 .text 部分自动拆分到多个内存区域?
Is it possible to auto split the .text section across mulitple memory areas?
我目前正在为具有拆分闪存区域的微控制器编写嵌入式程序。像
MEMORY {
flash1 : ORIGIN = 0x1000, LENGTH = 0x1000
/* 1K gap */
flash2 : ORIGIN = 0x3000, LENGTH = 0x1000
}
我的应用程序已经发展到 ".text" 部分大于 flash1 的区域。
是否可以在两个区域中自动拆分文本部分?
类似于:
SECTIONS {
.text0 : { *(.text) } > flash1
.text1 : { *(.text) } > flash2
}
如果我正在做上面那样的事情。我收到一条错误消息,告诉我
ld: a.out section `.text' will not fit in region `flash1'
ld: region `flash1' overflowed by 240 bytes
我知道我可以做类似的事情:
SECTIONS {
.text0 : { mylargefile (.text) } > flash1
.text1 : { *(.text) } > flash2
}
但我没有一个大文件。所以问题是是否可以告诉链接器填充第一个输出部分,直到它已满,然后继续下一部分?
更多信息:
我没有提供完整的示例,因为我的实际应用程序比我复杂得多,尽管使用 LMA 和 VMA 部分以及一些特殊文件的特殊部分。但是这个答案应该能为我提供足够的帮助来继续或提出更多问题。
我期待着答案。我希望我的问题在没有完整样本的情况下足够清楚
编辑1:
问题 的解决方案在我的情况下不正确,因为该解决方案使用通配符手动溢出文件。我问是否有自动解决方案。否则,每次对代码库进行较大更改时,我都将被迫手动调整链接器文件。
编辑2:
更改示例以确保存在 Tom V
所指出的差距
好吧,如果第一个部分已满,一些编译器允许 link 程序(和脚本)溢出到另一个部分。
但这些有时也有问题,这取决于部分何时满的顺序以及何时切换,它不会回来填满第一个,例如你在开头或中间有很大一部分。当 linker 由于溢出现在跳到下一节时,第一节可能刚填满一半。它不会在那个大的部分之后使用较小的部分,回到第一部分来填写。
所以,似乎 GNU LD、Gold-linker 以及 LLVM/CLANG linker(在他们的站点上声明 linker 脚本和GNU LD是一样的,exceptions page没有说明),不支持overflow into another section
所以,为了让它更容易,也许有一些提示:
随着时间的推移,按文件名模式过滤会变得乏味。而且,它不允许您按符号过滤,只能按文件名模式和输入部分名称过滤。
如果你有像 AUTOSAR MemMap 这样的方法,你可以通过将它们放入新的部分来拆分代码,这些部分的命名与默认 .text, .rodata, .data, .bss
不同。 (不幸的是,GCC + CLANG 使用 attribute(( )) 而不是 C-standard 的 #pragma 或 _Pragma() 方式。 (AUTOSAR MemMap例子在最后)
- 你可以分成快速和慢速代码,例如引入新的部分,如
.codefast, .codeslow
(例如 ISR 代码快,任务级代码“慢”)
- 您可以分成 QM 和 ASIL 分区,引入像
.code_qm, .code_asila, .code_asilb
这样的部分
- 您可以拆分代码和配置/常量数据,将配置放入单独的可刷新部分,例如
.text, .rodata
部分可以拆分为 .text, .const .postbuildconfig, .calib
.. 使用部分不同的编码风格,您可以在这里保持代码相同,但项目配置(例如 CAN 配置、过滤器链等)可以使用可配置的配置,您只需刷新一个新配置,而无需更新代码本身
.text
--> 闪存扇区1
.postbuildconfig
--> 闪存扇区2
.calib
--> 闪存扇区 3
在实际开始真正编译和 link 之前,也许中间构建阶段可以使用 objdump / nm / size / readelf
之类的工具首先扫描 .o
文件以提供输出 object / 部分大小并根据特定标准通过脚本对其进行总结,例如输入内存布局和上述特殊部分或按大小排序和 .o
文件名模式,您可以使用它来更新 linker 脚本以适应内存定义。所以,脚本可以尝试适应直到间隙尽可能多,然后切换到另一个内存。它甚至可以尝试生成稍后传递给 linker 的 linker 脚本部分。您可以在 perl 或 python.
中编写这样的 linker 预处理脚本
连同到不同部分的内存映射,您现在可以像这样过滤:
MEMORY {
// Maybe you have two 128k areas separate by a gap
// Put the code in the first big part, and the more
// "configurable part" in the second area might even
// allow you to flash/update the separately
CODE1 : origin = 0x10000, len = 128k
// maybe here is a gap
CODE2 : origin = 0x10000, len = 64k
CONST : origin = 0x20000, len = 4k
CALIB : origin = 0x40000, len = 4k
POSTBUILD : origin = 0x50000, len = 48k
}
SECTIONS {
.code1 : {
*(.text) // default .text
*(.code_fast) // fast/slow code split
*(.code_qm) // qm/asil code split
} > CODE1
.code2 : {
*(.code_slow) // fast/slow code split
*(.code_asila) // qm/asil code split
*(.code_asilb) // qm/asil code split
} > CODE2
.const : {
*(.const)
*(.rodata)
} > CALIB
.calib : {
*(.calib)
*_calib.o(.calib) // in case you separate them also out into xxx_Calib.c files compiled to xxx_Calib.o files
} > CALIB
.postbuild : {
*(.postbuild)
*_PBCfg.o(.postbuild) // in case you separate them also out into xxx_PBCfg.c files compiled to xxx_PBCfg.o files
} > POSTBUILD
}
这种方法还允许您在 header 中准备布局,并通过某些外部工具生成配置和校准。
此外,它还可以更好地估计您的内存资源消耗,因此也可以估算如何以及在何处将它们 place/link 到内存部分。
这是代码中的 AUTOSAR MemMap 使用示例
#define XXX_START_SEC_CODE_FAST
#include "MemMap.h"
void XXX_Channel0_Notification(void) {
// Channel0 Finished Notification e.g. called from ISR
}
#define XXX_STOP_SEC_CODE_FAST
#include "MemMap.h"
MemMap.h 可以这样配置:
#if defined(XXX_START_SEC_CODE_FAST)
#undef XXX_START_SEC_CODE_FAST
// open section .codefast
#pragma section code ".codefast"
#elif defined(XXX_START_SEC_CODE_SLOW)
#undef XXX_START_SEC_CODE_SLOW
// open section .codeslow
#pragma section code ".codeslow"
#endif
#if defined(XXX_STOP_SEC_CODE_FAST)
#undef XXX_STOP_SEC_CODE_FAST
// back to default section
#pragma section code
#elif defined(XXX_STOP_SEC_CODE_SLOW)
#undef XXX_STOP_SEC_CODE_SLOW
// back to default section
#pragma section code
#endif
可以生成MemMap.h文件,通过扫描所有start/stop部分定义,并通过配置工具生成pragmas(甚至可以是Excel)。
优点是,您不会溢出所有可能的编译指示(例如不同的编译器 <-> 不同的方言),或者映射或不映射。因此,如果可能的话,您实际上可以在不同的项目中重用代码。
我目前正在为具有拆分闪存区域的微控制器编写嵌入式程序。像
MEMORY {
flash1 : ORIGIN = 0x1000, LENGTH = 0x1000
/* 1K gap */
flash2 : ORIGIN = 0x3000, LENGTH = 0x1000
}
我的应用程序已经发展到 ".text" 部分大于 flash1 的区域。 是否可以在两个区域中自动拆分文本部分?
类似于:
SECTIONS {
.text0 : { *(.text) } > flash1
.text1 : { *(.text) } > flash2
}
如果我正在做上面那样的事情。我收到一条错误消息,告诉我
ld: a.out section `.text' will not fit in region `flash1'
ld: region `flash1' overflowed by 240 bytes
我知道我可以做类似的事情:
SECTIONS {
.text0 : { mylargefile (.text) } > flash1
.text1 : { *(.text) } > flash2
}
但我没有一个大文件。所以问题是是否可以告诉链接器填充第一个输出部分,直到它已满,然后继续下一部分?
更多信息:
我没有提供完整的示例,因为我的实际应用程序比我复杂得多,尽管使用 LMA 和 VMA 部分以及一些特殊文件的特殊部分。但是这个答案应该能为我提供足够的帮助来继续或提出更多问题。
我期待着答案。我希望我的问题在没有完整样本的情况下足够清楚
编辑1:
问题
编辑2: 更改示例以确保存在 Tom V
所指出的差距好吧,如果第一个部分已满,一些编译器允许 link 程序(和脚本)溢出到另一个部分。
但这些有时也有问题,这取决于部分何时满的顺序以及何时切换,它不会回来填满第一个,例如你在开头或中间有很大一部分。当 linker 由于溢出现在跳到下一节时,第一节可能刚填满一半。它不会在那个大的部分之后使用较小的部分,回到第一部分来填写。
所以,似乎 GNU LD、Gold-linker 以及 LLVM/CLANG linker(在他们的站点上声明 linker 脚本和GNU LD是一样的,exceptions page没有说明),不支持overflow into another section
所以,为了让它更容易,也许有一些提示:
随着时间的推移,按文件名模式过滤会变得乏味。而且,它不允许您按符号过滤,只能按文件名模式和输入部分名称过滤。
如果你有像 AUTOSAR MemMap 这样的方法,你可以通过将它们放入新的部分来拆分代码,这些部分的命名与默认
.text, .rodata, .data, .bss
不同。 (不幸的是,GCC + CLANG 使用 attribute(( )) 而不是 C-standard 的 #pragma 或 _Pragma() 方式。 (AUTOSAR MemMap例子在最后)- 你可以分成快速和慢速代码,例如引入新的部分,如
.codefast, .codeslow
(例如 ISR 代码快,任务级代码“慢”) - 您可以分成 QM 和 ASIL 分区,引入像
.code_qm, .code_asila, .code_asilb
这样的部分
- 您可以拆分代码和配置/常量数据,将配置放入单独的可刷新部分,例如
.text, .rodata
部分可以拆分为.text, .const .postbuildconfig, .calib
.. 使用部分不同的编码风格,您可以在这里保持代码相同,但项目配置(例如 CAN 配置、过滤器链等)可以使用可配置的配置,您只需刷新一个新配置,而无需更新代码本身.text
--> 闪存扇区1.postbuildconfig
--> 闪存扇区2.calib
--> 闪存扇区 3
- 你可以分成快速和慢速代码,例如引入新的部分,如
在实际开始真正编译和 link 之前,也许中间构建阶段可以使用
中编写这样的 linker 预处理脚本objdump / nm / size / readelf
之类的工具首先扫描.o
文件以提供输出 object / 部分大小并根据特定标准通过脚本对其进行总结,例如输入内存布局和上述特殊部分或按大小排序和.o
文件名模式,您可以使用它来更新 linker 脚本以适应内存定义。所以,脚本可以尝试适应直到间隙尽可能多,然后切换到另一个内存。它甚至可以尝试生成稍后传递给 linker 的 linker 脚本部分。您可以在 perl 或 python.
连同到不同部分的内存映射,您现在可以像这样过滤:
MEMORY {
// Maybe you have two 128k areas separate by a gap
// Put the code in the first big part, and the more
// "configurable part" in the second area might even
// allow you to flash/update the separately
CODE1 : origin = 0x10000, len = 128k
// maybe here is a gap
CODE2 : origin = 0x10000, len = 64k
CONST : origin = 0x20000, len = 4k
CALIB : origin = 0x40000, len = 4k
POSTBUILD : origin = 0x50000, len = 48k
}
SECTIONS {
.code1 : {
*(.text) // default .text
*(.code_fast) // fast/slow code split
*(.code_qm) // qm/asil code split
} > CODE1
.code2 : {
*(.code_slow) // fast/slow code split
*(.code_asila) // qm/asil code split
*(.code_asilb) // qm/asil code split
} > CODE2
.const : {
*(.const)
*(.rodata)
} > CALIB
.calib : {
*(.calib)
*_calib.o(.calib) // in case you separate them also out into xxx_Calib.c files compiled to xxx_Calib.o files
} > CALIB
.postbuild : {
*(.postbuild)
*_PBCfg.o(.postbuild) // in case you separate them also out into xxx_PBCfg.c files compiled to xxx_PBCfg.o files
} > POSTBUILD
}
这种方法还允许您在 header 中准备布局,并通过某些外部工具生成配置和校准。
此外,它还可以更好地估计您的内存资源消耗,因此也可以估算如何以及在何处将它们 place/link 到内存部分。
这是代码中的 AUTOSAR MemMap 使用示例
#define XXX_START_SEC_CODE_FAST
#include "MemMap.h"
void XXX_Channel0_Notification(void) {
// Channel0 Finished Notification e.g. called from ISR
}
#define XXX_STOP_SEC_CODE_FAST
#include "MemMap.h"
MemMap.h 可以这样配置:
#if defined(XXX_START_SEC_CODE_FAST)
#undef XXX_START_SEC_CODE_FAST
// open section .codefast
#pragma section code ".codefast"
#elif defined(XXX_START_SEC_CODE_SLOW)
#undef XXX_START_SEC_CODE_SLOW
// open section .codeslow
#pragma section code ".codeslow"
#endif
#if defined(XXX_STOP_SEC_CODE_FAST)
#undef XXX_STOP_SEC_CODE_FAST
// back to default section
#pragma section code
#elif defined(XXX_STOP_SEC_CODE_SLOW)
#undef XXX_STOP_SEC_CODE_SLOW
// back to default section
#pragma section code
#endif
可以生成MemMap.h文件,通过扫描所有start/stop部分定义,并通过配置工具生成pragmas(甚至可以是Excel)。
优点是,您不会溢出所有可能的编译指示(例如不同的编译器 <-> 不同的方言),或者映射或不映射。因此,如果可能的话,您实际上可以在不同的项目中重用代码。