👹
Carlos's Tech Blog
  • 🧔ECUs
    • ZYNQ_Documents
      • [ZYNQ] 构建ZYNQ的BSP工程
      • [ZYNQ] 启动流程
      • [ZYNQ] Secure Boot Flow
      • [ZYNQ] Provisioning Guideline
      • [ZYNQ] Decrypting Partition by the Decrypt Agent Using PUF key
      • [ZYNQ] enabling the cryptsetup on ramdisk
      • [ZYNQ] Encrypt external files based on file system using PUF key
      • [ZYNQ] Loading an Encrypted Linux kernel at U-Boot with a KUP Key
      • [ZYNQ] cross-compile the cryptsetup on Xilinx ZYNQ aarch64 platform
      • [ZYNQ] Linux Linaro系统镜像制作SD卡启动
    • S32G_Documents
      • [S32G] Going through the s32g hard/soft platform
      • [S32G] S32g247's Secure Boot using HSE firmware
        • S32g2 HSE key config
        • How S32g verify secure boot image
        • S32g secure boot signature generation
        • How to download and build S32g Secure boot image
        • [S32G] OTA with Secure Boot
    • RT117x_Documents
      • [RT-117x]IMX RT1170 Provisioning Guideline
      • [RT-117x] Going through the MX-RT1170 hard/soft platform
      • [RT-117x] i.MX-RT1170's Secure Boot
        • [RT-117x]Signing image with the HSM (SignServer)
    • LS104x_Documents
      • [LS104x] bsp project
      • [LS104x] boot flow
      • [LS104x] secure boot
      • [LS104x] Application Note, Using the PKCS#11 in TCU platform
      • [LS104x] 使用ostree更新rootfs
      • [LS104x] ostree的移植
      • [LS104x] Starting with Yocto
      • [LS104x] 使用FIT的kernel格式和initramfs
    • IMX6/8_Documents
      • [IMX6] Defining A U-Boot Command
      • NXP IMX6 嵌入式板子一些笔记
      • NXP-imx6 initialization
    • Vehicle_Apps
      • [SecOC] Tree
        • [SecOC] SecOC Freshness and MAC Truncation
  • 😾TECH
    • Rust Arm OS
      • ARMv7m_Using_The_RUST_Cross_Compiler
    • ARM
      • ARM-v7-M
        • 01_ARMv7-M_处理器架构技术综述
        • 02_ARMv7-M_编程模型与模式
        • 03_ARMv7-M_存储系统结构
        • 04_ARMv7-M_异常处理及中断处理
      • ARM-v8-A
        • 02_ARMv8_基本概念
        • 03_ARMv8_指令集介绍_加载指令集和存储指令集
        • 04_ARMv8_指令集_运算指令集
        • 05_ARMv8_指令集_跳转_比较与返回指令
        • 06_ARMv8_指令集_一些重要的指令
        • 0X_ARMv8_指令集_基于汇编的UART驱动
        • 07_ARMv8_汇编器Using as
        • 08_ARMv8_链接器和链接脚本
        • 09_ARMv8_内嵌汇编(内联汇编)Inline assembly
        • 10_ARMv8_异常处理(一) - 入口与返回、栈选择、异常向量表
        • 11_ARMv8_异常处理(二)- Legacy 中断处理
        • 12_ARMv8_异常处理(三)- GICv1/v2中断处理
        • 13_ARMv8_内存管理(一)-内存管理要素
        • 14_ARMv8_内存管理(二)-ARM的MMU设计
        • 15_ARMv8_内存管理(三)-MMU恒等映射及Linux实现
        • 16_ARMv8_高速缓存(一)cache要素
        • 17_ARMv8_高速缓存(二)ARM cache设计
        • 18_ARMv8_高速缓存(三)多核与一致性要素
        • 19_ARMv8_TLB管理(Translation Lookaside buffer)
        • 20_ARMv8_barrier(一)流水线和一致性模型
        • 21_ARMv8_barrier(二)内存屏障案例
      • ARM Boot Flow
        • 01_Embedded_ARMv7/v8 non-secure Boot Flow
        • 02_Embedded_ARMv8 ATF Secure Boot Flow (BL1/BL2/BL31)
        • 03_Embedded_ARMv8 BL33 Uboot Booting Flow
      • ARM Compiler
        • Compiler optimization and the volatile keyword
      • ARM Development
        • 在MACBOOK上搭建ARMv8架构的ARM开发环境
        • Starting with JLink debugger or QEMU
    • Linux
      • Kernel
        • 0x01_LinuxKernel_内核的启动(一)之启动前准备
        • 0x02_LinuxKernel_内核的启动(二)SMP多核处理器启动过程分析
        • 0x21_LinuxKernel_内核活动(一)之系统调用
        • 0x22_LinuxKernel_内核活动(二)中断体系结构(中断上文)
        • 0x23_LinuxKernel_内核活动(三)中断体系结构(中断下文)
        • 0x24_LinuxKernel_进程(一)进程的管理(生命周期、进程表示)
        • 0x25_LinuxKernel_进程(二)进程的调度器的实现
        • 0x26_LinuxKernel_设备驱动(一)综述与文件系统关联
        • 0x27_LinuxKernel_设备驱动(二)字符设备操作
        • 0x28_LinuxKernel_设备驱动(三)块设备操作
        • 0x29_LinuxKernel_设备驱动(四)资源与总线系统
        • 0x30_LinuxKernel_设备驱动(五)模块
        • 0x31_LinuxKernel_内存管理(一)物理页面、伙伴系统和slab分配器
        • 0x32_LinuxKernel_内存管理(二)虚拟内存管理、缺页与调试工具
        • 0x33_LinuxKernel_同步管理_原子操作_内存屏障_锁机制等
        • 01_LinuxDebug_调试理论和基础综述
      • Userspace
        • Linux-用户空间-多线程与同步
        • Linux进程之间的通信-管道(上)
        • Linux进程之间的通信-管道(下)
        • Linux进程之间的通信-信号量(System V)
        • Linux进程之间的通信-内存共享(System V)
        • Linux进程之间的通信-消息队列(System V)
        • Linux应用调试(一)方法、技巧和工具 - 综述
        • Linux应用调试(二)工具之coredump
        • Linux应用调试(三)工具之Valgrind
        • Linux机制之内存池
        • Linux机制之对象管理和引用计数(kobject/ktype/kset)
        • Linux机制copy_{to, from}_user
        • Linux设备树 - DTS语法、节点、设备树解析等
        • Linux System : Managing Linux Services - inittab & init.d
        • Linux System : Managing Linux Services - initramfs
      • Kernel Examples
        • Linux Driver - GPIO键盘驱动开发记录_OMAPL138
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(一)之miscdevice和ioctl
        • 基于OMAPL138的Linux字符驱动_GPIO驱动AD9833(二)之cdev与read、write
        • 基于OMAPL138的字符驱动_GPIO驱动AD9833(三)之中断申请IRQ
        • Linux内核调用SPI驱动_实现OLED显示功能
        • Linux内核调用I2C驱动_驱动嵌套驱动方法MPU6050
    • OPTEE
      • 01_OPTEE-OS_基础之(一)功能综述、简要介绍
      • 02_OPTEE-OS_基础之(二)TrustZone和ATF功能综述、简要介绍
      • 03_OPTEE-OS_系统集成之(一)编译、实例、在QEMU上执行
      • 05_OPTEE-OS_系统集成之(三)ATF启动过程
      • 06_OPTEE-OS_系统集成之(四)OPTEE镜像启动过程
      • 07_OPTEE-OS_系统集成之(五)REE侧上层软件
      • 08_OPTEE-OS_系统集成之(六)TEE的驱动
      • 09_OPTEE-OS_内核之(一)ARM核安全态和非安全态的切换
      • 10_OPTEE-OS_内核之(二)对安全监控模式的调用的处理
      • 11_OPTEE-OS_内核之(三)中断与异常的处理
      • 12_OPTEE-OS_内核之(四)对TA请求的处理
      • 13_OPTEE-OS_内核之(五)内存和cache管理
      • 14_OPTEE-OS_内核之(六)线程管理与并发
      • 15_OPTEE-OS_内核之(七)系统调用及IPC机制
      • 16_OPTEE-OS_应用之(一)TA镜像的签名和加载
      • 17_OPTEE-OS_应用之(二)密码学算法和安全存储
      • 18_OPTEE-OS_应用之(三)可信应用的开发
      • 19_OPTEE-OS_应用之(四)安全驱动开发
      • 20_OPTEE-OS_应用之(五)终端密钥在线下发系统
    • Binary
      • 01_ELF文件_目标文件格式
      • 02_ELF文件结构_浅析内部文件结构
      • 03_ELF文件_静态链接
      • 04_ELF文件_加载进程虚拟地址空间
      • 05_ELF文件_动态链接
      • 06_Linux的动态共享库
      • 07_ELF文件_堆和栈调用惯例以ARMv8为例
      • 08_ELF文件_运行库(入口、库、多线程)
      • 09_ELF文件_基于ARMv7的Linux系统调用原理
      • 10_ELF文件_ARM的镜像文件(.bin/.hex/.s19)
    • Build
      • 01_Script_makefile_summary
    • Rust
      • 02_SYS_RUST_文件IO
    • Security
      • Crypto
        • 1.0_Security_计算机安全概述及安全需求
        • 2.0_Security_随机数(伪随机数)
        • 3.0_Security_对称密钥算法加解密
        • 3.1_Security_对称密钥算法之AES
        • 3.2_Security_对称密钥算法之MAC(CMAC/HMAC)
        • 3.3_Security_对称密钥算法之AEAD
        • 8.0_Security_pkcs7(CMS)_embedded
        • 9.0_Security_pkcs11(HSM)_embedded
      • Tools
        • Openssl EVP to implement RSA and SM2 en/dec sign/verify
        • 基于Mac Silicon M1 的OpenSSL 编译
        • How to compile mbedtls library on Linux/Mac/Windows
    • Embedded
      • eMMC启动介质
  • 😃Design
    • Secure Boot
      • JY Secure Boot Desgin
    • FOTA
      • [FOTA] Module of ECUs' FOTA unit design
        • [FOTA] Tech key point: OSTree Deployment
        • [FOTA] Tech key point: repositories role for onboard
        • [FOTA] Tech key point: metadata management
        • [FOTA] Tech key point: ECU verifying and Decrpting
        • [FOTA] Tech key point: time server
      • [FOTA] Local-OTA for Embedded Linux System
    • Provisioning
      • [X-Shield] Module of the Embedded Boards initialization
    • Report
