0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

使用分散文件指定栈和堆创建root执行区

技术让梦想更伟大 来源:技术让梦想更伟大 作者:李肖遥 2022-11-07 15:26 次阅读

使用分散文件指定栈和堆

ARM C 库提供了该函数的多种实现__user_setup_stackheap(),并且可以从分散文件中提供的信息中自动为您选择正确的一种。

要选择两个区域内存模型,请在名为ARM_LIB_HEAP和的分散文件中定义两个特殊的执行区域ARM_LIB_STACK。两个区域都有该EMPTY属性。这会导致库选择__user_setup_stackheap()使用符号值的非默认实现:

Image$$ARM_LIB_STACK$$Base

Image$$ARM_LIB_STACK$$ZI$$Limit

Image$$ARM_LIB_HEAP$$Base

Image$$ARM_LIB_HEAP$$ZI$$Limit
1234567

只能指定一个ARM_LIB_STACKARM_LIB_HEAP区域,并且必须分配一个大小,例如:

ARM_LIB_HEAP0x20100000EMPTY0x100000-0x8000;Heapstartsat1MB
;andgrowsupwards
ARM_LIB_STACK0x20200000EMPTY-0x8000;Stackspacestartsattheend
;ofthe2MBofRAM
;Andgrowsdownwardsfor32KB
12345

可以通过定义名为单一执行区域使用组合的栈和堆区域ARM_LIB_STACKHEAP,与EMPTY属性。这会导致__user_setup_stackheap()使用符号Image$$ARM_LIB_STACKHEAP$$BaseImage$$ARM_LIB_STACKHEAP$$ZI$$Limit的值。

注意如果您重新实现__user_setup_stackheap(),这将覆盖所有库里面的实现。

创建root执行区

要将区域指定为分散文件中的根区域,您可以:

  • 指定

    ABSOLUTE
    

    为执行区的属性(显式或允许它默认),并为第一个执行区和封闭加载区使用相同的地址。要使执行区地址与加载区地址相同,请执行以下任一操作:

    • 为执行区的基地址和加载区的基地址指定相同的数值。
    • 指定+0加载区中第一个执行区的偏移量。如果+0为加载区中的所有后续执行区指定零偏移(+0),则所有不跟随包含 ZI 的执行区的执行区也是根区。

以下示例显示了隐式定义的根区域:

LR_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);allROsections(mustincludesectionwith
;initialentrypoint)
}
...;restofscatter-loadingdescription
}
123456789
  • 使用FIXED执行区属性可以确保特定区域的加载地址和执行地址相同。您可以使用该FIXED属性将任何执行区放置在 ROM 中的特定地址。例如,以下内存映射显示了固定执行区:图 8. 固定执行区的内存映射

4546342c-5c64-11ed-a3b6-dac502259ad0.png

The following example shows the corresponding scatter-loading description:下面的例子给出了相应分散加载描述:

LR_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);ROsectionsotherthanthoseininit.o
}
ER_INIT0x080000FIXED;loadaddressandexecutionaddressofthis
;executionregionarefixedat0x80000
{
init.o(+RO);allROsectionsfrominit.o
}
...;restofscatter-loadingdescription
}
12345678910111213

Examples of misusing the FIXED attribute误用 FIXED 属性 例子

The following example shows common cases where the FIXED execution region attribute is misused:

LR10x8000
{
ER_LOW+00x1000
{
*(+RO)
}
;AtthispointthenextavailableLoadandExecutionaddressis0x8000+sizeof
;contentsofER_LOW.Themaximumsizeislimitedto0x1000sothenextavailableLoad
;andExecutionaddressisatmost0x9000
ER_HIGH0xF0000000FIXED
{
*(+RW+ZI)
}
;Therequiredexecutionaddressandloadaddressis0xF0000000.Thelinkerinserts
;0xF0000000-(0x8000+sizeof(ER_LOW))bytesofpaddingsothatloadaddressmatches
;executionaddress
}

;TheothercommonmisuseofFIXEDistogivealowerexecutionaddressthanthenext
;availableloadaddress.

LR_HIGH0x100000000
{
ER_LOW0x1000FIXED
{
*(+RO)
}
;ThenextavailableloadaddressinLR_HIGHis0x10000000.TherequiredExecution
;addressis0x1000.BecausethenextavailableloadaddressinLR_HIGHmustincrease
;monotonicallythelinkercannotgiveER_LOWaLoadAddresslowerthan0x10000000
}
12345678910111213141516171819202122232425262728293031

使用 FIXED 属性创建根区域

您可以FIXED在执行区分散文件中使用该属性来创建在固定地址加载和执行的根区。FIXED用于在单个加载区域内创建多个根区域,因此通常是单个 ROM 设备。例如,您可以使用它来将函数或数据块(例如常量表或校验和)放置在 ROM 中的固定地址,以便可以通过指针轻松访问。

例如,如果您指定将一些初始化代码放置在 ROM 的开头并在 ROM 的末尾放置一个校验和,则某些内存内容可能未被使用。使用*或.ANY模块选择器来填充初始化块末尾和数据块开头之间的区域。

为了使您的代码更易于维护和调试,建议您在分散文件中使用最少的布局规范,并将函数和数据的详细布局留给链接器。

您不能指定已部分链接的组件对象。例如,如果您将对象obj1.oobj2.o和部分链接obj3.o在一起以产生obj_all.o,则在生成的对象中会丢弃组件对象名称。因此,您不能按名称引用其中一个对象,例如,obj1.o。您只能引用组合对象obj_all.o

注意在某些情况下,使用FIXED和 单个加载区域是不合适的。指定固定位置的其他方式是:

  • 如果您的加载程序可以处理多个加载区域,请将 RO 代码或数据放在其自己的加载区域中。
  • 如果您不要求函数或数据位于 ROM 中的固定位置,请使用ABSOLUTE代替FIXED。然后加载器将数据从加载区复制到 RAM 中的指定地址。ABSOLUTE是默认属性。
  • 要将数据结构放置在内存映射 I/O 的位置??,请使用两个加载区域并指定UNINIT. UNINIT确保内存位置不会被初始化为零。

在特定地址放置函数和数据

通常,编译器从单个源文件生成 RO、RW 和 ZI 节。这些区域包含源文件中的所有代码和数据。要将单个函数或数据项放置在固定地址,您必须使链接器能够将函数或数据与其余输入文件分开处理。

链接器有两种方法可以让您将段放置在特定地址:

  • 您可以创建一个分散文件,该文件在所需地址处定义一个执行区,并带有仅选择一个段的段描述。
  • 对于特殊命名的段,链接器可以从段名中获取放置地址。这些专门命名的部分称为__at段。

