我试着编写一个ARM-LEGv 8汇编程序,计算数组中某个位置的两个值的平均值,它在Raspberry Pi上运行。
伪代码应如下所示:
int average(int v[], int i){
int average = (v[i] + v[i-1])/2;
return average;
}
数组位于X 0,i位于X1。
我的汇编代码如下所示:
.globl _start
.data
myArray: .word 0, 1, 2, 3, 35, 5
.text
average:
LSL X9, X1, #2
ADD X9, X9, X0
LDR X10, [X9, #0] // guess Segmentation Fault
LDUR X11, [X9, #-4]
ADD X3, X10, X11
LSR X3, X3, #1
BR X30
_start:
LDR X0, myArray
MOV X1, #5
BL mittelwert
MOV X8, #0x5d
MOV X0, X3
SVC 0
我使用了以下命令来构建它:as average.s -o average.o
gcc average.o -o average -nostdlib -static
个
当我运行我的程序时,我得到一个分段错误。为什么?
1条答案
按热度按时间7xzttuei1#
( 免责 声明 :以下 内容 基于 实际 的 ARMv8 - A 指令 集 。 我 不 确定 LEGv8 可能 做 了 哪些 更改 。 )
LDR X0, myArray
不会 加载 带有 标签myArray
的 地址 的X0
。 它 会 从 该 地址 * 加载 一 个 双 字 ( ARM 称 之 为 加载 指令 的 " 文字 " 形式 ) 。 因此 , 在 此 指令 之后 ,X0
包含0x0000000100000000
, 在 执行LDR X10, [X9, #0]
时 ,0x0000000100000000
自然 会 导致 指针 无效 。你 可能 指 的 是
LDR X0, =myArray
, 它 会 把 一 个 指向myArray
的 指针 放到 文本 池 中 , 然后 从 该 指针 在 文本 池 中 的 地址 组装 该 指针 的 文本 负载 。 假设 你 的 系统 可以 处理 这种 类型 的 重 定位 , 这 是 可行 的 。 但是 , 对于 常见 操作 系统 使用 的 现代 位置 无关 的 可 执行 文件 , 首选 的 方法 是中 的 每 一 个
第 一 条 指令 使用
myArray
地址 的 高 52 位 填充X0
的 这些 位 ( 使用 PC 的 偏移 量 ) 。 第 二 条 指令 添加 低 12 位 。 另 请 参阅 了解 ARM 重 定位 ( 示例 :字符 串 x0 , [ 临时 , # : lo12 : zbi _ paddr ] )其他 几 个 错误 和 备注 :
LDR X10, [X9, #0]
和LDUR X11, [X9, #-4]
是 64 位 加载 , 因为 你 使用 了 一 个 X 寄存 器 作为 目标 。 但是myArray
的 元素 是 用.word
定义 的 , 32 位 。 所以 每个 寄存 器 的 高 32 位 将 包含 垃圾 。为了 与 32 位 元素 保持 一致 , 将 它们 加载 到 W 寄存 器LDR W10, [X9, #0]
和LDUR W11, [X9, #-4]
中 ,然后 在 W 寄存 器 上 进行 运算 。int
类型 , 但 您 的 代码 当前 无法 正确 处理 负值 ( 提示 :LSR
中 的L
是 什么 ? ) 。 考虑 一下 如何 修复 此 问题 , 或者 将 其 更改 为unsigned
。i
在 C 中 声明 为int
, 但 您 访问X1
时 将 其 作为 64 位 寄存 器 。 如果 您 从 C 中 调用 此 函数 , ARM64 ABI 允许 X1 的 高位 为 垃圾 。 您 可能 希望 将 其 声明 为size_t
或unsigned long
。 如果 您 确实 将 其 保留 为 32 位 类型 ,很 可能unsigned
就是 您 想要 的 , 然后 您 需要 在 使用 它 之前 将W1
零 扩展 为X1
。RET
而 不是BR X30
, 因为 前者 更 适合 于 此 目的 。 ( 尽管 LEGv8 可能 没有RET
? )