由 GitBook 提供支持
在本页
  • 09_ELF文件_基于ARMv7的Linux系统调用原理
  • 1 系统调用原理
  • 2. 虚拟系统调用
  • 2.1 链接形态
  • 2.2 什么是VODS
  • 2.3 内核和用户空间建立
  • Ref
  1. TECH
  2. Binary

09_ELF文件_基于ARMv7的Linux系统调用原理

https://github.com/carloscn/blog/issues/56

上一页08_ELF文件_运行库(入口、库、多线程)下一页10_ELF文件_ARM的镜像文件(.bin/.hex/.s19)

最后更新于1年前

09_ELF文件_基于ARMv7的Linux系统调用原理

Linux的应用程序在运行的时候,本质是和linux kernel不断交互的过程,而userspace和kernel之间的通信就是通过系统调用**(system call)**来完成的。Linux的内部有300多个系统调用,这些系统调用被定义在/usr/include/unistd.h中。我们在学习Linux应用程序的时候,使用文件句柄可以包含file头文件使用fread,fwrite等函数,但也可以使用read、write,这里就可以说出他们两个区别,fwrite这些函数都是glibc提供的函数,而read,write这些都是系统调用的接口,在glibc里面底层也是调用系统调用的read、write来实现的。

系统调用存在一些弊端:

  • 使用不便,使用系统调用的接口就要求程序员具备一些操作系统的知识。

  • 操作系统之间不兼容,兼容性还需要程序员来开发。

