CrossCore Embedded Studio 中修改 LDF 文件

news/2025/3/13 22:05:55/

CrossCore Embedded Studio 中修改 LDF 文件

引言
当遇到链接器错误(如本文档"常见错误"部分所述)时,解决方案通常需要掌握修改 LDF 文件来控制链接过程的技巧。虽然本文档内容并非详尽无遗,但将详细说明修改 LDF 文件的基本要素,以及如何使用项目选项和编译器编译指令辅助链接器。

Blackfin 与系统构建器
在 CrossCore Embedded Studio 中,Blackfin 和 SHARC 处理器都使用生成的 LDF 文件。通过新建项目向导或通过系统配置对话框(可通过在项目资源管理器窗口中双击 *.svc 文件访问)添加"启动代码/LDF"插件来添加 LDF。在尝试修改 LDF 时,需要注意该插件的某些特性。

首要考虑的是"启动代码/LDF"插件的 LDF 部分提供了多个选项,可用于控制系统构建器自动生成 LDF 的方式。此选项的优点是无需深入了解 LDF 命令,但明显缺点是相比手动修改 LDF,这些项目选项提供的灵活性较低。

修改 MEMORY{...} 映射
LDF 包含 MEMORY{...} 块,用于定义处理器上的各种内存范围,以便链接器知道如何组合可执行文件。CrossCore Embedded Studio 包含的默认 LDF 以注释形式列出了处理器的所有有效内存范围,然后在 MEMORY{...} 块中定义每个内存范围。例如:

LDF

MEMORY

{

  MEM_L1_SCRATCH    { START(0xFFB00000) END(0xFFB00FFF) TYPE(RAM) WIDTH(8) }

  MEM_L1_CODE_CACHE { START(0xFFA10000) END(0xFFA13FFF) TYPE(RAM) WIDTH(8) }

  MEM_L1_CODE       { START(0xFFA00000) END(0xFFA0BFFF) TYPE(RAM) WIDTH(8) }

  ...

}

对于 Blackfin,内存映射较为简单,因为其宽度固定为 8。而 SHARC 处理器的内存段可以有多种宽度:8、16、32、48 或 64 位。在修改或定义新段时,需确保内存范围(START 到 END)对指定的 WIDTH 有效。还需注意,连接到外部内存接口的内存宽度必须与该内存或总线的物理宽度一致。例如,21369 的外部内存接口为 32 位,因此宽度必须设为 32。

对于系统构建器生成的 LDF,需注意其重新生成时会覆盖对内存映射的修改。可以通过在适当的 VDSG 部分插入新定义来添加新段。此外,对于 SDRAM 内存范围,可通过系统配置对话框中 LDF 部分的"启动代码/LDF"插件启用"自定义分区"选项,这将用VDSG部分插入新定义来添加新段。此外,对于SDRAM内存范围,可通过系统配置对话框中LDF部分的"启动代码/LDF"插件启用"自定义分区"选项,这将用VDSG 标签包裹 MEMORY{...} 块中的 SDRAM 定义,从而允许自定义内存范围。


默认 LDF 使用宏来方便地引用链接中的对象组,并作为整体控制其放置,而无需单独引用每个对象。最基本的宏(根据目标不同可能有变体)是 OBJECTS 和OBJECTS和LIBRARIES。

LIBRARIES 宏提供对链接到项目中的 .dlb 库文件内所有对象的集体引用,而LIBRARIES宏提供对链接到项目中的.dlb库文件内所有对象的集体引用,而OBJECTS 宏提供对项目中源文件经编译/汇编处理生成的 .doj 目标文件的集体引用。

大多数情况下,这两个宏已足够。但有时可能需要将某些对象/库单独列为更高优先级,或与默认的 OBJECTS 和OBJECTS和LIBRARIES 集合区分开。此时可自定义宏来包含所需对象/库。例如:

LDF

$MY_OBJS_AND_LIBS = main.doj, afunction.doj, mylibrary.dlb;

随后可在 SECTIONS{...} 块中使用 INPUT_SECTIONS 命令引用该宏,将其放置在特定内存段:

LDF

aSection

{

  INPUT_SECTIONS($MY_OBJS_AND_LIBS(program))

} > MEM_L1_CODE

修改 SECTIONS{...}
控制对象的最终放置位置取决于 LDF 配置。LDF 中包含输出段命令,用于指示链接器将哪些对象(及其部分)组合后放置在内存段中。

需牢记 LDF 中的所有命令按出现顺序处理。输出段命令基本形式如下:

LDF

output_section_name

{

  section_commands

  // 例如:

  INPUT_SECTIONS($OBJECTS(input_section_name))

} > memory_segment

还可使用其他选项控制内存类型和初始化(零初始化、非初始化等),但本文档不涉及这些内容。输出段命令的详细信息请参考《链接器和实用程序手册》。

