我正在尝试学习 ARM 汇编,并且计算机生成的 .s 文件中有几行让我感到困惑,主要是这个 block :
.L6:
.word .LC0-(.LPIC8+4)
.word .LC1-(.LPIC10+4
.word .LC0-(.LPIC11+4)
以及它与此 block 的关系:
.LCFI0:
ldr r3, .L6
.LPIC8:
add r3, pc
我的最佳猜测告诉我,这是将我的 ascii 字符串(开头)的内存地址加载到 r3 中,但我很困惑这是如何发生的。 .LC0-(.LPIC8+4) 是调用 add r3, pc 的位置与字符串所在位置之间的差异。将 pc 添加到该差异应该最终返回到字符串,但为什么不直接调用
ldr r3, .LC0
而不是拥有这些 .word 的东西和这个尴尬的 ldr/add 对?这是编译器处理此问题的唯一或最佳方法,还是只是编译器用来生成这样的代码的某些通用算法的结果?
另外,什么是
@ sp needed for prologue
这听起来像是提醒编译器将堆栈指针处理添加到序言中。但我觉得这应该已经发生了,而这不是序幕所在..
下面是大部分汇编代码,其中包含我希望正确的注释(最后有一些调试内容,但它太长了,无法包含在内。
任何人都可以提供的任何帮助将不胜感激!
.arch armv5te
.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, 2
.eabi_attribute 18, 4
.code 16
.file "helloneon.c"
.section .debug_abbrev,"",%progbits
.Ldebug_abbrev0:
.section .debug_info,"",%progbits
.Ldebug_info0:
.section .debug_line,"",%progbits
.Ldebug_line0:
.text
.Ltext0:
.section .text.main,"ax",%progbits
.align 2
.global main
.code 16
.thumb_func
.type main, %function
main:
.fnstart
.LFB4:
.file 1 "jni/helloneon.c"
.loc 1 4 0
.save {r4, lr}
push {r4, lr}
.LCFI0:
.loc 1 4 0
ldr r3, .L6 ; r3 = char* hello, first position
.LPIC8:
add r3, pc ; pc = program counter, r3 += pc?
.loc 1 3 0
mov r1, r3 ; r1 = r3
add r1, r1, #127 ; r1 += 127
.L2:
.loc 1 10 0 ; r2 = holding an item in char* hello. r3 = pointer to location in hello
ldrb r2, [r3] ; r2 = r3 load next char
sub r2, r2, #32 ; r2 -=32 subtract 32 to char in register
strb r2, [r3] ; r3 = r2 put uppercase char
add r3, r3, #1 ; r3 += 1
.loc 1 8 0
cmp r3, r1 ; compare r3, r1
bne .L2 ; if not equal, goto L2
.loc 1 13 0
ldr r0, .L6+4 ; r0 =
ldr r1, .L6+8 ; r1 =
.loc 1 16 0
@ sp needed for prologue
.loc 1 13 0
.LPIC10:
add r0, pc ; r0 += pc
.LPIC11:
add r1, pc ; r1 += pc
bl printf ; goto printf
.loc 1 16 0
mov r0, #0 ; r0 = 0
pop {r4, pc} ; epilogue
.L7:
.align 2
.L6:
.word .LC0-(.LPIC8+4) ;
.word .LC1-(.LPIC10+4) ;
.word .LC0-(.LPIC11+4) ;
.LFE4:
.fnend
.size main, .-main
.section .rodata.str1.4,"aMS",%progbits,1
.align 2
.LC0:
.ascii "helloworldthisismytestprogramtoconvertlowcharstobig"
.ascii "charsiamtestingneonandineedaninputofonehundredandtw"
.ascii "entyeightcharactersinleng\000"
.LC1:
.ascii "%s\000"
.section .debug_frame,"",%progbits
.Lframe0:
.4byte .LECIE0-.LSCIE0
.LSCIE0:
.4byte 0xffffffff
.byte 0x1
.ascii "\000"
.uleb128 0x1
.sleb128 -4
.byte 0xe
.byte 0xc
.uleb128 0xd
.uleb128 0x0
.align 2
这是 C 代码:
#include <stdio.h>
int main()
{
char* hello = "helloworldthisismytestprogramtoconvertlowcharstobigcharsiamtestingneonandineedaninputofonehundredandtwentyeightcharactersinleng"; // len = 127 + \0
int i, size = 127;
for (i = 0; i < size; i++)
{
hello[i] -= 32;
}
printf("%s", hello);
return 0;
}
最佳答案
ldr r3, .L6
是一条伪指令。它实际上翻译成类似 ldr r3,[pc, #offset]
,其中offset
是 LDR 指令与其尝试加载的值的位置之间的内存距离。
ARM 处理器的固定宽度指令意味着您只有这么多位可用于 LDR/STR 指令中的偏移量,这又意味着通过 PC 相关加载加载的值必须存储在与相应加载指令相当接近的位置。/p>
.LC0
与 .LPIC8
位于完全不同的部分,因此很可能超出 PC 相关负载的范围。
一些 ARM 汇编器提供 .LTORG指令可用于将文字“池”分散在与代码相同的部分中。例如,这个:
LDR r3,=.LC0 ; note the '='
....
.LTORG
在这样的汇编器中会翻译成这样的东西:
LDR r3,[pc,#offset_to_LC0Value]
....
LC0Value: .word .LC0
在问题中显示的汇编代码中,不仅仅是与 PC 相关的负载;还包括与 PC 相关的负载。正在加载的值也是与 PC 相关的。这样做的原因是为了获得位置无关代码。如果加载并使用绝对地址,则代码将失败,除非从特定的虚拟地址执行。通过与当前 PC 相关的地址访问所有相关数据可以打破这种依赖性。
关于assembly - ARM 汇编加载 ASCII 内存地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13940784/