为了解决这些弊端,引入了运行库,运行库相当于在系统调用上面增加一个兼容层,很多在操作系统需要处理和配置的工作都放在运行库中进行处理。使用运行库的优点可以总结为:

  • 使用简便

  • 形式统一,运行库叫做标准库,凡是能作为运行库的,必然要遵循某种标准。

但是运行库还是存在诸多缺点:

  • 平台兼容性,比如XWindows的界面程序,在linux中,CRT只能把这部分省略。

1 系统调用原理

从硬件层面,系统调用时需要CPU做一些支持的,在CPU上面需要用户模式(user mode)和特权模式(kernel mode)的区分,因此,cpu需要将两种模式区分开,需要提供特权指令和特权执行的环境。在操作系统层面,就需要逻辑地将kernel划分成为用户态和内核态,当一个应用程序运行的时候,自己的业务逻辑是在用户态运行的,而当对于一些内核数据的访问,就需要使用系统调用,临时地从用户态切入内核态。

现代操作系统通过中断(interrupt,在armv8中称为异常exception),来从用户态切换到内核态,这个过程依赖于异常处理,这部分和11_ARMv8_异常处理(二)- Legacy 中断处理非常类似。

image-20220506140125504

x86架构下,把中断分为两种,一种是硬件中断,由外部的硬件中断线触发;还有一种是软中断,通常使用一条指令,int + 中断号向cpu申请中断。系统调用在x86架构下使用的是int指令的软中断实现的。

