SVC称为系统服务调用(SuperVisorCall),异常类型为11,通过svc指令可用触发异常,SVC在触发异常后必须立即得到相应(触发异常后在执行异常前不能执行其它代码),除非有更高优先级的异常在执行。
对于可靠系统而言,可以使用SVC异常实现资源的特权访问,使系统更加安全。通过SVC系统调用编号能够实现参数传递,从而实现不同功能的系统服务。使用系统服务可以不需要知道具体服务函数的地址,这样能够隐藏更多的细节,降低软件间的耦合性。
在ARM开发工具链下,有比较优雅的方式直接使用SVC异常,在GCC下需要配合汇编来使用。
这里不讨论ARM处理器对于异常的处理机制,它在触发SVC指令后响应的异常行为与系统中其它异常是一致的。
SVC异常处理
1 | .syntax unified |
SVC_Handler函数是异常处理函数,进入函数,判断LR寄存器来分析进入异常前的环境,获取到对应的sp地址。通过sp寄存器就能访问到栈空间,在栈中可以获取到r0-r3,这个就是执行svc时传进来的参数,通常这4个寄存器不会被修改,除非发生了更高优先级的异常。通过在栈中获取到执行svc的pc指针,得到svc指令的二进制码,低8位存储了指令附加的异常id,使用该id在内部通过查表的方式定位具体需要执行的函数入口,执行指定的函数后,我们将r0也就是返回值存储到栈空间中,这样返回到用户态后,用户就能获得执行目标函数后返回结果。
为了方便定义用户系统调用函数,设计了这样的一个宏。
1 |
其中id表示该函数绑定到svc的那一个系统调用上,func表示函数名称,ret_t和后面的参数分别表示函数的返回值和形参列表,如果无形参则填入void。通过 SVC_VIRTUAL_CALL_id()来执行系统调用。
这是一个简单的例子:
1 | SVC_CALL_DEF(1, test_svc_add, int, int a, int b){ |
进阶写法
前面的定义方法和调用方法与传统的C语言函数定义和函数调用存在区别,所以在使用上存在不方便的地方,按照下面的写法更加合理,且函数调用与C语言是一致的,减低了代码移植上的一致性问题。
1 |
|
有了这个宏定义后,定义一个svc函数以及函数的调用写法将变为:
1 | DEF_SVC_FUNC(2, int, add_func, int a, int b){ |
这样的写法能够兼容不同的编译器,且不用提前定义SVC_VIRTUAL_CALL_0函数。