要将函数或变量放置在特定地址,必须将其放置在其自己的段中。有几种方法可以做到这一点:

  • 将函数或数据项放在其自己的源文件中。
  • 使用到地方变量在一个单独的部分,在一个特定的地址__attribute__((at(address)))
  • 用于在指定段中放置函数和变量__attribute__((section("name")))
  • 使用AREA汇编语言中的指令。在汇编代码中,最小的可定位单元是AREA.
  • 使用--split_sections编译器选项为源文件中的每个函数生成一个 ELF 部分。此选项会导致某些函数的代码大小略有增加,因为它降低了在函数之间共享地址、数据和字符串文字的可能性。但是,当您指定armlink --remove这可以帮助减少最终固件镜像整体大小,使链接器能够删除未使用的函数。

在没有分散加载的情况下将变量放置在特定地址的示例

此示例显示如何修改源代码以将代码和数据放置在特定地址,并且不需要分散文件:1、创建main.c包含以下代码的源文件:

#includeexternintsqr(intn1);
intgSquared__attribute__((at(0x5000)));//Placeat0x5000intmain()
{
gSquared=sqr(3);
printf("Valuesquaredis:%d
",gSquared);
}
12345678910

2、创建function.c包含以下代码的源文件:

intsqr(intn1)
{
returnn1*n1;
}
1234

3、编译并链接源:

armcc-c-gfunction.c
armcc-c-gmain.c
armlink--mapfunction.omain.o-osquared.axf
123

--map选项用于生成内存映射文件即.map文件,同样--autoat是默认值

在此示例中,__attribute__((at(0x5000)))指定将全局变量gSquared放置在绝对地址处0x20000gSquared被放置在执行区ER$$.ARM.__AT_0x00005000和加载区中LR$$.ARM.__AT_0x00005000

The memory map shows:

...
LoadRegionLR$$.ARM.__AT_0x00005000(Base:0x00005000,Size:0x00000000,Max:0x00000004,ABSOLUTE)

ExecutionRegionER$$.ARM.__AT_0x00005000(Base:0x00005000,Size:0x00000004,Max:0x00000004,ABSOLUTE,UNINIT)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000050000x00000004ZeroRW15.ARM.__AT_0x00005000main.o

123456789

使用分散加载将变量放置在指定段中的示例

此示例显示如何使用分散文件修改源代码以将代码和数据放置在特定部分中:1、创建main.c包含以下代码的源文件:

#includeexternintsqr(intn1);
intgSquared__attribute__((section("foo")));//Placeinsectionfoointmain()
{
gSquared=sqr(3);
printf("Valuesquaredis:%d
",gSquared);
}
12345678910

2、创建function.c包含以下代码的源文件:

intsqr(intn1)
{
returnn1*n1;
}
1234

3、创建scatter.scat包含以下加载区域的分散文件:

LR10x00000x20000
{
ER10x00x2000
{
*(+RO);restofcodeandread-onlydata
}
ER20x80000x2000
{
main.o
}
ER30x100000x2000
{
function.o
*(foo);PlacegSquaredinER3
}
RAM0x200000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x200000
{
*(+RW,+ZI)
}
ARM_LIB_STACK0x800000EMPTY-0x10000
{
}
ARM_LIB_HEAP+0EMPTY0x10000
{
}
}
1234567891011121314151617181920212223242526

ARM_LIB_STACKARM_LIB_HEAP都需要,因为程序将与半主机库链接。4、编译并链接

armcc-c-gfunction.c
armcc-c-gmain.c
aarmlink--map--scatter=scatter.scatfunction.omain.o-osquared.axf
123

内存映射显示:

LoadRegionLR1(Base:0x00000000,Size:0x00001778,Max:0x00020000,ABSOLUTE)
...
ExecutionRegionER3(Base:0x00010000,Size:0x00000004,Max:0x00002000,ABSOLUTE)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000100000x00000004DataRW15foomain.o
...
12345678

注意

如果*(foo)从分散文件中省略,则该部分将放置在相同类型的区域中。在这个例子中就是RAM区。

使用分散加载将变量放置在特定地址的示例

1、创建main.c包含以下代码的源文件

#includeexternintsqr(intn1);

//Placeataddress0x10000constintgValue__attribute__((section(".ARM.__at_0x10000")))=3;

intmain()
{
intsquared;
squared=sqr(gValue);
printf("Valuesquaredis:%d
",squared);
}
12345678910111213

2、创建function.c包含以下代码的源文件:

intsqr(intn1)
{
returnn1*n1;
}
1234

3、创建scatter.scat包含以下加载区域的分散文件:

LR10x0
{
ER10x0
{
*(+RO);restofcodeandread-onlydata
}
ER2+0
{
function.o
*(.ARM.__at_0x10000);PlacegValueat0x10000
}
RAM0x200000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x200000
{
*(+RW,+ZI)
}
ARM_LIB_STACK0x800000EMPTY-0x10000
{
}
ARM_LIB_HEAP+0EMPTY0x10000
{
}
}
12345678910111213141516171819202122

ARM_LIB_STACKARM_LIB_HEAP都需要,因为程序将与半主机库链接。

4、编译并链接

armcc-c-gfunction.c
armcc-c-gmain.c
armlink--no_autoat--scatter=scatter.scat--mapfunction.omain.o-osquared.axf
123

内存映射显示变量放置ER2在地址处的执行区中0x11000:

...
ExecutionRegionER2(Base:0x00001598,Size:0x0000ea6c,Max:0xffffffff,ABSOLUTE)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000015980x0000000cCodeRO3.textfunction.o
0x000015a40x0000ea5cPAD
0x000100000x00000004DataRO15.ARM.__at_0x10000main.o...
12345678

在这个例子中,ER1的大小是未知的。因此,gValue可能放在ER1或 中ER2。要确保将gValue其放置在ER2中,您必须包含相应的选择器ER2并与--no_autoat命令行选项链接。如果省略--no_autoat, gValue将被放在一个单独的加载区域LR$$.ARM.__AT_0x00010000,包含执行区域ER$$.ARM.__AT_0x00020000

变量指定段

方式一

intvariable__attribute__((section("foo")))=10;
1
FLASH0x240000000x4000000
{
...;restofcode

ADDER0x08000000
{
file.o(foo);selectsectionfoofromfile.o
}
}
123456789

方式二

//placevariable1inasectioncalled.ARM.__at_0x00008000intvariable1__attribute__((at(0x8000)))=10;

//placevariable2inasectioncalled.ARM.__at_0x8000intvariable2__attribute__((section(".ARM.__at_0x8000")))=10;
12345
ER_FLASH0x80000x2000
{
*(+RO)
*(.ARM.__at_0x8000);
}
12345

函数地址指定

intsqr(intn1)__attribute__((section(".ARM.__at_0x20000")));

intsqr(intn1)
{
returnn1*n1;
}
123456

注意

  • 如果不使用分散加载,则该部分将放置在加载区的默认ER_RW执行区中LR_1
  • 如果源码中使用了未定义段名(分散加载文件中无此段名),则该部分将放置在定义的 RW 执行区中
  • --autoator--no_autoat不影响放置

使用分散加载显式放置命名部分

以下示例显示如何使用分散加载显式放置命名部分:

LR10x00x10000
{
ER10x00x2000;RootRegion,containinginitcode
{
init.o(INIT,+FIRST);placeinitcodeatexactly0x0
*(+RO);restofcodeandread-onlydata
}
RAM_RW0x400000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x400000
{
*(+RW)
}
RAM_ZI+0
{
*(+ZI)
}
DATABLOCK0x1FF000xFF;executionregionat0x1FF00
{;maximumspaceavailablefortableis0xFF
data.o(+RO-DATA);placeROdatabetween0x1FF00and0x1FFFF
}
}
1234567891011121314151617181920

在这个例子中,分散加载描述放置:

  • 初始化代码放在文件的INIT段中init.o。此示例显示该INIT段中的代码首先放置在地址 处0x0,然后是 RO 代码的其余部分以及除对象中的 RO 数据之外的所有 RO 数据data.o。
  • RAM 中的所有全局 RW 变量位于0x400000
  • data.o中的所有RO-DATA数据放置在0x1FF00

使用.ANY模块选择器放置未分配的段

链接器尝试将输入节放入特定的执行区。对于无法解析的任何输入部分,并且这些部分的放置不重要,您可以使用.ANY分散文件中的模块选择器。

在大多数情况下,使用单个.ANY选择器等同于使用*模块选择器。但是不同的是,您可以.ANY在多个执行区中指定。

放置未分配段的默认规则

默认情况下,链接器使用以下条件放置未分配的段:

  • 在当前拥有最多可用空间的执行区中放置一个未分配的段。您可以使用执行区域属性指定用于未分配段的最大空间量ANY_SIZE
  • 按大小降序对部分进行排序。

使用多个.ANY选择器时的放置规则

如果分散文件中存在多个.ANY选择器,则链接器采用最大大小的未分配段并将该段分配给具有足够可用空间的最具体的.ANY执行区。例如,.ANY(.text)被判断为比.ANY(+RO)更具体。

如果多个执行区具有相同的特性,则该段将分配给具有最多可用剩余空间的执行区。

例如:

  • 如果您有两个同样特定的执行区,其中一个的大小限制为0x2000,另一个没有限制,则所有段都分配给第二个无界.ANY区域。
  • 如果你有两个同样的特定执行区,其中一个大小限制为0x2000和另一个大小限制为0x3000,然后第一个段将被分配到第二个.ANY(区域大小限制0x3000),直到第二个.ANY剩余的大小减少到0x2000。从这一点开始,section在两个.ANY执行区域之间交替分配。

.ANY优先段

如果您有多个.ANY带有选择器的部分,您可以给出优先顺序,其中是从零向上的正整数。最高优先级被赋予具有最高整数的选择器。.ANYnum以下示例显示了如何使用:.ANYnum

lr10x80001024
{
er1+0512
{
.ANY1(+RO);evenlydistributedwither3
}
er2+0256
{
.ANY2(+RO);Highestpriority,sofilledfirst
}
er3+0256
{
.ANY1(+RO);evenlydistributedwither1
}
}
123456789101112131415

控制多个.ANY选择器的输入段的放置

.ANY通过使用不同的放置算法或不同的排序顺序,您可以修改链接器在使用多个选择器时放置未分配输入段的方式。以下命令行选项可用:

  • --any_placement=algorithm, 其中algorithm是first_fit,worst_fit,best_fit, 或next_fit之一
  • --any_sort_order=order,其中order是cmdlinedescending_size之一

first_fit当您想要按顺序填充区域时使用。best_fit当您想最大程度地填充区域时使用。worst_fit当您想要均匀填充区域时使用。使用相同大小的区域和部分worst_fit循环填充区域。当您需要更具确定性的填充模式时,请使用 next_fit。

如果链接器尝试将区域填充到其极限,就像使用first_fit和 一样best_fit,它可能会过度填充该区域。这是因为在将节分配给.ANY选择器之前,链接器生成的内容(例如填充和单板)是未知的。如果发生这种情况,您可能会看到以下错误:

Error: L6220E: Execution region regionname size (size bytes) exceeds limit (limit bytes).错误:L6220E:执行区regionname大小(size字节)超过限制(limit字节)。

--any_contingency选项可防止链接器将区域填充到最大值。它为链接器生成的内容保留了该区域大小的一部分,并且仅当其他区域没有空间时才填充此应急区域。默认情况下为first_fitbest_fit算法启用它,因为它们最有可能表现出这种行为。

指定允许放置未分配段的最大尺寸

执行区属性使您能够指定armlink可以用未分配的节填充的区域中的最大大小。ANY_SIZE max_size

使用此关键字时请注意以下限制:

  • max_size必须小于或等于区域大小
  • 您可以ANY_SIZE在没有.ANY选择器的区域上使用,但它会被armlink忽略

当ANY_SIZE存在时,armlink:

  • 不覆盖给定的.ANY大小。也就是说,它不会降低优先级,然后尝试在稍后放入更多段。
  • 从不重新计算意外事件。
  • 从不分配应急空间中的段。

ANY_SIZE不需要--any_contingency指定。但是,无论何时--any_contingency指定和ANY_SIZE未指定,armlink 都会尝试调整意外情况。目标是:

  • 永远不会溢出一个.ANY区域
  • 永远不要拒绝在应急保留空间中放置一个段。

如果您--any_contingency在命令行上指定,则对于已ANY_SIZE指定的区域将忽略它。它通常用于未ANY_SIZE指定的区域。

以下示例显示了如何使用ANY_SIZE

LOAD_REGION0x00x3000
{
ER_10x0ANY_SIZE0xF000x1000
{
.ANY
}
ER_20x0ANY_SIZE0xFB00x1000
{
.ANY
}
ER_30x0ANY_SIZE0x10000x1000
{
.ANY
}
}
123456789101112131415

在这个例子中:

ER_1为链接器生成的内容保留了0x100。ER_2为链接器生成的内容保留了0x50。这和--any_contingency的自动应急保留类似。ER_3没有预留空间。因此,100%的区域被填满,没有应急保留。省略ANY_SIZE参数会导致98%的区域被填满,只有2%的应急保留。

使用 __at 在外设寄存器上放置

要将未初始化的变量放置在外设寄存器上,您可以使用 ZI__at部分。假设一个寄存器可用于0x10000000,定义一个__at名为.ARM.__at_0x10000000. 例如:

intfoo__attribute__((section(".ARM.__at_0x10000000"),zero_init));
1
ER_PERIPHERAL0x10000000UNINIT
{
*(.ARM.__at_0x10000000)
}
1234

使用自动放置,并假设附近没有其他执行区0x10000000,链接器会自动创建一个UNINIT属性为 at的区域0x10000000。该UNINIT属性创建一个包含未初始化数据或内存映射 I/O 的执行区。

预留一个空区域

可以EMPTY在执行区分散加载描述中使用该属性来为堆栈保留一块空内存。

内存块不构成加载区的一部分,而是在执行时分配使用。因为它是作为虚拟 ZI 区域创建的,所以链接器使用以下符号来访问它:

  • Image$$region_name$$ZI$$Base
  • Image$$region_name$$ZI$$Limit
  • Image$$region_name$$ZI$$Length

如果长度为负值,则该地址被视为区域的结束地址。这必须是绝对地址而不是相对地址。

在以下示例中,执行区定义STACK 0x800000 EMPTY -0x10000定义了一个名为的区域STACK,该区域从 address 开始并在 address0x7F0000结束0x800000:

LR_10x80000;loadregionstartsat0x80000
{
STACK0x800000EMPTY-0x10000;regionendsat0x800000becauseofthe
;negativelength.Thestartoftheregion
;iscalculatedusingthelength.
{
;Emptyregionusedtoplacestack
}
HEAP+0EMPTY0x10000;regionstartsattheendofprevious
;region.Endofregioncalculatedusing
;positivelength
{
;Emptyregionusedtoplaceheap
}
...;restofscatter-loadingdescription...
}
12345678910111213141516

注意

为EMPTY执行区域创建的虚拟ZI区域在运行时不会初始化为零。

如果地址是相对的(+offset)形式并且长度是负的,链接器会产生一个错误。下图显示了该示例的图解表示。

图 9. 为堆栈保留一个区域

4582e688-5c64-11ed-a3b6-dac502259ad0.png

在这里插入图片描述

在本例中,链接器生成符号:

Image$$STACK$$ZI$$Base=0x7f0000
Image$$STACK$$ZI$$Limit=0x800000
Image$$STACK$$ZI$$Length=0x10000
Image$$HEAP$$ZI$$Base=0x800000
Image$$HEAP$$ZI$$Limit=0x810000
Image$$HEAP$$ZI$$Length=0x10000123456

该EMPTY属性仅适用于执行区。链接器生成警告并忽略EMPTY加载区定义中使用的属性。

链接器检查用于该EMPTY区域的地址空间是否与任何其他执行区域不一致。

在分散文件中使用预处理命令

您可以通过 C 预处理器传递分散文件。这允许访问 C 预处理器的所有功能。

使用分散文件中的第一行指定链接器调用以处理文件的预处理器命令。命令的格式如下:

#!preprocessor[
pre_processor_flags
]
123

最典型的命令是#! armcc -E. 这会通过armcc预处理器传递分散文件。

你可以:

  • 将预处理指令添加到分散文件的顶部
  • 在分散文件中使用简单的表达式评估。

例如,分散文件file.scat, 可能包含:

#!armcc-E#defineADDRESS0x20000000#include"include_file_1.h"

lr1ADDRESS
{
...
}
123456789

链接器解析预处理后的分散文件并将指令视为注释。

您还可以将分散文件的预处理与–predefine命令行选项结合使用。对于这个例子:

  • 修改file.scat以删除指令。#define ADDRESS 0x20000000
  • 指定命令:armlink --predefine="-DADDRESS=0x20000000" --scatter=file.scat

在分散文件中使用表达式求值以避免填充

使用ALIGN,ALIGNALL或FIXED在分散的文件属性可导致在镜像中的大量填充的。要删除此填充,您可以使用表达式计算来指定加载区和执行区的起始地址。内置函数AlignExpr可用于帮助您指定地址表达式。

避免在分散文件中填充的示例以下分散文件生成带有填充的图像:

LR10x4000
{
ER1+0ALIGN0x8000
{
...
}
}
1234567

使用ALIGN关键字ER10x8000加载视图和执行视图中的边界对齐。要在加载视图中对齐,链接器必须插入0x4000填充字节。

以下分散文件生成没有填充的图像:

LR10x4000
{
ER1AlignExpr(+0,0x8000)
{
...
}
}
1234567

使用AlignExpr的结果+00x8000边界对齐。这将创建一个执行区,其加载地址为0x4000但执行地址为0x8000。

审核编辑:郭婷

Image$$ARM_LIB_STACK$$Base

Image$$ARM_LIB_STACK$$ZI$$Limit

Image$$ARM_LIB_HEAP$$Base

Image$$ARM_LIB_HEAP$$ZI$$Limit
1234567

只能指定一个ARM_LIB_STACKARM_LIB_HEAP区域,并且必须分配一个大小,例如:

ARM_LIB_HEAP0x20100000EMPTY0x100000-0x8000;Heapstartsat1MB
;andgrowsupwards
ARM_LIB_STACK0x20200000EMPTY-0x8000;Stackspacestartsattheend
;ofthe2MBofRAM
;Andgrowsdownwardsfor32KB
12345

可以通过定义名为单一执行区域使用组合的栈和堆区域ARM_LIB_STACKHEAP,与EMPTY属性。这会导致__user_setup_stackheap()使用符号Image$$ARM_LIB_STACKHEAP$$BaseImage$$ARM_LIB_STACKHEAP$$ZI$$Limit的值。

注意如果您重新实现__user_setup_stackheap(),这将覆盖所有库里面的实现。

创建root执行区

要将区域指定为分散文件中的根区域,您可以:

  • 指定

    ABSOLUTE
    

    为执行区的属性(显式或允许它默认),并为第一个执行区和封闭加载区使用相同的地址。要使执行区地址与加载区地址相同,请执行以下任一操作:

    • 为执行区的基地址和加载区的基地址指定相同的数值。
    • 指定+0加载区中第一个执行区的偏移量。如果+0为加载区中的所有后续执行区指定零偏移(+0),则所有不跟随包含 ZI 的执行区的执行区也是根区。

以下示例显示了隐式定义的根区域:

LR_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);allROsections(mustincludesectionwith
;initialentrypoint)
}
...;restofscatter-loadingdescription
}
123456789
  • 使用FIXED执行区属性可以确保特定区域的加载地址和执行地址相同。您可以使用该FIXED属性将任何执行区放置在 ROM 中的特定地址。例如,以下内存映射显示了固定执行区:图 8. 固定执行区的内存映射

