linux 汇编 `.eabi_attribute',ARMv7汇编代码分析

本文通过一个简单的C程序示例,详细分析了ARMv7架构下的汇编代码生成过程及寄存器使用情况。重点介绍了如何通过交叉编译生成ARM汇编代码,以及对主函数和子函数的调用机制、参数传递方式进行了深入探讨。
AI助手已提取文章相关产品:

为了分析ARMv7架构寄存器的使用,利用C程序生成ARMv7汇编,并分析之。

1、C源程序代码如下(为了简化,函数功能很简单):

# cat  callfunc.c

#include

#include

#include

int main()

{

int input=10;

int tmp,result;

tmp = func1(input);

result = func2(tmp);

printf("result = %d \n", result);

return 0;

}

int func1(int a)

{

a++;

return a;

}

int func2(int b)

{

b=b*b;

return b;

}

2、交叉编译生成ARM汇编代码:

# arm-none-linux-gnueabi-gcc -march=armv7-a  callfunc.c  -S -o callfunc.asm

生成ARMv7的汇编代码如下:

.arch armv7-a

.fpu softvfp

.eabi_attribute 20, 1

.eabi_attribute 21, 1

.eabi_attribute 23, 3

.eabi_attribute 24, 1

.eabi_attribute 25, 1

.eabi_attribute 26, 2

.eabi_attribute 30, 6

.eabi_attribute 34, 1

.eabi_attribute 18, 4

.file"callfunc.c"

.section.rodata

.align2;2^2,即4字节对齐;以"."开头的是伪指令,具有编译器相关,平台无关性;

.LC0:

.ascii"result = %d \012\000"

.text

.align2

.globalmain;全局函数声明,相当于C语言中的extern

.typemain, %function

main:

.fnstart;函数开始标志

@ args = 0, pretend = 0, frame = 16;@标志注释,由编译器添加

@ frame_needed = 1, uses_anonymous_args = 0

stmfdsp!, {fp, lr};fp=r11=0,lr=0x40291664

;分别存储fp,lr到sp,sp-4;sp0=sp-4;fp(R11)桢指针 ;

;关于fp:通常在C程序编译过程中,所有函数的局部变量被分配在一个连续的存储区中,

;一般这个存储区是在堆栈中,这个连续的存储区称为这个函数的存储“帧”,它是通过一个指针访问的。

;寄存器 fp (桢指针)应当是零或者是指向栈回溯结构的列表中的最后一个结构,

;提供了一种追溯程序的方式,来反向跟踪调用的函数。

;即:其作用是用来回溯,从代码中也可以看出来,fp只是起标签作用,理论上不使用fp也不会影响程序执行

.save {fp, lr};关于.save:其作用与以下.setfp、.pad等的伪指令作用类似,对相应汇编指令产生注释。

;具体注释内容见网页(http://sourceware.org/binutils/docs/as/ARM-Directives.html)

.setfp fp, sp, #4

addfp, sp, #4;sp=0xbe88f3e0,fp=0xbe88f3e4

.pad #16

subsp, sp, #16;sp=0xbe88f3d0

movr3, #10;r3=10

strr3, [fp, #-8];r3->fp-8即sp-4

ldrr0, [fp, #-8];r3->r0

blfunc1;jump to func1

strr0, [fp, #-12];r0->fp-12

ldrr0, [fp, #-12]

blfunc2

strr0, [fp, #-16]

movwr3, #:lower16:.LC0

movtr3, #:upper16:.LC0 ;movw和movt的作用为:r3 = (movt #:upper16:.LC0 << 16) | (movw #:lower16:.LC0)).

;以上两句的作用就是输出.LC0段的.ascii那一行的内容,即打印那个result = %d \012\000

movr0, r3

ldrr1, [fp, #-16]

blprintf;打印r0、r1中内容

movr3, #0

movr0, r3

subsp, fp, #4

ldmfdsp!, {fp, pc}

.fnend

.sizemain, .-main

.align2

.globalfunc1

.typefunc1, %function

func1:

.fnstart

@ args = 0, pretend = 0, frame = 8

@ frame_needed = 1, uses_anonymous_args = 0

@ link register save eliminated.

strfp, [sp, #-4]! ;r11(fp)入栈;sp=0xbe88f3d0-4=0xbe88f3cc,r11=0xbe88f3e4;fp(即r11)存储到sp-4(0x)的地址中

.save {fp}

.setfp fp, sp, #0

addfp, sp, #0;fp=0xbe88f3cc

.pad #12

subsp, sp, #12;sp=0xbe88f3c0

strr0, [fp, #-8];r0存储到fp-8指向的地址处

ldrr3, [fp, #-8]

addr3, r3, #1

strr3, [fp, #-8]

ldrr3, [fp, #-8]

movr0, r3

addsp, fp, #0

ldmfdsp!, {fp};r11(fp)出栈

bxlr

.fnend

.sizefunc1, .-func1

.align2

.globalfunc2

.typefunc2, %function

func2:

.fnstart

@ args = 0, pretend = 0, frame = 8

@ frame_needed = 1, uses_anonymous_args = 0

@ link register save eliminated.

strfp, [sp, #-4]!

.save {fp}

.setfp fp, sp, #0

addfp, sp, #0

.pad #12

subsp, sp, #12

strr0, [fp, #-8]

ldrr3, [fp, #-8];r0->r3

ldrr2, [fp, #-8];r0->r2

mulr3, r2, r3 ;r2*r3->r3

strr3, [fp, #-8]

ldrr3, [fp, #-8]

movr0, r3;r3->r0

addsp, fp, #0

ldmfdsp!, {fp};sp->fp ;pop fp

bxlr

.fnend

.sizefunc2, .-func2

.ident"GCC: (Sourcery CodeBench Lite 2011.09-70) 4.6.1"

.section.note.GNU-stack,"",%progbits

分析内容见代码注释

注意:

1、编译器默认用来传输参数的寄存器是r0~r3,参数超过四个就要用到栈。

2、bl printf:该命令是打印命令,默认打印r0开始的寄存器内容,测试过参数超过4个时候,打印时r0存储打印格式,r1-r3存储要打印的数,剩下的参数需要其他寄存器存储,但是printf如何实现打印多出来的几个寄存器的,目前暂不清楚。欢迎补充。

您可能感兴趣的与本文相关内容

<think>好的,我现在需要解决用户的问题:在使用arm aarch32-13.3 GCC版本编译生成的M55二进制文件中,__attribute__((aligned(8)))属性没有生效。用户希望了解可能的原因和解决方法。我需要仔细分析可能的原因,并提供结构化的解答。 首先,我需要回忆一下GCC中__attribute__((aligned))的使用方法和可能的影响因素。这个属性用于指定变量或结构体的对齐方式。但是,是否生效可能受到多种因素影响,比如编译器的限制、目标架构的要求、链接脚本的设置,或者代码中的其他部分覆盖了该属性。 可能的第一个原因是编译器版本或目标架构的限制。M55是ARMv8.1-M架构,属于Cortex-M系列,可能使用Thumb指令集。在AArch32状态下,某些架构可能对对齐有特定要求或限制。比如,某些情况下编译器可能会优化对齐方式,尤其是当指定对齐值超过默认值时,或者架构本身不支持更大的对齐。 其次,检查代码中的实际应用是否正确。用户是否将属性应用在正确的位置?例如,结构体、变量声明的位置是否正确。例如: ```c struct my_struct { int a; } __attribute__((aligned(8))); ``` 或者变量声明: ```c int my_var __attribute__((aligned(8))) = 0; ``` 如果应用在类型定义上,可能需要确认是否所有使用该类型的实例都会继承该对齐属性。有时候,结构体内部成员的排列也可能影响整体对齐,比如成员的最大对齐要求可能小于指定的值,导致整体对齐被覆盖。 第三个可能性是链接器脚本的影响。链接器脚本中的ALIGN指令可能会覆盖代码中的属性设置。如果链接脚本中指定了特定的对齐方式,比如在section定义中使用ALIGN(4),那么即使代码中指定了8字节对齐,链接器可能会强制为4字节。需要检查链接脚本中相关段的定义,尤其是涉及该变量或结构体的段。 另外,内存区域的限制也可能导致对齐失败。例如,如果变量被分配到某个内存区域,而该区域的起始地址无法满足8字节对齐的要求,那么实际地址可能无法对齐。这种情况在嵌入式系统中比较常见,特别是在使用特定内存段或硬件缓冲区时。 编译器优化也是一个可能的因素。某些优化选项可能会忽略对齐属性,尤其是在优化速度时可能会重新排列数据布局。用户可以尝试关闭优化(如使用-O0)来测试是否是优化导致的问题。 调试方法方面,建议用户检查生成的汇编代码或目标文件,查看变量或结构体的实际对齐情况。使用GCC的-S选项生成汇编代码,然后查看对应的.align指令是否存在。或者使用objdump工具查看符号的地址,确认是否为8的倍数。例如,使用命令: ```bash arm-none-eabi-objdump -t output.elf | grep my_var ``` 查看符号的地址是否符合预期。 此外,可能需要确认是否启用了C扩展,比如C11中的_Alignas关键字是否被正确支持。不过,使用GCC的attribute应该是标准的做法,但不同版本可能有差异。 最后,考虑是否是硬件或运行时环境的限制。例如,某些嵌入式平台可能不支持非对齐访问,或者需要特定的设置才能启用。但M55作为较新的架构,应该支持8字节对齐,不过需要确认具体的核心配置和内存管理单元的设置。 总结可能的解决步骤: 1. 检查代码中属性的应用是否正确。 2. 查看生成的汇编或符号表确认对齐是否生效。 3. 检查链接脚本中的对齐设置。 4. 尝试关闭编译器优化。 5. 确认目标架构支持所需的对齐方式。 6. 确保内存区域允许8字节对齐的分配。 用户可能需要逐一排查这些可能性,找到具体原因。</think>在ARM aarch32-13.3 GCC编译环境下,`__attribute__((aligned(8)))`未生效的可能原因及排查步骤如下: --- ### 一、基础验证步骤 1. **检查语法正确性** 确保属性应用位置正确: ```c // 变量声明(正确) int var __attribute__((aligned(8))) = 0; // 结构体类型定义(正确) struct S { ... } __attribute__((aligned(8))); ``` 2. **查看生成地址** 通过 `objdump` 验证符号地址是否为8的倍数: ```bash arm-none-eabi-objdump -t output.elf | grep <变量名> ``` 输出示例: `00000008 00000008 O .bss 00000004 var` --- ### 二、常见原因分析 #### 1. **编译器限制** - **架构限制**:Cortex-M55(ARMv8.1-M)在AArch32状态下可能对栈对齐有特殊要求(默认8字节对齐),但数据段需单独验证。 - **GCC版本特性**:某些GCC版本对`aligned`属性支持不完善,可尝试升级至13.3的补丁版本。 #### 2. **链接脚本覆盖** 检查链接脚本中对应段的`ALIGN`设置: ```ld .bss : { /* 若此处指定ALIGN(4),会覆盖代码中的8字节对齐 */ . = ALIGN(4); *(.bss*) } ``` **修复方案**:移除链接脚本中的强制对齐或改为`ALIGN(8)`。 #### 3. **内存区域限制** 若变量分配到特定内存区域(如`.sdram`),需确认该区域的起始地址和长度支持8字节对齐: ```ld MEMORY { RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 512K /* 起始地址0x20000000是8字节对齐的 */ } ``` #### 4. **编译器优化干扰** **现象**:启用`-O2`/`-Os`时,编译器可能重组数据布局。 **验证**:添加编译选项`-O0 -fno-strict-aliasing`临时关闭优化。 --- ### 三、高级调试方法 #### 1. **查看汇编代码** 通过`-S`选项生成汇编代码,检查`.align`指令: ```bash arm-none-eabi-gcc -S -o output.s input.c ``` 预期输出: ```asm .data .align 3 # 3表示2^3=8字节对齐 var: .space 4 ``` #### 2. **强制类型对齐(C11标准)** 使用`_Alignas`关键字替代GCC属性: ```c #include <stdalign.h> _Alignas(8) int var; ``` --- ### 四、M55核心特殊注意事项 1. **TrustZone配置影响** 若启用TrustZone安全隔离,非安全世界的内存分配可能受限制,需检查MPU/SAU配置。 2. **浮点运算单元对齐** 若变量用于NEON或FPU操作,需确保对齐满足硬件要求(如`float64_t`通常需8字节对齐)。 --- ### 五、最终解决方案参考 1. **明确指定段名** 强制将变量放入独立段并指定对齐: ```c int var __attribute__((aligned(8), section(".my_aligned_data"))); ``` 链接脚本中补充: ```ld .my_aligned_data : { . = ALIGN(8); *(.my_aligned_data) } > RAM ``` 2. **使用`__alignas`宏(CMSIS兼容)** ```c #include <arm_compat.h> __alignas(8) int var; ``` --- 若问题仍未解决,建议提供以下信息进一步分析: - 变量定义代码片段 - 链接脚本相关段定义 - `objdump -t`输出中变量的地址信息 - 编译时使用的完整参数(含优化选项)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值