armv7/armv8架构下和x86有很大的不同,armv7/armv8的异常中断种类中有复位,未定义指令,软件中断(SWI),指令预取终止,IRQ,IFQ。系统调用通过中断指令,可由用户模式下的程序调用特权操作指令。

Exception
Description

Reset

Occurs when the processor reset pin is asserted. This exception is only expected to occur for signalling power-up, or for resetting as if the processor has just powered up. A soft reset can be done by branching to the reset vector (0x0000).

Undefined Instruction

Occurs if neither the processor, or any attached coprocessor, recognizes the currently executing instruction.

Software Interrupt (SWI)

This is a user-defined synchronous interrupt instruction.It allows a program running in User mode, for example, to request privileged operations that run in Supervisor mode, such as an RTOS function.

Prefetch Abort

Data Abort

Occurs when a data transfer instruction attempts to load or store data at an illegal addressa.

IRQ

Occurs when the processor external interrupt request pin is asserted (LOW) and the I bit in the CPSR is clear.

FIQ

Occurs when the processor external fast interrupt request pin is asserted (LOW) and the F bit in the CPSR is clear.

还需要注意的是,该异常的优先级是最低的,是6。

Vector address
Exception type
Exception mode
Priority (1=high, 6=low)

0x0

Reset

Supervisor (SVC)

1

0x4

Undefined Instruction

Undef

6

0x8

Software Interrupt (SWI)

Supervisor (SVC)

6

0xC

Prefetch Abort

Abort

5

0x10

Data Abort

Abort

2

0x14

Reserved

Not applicable

Not applicable

0x18

Interrupt (IRQ)

Interrupt (IRQ)

4

0x1C

Fast Interrupt (FIQ)

Fast Interrupt (FIQ)

3

我们以armv7为例子,说一下SWI异常中断的处理过程。SWI包括一个24位的立即数,这个立即数指示用户特定SWI的功能。通常SWI异常处理的中断分为2级。第一级,SWI异常中断处理程序为汇编程序,用于确定SWI指令中的24位的立即数;第2级具体实现SWI的各个功能,可以是汇编也可以是C程序。