4546342c-5c64-11ed-a3b6-dac502259ad0.png

The following example shows the corresponding scatter-loading description:下面的例子给出了相应分散加载描述:

LR_10x040000;loadregionstartsat0x40000
{;startofexecutionregiondescriptions
ER_RO0x040000;loadaddress=executionaddress
{
*(+RO);ROsectionsotherthanthoseininit.o
}
ER_INIT0x080000FIXED;loadaddressandexecutionaddressofthis
;executionregionarefixedat0x80000
{
init.o(+RO);allROsectionsfrominit.o
}
...;restofscatter-loadingdescription
}
12345678910111213

Examples of misusing the FIXED attribute误用 FIXED 属性 例子

The following example shows common cases where the FIXED execution region attribute is misused:

LR10x8000
{
ER_LOW+00x1000
{
*(+RO)
}
;AtthispointthenextavailableLoadandExecutionaddressis0x8000+sizeof
;contentsofER_LOW.Themaximumsizeislimitedto0x1000sothenextavailableLoad
;andExecutionaddressisatmost0x9000
ER_HIGH0xF0000000FIXED
{
*(+RW+ZI)
}
;Therequiredexecutionaddressandloadaddressis0xF0000000.Thelinkerinserts
;0xF0000000-(0x8000+sizeof(ER_LOW))bytesofpaddingsothatloadaddressmatches
;executionaddress
}

