如何在Supervisor模式下用C内联汇编写入lr_usr寄存器
问题分析与解决方案
1. lr_usr寄存器识别错误的问题
GCC内联汇编不支持直接使用lr_usr作为寄存器名,正确的用户模式LR寄存器的ARM指令命名是r14_usr。同时你的代码存在逻辑错误:"=r"(syscall_exit)是输出约束,意味着要把寄存器的值写入变量,但你实际是要把变量的值写入r14_usr,应该用输入约束。
修正后的代码:
asm volatile("msr r14_usr, %0" : : "r"(syscall_exit) : "memory");
volatile:防止编译器优化掉这条汇编指令"r"(syscall_exit):让编译器把syscall_exit的值放到某个通用寄存器,替换%0"memory":告诉编译器这段汇编会修改内存相关状态,确保内存操作的正确性
另外注意:你的第一行代码uint32_t syscall_exit = (uint32_t) syscall_exit;是自赋值,会导致未定义行为,应该改成正确的函数地址赋值,比如uint32_t syscall_exit = (uint32_t)&syscall_exit_func;(假设函数名为syscall_exit_func)。
2. ldr SPSR, #0x10的错误
这条指令是非法的:ARM架构中,SPSR只能通过msr/mrs指令访问,不能直接用ldr加载立即数。如果你想设置SPSR为0x10(用户模式的PSR值),应该用:
asm volatile("msr spsr_cxsf, #0x10");
3. ldmia ®isters,{r0-r12,PC}^的有效性问题
这条指令存在两个问题:
- 直接在汇编里写
®isters会导致编译器无法正确处理地址,应该通过输入约束传递数组地址 ldmia的^后缀会在加载PC的同时,将SPSR的值恢复到CPSR,这会直接切换到用户模式,符合你的需求,但需要确保SPSR已经正确设置为用户模式的状态(即前面的msr spsr_cxsf, #0x10是正确的)
修正后的代码:
asm volatile("ldmia %0, {r0-r12, pc}^" : : "r"(registers) : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "pc");
"r"(registers):传递数组的首地址到通用寄存器,替换%0- clobber列表显式声明被修改的寄存器,告诉编译器这些寄存器的值会被覆盖,避免优化冲突
完整修正后的代码示例
// 假设你的函数定义 void syscall_exit_func(void) { /* ... */ } void idle_thread_main(void) { /* ... */ } void setup_user_mode(void) { uint32_t syscall_exit = (uint32_t)&syscall_exit_func; uint32_t registers[14] = {97,6,7,6,7,6,7,6,7,6,7,5,7,(uint32_t)idle_thread_main}; // 设置SPSR为用户模式(0x10是ARM用户模式的PSR模式位) asm volatile("msr spsr_cxsf, #0x10"); // 写入用户模式LR寄存器 asm volatile("msr r14_usr, %0" : : "r"(syscall_exit) : "memory"); // 加载r0-r12并跳转到用户模式的idle_thread_main asm volatile("ldmia %0, {r0-r12, pc}^" : : "r"(registers) : "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12", "pc"); }
关键注意事项
- 确保代码运行在Supervisor模式下,否则无法访问
r14_usr和修改SPSR ldmia ... pc^指令执行后,CPU会立即切换到用户模式,执行idle_thread_main,同时r0-r12被设置为数组中的值- 编译时需要添加ARM架构相关的编译选项,比如
-march=armv7-a(根据你的实际架构调整)
内容的提问来源于stack exchange,提问作者user28496108
相关产品推荐
相关产品推荐