Vector_Init_Block
                LDR    PC, Reset_Addr
                LDR    PC, Undefined_Addr
                LDR    PC, SWI_Addr
                LDR    PC, Prefetch_Addr
                LDR    PC, Abort_Addr
                NOP                     ;Reserved vector
                LDR    PC, IRQ_Addr
                LDR    PC, FIQ_Addr
Reset_Addr      DCD    Start_Boot
Undefined_Addr  DCD    Undefined_Handler
SWI_Addr        DCD    SWI_Handler
Prefetch_Addr   DCD    Prefetch_Handler
Abort_Addr      DCD    Abort_Handler
                DCD    0                ;Reserved vector
IRQ_Addr        DCD    IRQ_Handler
FIQ_Addr        DCD    FIQ_Handler

SWI handlers in assembly language

    CMP    r0,#MaxSWI          ; Range check
    LDRLS  pc, [pc,r0,LSL #2]
    B      SWIOutOfRange
SWIJumpTable
    DCD    SWInum0
    DCD    SWInum1
                    ; DCD for each of other SWI routines
SWInum0             ; SWI number 0 code
    B    EndofSWI
SWInum1             ; SWI number 1 code
    B    EndofSWI
                    ; Rest of SWI handling code
                    ;
EndofSWI
                    ; Return execution to top level 
                    ; SWI handler so as to restore
                    ; registers and return to program.

SWI handlers in C and assembly language

BL    C_SWI_Handler     ; Call C routine to handle the SWI

void C_SWI_handler (unsigned number)
{ 
    switch (number)
    {case 0 :                /* SWI number 0 code */
        break;
    case 1 :                 /* SWI number 1 code */
        break;
    :
    :
    default :                /* Unknown SWI - report error */
    }
}

Using SWIs in Supervisor mode

    STMFD    sp!,{r0-r3,r12,lr}   ; Store registers.
    MOV      r1, sp               ; Set pointer to parameters.
    MRS      r0, spsr             ; Get spsr.
    STMFD    sp!, {r0}            ; Store spsr onto stack. This is only really needed in case of
                                  ; nested SWIs.
        ; the next two instructions only work for SWI calls from ARM state.
        ; See Example 5.17 for a version that works for calls from either ARM or Thumb.
    LDR      r0,[lr,#-4]          ; Calculate address of SWI instruction and load it into r0.
    BIC      r0,r0,#0xFF000000    ; Mask off top 8 bits of instruction to give SWI number.
        ; r0 now contains SWI number
        ; r1 now contains pointer to stacked registers
    BL       C_SWI_Handler        ; Call C routine to handle the SWI.
    LDMFD    sp!, {r0}            ; Get spsr from stack.
    MSR      spsr_cf, r0          ; Restore spsr.
    LDMFD    sp!, {r0-r3,r12,pc}^ ; Restore registers and return.

具体参考中armv7对于特权的处理。

2. 虚拟系统调用

2.1 链接形态

如果使用ldd来获取一个可执行文件动态库的依赖情况,ldd /bin/ls可能会发现一个比较奇怪的现象(以下图片是用armv7的imx6.u截取),这个和程序员自我修养一本书上有点差异,在程序员自我修养那个书里面使用的linux内核2.5版本,而在新的内核里面就变更linux-gate.so为linux-vdso.so了。而且映射的地址也对不上了。

linux-vdso.so.1没有和任何文件关联。这个是linux用于支持新型系统调用的“虚拟共享”库(virtual dynamic shared library, VDSO)。这个库被加载到了0x7efa9000地址上面,我们可以通过cat /proc/self/maps来查看一个可执行程序的内存映像:

这里面VDSO的设计是一个非常巧妙的设计,里面也有个小故事,向Linux内核里面添加一个功能,glibc的代价极大,可能需要做很多讨论才可以,而且还有BSD,sysV各种标准。而内核也要适配glibc,双方都背负沉重的历史包袱。因此,linuxer设计者考虑一个非常巧妙的设计,让libc变为以动态链接的形式进入内核。

可以将vdso看成一个shared objdect file(这个文件实际上不存在),内核将其映射到某个地址空间,被所有程序所共享。(我觉得这里用到了一个技术:多个虚拟页面映射到同一个物理页面。即内核把vdso映射到某个物理页面上,然后所有程序都会有一个页表项指向它,以此来共享,这样每个程序的vdso地址就可以不相同了)

2.2 什么是VODS

  • 一个内核提供的成熟的动态链接文件。

  • 被内核映射进入所有的用户进程。

  • 与一般的.so文件链接规则都是一样的,但是gdb可能会不支持,The one gdb used to complain about! (warning: Could not load shared library symbols for linux-vdso.so.1)

  • 提供system call的一种手段,可以理解为虚拟的系统调用。

2.3 内核和用户空间建立

关于vsdo的数据,我们可以从数据结构定义处拿到:

struct vdso_data {
        __u64 cs_cycle_last ; /* Timebase at clocksource i n i t */
        __u64 raw_time_sec ; /* Raw time */
        __u64 raw_time_nsec ;
        __u64 xtime_clock_sec ; /* Kernel time */
        __u64 xtime_clock_nsec ;
        __u64 xtime_coarse_sec ; /* Coarse time */
        __u64 xtime_coarse_nsec ;
        __u64 wtm_clock_sec ; /* Wall to monotonic time */
        __u64 wtm_clock_nsec ;
        __u32 tb_seq_count ; /* Timebase sequence counter */
        __u32 cs_mono_mult ; /* NTP−adjusted clocksource multiplier */
        __u32 cs_shift ; /* Clocksource s h i f t (mono = raw) */
        __u32 cs_raw_mult ; /* Raw clocksource multiplier */
        __u32 tz_minuteswest ; /* Whacky timezone stuff */
        __u32 tz_dsttime ;
        __u32 use_syscall ;
} ;

在x86上面,vdso导出了一系列函数,比如__kernel_vsyscall函数,这个函数负责虚拟系统调用,这个函数里面会有内核之中调用特权指令,对于armv7请参考,Using SWIs in Supervisor mode,在特权模式下使用SWI异常。

在man手册里面可以找到aarch64 (armv8)系统调用的符号:

   ARM functions
       The table below lists the symbols exported by the vDSO.

       symbol                 version
       ────────────────────────────────────────────────────────────
       __vdso_gettimeofday    LINUX_2.6 (exported since Linux 4.1)
       __vdso_clock_gettime   LINUX_2.6 (exported since Linux 4.1)

       Additionally, the ARM port has a code page full of utility
       functions.  Since it's just a raw page of code, there is no ELF
       information for doing symbol lookups or versioning.  It does
       provide support for different versions though.

       For information on this code page, it's best to refer to the
       kernel documentation as it's extremely detailed and covers
       everything you need to know:
       Documentation/arm/kernel_user_helpers.txt.

   aarch64 functions
       The table below lists the symbols exported by the vDSO.

       symbol                   version
       ──────────────────────────────────────
       __kernel_rt_sigreturn    LINUX_2.6.39
       __kernel_gettimeofday    LINUX_2.6.39
       __kernel_clock_gettime   LINUX_2.6.39
       __kernel_clock_getres    LINUX_2.6.39

使用这些函数来实现虚拟系统调用,我们这里就不具体展开到内核讨论了,这部分会在内核里面记录。

Ref

  1. The story of linux-{gate,vdso}.so

  2. The vDSO on arm64

  3. vdso(7) — Linux manual page

Occurs when the processor attempts to execute an instruction that was not fetched, because the address was illegal[].

telegram-cloud-document-5-6318986229266253514
telegram-cloud-document-5-6318986229266253513
image-20220507155806532
image-20220507160027590
😾
1