;TheothercommonmisuseofFIXEDistogivealowerexecutionaddressthanthenext
;availableloadaddress.

LR_HIGH0x100000000
{
ER_LOW0x1000FIXED
{
*(+RO)
}
;ThenextavailableloadaddressinLR_HIGHis0x10000000.TherequiredExecution
;addressis0x1000.BecausethenextavailableloadaddressinLR_HIGHmustincrease
;monotonicallythelinkercannotgiveER_LOWaLoadAddresslowerthan0x10000000
}
12345678910111213141516171819202122232425262728293031

使用 FIXED 属性创建根区域

您可以FIXED在执行区分散文件中使用该属性来创建在固定地址加载和执行的根区。FIXED用于在单个加载区域内创建多个根区域,因此通常是单个 ROM 设备。例如,您可以使用它来将函数或数据块(例如常量表或校验和)放置在 ROM 中的固定地址,以便可以通过指针轻松访问。

例如,如果您指定将一些初始化代码放置在 ROM 的开头并在 ROM 的末尾放置一个校验和,则某些内存内容可能未被使用。使用*或.ANY模块选择器来填充初始化块末尾和数据块开头之间的区域。

为了使您的代码更易于维护和调试,建议您在分散文件中使用最少的布局规范,并将函数和数据的详细布局留给链接器。