output_section_name 是帮助链接器根据后续的 section_commands 创建不同对象组的名称,以便为其分配空间并放置到 MEMORY{...} 块定义的内存段中。该名称可任意指定,但在其所属的 PROCESSOR{...} 块中必须唯一。

完整输出段命令的基本示例如下:

LDF

an_output_section

{

  INPUT_SECTION_ALIGN(4)

  INPUT_SECTIONS($OBJECTS(program) $LIBRARIES(program))

} > MEM_L1_CODE

在此示例中,输出段名称 "an_output_section" 映射到 MEMORY{...} 映射中定义的 "MEM_L1_CODE"。段命令包括:

  1. INPUT_SECTION_ALIGN(4):指示链接器将所有对象按 4 字边界对齐。例如,若对象为 3 字,则跳过下一字,下一对象从其后开始,确保所有内容占用 4 字节的倍数。
  2. INPUT_SECTIONS:从 OBJECTS 和OBJECTS和LIBRARIES 宏包含的对象中提取 'program' 段,并将其链接到指定的 MEM_L1_CODE 内存段。

INPUT_SECTIONS 命令与 section/default_section 编译指令
所有目标文件 (.doj) 都包含"段"。不同数据类型有默认段名,例如数据放在 'data1',代码放在 'program',零初始化数据放在 'bsz' 等。也可自定义段名,这对控制对象位置非常有用。

推荐使用 C/C++ 源文件中的 "section" 和 "default_section" 编译指令,以及汇编文件中的 ".section" 关键字来实现。例如,若源文件包含多个函数,并希望将某一函数与默认 "program" 段中的其他函数分开以便更精细地控制其位置:

C

#pragma section("MyData") 

int myFirstLargeArray[10000];

int mySecondLargeArray[10000];

#pragma section("MyCode")

void myFunction

{

  ...

}

在此示例中,"myFirstLargeArray" 将被放置在 "MyData" 段,而 "mySecondLargeArray" 因编译指令作用域结束,将被放置在默认段(如 "data1")。函数 myFunction 由下一个 section 编译指令放置在 "MyCode" 段。

'default_section' 编译指令可用于在更大范围内控制段名,其最大作用域到当前源文件结束,之后编译器将恢复默认段。例如:

C

#pragma default_section(DATA, "MyData")

int myFirstLargeArray[10000];

int mySecondLargeArray[10000];

#pragma default_section(DATA, "data1")

int myThirdLargeArray[10000];

在此示例中,将所有 "DATA" 的段名改为 "MyData",因此前两个数组使用该段名。将 DATA 的默认段重置为 "data1" 后,第三个数组将使用该段名,直至再次更改或文件结束。

了解编译器/汇编器如何为对象分配段,以及链接器如何处理输出段命令后,下一步是将这些知识结合以控制对象的放置。

最基本的方法是通过 section 编译指令/关键字,利用 LDF 中的现有命令放置对象。例如,若要在 BF537 目标的 SDRAM 中放置数组,可查看 BF537 LDF 中的如下命令:

LDF

sdram0_bank1

{

  INPUT_SECTION_ALIGN(4)

  INPUT_SECTIONS( $OBJECTS(sdram0_data) $LIBRARIES(sdram0_data))

  ...

} > MEM_SDRAM0_BANK1

将数组放置在此段的方法:

C

#pragma section("sdram0_data")

int myLargeArrayInSDRAM[10000];

也可通过 section 编译指令定义自定义段名,然后在 LDF 中添加 INPUT_SECTIONS 命令:

C

#pragma section("myCustomData") 

int myLargeArrayInSDRAM[10000];

在 LDF 中修改:

LDF

sdram0_bank1

{

  INPUT_SECTION_ALIGN(4)

  INPUT_SECTIONS( $OBJECTS(sdram0_data) $LIBRARIES(sdram0_data))

  INPUT_SECTIONS( $OBJECTS(myCustomData))

  ...

} > MEM_SDRAM0_BANK1

在更复杂的场景中,若需将对象与其他对象分开映射以控制其优先级,可使用自定义段名并添加输出段命令:

LDF

my_sdram_data

{

  INPUT_SECTION_ALIGN(4)

  INPUT_SECTIONS( $OBJECTS(myCustomData))

  ...

} > MEM_SDRAM0_BANK1

INPUT_SECTIONS 命令的直接引用
INPUT_SECTIONS 命令可直接引用链接中的对象以控制其段放置。例如,假设 "main.c" 文件包含多个 "program" 和 "data1" 段,生成 main.doj 对象,可通过以下命令放置其段:

LDF

L1_code

  INPUT_SECTIONS(main.doj(program))

} >MEM_L1_CODE

L1_data_a

{

  INPUT_SECTIONS(main.doj(data1))

}

类似地,可通过仅引用库来放置库中所有对象的 "program" 段:

LDF

INPUT_SECTIONS(libc532y.dlb(program))

更高级的用法可定位库中的特定对象:

