64 位 cpu 上的 arm 32 位指令 "swi"
arm 32bit instruction "swi" on 64bit cpu
我正在 raspberry pi 3(无蓝牙)上制作我的内核。
我的内核使用 arm 汇编语言(32 位)和 c 和 uboot 引导我的内核。
我找到了中断向量 table 并像这样将其应用到我的代码中。
.globl _ram_entry
_ram_entry:
bl kernel_init
b _ram_entry //
ldr pc,=print_mem1
b print_mem1
b print_mem1
b print_mem2
b print_mem3
b print_mem4
b print_mem1
b print_mem2
b print_mem3
b print_mem4
#define svc_stack 0xa0300000
#define irq_stack 0xa0380000
#define sys_stack 0xa0400000
.global kernel_init
kernel_init:
ldr r0,=0x00080008
mov r1,#0x0000
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
bl main
b _ram_entry
.global print_mem1
print_mem1:
bl print_c_mem1
.global print_mem2
print_mem2:
bl print_c_mem2
.global print_mem3
print_mem3:
bl print_c_mem3
.global print_mem4
print_mem4:
bl print_c_mem4
_ram_entry从0x00080008开始,这是我的中断向量table。
当我打印我的记忆时,0x00 有 bl kernel_init。
所有中断处理程序只打印简单的数字。
但是如果我像这个主要代码一样使用 swi,则会调用重置处理程序。
int main()
{
R_GPIO_REGS * gp_regs= (R_GPIO_REGS*)GPIO_BASE_ADDRESS;
gp_regs->GPFSEL[1] =0x1000000;
uart_init();
printf("hellow world\n");
vector_memory_dump();
unsigned int destrst=0xea020000;
unsigned int destirq=0xea020000;
unsigned int destswi=0xea020000;
PUT32(MEMZERO,destrst);
PUT32(MEMY,destirq);
PUT32(MEMSWI,destswi);
vector_memory_dump();
//asm("b 0x04");
asm("swi 0"); //which call swi handler on 0x08. I thought.
while(1)
{
gp_regs->GPSET[0]=0x40000;
}
return 0;
}
有什么问题?
因此,根据标签等,我假设这是一个树莓派 pi3,处于 aarch32 模式,可能是 HYP 模式。请注意,我非常感谢您直接或间接地 reading/borrowing 我的一些代码。
使用您的代码让我们从这里开始:
ldr r0,=0x00080008
mov r1,#0x0000
这在技术上不是错误,但有点忽略了该副本的作用。
b print_mem1
b print_mem1
b print_mem2
b print_mem3
b print_mem4
b print_mem1
b print_mem2
b print_mem3
b print_mem4
结合这些,是的,这是一个问题。因为它们依赖于位置,所以让工具链为您创建 table 然后复制它的整个想法都丢失了。
Disassembly of section .text:
00080000 <_ram_entry>:
80000: eb00000a bl 80030 <kernel_init>
80004: eafffffd b 80000 <_ram_entry>
80008: e59ff074 ldr pc, [pc, #116] ; 80084 <print_c_mem4+0x4>
8000c: ea000013 b 80060 <print_mem1>
80010: ea000012 b 80060 <print_mem1>
80014: ea000012 b 80064 <print_mem2>
80018: ea000012 b 80068 <print_mem3>
8001c: ea000012 b 8006c <print_mem4>
80020: ea00000e b 80060 <print_mem1>
80024: ea00000e b 80064 <print_mem2>
80028: ea00000e b 80068 <print_mem3>
8002c: ea00000e b 8006c <print_mem4>
当我assemble then disassemble, ldr pc,这是正确的做法,但是登陆错误的地方显示0x80084,也就是84-8 = 0x7C在前面是 1111100 0x1F 寄存器用来做一个副本以得到那么远......
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
32 个寄存器,复制了 0x80 个字节。从技术上讲,它涵盖了第一个向量也许是第二个,但肯定不是 swi 向量。
当您再次查看 arm 文档时(armv7-ar,因为这是 aarch32 或 armv7-a 兼容模式)0x00000008 是 supervisor/svc/swi 调用的入口点。
所以你需要一条从 0x00000008 到所需 address/label.
的指令
因此,如果您返回此示例或您 borrowed/learned 来自的任何示例。
.globl _start
_start:
ldr pc,reset_handler
ldr pc,undefined_handler
ldr pc,swi_handler
ldr pc,prefetch_handler
ldr pc,data_handler
ldr pc,unused_handler
ldr pc,irq_handler
ldr pc,fiq_handler
reset_handler: .word reset
undefined_handler: .word hang
swi_handler: .word hang
prefetch_handler: .word hang
data_handler: .word hang
unused_handler: .word hang
irq_handler: .word irq
fiq_handler: .word hang
reset:
mov r0,#0x80000
mov r1,#0x0000
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
Disassembly of section .text:
00080000 <_stack>:
80000: e59ff018 ldr pc, [pc, #24] ; 80020 <reset_handler>
80004: e59ff018 ldr pc, [pc, #24] ; 80024 <undefined_handler>
80008: e59ff018 ldr pc, [pc, #24] ; 80028 <swi_handler>
8000c: e59ff018 ldr pc, [pc, #24] ; 8002c <prefetch_handler>
80010: e59ff018 ldr pc, [pc, #24] ; 80030 <data_handler>
80014: e59ff018 ldr pc, [pc, #24] ; 80034 <unused_handler>
80018: e59ff018 ldr pc, [pc, #24] ; 80038 <irq_handler>
8001c: e59ff018 ldr pc, [pc, #24] ; 8003c <fiq_handler>
00080020 <reset_handler>:
80020: 00080040 andeq r0, r8, r0, asr #32
00080024 <undefined_handler>:
80024: 00080058 andeq r0, r8, r8, asr r0
00080028 <swi_handler>:
80028: 00080058 andeq r0, r8, r8, asr r0
0008002c <prefetch_handler>:
8002c: 00080058 andeq r0, r8, r8, asr r0
00080030 <data_handler>:
80030: 00080058 andeq r0, r8, r8, asr r0
00080034 <unused_handler>:
80034: 00080058 andeq r0, r8, r8, asr r0
00080038 <irq_handler>:
80038: 0008005c andeq r0, r8, ip, asr r0
0008003c <fiq_handler>:
8003c: 00080058 andeq r0, r8, r8, asr r0
00080040 <reset>:
80040: e3a00702 mov r0, #524288 ; 0x80000
80044: e3a01000 mov r1, #0
80048: e8b003fc ldm r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
8004c: e8a103fc stmia r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
80050: e8b003fc ldm r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
80054: e8a103fc stmia r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
00080058 <hang>:
80058: eafffffe b 80058 <hang>
0008005c <irq>:
8005c: eafffffe b 8005c <irq>
它强制入口点的 8 个字从异常处理程序中启动 table,并将这些用于 pc 相对访问的地址放在接下来的 8 个字中,因此您需要将 16 个字复制到让 assembler 为您完成工作,而不必计算这些东西。 32 字,4 条指令,每条 8 个寄存器,即 32 字。或者,如果您更喜欢 8 组说明,每组 4 个单词,也可以。
这就是您对整个方法的追求
80008: e59ff018 ldr pc, [pc, #24] ; 80028 <swi_handler>
00080028 <swi_handler>:
80028: 00080058
让工具为您完成工作
如果我这样做会怎样:
.globl _start
_start:
ldr pc,reset_handler
ldr pc,undefined_handler
ldr pc,swi_handler
ldr pc,prefetch_handler
ldr pc,data_handler
ldr pc,unused_handler
b irq
ldr pc,fiq_handler
reset_handler: .word reset
undefined_handler: .word hang
swi_handler: .word hang
prefetch_handler: .word hang
data_handler: .word hang
unused_handler: .word hang
irq_handler: .word irq
fiq_handler: .word hang
reset:
mov r0,#0x80000
mov r1,#0x0000
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
hang:
b hang
irq:
b irq
我明白了
80018: ea00000f b 8005c <irq>
而不是这个
80018: e59ff018 ldr pc, [pc, #24] ; 80038 <irq_handler>
后者是说从 pc+24 读取,在这种情况下,pc 提前 8,因此指令地址 + 32 即指令地址 +0x20。
还有这个
80018: ea00000f b 8005c <irq>
是说跳转到指令地址
之前的地址0x44
现在让 disassemble 来自不同的基地址,例如对象(而不是链接的 elf 二进制文件)是一个很好的选择
00000000 <_start>:
0: e59ff018 ldr pc, [pc, #24] ; 20 <reset_handler>
4: e59ff018 ldr pc, [pc, #24] ; 24 <undefined_handler>
8: e59ff018 ldr pc, [pc, #24] ; 28 <swi_handler>
c: e59ff018 ldr pc, [pc, #24] ; 2c <prefetch_handler>
10: e59ff018 ldr pc, [pc, #24] ; 30 <data_handler>
14: e59ff018 ldr pc, [pc, #24] ; 34 <unused_handler>
18: ea00000f b 5c <irq>
1c: e59ff018 ldr pc, [pc, #24] ; 3c <fiq_handler>
注意所有其他的机器代码,将此指令前面的字0x20字节加载到pc中。
分支表示程序计数器前面的分支 0x44 字节。
我们使用工具链来实现 table
00080020 <reset_handler>:
80020: 00080040 andeq r0, r8, r0, asr #32
00080024 <undefined_handler>:
80024: 00080058 andeq r0, r8, r8, asr r0
00080028 <swi_handler>:
80028: 00080058 andeq r0, r8, r8, asr r0
0008002c <prefetch_handler>:
8002c: 00080058 andeq r0, r8, r8, asr r0
00080030 <data_handler>:
80030: 00080058 andeq r0, r8, r8, asr r0
00080034 <unused_handler>:
80034: 00080058 andeq r0, r8, r8, asr r0
00080038 <irq_handler>:
80038: 0008005c andeq r0, r8, ip, asr r0
0008003c <fiq_handler>:
8003c: 00080058 andeq r0, r8, r8, asr r0
如果我们将 0x40 字节从 0x80000 复制到 0x00000,那么当它在 0x18 处遇到表示从 0x38 读取的机器代码并将其放入程序计数器时,它将得到 0008005c,这是正确的位置
但如果它找到
18: ea00000f b 5c <irq>
这意味着分支到我们没有处理程序的 0x5c。
所以除了不设置堆栈指针以及你的代码是如何进入 swi 之外的,但是无论如何,如果你构建了这个
80008: e59ff074 ldr pc, [pc, #116] ; 80084 <print_c_mem4+0x4>
8000c: ea000013 b 80060 <print_mem1>
80010: ea000012 b 80060 <print_mem1>
80014: ea000012 b 80064 <print_mem2>
80018: ea000012 b 80068 <print_mem3>
8001c: ea000012 b 8006c <print_mem4>
80020: ea00000e b 80060 <print_mem1>
80024: ea00000e b 80064 <print_mem2>
80028: ea00000e b 80068 <print_mem3>
8002c: ea00000e b 8006c <print_mem4>
或类似的东西,因为你的 print_mems 不仅仅是占位符,可以让这个例子为这个答案构建。但仍然是 pc 相关分支。
然后你从 0x80008 复制了一段时间到 0x00000 然后最终位于地址 0x00000008 的指令是 svc/swi 处理程序是
80010: ea000012 b 80060 <print_mem1>
到 print_mem1 的分支,但它不会去 print_mem1 附近的任何地方,因为它将在 0x00000 之后分支一些字节,这将是 0x80008 字节远离地址你真的想让它落地。
综上所述,如果您在 arm 文档中搜索 HVBAR,您会发现您不必进行任何复制,您可以在内存中设置一个异常 table 并更改基地址发生异常(复位除外)时处理器的去向。但请注意低 5 位必须为零,因此 0x80008 将不起作用。因此,在您的代码中使用 .balign,在那里构建 table,使用标签获取其地址并将其粘贴到 HVBAR 中,然后您可以使用分支而不是 ldr pc。对于 armv6 和更早的版本,需要完成 table(s) 的复制或构建,因为除了用于高地址的处理器带外,向量必须位于 0x00000000。对于 armv7 和一些 cortex-ms,您可以在 table 的某个其他地址(直到重置)代替 move/point。
很高兴理解我演示的复制技巧,但您必须正确使用它才能发挥作用。这不是一个不常见的解决方案。请注意另一种方法,您可以在此处执行此操作:
.globl _start
_start:
b 0x80000
b 0x80004
b 0x80008
b 0x8000C
链接到 0x0000 时
00000000 <_start>:
0: ea01fffe b 80000 <_stack>
4: ea01fffe b 80004 <*ABS*0x80004>
8: ea01fffe b 80008 <*ABS*0x80008>
c: ea01fffe b 8000c <*ABS*0x8000c>
所以这个机器代码 ea01fffe 意味着分支到相对于该指令地址的 0x80000,所以你可以只写从 0x00000000 开始的前 8 个字而不是副本,处理器将分支到你的 0x80000 table.如果你想在 0x80008 处构建它,那么让工具为你完成工作:
.globl _start
_start:
b 0x80008
b 0x8000c
b 0x80010
b 0x80014
不出所料,立即数是字数,0x8是两个字加2到1fffe你得到0x20000
00000000 <_start>:
0: ea020000 b 80008 <*ABS*0x80008>
4: ea020000 b 8000c <*ABS*0x8000c>
8: ea020000 b 80010 <*ABS*0x80010>
c: ea020000 b 80014 <*ABS*0x80014>
我们也知道 pc 是两个前面所以当在地址 0 执行时 pc 以这种方式使用时是 8 我们想去 0x80008 即 0x80000 前面的 pc,指令中的立即数在字的单位,所以前面有 0x20000 个字。
所以不用复制
ldr r0,=0xEA020000
ldr r1,=0x00000000
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
或其他一些解决方案,将用分支填充这 8 个位置到正确的位置。
编辑
让工具为我们做这件事的另一种方法:
.text 部分的反汇编:
00080000 <_stack>:
80000: e59ff018 ldr pc, [pc, #24] ; 80020 <reset_handler>
80004: e59ff018 ldr pc, [pc, #24] ; 80024 <undefined_handler>
80008: e59ff018 ldr pc, [pc, #24] ; 80028 <swi_handler>
8000c: e59ff018 ldr pc, [pc, #24] ; 8002c <prefetch_handler>
80010: e59ff018 ldr pc, [pc, #24] ; 80030 <data_handler>
80014: e59ff018 ldr pc, [pc, #24] ; 80034 <unused_handler>
80018: e59ff018 ldr pc, [pc, #24] ; 80038 <irq_handler>
8001c: e59ff018 ldr pc, [pc, #24] ; 8003c <fiq_handler>
我们可以用e59ff018填充内存的前8个字然后在我们需要它们之前的某个时候可以稍后填充地址,在创建中断之前用处理程序的地址填充0x00000038,可以使用C或 ASM 或其他什么。每次都可以更改处理程序,在执行 svc/swi 指令之前,将 0xe59ff018 放入内存中的 0x00000008 和您的 swi 处理程序的地址 0x00000028,更改 0x00000028 处的处理程序,然后重试。
我正在 raspberry pi 3(无蓝牙)上制作我的内核。 我的内核使用 arm 汇编语言(32 位)和 c 和 uboot 引导我的内核。
我找到了中断向量 table 并像这样将其应用到我的代码中。
.globl _ram_entry
_ram_entry:
bl kernel_init
b _ram_entry //
ldr pc,=print_mem1
b print_mem1
b print_mem1
b print_mem2
b print_mem3
b print_mem4
b print_mem1
b print_mem2
b print_mem3
b print_mem4
#define svc_stack 0xa0300000
#define irq_stack 0xa0380000
#define sys_stack 0xa0400000
.global kernel_init
kernel_init:
ldr r0,=0x00080008
mov r1,#0x0000
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
bl main
b _ram_entry
.global print_mem1
print_mem1:
bl print_c_mem1
.global print_mem2
print_mem2:
bl print_c_mem2
.global print_mem3
print_mem3:
bl print_c_mem3
.global print_mem4
print_mem4:
bl print_c_mem4
_ram_entry从0x00080008开始,这是我的中断向量table。 当我打印我的记忆时,0x00 有 bl kernel_init。 所有中断处理程序只打印简单的数字。
但是如果我像这个主要代码一样使用 swi,则会调用重置处理程序。
int main()
{
R_GPIO_REGS * gp_regs= (R_GPIO_REGS*)GPIO_BASE_ADDRESS;
gp_regs->GPFSEL[1] =0x1000000;
uart_init();
printf("hellow world\n");
vector_memory_dump();
unsigned int destrst=0xea020000;
unsigned int destirq=0xea020000;
unsigned int destswi=0xea020000;
PUT32(MEMZERO,destrst);
PUT32(MEMY,destirq);
PUT32(MEMSWI,destswi);
vector_memory_dump();
//asm("b 0x04");
asm("swi 0"); //which call swi handler on 0x08. I thought.
while(1)
{
gp_regs->GPSET[0]=0x40000;
}
return 0;
}
有什么问题?
因此,根据标签等,我假设这是一个树莓派 pi3,处于 aarch32 模式,可能是 HYP 模式。请注意,我非常感谢您直接或间接地 reading/borrowing 我的一些代码。
使用您的代码让我们从这里开始:
ldr r0,=0x00080008
mov r1,#0x0000
这在技术上不是错误,但有点忽略了该副本的作用。
b print_mem1
b print_mem1
b print_mem2
b print_mem3
b print_mem4
b print_mem1
b print_mem2
b print_mem3
b print_mem4
结合这些,是的,这是一个问题。因为它们依赖于位置,所以让工具链为您创建 table 然后复制它的整个想法都丢失了。
Disassembly of section .text:
00080000 <_ram_entry>:
80000: eb00000a bl 80030 <kernel_init>
80004: eafffffd b 80000 <_ram_entry>
80008: e59ff074 ldr pc, [pc, #116] ; 80084 <print_c_mem4+0x4>
8000c: ea000013 b 80060 <print_mem1>
80010: ea000012 b 80060 <print_mem1>
80014: ea000012 b 80064 <print_mem2>
80018: ea000012 b 80068 <print_mem3>
8001c: ea000012 b 8006c <print_mem4>
80020: ea00000e b 80060 <print_mem1>
80024: ea00000e b 80064 <print_mem2>
80028: ea00000e b 80068 <print_mem3>
8002c: ea00000e b 8006c <print_mem4>
当我assemble then disassemble, ldr pc,这是正确的做法,但是登陆错误的地方显示0x80084,也就是84-8 = 0x7C在前面是 1111100 0x1F 寄存器用来做一个副本以得到那么远......
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
ldmia r0!,{r2,r3,r4,r5}
stmia r1!,{r2,r3,r4,r5}
32 个寄存器,复制了 0x80 个字节。从技术上讲,它涵盖了第一个向量也许是第二个,但肯定不是 swi 向量。
当您再次查看 arm 文档时(armv7-ar,因为这是 aarch32 或 armv7-a 兼容模式)0x00000008 是 supervisor/svc/swi 调用的入口点。
所以你需要一条从 0x00000008 到所需 address/label.
的指令因此,如果您返回此示例或您 borrowed/learned 来自的任何示例。
.globl _start
_start:
ldr pc,reset_handler
ldr pc,undefined_handler
ldr pc,swi_handler
ldr pc,prefetch_handler
ldr pc,data_handler
ldr pc,unused_handler
ldr pc,irq_handler
ldr pc,fiq_handler
reset_handler: .word reset
undefined_handler: .word hang
swi_handler: .word hang
prefetch_handler: .word hang
data_handler: .word hang
unused_handler: .word hang
irq_handler: .word irq
fiq_handler: .word hang
reset:
mov r0,#0x80000
mov r1,#0x0000
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
Disassembly of section .text:
00080000 <_stack>:
80000: e59ff018 ldr pc, [pc, #24] ; 80020 <reset_handler>
80004: e59ff018 ldr pc, [pc, #24] ; 80024 <undefined_handler>
80008: e59ff018 ldr pc, [pc, #24] ; 80028 <swi_handler>
8000c: e59ff018 ldr pc, [pc, #24] ; 8002c <prefetch_handler>
80010: e59ff018 ldr pc, [pc, #24] ; 80030 <data_handler>
80014: e59ff018 ldr pc, [pc, #24] ; 80034 <unused_handler>
80018: e59ff018 ldr pc, [pc, #24] ; 80038 <irq_handler>
8001c: e59ff018 ldr pc, [pc, #24] ; 8003c <fiq_handler>
00080020 <reset_handler>:
80020: 00080040 andeq r0, r8, r0, asr #32
00080024 <undefined_handler>:
80024: 00080058 andeq r0, r8, r8, asr r0
00080028 <swi_handler>:
80028: 00080058 andeq r0, r8, r8, asr r0
0008002c <prefetch_handler>:
8002c: 00080058 andeq r0, r8, r8, asr r0
00080030 <data_handler>:
80030: 00080058 andeq r0, r8, r8, asr r0
00080034 <unused_handler>:
80034: 00080058 andeq r0, r8, r8, asr r0
00080038 <irq_handler>:
80038: 0008005c andeq r0, r8, ip, asr r0
0008003c <fiq_handler>:
8003c: 00080058 andeq r0, r8, r8, asr r0
00080040 <reset>:
80040: e3a00702 mov r0, #524288 ; 0x80000
80044: e3a01000 mov r1, #0
80048: e8b003fc ldm r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
8004c: e8a103fc stmia r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
80050: e8b003fc ldm r0!, {r2, r3, r4, r5, r6, r7, r8, r9}
80054: e8a103fc stmia r1!, {r2, r3, r4, r5, r6, r7, r8, r9}
00080058 <hang>:
80058: eafffffe b 80058 <hang>
0008005c <irq>:
8005c: eafffffe b 8005c <irq>
它强制入口点的 8 个字从异常处理程序中启动 table,并将这些用于 pc 相对访问的地址放在接下来的 8 个字中,因此您需要将 16 个字复制到让 assembler 为您完成工作,而不必计算这些东西。 32 字,4 条指令,每条 8 个寄存器,即 32 字。或者,如果您更喜欢 8 组说明,每组 4 个单词,也可以。
这就是您对整个方法的追求
80008: e59ff018 ldr pc, [pc, #24] ; 80028 <swi_handler>
00080028 <swi_handler>:
80028: 00080058
让工具为您完成工作
如果我这样做会怎样:
.globl _start
_start:
ldr pc,reset_handler
ldr pc,undefined_handler
ldr pc,swi_handler
ldr pc,prefetch_handler
ldr pc,data_handler
ldr pc,unused_handler
b irq
ldr pc,fiq_handler
reset_handler: .word reset
undefined_handler: .word hang
swi_handler: .word hang
prefetch_handler: .word hang
data_handler: .word hang
unused_handler: .word hang
irq_handler: .word irq
fiq_handler: .word hang
reset:
mov r0,#0x80000
mov r1,#0x0000
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
ldmia r0!,{r2,r3,r4,r5,r6,r7,r8,r9}
stmia r1!,{r2,r3,r4,r5,r6,r7,r8,r9}
hang:
b hang
irq:
b irq
我明白了
80018: ea00000f b 8005c <irq>
而不是这个
80018: e59ff018 ldr pc, [pc, #24] ; 80038 <irq_handler>
后者是说从 pc+24 读取,在这种情况下,pc 提前 8,因此指令地址 + 32 即指令地址 +0x20。
还有这个
80018: ea00000f b 8005c <irq>
是说跳转到指令地址
之前的地址0x44现在让 disassemble 来自不同的基地址,例如对象(而不是链接的 elf 二进制文件)是一个很好的选择
00000000 <_start>:
0: e59ff018 ldr pc, [pc, #24] ; 20 <reset_handler>
4: e59ff018 ldr pc, [pc, #24] ; 24 <undefined_handler>
8: e59ff018 ldr pc, [pc, #24] ; 28 <swi_handler>
c: e59ff018 ldr pc, [pc, #24] ; 2c <prefetch_handler>
10: e59ff018 ldr pc, [pc, #24] ; 30 <data_handler>
14: e59ff018 ldr pc, [pc, #24] ; 34 <unused_handler>
18: ea00000f b 5c <irq>
1c: e59ff018 ldr pc, [pc, #24] ; 3c <fiq_handler>
注意所有其他的机器代码,将此指令前面的字0x20字节加载到pc中。
分支表示程序计数器前面的分支 0x44 字节。
我们使用工具链来实现 table
00080020 <reset_handler>:
80020: 00080040 andeq r0, r8, r0, asr #32
00080024 <undefined_handler>:
80024: 00080058 andeq r0, r8, r8, asr r0
00080028 <swi_handler>:
80028: 00080058 andeq r0, r8, r8, asr r0
0008002c <prefetch_handler>:
8002c: 00080058 andeq r0, r8, r8, asr r0
00080030 <data_handler>:
80030: 00080058 andeq r0, r8, r8, asr r0
00080034 <unused_handler>:
80034: 00080058 andeq r0, r8, r8, asr r0
00080038 <irq_handler>:
80038: 0008005c andeq r0, r8, ip, asr r0
0008003c <fiq_handler>:
8003c: 00080058 andeq r0, r8, r8, asr r0
如果我们将 0x40 字节从 0x80000 复制到 0x00000,那么当它在 0x18 处遇到表示从 0x38 读取的机器代码并将其放入程序计数器时,它将得到 0008005c,这是正确的位置
但如果它找到
18: ea00000f b 5c <irq>
这意味着分支到我们没有处理程序的 0x5c。
所以除了不设置堆栈指针以及你的代码是如何进入 swi 之外的,但是无论如何,如果你构建了这个
80008: e59ff074 ldr pc, [pc, #116] ; 80084 <print_c_mem4+0x4>
8000c: ea000013 b 80060 <print_mem1>
80010: ea000012 b 80060 <print_mem1>
80014: ea000012 b 80064 <print_mem2>
80018: ea000012 b 80068 <print_mem3>
8001c: ea000012 b 8006c <print_mem4>
80020: ea00000e b 80060 <print_mem1>
80024: ea00000e b 80064 <print_mem2>
80028: ea00000e b 80068 <print_mem3>
8002c: ea00000e b 8006c <print_mem4>
或类似的东西,因为你的 print_mems 不仅仅是占位符,可以让这个例子为这个答案构建。但仍然是 pc 相关分支。
然后你从 0x80008 复制了一段时间到 0x00000 然后最终位于地址 0x00000008 的指令是 svc/swi 处理程序是
80010: ea000012 b 80060 <print_mem1>
到 print_mem1 的分支,但它不会去 print_mem1 附近的任何地方,因为它将在 0x00000 之后分支一些字节,这将是 0x80008 字节远离地址你真的想让它落地。
综上所述,如果您在 arm 文档中搜索 HVBAR,您会发现您不必进行任何复制,您可以在内存中设置一个异常 table 并更改基地址发生异常(复位除外)时处理器的去向。但请注意低 5 位必须为零,因此 0x80008 将不起作用。因此,在您的代码中使用 .balign,在那里构建 table,使用标签获取其地址并将其粘贴到 HVBAR 中,然后您可以使用分支而不是 ldr pc。对于 armv6 和更早的版本,需要完成 table(s) 的复制或构建,因为除了用于高地址的处理器带外,向量必须位于 0x00000000。对于 armv7 和一些 cortex-ms,您可以在 table 的某个其他地址(直到重置)代替 move/point。
很高兴理解我演示的复制技巧,但您必须正确使用它才能发挥作用。这不是一个不常见的解决方案。请注意另一种方法,您可以在此处执行此操作:
.globl _start
_start:
b 0x80000
b 0x80004
b 0x80008
b 0x8000C
链接到 0x0000 时
00000000 <_start>:
0: ea01fffe b 80000 <_stack>
4: ea01fffe b 80004 <*ABS*0x80004>
8: ea01fffe b 80008 <*ABS*0x80008>
c: ea01fffe b 8000c <*ABS*0x8000c>
所以这个机器代码 ea01fffe 意味着分支到相对于该指令地址的 0x80000,所以你可以只写从 0x00000000 开始的前 8 个字而不是副本,处理器将分支到你的 0x80000 table.如果你想在 0x80008 处构建它,那么让工具为你完成工作:
.globl _start
_start:
b 0x80008
b 0x8000c
b 0x80010
b 0x80014
不出所料,立即数是字数,0x8是两个字加2到1fffe你得到0x20000
00000000 <_start>:
0: ea020000 b 80008 <*ABS*0x80008>
4: ea020000 b 8000c <*ABS*0x8000c>
8: ea020000 b 80010 <*ABS*0x80010>
c: ea020000 b 80014 <*ABS*0x80014>
我们也知道 pc 是两个前面所以当在地址 0 执行时 pc 以这种方式使用时是 8 我们想去 0x80008 即 0x80000 前面的 pc,指令中的立即数在字的单位,所以前面有 0x20000 个字。
所以不用复制
ldr r0,=0xEA020000
ldr r1,=0x00000000
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
str r0,[r1],#4
或其他一些解决方案,将用分支填充这 8 个位置到正确的位置。
编辑
让工具为我们做这件事的另一种方法:
.text 部分的反汇编:
00080000 <_stack>:
80000: e59ff018 ldr pc, [pc, #24] ; 80020 <reset_handler>
80004: e59ff018 ldr pc, [pc, #24] ; 80024 <undefined_handler>
80008: e59ff018 ldr pc, [pc, #24] ; 80028 <swi_handler>
8000c: e59ff018 ldr pc, [pc, #24] ; 8002c <prefetch_handler>
80010: e59ff018 ldr pc, [pc, #24] ; 80030 <data_handler>
80014: e59ff018 ldr pc, [pc, #24] ; 80034 <unused_handler>
80018: e59ff018 ldr pc, [pc, #24] ; 80038 <irq_handler>
8001c: e59ff018 ldr pc, [pc, #24] ; 8003c <fiq_handler>
我们可以用e59ff018填充内存的前8个字然后在我们需要它们之前的某个时候可以稍后填充地址,在创建中断之前用处理程序的地址填充0x00000038,可以使用C或 ASM 或其他什么。每次都可以更改处理程序,在执行 svc/swi 指令之前,将 0xe59ff018 放入内存中的 0x00000008 和您的 swi 处理程序的地址 0x00000028,更改 0x00000028 处的处理程序,然后重试。