您不能指定已部分链接的组件对象。例如,如果您将对象obj1.oobj2.o和部分链接obj3.o在一起以产生obj_all.o,则在生成的对象中会丢弃组件对象名称。因此,您不能按名称引用其中一个对象,例如,obj1.o。您只能引用组合对象obj_all.o

注意在某些情况下,使用FIXED和 单个加载区域是不合适的。指定固定位置的其他方式是:

  • 如果您的加载程序可以处理多个加载区域,请将 RO 代码或数据放在其自己的加载区域中。
  • 如果您不要求函数或数据位于 ROM 中的固定位置,请使用ABSOLUTE代替FIXED。然后加载器将数据从加载区复制到 RAM 中的指定地址。ABSOLUTE是默认属性。
  • 要将数据结构放置在内存映射 I/O 的位置??,请使用两个加载区域并指定UNINIT. UNINIT确保内存位置不会被初始化为零。

在特定地址放置函数和数据

通常,编译器从单个源文件生成 RO、RW 和 ZI 节。这些区域包含源文件中的所有代码和数据。要将单个函数或数据项放置在固定地址,您必须使链接器能够将函数或数据与其余输入文件分开处理。

链接器有两种方法可以让您将段放置在特定地址:

  • 您可以创建一个分散文件,该文件在所需地址处定义一个执行区,并带有仅选择一个段的段描述。
  • 对于特殊命名的段,链接器可以从段名中获取放置地址。这些专门命名的部分称为__at段。

要将函数或变量放置在特定地址,必须将其放置在其自己的段中。有几种方法可以做到这一点:

  • 将函数或数据项放在其自己的源文件中。
  • 使用到地方变量在一个单独的部分,在一个特定的地址__attribute__((at(address)))
  • 用于在指定段中放置函数和变量__attribute__((section("name")))
  • 使用AREA汇编语言中的指令。在汇编代码中,最小的可定位单元是AREA.
  • 使用--split_sections编译器选项为源文件中的每个函数生成一个 ELF 部分。此选项会导致某些函数的代码大小略有增加,因为它降低了在函数之间共享地址、数据和字符串文字的可能性。但是,当您指定armlink --remove这可以帮助减少最终固件镜像整体大小,使链接器能够删除未使用的函数。

在没有分散加载的情况下将变量放置在特定地址的示例

此示例显示如何修改源代码以将代码和数据放置在特定地址,并且不需要分散文件:1、创建main.c包含以下代码的源文件:

#includeexternintsqr(intn1);
intgSquared__attribute__((at(0x5000)));//Placeat0x5000intmain()
{
gSquared=sqr(3);
printf("Valuesquaredis:%d
",gSquared);
}
12345678910

2、创建function.c包含以下代码的源文件:

intsqr(intn1)
{
returnn1*n1;
}
1234

3、编译并链接源:

armcc-c-gfunction.c
armcc-c-gmain.c
armlink--mapfunction.omain.o-osquared.axf
123

--map选项用于生成内存映射文件即.map文件,同样--autoat是默认值

在此示例中,__attribute__((at(0x5000)))指定将全局变量gSquared放置在绝对地址处0x20000gSquared被放置在执行区ER$$.ARM.__AT_0x00005000和加载区中LR$$.ARM.__AT_0x00005000

The memory map shows:

...
LoadRegionLR$$.ARM.__AT_0x00005000(Base:0x00005000,Size:0x00000000,Max:0x00000004,ABSOLUTE)

ExecutionRegionER$$.ARM.__AT_0x00005000(Base:0x00005000,Size:0x00000004,Max:0x00000004,ABSOLUTE,UNINIT)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000050000x00000004ZeroRW15.ARM.__AT_0x00005000main.o

123456789

使用分散加载将变量放置在指定段中的示例

此示例显示如何使用分散文件修改源代码以将代码和数据放置在特定部分中:1、创建main.c包含以下代码的源文件:

#includeexternintsqr(intn1);
intgSquared__attribute__((section("foo")));//Placeinsectionfoointmain()
{
gSquared=sqr(3);
printf("Valuesquaredis:%d
",gSquared);
}
12345678910

2、创建function.c包含以下代码的源文件:

intsqr(intn1)
{
returnn1*n1;
}
1234

3、创建scatter.scat包含以下加载区域的分散文件:

LR10x00000x20000
{
ER10x00x2000
{
*(+RO);restofcodeandread-onlydata
}
ER20x80000x2000
{
main.o
}
ER30x100000x2000
{
function.o
*(foo);PlacegSquaredinER3
}
RAM0x200000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x200000
{
*(+RW,+ZI)
}
ARM_LIB_STACK0x800000EMPTY-0x10000
{
}
ARM_LIB_HEAP+0EMPTY0x10000
{
}
}
1234567891011121314151617181920212223242526

ARM_LIB_STACKARM_LIB_HEAP都需要,因为程序将与半主机库链接。4、编译并链接

armcc-c-gfunction.c
armcc-c-gmain.c
aarmlink--map--scatter=scatter.scatfunction.omain.o-osquared.axf
123

内存映射显示:

LoadRegionLR1(Base:0x00000000,Size:0x00001778,Max:0x00020000,ABSOLUTE)
...
ExecutionRegionER3(Base:0x00010000,Size:0x00000004,Max:0x00002000,ABSOLUTE)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000100000x00000004DataRW15foomain.o
...
12345678

注意

如果*(foo)从分散文件中省略,则该部分将放置在相同类型的区域中。在这个例子中就是RAM区。

使用分散加载将变量放置在特定地址的示例

1、创建main.c包含以下代码的源文件

#includeexternintsqr(intn1);

//Placeataddress0x10000constintgValue__attribute__((section(".ARM.__at_0x10000")))=3;

intmain()
{
intsquared;
squared=sqr(gValue);
printf("Valuesquaredis:%d
",squared);
}
12345678910111213

2、创建function.c包含以下代码的源文件:

intsqr(intn1)
{
returnn1*n1;
}
1234

3、创建scatter.scat包含以下加载区域的分散文件:

LR10x0
{
ER10x0
{
*(+RO);restofcodeandread-onlydata
}
ER2+0
{
function.o
*(.ARM.__at_0x10000);PlacegValueat0x10000
}
RAM0x200000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x200000
{
*(+RW,+ZI)
}
ARM_LIB_STACK0x800000EMPTY-0x10000
{
}
ARM_LIB_HEAP+0EMPTY0x10000
{
}
}
12345678910111213141516171819202122

ARM_LIB_STACKARM_LIB_HEAP都需要,因为程序将与半主机库链接。

4、编译并链接

armcc-c-gfunction.c
armcc-c-gmain.c
armlink--no_autoat--scatter=scatter.scat--mapfunction.omain.o-osquared.axf
123

内存映射显示变量放置ER2在地址处的执行区中0x11000:

...
ExecutionRegionER2(Base:0x00001598,Size:0x0000ea6c,Max:0xffffffff,ABSOLUTE)

BaseAddrSizeTypeAttrIdxESectionNameObject

0x000015980x0000000cCodeRO3.textfunction.o
0x000015a40x0000ea5cPAD
0x000100000x00000004DataRO15.ARM.__at_0x10000main.o...
12345678

在这个例子中,ER1的大小是未知的。因此,gValue可能放在ER1或 中ER2。要确保将gValue其放置在ER2中,您必须包含相应的选择器ER2并与--no_autoat命令行选项链接。如果省略--no_autoat, gValue将被放在一个单独的加载区域LR$$.ARM.__AT_0x00010000,包含执行区域ER$$.ARM.__AT_0x00020000

变量指定段

方式一

intvariable__attribute__((section("foo")))=10;
1
FLASH0x240000000x4000000
{
...;restofcode

ADDER0x08000000
{
file.o(foo);selectsectionfoofromfile.o
}
}
123456789

方式二

//placevariable1inasectioncalled.ARM.__at_0x00008000intvariable1__attribute__((at(0x8000)))=10;

//placevariable2inasectioncalled.ARM.__at_0x8000intvariable2__attribute__((section(".ARM.__at_0x8000")))=10;
12345
ER_FLASH0x80000x2000
{
*(+RO)
*(.ARM.__at_0x8000);
}
12345

函数地址指定

intsqr(intn1)__attribute__((section(".ARM.__at_0x20000")));

intsqr(intn1)
{
returnn1*n1;
}
123456

注意

  • 如果不使用分散加载,则该部分将放置在加载区的默认ER_RW执行区中LR_1
  • 如果源码中使用了未定义段名(分散加载文件中无此段名),则该部分将放置在定义的 RW 执行区中
  • --autoator--no_autoat不影响放置

使用分散加载显式放置命名部分

以下示例显示如何使用分散加载显式放置命名部分:

LR10x00x10000
{
ER10x00x2000;RootRegion,containinginitcode
{
init.o(INIT,+FIRST);placeinitcodeatexactly0x0
*(+RO);restofcodeandread-onlydata
}
RAM_RW0x400000(0x1FF00-0x2000);RW&ZIdatatobeplacedat0x400000
{
*(+RW)
}
RAM_ZI+0
{
*(+ZI)
}
DATABLOCK0x1FF000xFF;executionregionat0x1FF00
{;maximumspaceavailablefortableis0xFF
data.o(+RO-DATA);placeROdatabetween0x1FF00and0x1FFFF
}
}
1234567891011121314151617181920

在这个例子中,分散加载描述放置:

  • 初始化代码放在文件的INIT段中init.o。此示例显示该INIT段中的代码首先放置在地址 处0x0,然后是 RO 代码的其余部分以及除对象中的 RO 数据之外的所有 RO 数据data.o。
  • RAM 中的所有全局 RW 变量位于0x400000
  • data.o中的所有RO-DATA数据放置在0x1FF00

使用.ANY模块选择器放置未分配的段

链接器尝试将输入节放入特定的执行区。对于无法解析的任何输入部分,并且这些部分的放置不重要,您可以使用.ANY分散文件中的模块选择器。

在大多数情况下,使用单个.ANY选择器等同于使用*模块选择器。但是不同的是,您可以.ANY在多个执行区中指定。

放置未分配段的默认规则

默认情况下,链接器使用以下条件放置未分配的段:

  • 在当前拥有最多可用空间的执行区中放置一个未分配的段。您可以使用执行区域属性指定用于未分配段的最大空间量ANY_SIZE
  • 按大小降序对部分进行排序。

使用多个.ANY选择器时的放置规则

如果分散文件中存在多个.ANY选择器,则链接器采用最大大小的未分配段并将该段分配给具有足够可用空间的最具体的.ANY执行区。例如,.ANY(.text)被判断为比.ANY(+RO)更具体。

如果多个执行区具有相同的特性,则该段将分配给具有最多可用剩余空间的执行区。

例如:

  • 如果您有两个同样特定的执行区,其中一个的大小限制为0x2000,另一个没有限制,则所有段都分配给第二个无界.ANY区域。
  • 如果你有两个同样的特定执行区,其中一个大小限制为0x2000和另一个大小限制为0x3000,然后第一个段将被分配到第二个.ANY(区域大小限制0x3000),直到第二个.ANY剩余的大小减少到0x2000。从这一点开始,section在两个.ANY执行区域之间交替分配。

.ANY优先段

如果您有多个.ANY带有选择器的部分,您可以给出优先顺序,其中是从零向上的正整数。最高优先级被赋予具有最高整数的选择器。.ANYnum以下示例显示了如何使用:.ANYnum

lr10x80001024
{
er1+0512
{
.ANY1(+RO);evenlydistributedwither3
}
er2+0256
{
.ANY2(+RO);Highestpriority,sofilledfirst
}
er3+0256
{
.ANY1(+RO);evenlydistributedwither1
}
}
123456789101112131415

控制多个.ANY选择器的输入段的放置

.ANY通过使用不同的放置算法或不同的排序顺序,您可以修改链接器在使用多个选择器时放置未分配输入段的方式。以下命令行选项可用:

  • --any_placement=algorithm, 其中algorithm是first_fit,worst_fit,best_fit, 或next_fit之一
  • --any_sort_order=order,其中order是cmdlinedescending_size之一

first_fit当您想要按顺序填充区域时使用。best_fit当您想最大程度地填充区域时使用。worst_fit当您想要均匀填充区域时使用。使用相同大小的区域和部分worst_fit循环填充区域。当您需要更具确定性的填充模式时,请使用 next_fit。

如果链接器尝试将区域填充到其极限,就像使用first_fit和 一样best_fit,它可能会过度填充该区域。这是因为在将节分配给.ANY选择器之前,链接器生成的内容(例如填充和单板)是未知的。如果发生这种情况,您可能会看到以下错误:

Error: L6220E: Execution region regionname size (size bytes) exceeds limit (limit bytes).错误:L6220E:执行区regionname大小(size字节)超过限制(limit字节)。

--any_contingency选项可防止链接器将区域填充到最大值。它为链接器生成的内容保留了该区域大小的一部分,并且仅当其他区域没有空间时才填充此应急区域。默认情况下为first_fitbest_fit算法启用它,因为它们最有可能表现出这种行为。

指定允许放置未分配段的最大尺寸

执行区属性使您能够指定armlink可以用未分配的节填充的区域中的最大大小。ANY_SIZE max_size

使用此关键字时请注意以下限制:

  • max_size必须小于或等于区域大小
  • 您可以ANY_SIZE在没有.ANY选择器的区域上使用,但它会被armlink忽略

当ANY_SIZE存在时,armlink:

  • 不覆盖给定的.ANY大小。也就是说,它不会降低优先级,然后尝试在稍后放入更多段。
  • 从不重新计算意外事件。
  • 从不分配应急空间中的段。

ANY_SIZE不需要--any_contingency指定。但是,无论何时--any_contingency指定和ANY_SIZE未指定,armlink 都会尝试调整意外情况。目标是:

  • 永远不会溢出一个.ANY区域
  • 永远不要拒绝在应急保留空间中放置一个段。

如果您--any_contingency在命令行上指定,则对于已ANY_SIZE指定的区域将忽略它。它通常用于未ANY_SIZE指定的区域。

以下示例显示了如何使用ANY_SIZE

LOAD_REGION0x00x3000
{
ER_10x0ANY_SIZE0xF000x1000
{
.ANY
}
ER_20x0ANY_SIZE0xFB00x1000
{
.ANY
}
ER_30x0ANY_SIZE0x10000x1000
{
.ANY
}
}
123456789101112131415

在这个例子中:

ER_1为链接器生成的内容保留了0x100。ER_2为链接器生成的内容保留了0x50。这和--any_contingency的自动应急保留类似。ER_3没有预留空间。因此,100%的区域被填满,没有应急保留。省略ANY_SIZE参数会导致98%的区域被填满,只有2%的应急保留。

使用 __at 在外设寄存器上放置

要将未初始化的变量放置在外设寄存器上,您可以使用 ZI__at部分。假设一个寄存器可用于0x10000000,定义一个__at名为.ARM.__at_0x10000000. 例如:

intfoo__attribute__((section(".ARM.__at_0x10000000"),zero_init));
1
ER_PERIPHERAL0x10000000UNINIT
{
*(.ARM.__at_0x10000000)
}
1234

使用自动放置,并假设附近没有其他执行区0x10000000,链接器会自动创建一个UNINIT属性为 at的区域0x10000000。该UNINIT属性创建一个包含未初始化数据或内存映射 I/O 的执行区。

预留一个空区域

可以EMPTY在执行区分散加载描述中使用该属性来为堆栈保留一块空内存。

内存块不构成加载区的一部分,而是在执行时分配使用。因为它是作为虚拟 ZI 区域创建的,所以链接器使用以下符号来访问它:

  • Image$$region_name$$ZI$$Base
  • Image$$region_name$$ZI$$Limit
  • Image$$region_name$$ZI$$Length

如果长度为负值,则该地址被视为区域的结束地址。这必须是绝对地址而不是相对地址。

在以下示例中,执行区定义STACK 0x800000 EMPTY -0x10000定义了一个名为的区域STACK,该区域从 address 开始并在 address0x7F0000结束0x800000:

LR_10x80000;loadregionstartsat0x80000
{
STACK0x800000EMPTY-0x10000;regionendsat0x800000becauseofthe
;negativelength.Thestartoftheregion
;iscalculatedusingthelength.
{
;Emptyregionusedtoplacestack
}
HEAP+0EMPTY0x10000;regionstartsattheendofprevious
;region.Endofregioncalculatedusing
;positivelength
{
;Emptyregionusedtoplaceheap
}
...;restofscatter-loadingdescription...
}
12345678910111213141516

注意

为EMPTY执行区域创建的虚拟ZI区域在运行时不会初始化为零。

如果地址是相对的(+offset)形式并且长度是负的,链接器会产生一个错误。下图显示了该示例的图解表示。

图 9. 为堆栈保留一个区域

4582e688-5c64-11ed-a3b6-dac502259ad0.png

在这里插入图片描述

在本例中,链接器生成符号:

Image$$STACK$$ZI$$Base=0x7f0000
Image$$STACK$$ZI$$Limit=0x800000
Image$$STACK$$ZI$$Length=0x10000
Image$$HEAP$$ZI$$Base=0x800000
Image$$HEAP$$ZI$$Limit=0x810000
Image$$HEAP$$ZI$$Length=0x10000123456

该EMPTY属性仅适用于执行区。链接器生成警告并忽略EMPTY加载区定义中使用的属性。

链接器检查用于该EMPTY区域的地址空间是否与任何其他执行区域不一致。

在分散文件中使用预处理命令

您可以通过 C 预处理器传递分散文件。这允许访问 C 预处理器的所有功能。

使用分散文件中的第一行指定链接器调用以处理文件的预处理器命令。命令的格式如下:

#!preprocessor[
pre_processor_flags
]
123

最典型的命令是#! armcc -E. 这会通过armcc预处理器传递分散文件。

你可以:

  • 将预处理指令添加到分散文件的顶部
  • 在分散文件中使用简单的表达式评估。

例如,分散文件file.scat, 可能包含:

#!armcc-E#defineADDRESS0x20000000#include"include_file_1.h"

lr1ADDRESS
{
...
}
123456789

链接器解析预处理后的分散文件并将指令视为注释。

您还可以将分散文件的预处理与–predefine命令行选项结合使用。对于这个例子:

  • 修改file.scat以删除指令。#define ADDRESS 0x20000000
  • 指定命令:armlink --predefine="-DADDRESS=0x20000000" --scatter=file.scat

在分散文件中使用表达式求值以避免填充

使用ALIGN,ALIGNALL或FIXED在分散的文件属性可导致在镜像中的大量填充的。要删除此填充,您可以使用表达式计算来指定加载区和执行区的起始地址。内置函数AlignExpr可用于帮助您指定地址表达式。

避免在分散文件中填充的示例以下分散文件生成带有填充的图像:

LR10x4000
{
ER1+0ALIGN0x8000
{
...
}
}
1234567

使用ALIGN关键字ER10x8000加载视图和执行视图中的边界对齐。要在加载视图中对齐,链接器必须插入0x4000填充字节。

以下分散文件生成没有填充的图像:

LR10x4000
{
ER1AlignExpr(+0,0x8000)
{
...
}
}
1234567

使用AlignExpr的结果+00x8000边界对齐。这将创建一个执行区,其加载地址为0x4000但执行地址为0x8000。

审核编辑:郭婷


声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 代码
    +关注

    关注

    30

    文章

    4744

    浏览量

    68343

原文标题:在分散文件中使用表达式求值以避免填充

文章出处:【微信号:技术让梦想更伟大,微信公众号:技术让梦想更伟大】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    AG32 下的分散加载

    AG32 下的分散加载 分散加载: 在实际应用中,有时候需要强制把一段数据或一段代码固定到指定的一个地址。这个时候,就不能使用默认的编译配置,需要自己来配置连接属性。 AG32 中可以通过修改ld
    发表于 09-20 15:26

    MDBT50Q-512K基于Nordic nRF52833 SoC解决方案设计的BT 5.2堆栈模块

    电子发烧友网站提供《MDBT50Q-512K基于Nordic nRF52833 SoC解决方案设计的BT 5.2堆栈模块.pdf》资料免费下载
    发表于 06-22 09:17 1次下载

    创建DMA通道时,能否将DMA缓冲的大小指定为8字节,并将DMA缓冲的编号指定为1?

    创建 DMA 通道时,能否将 DMA 缓冲的大小指定为 8 字节,并将 DMA 缓冲的编号指定为 1?
    发表于 05-31 07:46

    如何用加载分散法将软件中部分变量从内部RAM转移到外部RAM?

    如何用加载分散法将软件中部分变量从内部RAM转移到外部RAM, 加载分散文件怎么设置?需要设置吗?
    发表于 05-10 07:52

    深入分析一下MDK的分散加载文件

    ARM C 库提供了函数 __user_setup_stackheap() 的多个实现,并且可以根据分散文件中提供的信息自动选择正确的一种。
    发表于 04-28 14:21 726次阅读
    深入分析一下MDK的<b class='flag-5'>分散</b>加载<b class='flag-5'>文件</b>

    rt-thread studio执行清空项目的时候发现系统找不到指定文件怎么解决?

    在基于rt-thread studio开发时,出于尝试添加了一些组件,后来又删掉了一些不需要的组件,重新编译的时候抛出错误,错误指出删掉的组件的相关.o文件还是存在,执行清空项目的时候发现系统找不到
    发表于 03-05 06:18

    的区别和使用注意事项

    是在计算机科学中广泛使用的两种数据结构,它们具有不同的用途和特点。的区别涉及到内存分配、访问方式、数据存储等方面。在使用
    的头像 发表于 01-18 17:24 2061次阅读

    e² studio创建lib文件及使用

    e² studio创建lib文件及使用
    的头像 发表于 01-18 08:06 423次阅读
    e² studio<b class='flag-5'>创建</b>lib<b class='flag-5'>文件</b>及使用

    如何创建重叠的封装文件

    创建重叠的封装文件是一种常用的软件设计模式,它允许程序员使用多层次的连接和封装来保护数据和功能。下面介绍如何创建重叠的封装文件。 重叠的封装是一种软件设计模式,可用于
    的头像 发表于 01-07 16:51 535次阅读

    Oracle如何执行sql脚本文件

    如何使用Oracle来执行SQL脚本文件。 一、什么是SQL脚本文件 SQL脚本文件是一个包含了一系列SQL语句的文本文件,可以用于
    的头像 发表于 12-06 10:51 6425次阅读

    java虚拟机内存包括远空间内存吗

    Java虚拟机(JVM)内存是Java程序执行时所使用的内存空间的总称,包括了Java、方法、本地方法、虚拟机和程序计数器等多个部分
    的头像 发表于 12-05 14:15 377次阅读

    jvm内存区域由哪几部分组成

    JVM(Java Virtual Machine)是Java程序运行的环境,在JVM中存在着多个不同功能的内存区域。这些内存区域可以被分为几个部分,包括内存、内存、方法、PC寄存器和本地方法
    的头像 发表于 12-05 14:10 785次阅读

    如何在Linux中重置忘记的root密码

    忘记了 Linux root 密码?不用担心!本文提供了简单易懂的说明,可以安全地重置和修改 root 密码。
    的头像 发表于 12-04 14:51 2521次阅读
    如何在Linux中重置忘记的<b class='flag-5'>root</b>密码

    linux怎么执行vim编辑的程序

    在Linux系统中执行vim编辑的程序非常简单,只需要依次执行以下步骤: 打开终端:在Linux系统中,按下Ctrl+Alt+T组合键可以快速打开终端。 输入vim命令并指定文件:在终
    的头像 发表于 11-28 15:23 861次阅读

    codeblocks怎么创建c文件

    在 CodeBlocks 中,你可以按照以下步骤创建一个新的 C 文件: 步骤 1: 打开 CodeBlocks 首先,你需要打开 CodeBlocks IDE。你可以在开始菜单或快捷方式上找到它
    的头像 发表于 11-26 10:21 4501次阅读