ARM 链接器优化功能介绍
消除公共部分组
链接器可以检测节组的多个副本,并丢弃其他副本。
® Arm Compiler for Embedded 生成用于链接的完整对象。因此:
- 如果 C 和 C++ 源代码中存在内联函数,则每个对象都包含该对象所需的内联函数的外联副本。
- 如果在 C++ 源代码中使用模板,则每个对象都包含对象所需的模板函数。
在公共头文件中声明这些函数时,这些函数可能会在随后链接在一起的单独对象中多次定义。为了消除重复项,编译器将这些函数编译为公共节组的单独实例。
公共节组的单独实例可能不相同。例如,某些副本可能位于使用不同但兼容的构建选项、不同的优化或调试选项构建的库中。
如果副本不相同,则 armlink
会根据输入对象的属性保留每个公共节组的最佳可用变体。Armlink
丢弃其余部分。
如果副本相同,则 armlink
将保留位于的第一个部分组。
您可以使用以下链接器选项来控制此优化:
- 使用 -
-bestdebug
选项使用最大的公共数据 (COMDAT) 组(可能提供最佳调试视图)。 -
使用 -
-no_bestdebug
选项使用最小的 COMDAT 组(可能提供最小的代码大小)。这是默认设置。如果使用 -
g
编译包含 COMDAT 组 A 的所有文件,即使使用 --no_bestdebug
,映像也会更改。
消除未使用的部分
消除未使用的部分是链接器对图像大小执行的最重要优化。
未使用部分消除:
- 从最终映像中删除无法访问的代码和数据。
- 在可能导致删除所有部分的情况下被禁止显示。
要控制此优化,请使用 armlink
选项 --remove
、--no_remove
、--first、--last
和 --keep
。
未使用的部分消除需要一个入口点。因此,如果未为映像指定入口点,请使用 armlink
选项 --entry
指定入口点。
使用 armlink
选项 --info unused
指示链接器生成它删除的未使用部分的列表。
注意
armlink
报告错误:L6218E:未定义的符号 <symbol>
,即使未使用的部分删除已删除此符号的要求。此行为与 GNU 链接器ld
不同。
在以下情况下,输入部分将保留在最终图像中:
- 它包含一个入口点或一个外部可访问的符号。例如,Arm®v8-M 安全扩展的安全代码中的输入函数。
- 它是
SHT_INIT_ARRAY
、SHT_FINI_ARRAY
或SHT_PREINIT_ARRAY
部分。 - 它被指定为第一个或最后一个输入部分,由 --
first
或--last
选项或分散加载等效项指定。 - 它被
--keep
选项标记为不可删除。 - 它直接或间接地由图像中保留的输入部分的非弱引用引用。
- 其名称与输入截面符号所引用的名称匹配,并且该符号引用自图像中保留的截面。
注意
编译器通常将函数和数据收集在一起,并为每个类别发出一个部分。链接器只能删除完全未使用的部分。
您可以使用
__attribute__(used))
属性标记源代码中的函数或变量。此属性使armclang
为每个函数或变量生成符号__tagsym$$used.
<num>,其中<num>
是用于区分每个符号的计数器。消除未使用的部分不会删除包含__tagsym$$used.<num>
的部分。您还可以使用
armclang
选项 -ffunction-sections
来指示编译器为源文件中的每个函数生成一个 ELF 部分。
使用 RW 数据压缩进行优化
RW 数据区通常包含大量重复值(如零),这使得它们适合压缩。
默认情况下,RW 数据压缩处于启用状态,以最小化 ROM 大小。
链接器压缩数据。然后,在运行时在目标上解压缩此数据。
Arm 库包含一些解压缩算法,链接器选择要添加到映像的最佳算法,以便在执行映像时解压缩数据区域。可以重写链接器选择的算法。
链接器如何选择压缩器
Armlink
在选择最合适的压缩算法以生成最小图像之前收集有关数据部分内容的信息。
如果压缩合适,armlink
只能对图像中的所有可压缩数据部分使用一个数据压缩器。可以在这些部分上尝试不同的压缩算法,以生成最佳的整体大小。如果出现以下情况,将自动应用压缩:
Compressed data size + Size of decompressor < Uncompressed data size
选择压缩器后,armlink
会将解压缩器添加到映像的代码区域。如果最终映像不包含任何压缩数据,则不会添加解压缩程序。
可用于替代链接器使用的压缩算法的选项
链接器具有用于禁用压缩或指定要使用的压缩算法的选项。
可以通过以下任一方式替代链接器使用的压缩算法:
- 使用 -
-datacompressor off
选项关闭压缩。 - 指定压缩算法。
若要指定压缩算法,请在链接器命令行上使用所需压缩器的编号,例如:
armlink --datacompressor 2 ...
使用命令行选项 --datacompressor list
获取链接器中可用的压缩算法列表:
armlink --datacompressor list
...
Num Compression algorithm
========================================================
0 Run-length encoding
1 Run-length encoding, with LZ77 on small-repeats
2 Complex LZ77 compression
选择压缩算法时,请注意:
- Compressor 0 在具有大量零字节但非零字节较少的数据上表现良好。
- Compressor 1 在处理非零字节重复的数据时表现良好。
- Compressor 2 在处理包含重复值的数据时表现良好。
链接器首选压缩器 0 或 1,其中数据主要包含零字节 (>75%)。选择 Compressor 2 时,数据包含很少的零字节 (<10%)。如果图像仅由 A32 代码组成,则会自动使用 A32 解压缩器。如果映像包含任何 T32 代码,则使用 T32 解压缩器。如果没有明确的偏好,则对所有压缩机进行测试,以产生最佳的整体尺寸。
使用 RW 数据压缩时的注意事项
使用 RW 数据压缩时需要注意一些注意事项。
使用 RW 数据压缩时:
- 使用链接器选项 -
-map
查看对代码中的区域应用压缩的位置。 - 如果存在从压缩区域到使用加载地址的链接器定义符号的引用,则链接器将关闭 RW 压缩。
- 如果您使用的是带有片上缓存的 Arm® 处理器,请在解压缩后启用缓存,以避免出现代码一致性问题。
压缩数据段在运行时自动解压缩,前提是使用 Arm 库中的代码执行__main
。此代码必须放置在根区域中。最好在散点文件中使用 InRoot$$Sections
来完成此操作。
如果使用的是散点文件,则可以通过添加 NOCOMPRESS
属性来指定不压缩加载或执行区域。
与链接器内联的函数
链接器内联功能取决于您指定的选项和输入文件的内容。
链接器可以内联小函数来代替该函数的分支指令。要使链接器能够执行此操作,函数(没有返回指令)必须适合分支指令的四个字节。
使用 --inline
和 --no_inline
命令行选项来控制分支内联。但是,--no_inline
仅关闭用户提供的对象的内联。默认情况下,链接器仍内联 Arm 标准库中的函数。
如果启用了分支内联优化,则链接器会扫描映像中的每个函数调用,然后根据需要进行内联。当链接器找到合适的函数进行内联时,它会将函数调用替换为正在调用的函数中的指令。
链接器在消除任何未使用的部分之前应用分支内联优化,以便在不再调用内联部分时也可以将其删除。
注意
- 对于 Arm®v7-A,链接器可以内联两个 16 位编码的 Thumb 指令,以代替 32 位编码的 Thumb®
BL
指令。- 对于 Armv8-A 和 Armv8-M,链接器可以内联两个 16 位 T32 指令来代替 32 位 T32
BL
指令。
使用 --info=inline
命令行选项列出所有内联函数。
关于优化到 NOP 的分支
尽管链接器可以将分支替换为 NOP
,但在某些情况下,您可能希望阻止这种情况发生。
默认情况下,链接器将任何分支替换为重新定位,该重定位将解析为具有 NOP
指令的下一条指令。如果链接器对尾部调用部分重新排序,也可以应用此优化。
但是,在某些情况下,您可能希望禁用该选项,例如,在执行验证或管道刷新时。
要控制此优化,请使用 --branchnop
和 --no_branchnop
命令行选项。
尾部调用部分的链接器重新排序
在某些情况下,您可能希望链接器对尾部调用部分重新排序。
尾部调用部分是在该部分末尾包含分支指令的部分。如果分支指令具有以另一个部分开头的函数为目标的重定位,则链接器可以将尾部调用部分放在被调用部分之前。然后,链接器可以将尾部调用部分末尾的分支指令优化为 NOP
指令。
若要利用此行为,请使用命令行选项 --tailreorder
将尾部调用部分移动到其目标之前。
使用 --info=tailreorder
命令行选项可显示有关链接器执行的任何尾调用优化的信息。
对尾部调用部分重新排序的限制
尾部调用部分的重新排序有一些限制。
链接器:
- 对于每个尾部调用目标,只能移动一个尾部调用部分。如果对单个部分有多个尾部调用,则具有相同部分名称的尾部调用部分将移动到目标之前。如果在具有匹配名称的尾部调用部分中找不到部分名称,则链接器将移动它遇到的第一个部分。
- 无法将尾部调用部分移出其执行区域。
- 在内联贴面之前不移动尾部。
合并相同的常量
链接器可以尝试合并以 AArch32 状态为目标的对象中的相同常量。必须使用 Arm® Compiler for Embedded 6 生成对象。如果使用 armclang -ffunction-sections
选项进行编译,则合并效率更高。此选项是默认选项。
关于此任务
以下过程是显示合并功能的示例。
注意
如果使用散点文件,则任何标有OVERLAY
或PROTECTED
属性的区域都会影响armlink --merge_litpools
选项的行为。
程序
- 创建一个 C 源文件
litpool.c
,其中包含以下代码:int f1() {return 0xdeadbeef; } int f2() {return 0xdeadbeef; }
- 使用
-S
编译源代码以创建汇编文件:armclang -c -S -target arm-arm-none-eabi -mcpu=cortex-m0 -ffunction-sections \litpool.c -o litpool.s
注意
-ffunction-sections
是默认值。由于
0xdeadbeef
是一个难以使用指令创建的常量,因此会创建一个文本池,例如:... f1:.fnstart @ BB#0:ldr r0, __arm_cp.0_0bx lr.p2align 2 @ BB#1: __arm_cp.0_0:.long 3735928559 @ 0xdeadbeef ....fnend....code 16 @ @f2.thumb_func f2:.fnstart @ BB#0:ldr r0, __arm_cp.1_0bx lr.p2align 2 @ BB#1: __arm_cp.1_0:.long 3735928559 @ 0xdeadbeef ....fnend ...
注意
每个函数都有一个常量副本,因为armclang
不能在两个函数之间共享这些常量。 - 编译源代码以创建对象:
armclang -c -target arm-arm-none-eabi -mcpu=cortex-m0 litpool.c -o litpool.o
- 使用
--merge_litpools
选项链接目标文件:armlink --cpu=Cortex-M0 --merge_litpools litpool.o -o litpool.axf
注意
--merge_litpools
是默认值。 - 运行
fromelf
查看镜像结构:fromelf -c -d -s -t -v -z litpool.axf
以下示例显示了合并的结果:
...f10x00008000: 4801 .H LDR r0,[pc,#4] ; [0x8008] = 0xdeadbeef0x00008002: 4770 pG BX lrf20x00008004: 4800 .H LDR r0,[pc,#0] ; [0x8008] = 0xdeadbeef0x00008006: 4770 pG BX lr$d.4__arm_cp.1_00x00008008: deadbeef .... DCD 3735928559 ...
相关文章:

ARM 链接器优化功能介绍
消除公共部分组 链接器可以检测节组的多个副本,并丢弃其他副本。 Arm Compiler for Embedded 生成用于链接的完整对象。因此: 如果 C 和 C 源代码中存在内联函数,则每个对象都包含该对象所需的内联函数的外联副本。如果在 C 源代码中使用…...

动手学深度学习之卷积神经网络之池化层
池化层 卷积层对位置太敏感了,可能一点点变化就会导致输出的变化,这时候就需要池化层了,池化层的主要作用就是缓解卷积层对位置的敏感性 二维最大池化 这里有一个窗口,来滑动,每次我们将窗口中最大的值给拿出来 还是上…...

HackTheBox - Medium - Linux - Ambassador
Ambassador Ambassador 是一台中等难度的 Linux 机器,用于解决硬编码的明文凭据留在旧版本代码中的问题。首先,“Grafana”CVE (“CVE-2021-43798”) 用于读取目标上的任意文件。在研究了服务的常见配置方式后,将在其…...

嵌入式——循环队列
循环队列 (Circular Queue) 是一种数据结构(或称环形队列、圆形队列)。它类似于普通队列,但是在循环队列中,当队列尾部到达数组的末尾时,它会从数组的开头重新开始。这种数据结构通常用于需要固定大小的队列,例如计算机内存中的缓冲区。循环队列可以通过数组或链表实现,…...

2024.1.7-实战-docker方式给自己网站部署prometheus监控ecs资源使用情况-2024.1.7(测试成功)
实战-docker方式给自己网站部署prometheus监控ecs资源使用情况-2024.1.7(测试成功) 目录 最终效果 原文链接 https://onedayxyy.cn/docs/prometheus-grafana-ecs 参考模板 https://i4t.com/ https://grafana.frps.cn 🔰 额,注意哦: 他这个是通过frp来…...

20240107 SQL基础50题打卡
20240107 SQL基础50题打卡 1978. 上级经理已离职的公司员工 表: Employees ----------------------- | Column Name | Type | ----------------------- | employee_id | int | | name | varchar | | manager_id | int | | salary | int | -…...

阿里云公网带宽出网和入网是什么?上行和下行是什么?
什么是阿里云服务器ECS的入网带宽和出网带宽?以云服务器为中心,流入云服务器占用的带宽是入网带宽,流量从云服务器流出的带宽是出网带宽。阿里云服务器网aliyunfuwuqi.com分享入网带宽和出网带宽说明表: 带宽类别说明入网带宽&am…...

eureka工作原理是什么
EUREKA 是一个基于 RESTful 风格的服务发现系统,它主要用于帮助实现在微服务架构中的服务自动发现与注册。其工作原理主要包括以下几个步骤: 注册中心:EUREKA 中有一个集中的注册中心,所有的服务都将在此注册和发现。注册中心可以…...

Vue中的事件委托(事件代理)使用方法介绍
事件委托(事件代理) 将原本需要绑定在子元素上的事件监听器委托在父元素上,让父元素充当事件监听的职务。 事件委托是一种利用事件冒泡的特性,在父节点上响应事件,而不是在子节点上响应事件的技术。它能够改善性能&a…...

「HDLBits题解」Wire decl
本专栏的目的是分享可以通过HDLBits仿真的Verilog代码 以提供参考 各位可同时参考我的代码和官方题解代码 或许会有所收益 题目链接:Wire decl - HDLBits default_nettype none module top_module(input a,input b,input c,input d,output out,output out_n ); w…...

[MAUI]在.NET MAUI中调用拨号界面
在.NET MAUI中调用拨号界面 前置要求: Visual Studio 2022 安装包“.NET Multi-platform App UI 开发” 参考文档: 电话拨号程序 新建一个MAUI项目 在解决方案资源管理器窗口中找到Platforms/Android/AndroidManifest.xml在AndroidManifest.xml中添加下文中…块如下:<?xml…...

Kali/Debian Linux 安装Docker Engine
0x01 卸载旧版本 在安装Docker Engine之前,需要卸载已经安装的可能有冲突的软件包。一些维护者在他们的仓库提供的Docker包可能是非Docker官方发行版,须先卸载这些软件包,然后才能安装Docker官方正式发行的Docker Engine版本。 要卸载的软件…...

Spring 应用合并之路(二):峰回路转,柳暗花明 | 京东云技术团队
书接上文,前面在 [Spring 应用合并之路(一):摸石头过河]介绍了几种不成功的经验,下面继续折腾… 四、仓库合并,独立容器 在经历了上面的尝试,在同事为啥不搞两个独立的容器提醒下,…...

SQL Error 1366, SQLState HY000
SQL错误 1366 和 SQLState HY000 通常指的是 MySQL 与字符编码或数据截断有关的问题。当尝试将数据插入具有与正在插入的数据不兼容的字符集或排序规则的列时,或者正在插入的数据对于列来说过长时,就会出现此错误。 解决方式: 检查列长度&am…...

Codeforces Round 893 (Div. 2)(VP-7,寒假加训)
VP时间 A. 关键在于按c的按钮 c&1 Alice可以多按一次c按钮 也就是a多一个(a) 之后比较a,b大小即可 !(c&1) Alice Bob操作c按钮次数一样 1.ac B.贪心 一开始会吃饼干 如果有卖饼的就吃 如果隔离一段时间到d没吃就吃(当时…...

MySQL第四战:视图以及常见面试题(上)
目录 目录: 一.视图 1.介绍什么是视图 2.视图的语法 语法讲解 实例操作 二.MySQL面试题 1.SQL脚本 2.面试题实战 三.思维导图 目录: 随着数字化时代的飞速发展,数据库技术,特别是MySQL,已经成为IT领域中不可…...

C语言程序设计——程序流程控制方法(一)
C语言关系运算符 ---等于ab!不等于a!b<、>小于和大于a>b 、a<b<、>小于等于、大于等于a>b 、a<b!非!(0)、!(NULL) 在C99之后,C语言开始支持布尔类型,头文件是stdbool.h。在文中我所演示的所有代码均是C99版。 在C语言上上述关…...

torch.backends.cudnn.benchmark
torch.backends.cudnn.benchmark 的设置对于使用 PyTorch 进行深度学习训练的性能优化至关重要。具体而言,它与 NVIDIA 的 CuDNN(CUDA Deep Neural Network library)库有关,该库是在 GPU 上加速深度神经网络计算的核心组件。 启用…...

SQL Server从0到1——写shell
xp_cmdshell 查看能否使用xpcmd_shell; select count(*) from master.dbo.sysobjects where xtype x and name xp_cmdshell 直接使用xpcmd_shell执行命令: EXEC master.dbo.xp_cmdshell whoami 发现居然无法使用 查看是否存在xp_cmdshell: EXEC…...

计算圆弧的起始角度、终止角度和矩形信息并使用drawArc绘制圆弧
Qt中常用绘制圆弧的库函数: //函数原型 void QPainter::drawArc(const QRectF &rectangle, int startAngle, int spanAngle)Qt规定1约占16个像素,比如一个完整的圆等于360度,对应的像素角度就是 5760度(16 * 360)…...

C++ Trie树模版 及模版题 || Trie字符串统计
Trie树:用来高效的存储和查找字符串集合的数据结构。 维护一个字符串集合,支持两种操作: I x 向集合中插入一个字符串 x ; Q x 询问一个字符串在集合中出现了多少次。 共有 N 个操作,所有输入的字符串总长度不超过 1…...

Linux基础命令@echo、tail、重定向符
目录 echo概念语法作用演示一演示二 反引号作用 tail概念语法作用不带选项,演示一带选项 -num,演示二带选项 -f , 持续跟踪 重定向符概念作用覆盖重定向,>演示一演示二 追加重定向,>>演示一演示二 总结 echo …...

uniapp:签字版、绘画板 插件l-signature
官方网站:LimeUi - 多端uniapp组件库 使用步骤: 1、首先从插件市场将代码下载到项目 海报画板 - DCloud 插件市场 2、下载后,在项目中的uni_modules目录(uni_modules优点:不需要import引入,还可以快捷更新…...

Python Pillow(PIL)库的用法介绍
Python的Pillow库(PIL)是一个强大的图像处理库,可以用来进行图像的读取、编辑、处理和保存等操作。下面是一些Pillow库的基本用法介绍: 安装Pillow库: 在命令行中输入以下命令即可安装Pillow库: 复制代码 p…...

uniapp 【专题详解 -- 时间】云数据库时间类型设计,时间生成、时间格式化渲染(uni-dateformat 组件的使用)
云数据表的时间类型设计 推荐使用时间戳 timestamp "createTime": {"bsonType": "timestamp","label": "创建时间:" }时间生成 获取当前时间 Date.now() .add({createTime: Date.now() })时间格式化渲染 下载安…...

k8s之flink的几种创建方式
在此之前需要部署一下私人docker仓库,教程搭建 Docker 镜像仓库 注意:每台节点的daemon.json都需要配置"insecure-registries": ["http://主机IP:8080"] 并重启 一、session 模式 Session 模式是指在 Kubernetes 上启动一个共享的…...

应用OpenCV绘制箭头
绘制箭头函数 方法:函数cv2.arrowedLine( ) 语法格式:cv2.arrowedLine(img, pt1, pt2, color[, thickness[, line_type[, shift[, tipLength]]]]) 参数说明: img:要画的直线所在的图像,也称为画布。。 pt1&#x…...

信息学奥赛一本通1032:大象喝水查
1032:大象喝水查 时间限制: 1000 ms 内存限制: 65536 KB 提交数: 104347 通过数: 64726 【题目描述】 一只大象口渴了,要喝20升水才能解渴,但现在只有一个深h厘米,底面半径为r厘米的小圆桶(h和r都是整数)。问大象至少…...

聊聊jvm的direct buffer统计
序 本文主要研究一下jvm的direct buffer统计 spring boot metrics jvm.memory.used {"name": "jvm.memory.used","description": "The amount of used memory","baseUnit": "bytes","measurements"…...

C/C++ 位段
目录 什么是位段? 位段的内存分配 位段的跨平台问题 什么是位段? 位段的声明与结构是类似的,但是有两个不同: 位段的成员必须是 int、unsigned int 或signed int 等整型家族。位段的成员名后边有一个冒号和一个数字 这是一个…...