LDF

INPUT_SECTIONS( libc532y.dlb [ sprintf32.doj (program) ] )

注意:括号的间距非常重要,错误使用会导致链接器错误。

输出段命令的优先级
链接器按 LDF 中的命令顺序处理。若 INPUT_SECTIONS 命令同时引用 OBJECTS 和OBJECTS和LIBRARIES 宏,则 $OBJECTS 优先:

LDF

INPUT_SECTIONS($OBJECTS(program) $LIBRARIES(program))

修改默认 LDF 时,需注意可能将对象映射到不希望的位置,并确保自定义命令位于默认命令之前。默认流程是 LDF 先填充 L1 内存,再 L2,最后 SDRAM。所有对象都包含在 $OBJECTS 宏中,因此需注意默认命令可能将对象放置在 L1 内存中,而您可能希望其位于 SDRAM。

为避免此问题,可在 LDF 的 SECTIONS{...} 块开头使用自定义输出段命令,确保其优先处理。对于生成的 LDF,使用 SECTIONS{...} 块中的第一组 $VDSG 标签。

RESOLVE 命令
LDF 提供 RESOLVE() 命令用于控制符号位置。例如,默认 Blackfin LDF 使用以下命令将 'start' 符号放置在 L1 代码内存的首地址:

LDF

RESOLVE(start, 0xFFA00000)

此原则可用于将特定函数等符号固定在指定地址。需注意对齐要求,避免因未对齐导致异常。

多核目标的多个 DXE/输出文件
生成多个 DXE 文件本质上是修改 LDF 以控制每个 DXE 链接的对象。需为每个 DXE 定义 PROCESSOR{...} 块,并在其中使用 OUTPUT(filename.dxe) 指定输出文件名。最后,在 SECTIONS{...} 块中使用自定义宏和 INPUT_SECTIONS 命令控制每个 DXE 链接的对象。BF561 和 BF609 EZ-KIT Lite 项目是很好的参考示例,它们是 CrossCore Embedded Studio 中目前仅有的多处理器目标。


http://www.ppmy.cn/news/1578899.html

相关文章

Redis Desktop Manager(Redis可视化工具)安装及使用详细教程

一、安装包下载 直接从官网下载,官网下载链接地址:Downloads - Redis 二、安装步骤 2.1说明 Redis Desktop Manager是一款简单快速、跨平台的Redis桌面管理工具,也也被称作Redis可视化工具。 支持命令控制台操作,以及常用&…

ESP8266TCP客户端(单连接TCP Client)

单连接TCP Client 电脑作为服务器,8266作为客户端 1.配置WiFi模式 ATCWMODE3 //softAPstation mode 相应:ok 2.连接路由器 ATCWJAP“SSID”,“password” //SSID就是wifi的名字, password WIFI密码 响应&#xff…

【python】如何判断是json数组还是字典

傻傻的搞不清楚 要判断这是否是一个JSON数组,可以检查它的结构。JSON数组是用方括号 [] 包围的,并且包含一系列用逗号分隔的值。每个值可以是字符串、数字、对象、数组、布尔值或 null。 在你的代码中,actions 是一个JSON数组,因…

异或和之和 第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 A 组

异或和之和 题目来源 第十四届蓝桥杯大赛软件赛省赛C/C++ 大学 A 组 原题链接 蓝桥杯 异或和之和 https://www.lanqiao.cn/problems/3507/learning/ 问题描述 问题分析 要点1:异或运算 概念 异或(Exclusive OR,简称 XOR)是一种数学运算符,常用于逻辑运算与计算机…

Webpack 深度解析:构建现代前端工程的基石

一、Webpack 的核心价值与演进 1.1 前端工程化的必然选择 根据 2024 年 JavaScript 现状调查报告,Webpack 以 76% 的使用率稳居构建工具榜首。其核心价值体现在: 模块化支持:处理 15 种模块规范(ESM/CJS/AMD 等)资源…

人工智能混合编程实践:Python ONNX进行图像超分重建

人工智能混合编程实践:Python ONNX进行图像超分重建 前言相关介绍Python简介ONNX简介图像超分辨率重建简介应用场景前提条件实验环境项目结构使用Python ONNX进行图像超分重建sr_py_infer.py参考文献前言 由于本人水平有限,难免出现错漏,敬请批评改正。更多精彩内容,可点击…

Python:函数(一)

python函数相关的知识点 1. 函数定义与调用 定义:使用 def 关键字,后接函数名和参数列表。 def greet(name):"""打印问候语(文档字符串)"""print(f"Hello, {name}!") 调用&#xff1a…

CF Round 1009 Div3 -ABCD

A 直接判断即可 LL a,b,c,d;void solve() {cin >> a >> b >> c >> d;if (a b && b c && c d) cout << "YES" << endl;else cout << "NO" << endl; }B 去掉两个数(a,b)&#xff0c…