ARM_Linux_NOTE_1

ARM_Linux NOTE_1

Vine Farer
2016.08.05

1、基本结构

  • 内核 ——— CPU

  • 软件

    • Linux:功能较多,较复杂
    • VX Works:稳定性高,用于航天和军用
    • μ COS:轻量级OS,因结构简单,也较稳定
  • 硬件

    • ARM
    • MSP430
    • Mips:功耗很低
    • PowerPC

2、计算机系统硬件组成

  • 输入设备

  • 输出设备

  • 存储器

    CPU(寄存器组)<—>高速缓存cache<—>主存储器RAM<—>辅助存储器ROM

    (速度 快———————————————–>>>慢)

    • cache 本身由 SRAM 组成

    • cache 常和 wite buffer 一起使用,起作用就是为了针对高速的CPU和低速的主存之间速率不匹配的问题,进行缓冲

    • MMU (存储管理单元):程序编译时将虚拟地址转为物理地址,多个程序可以有相同的虚拟地址(0~4G),但各自是不同的物理地址映射

    RAM :
       > SRAM
       > DRAM(SDRAM)

    ROM:
       > EPROM
       > EEPROM
       > Flash:nand型、nor型

    磁盘、硬盘

    光盘

    相变

  • 运算器ALU

  • 控制器

  • 总线

    • 单总线结构:效率低,低档单片机
    • 多总线结构:效率高,成本高,高档单片机

3、数据和指令类型

  • ARM数据类型

    byte 8bit
    halfword 16bit
    word 32bit
    doubleword 64bit
  • 指令集:

    ARM指令集:32bit,4字节对齐
    Thumb指令集:16bit,2字节对齐
    cortex-a
    thumb-2 : arm + thumb
    java:8bit,4字节对齐
    thumb-ee



4、ARM处理器工作模式

  • 八种内核工作模式:

    user: 用户模式(非特权模式)。正常执行时用此模式
    system:系统模式,和user共享相同资源(即公用一套寄存器组)
    fiq:快速中断模式。高优先级中断介入时进入此模式
    irq:普通中断模式。通常中断处理
    supervisor{svc}:特权模式。复位、swi软中断
    abort:数据访问终止模式。读取数据失败,加载指令失败
    undef:未定义指令终止模式
    cortex-a: monitor:监控模式。在安全与非安全模式间切换

    (除user,其他7种均为特权模式)

  • fiqirq快的原因

    FIQ 优先级比 IRQ 高,不会被中断
    FIQ有自己的专属寄存器:r8~r12,不用对通用寄存器入栈保护,可以加快速度
    FIQ位于异常向量表的末尾0x1c,故无需跳转,可以在这里直接放置异常处理函数



5、ARM寄存器

Contex-A8有40个32位寄存器

8种内核工作模式,每个模式对应一组相应寄存器组

32个通用寄存器
7个状态寄存器:1个CPSR(当前)、6个SPSR(备份)
1个PC(程序计数器)

程序状态寄存器结构(面试常考)

条件标志位:N(负数)、Z(0)、C(进位)、V(溢出)
大小端控制位:E
中断禁止位:I(普通中断)、F(快速中断)
状态位:T(选择ARM指令或Thumb指令)
模式控制位:M[ 4:0 ] 确定内核处于哪种工作模式(8种)



6、异常处理

———— 操作程序状态寄存器
  • 异常类型

    ResetData AbortFIQIRQPrefetch AbortSWIUndefined instruction
    Reset异常优先级最高
  • 异常产生:

    1)拷贝CPSRSPSR_<mode>
    2)设置CPSR

    设置第 5 位,进入ARM

    设置M[4:0],改变内核模式

    设置第 6、7 位的中断禁止位,禁止相应中断响应
    (为防止中断嵌套,一旦irq中断执行过程中另一个irq中断到来,pc被保存到lr中跳转到下个中断。但irq模式下的lr只有一个,一旦改变就被覆盖了,会丢失usr下原函数的返回地址,中断结束后无法返回)

    3)保存返回地址到LR_<mode>(设置r14
    4)设置PC为相应的异常向量
  • 异常返回:

    1)从SPSR_<mode>恢复CPSR
    2)从LR_<mode>恢复PC

    (这些操作仅在ARM态下进行)



7、流水线(Pipeline)

———— 改善硬件资源使用率和处理器吞吐量
  • 3级流水线(如ARM7系列):

    取指令fetch:从存储器中读指令
    译码decode:解码指令中用到的寄存器
    执行execute:寄存器读、移位和ALU操作、寄存器写
  • 5级流水线:

    取指令 <—> 译码 <—> 执行 <—> 缓冲 / 数据 <—> 回写
  • 流水线工作实例

    1) 最佳流水线( F—>D—>E )

    6个周期执行6条指令

    所有操作在寄存器中

    指令周期数CPI = 1

    2) LDR流水线( F、D、E、M、W )

    M:将数据从内存读到CPU; W:将读到的数据写回寄存器

    6个周期4条指令

    CPI = 1.5

    3) 分支流水线( F、D、E、L、A )

    L:设置LR = PC_old; A:硬件自动PC = PC_old - 4

    需要对BL跳转语句做处理,所以流水线被阻断

    内核运行在ARM态

    4) 中断流水线( F、D、E、DI、EI、L、A )

    DI:解码中断指令; EI:处理中断

    中断到来,跳转到中断向量表,执行相应地址内的跳转语句,跳转到下个地址执行中断处理函数

    IRQ中断的反应时间最小是 7 个周期



DESCEND objtool HOSTCC scripts/mod/modpost.o INSTALL libsubcmd_headers CC /usr/src/kernels/linux-6.12.33/tools/objtool/libsubcmd/exec-cmd.o scripts/mod/modpost.c: In function ‘addend_arm_rel’: scripts/mod/modpost.c:1178:7: error: ‘R_ARM_MOVW_ABS_NC’ undeclared (first use in this function); did you mean ‘R_ARM_THM_ABS5’? case R_ARM_MOVW_ABS_NC: ^~~~~~~~~~~~~~~~~ R_ARM_THM_ABS5 scripts/mod/modpost.c:1178:7: note: each undeclared identifier is reported only once for each function it appears in scripts/mod/modpost.c:1179:7: error: ‘R_ARM_MOVT_ABS’ undeclared (first use in this function); did you mean ‘R_ARM_THM_ABS5’? case R_ARM_MOVT_ABS: ^~~~~~~~~~~~~~ R_ARM_THM_ABS5 scripts/mod/modpost.c:1185:7: error: ‘R_ARM_CALL’ undeclared (first use in this function); did you mean ‘R_ARM_COPY’? case R_ARM_CALL: ^~~~~~~~~~ R_ARM_COPY scripts/mod/modpost.c:1186:7: error: ‘R_ARM_JUMP24’ undeclared (first use in this function); did you mean ‘R_ARM_PC24’? case R_ARM_JUMP24: ^~~~~~~~~~~~ R_ARM_PC24 scripts/mod/modpost.c:1190:7: error: ‘R_ARM_THM_MOVW_ABS_NC’ undeclared (first use in this function); did you mean ‘R_ARM_THM_ABS5’? case R_ARM_THM_MOVW_ABS_NC: ^~~~~~~~~~~~~~~~~~~~~ R_ARM_THM_ABS5 scripts/mod/modpost.c:1191:7: error: ‘R_ARM_THM_MOVT_ABS’ undeclared (first use in this function); did you mean ‘R_ARM_THM_ABS5’? case R_ARM_THM_MOVT_ABS: ^~~~~~~~~~~~~~~~~~ R_ARM_THM_ABS5 scripts/mod/modpost.c:1200:7: error: ‘R_ARM_THM_JUMP19’ undeclared (first use in this function); did you mean ‘R_ARM_THM_PC9’? case R_ARM_THM_JUMP19: ^~~~~~~~~~~~~~~~ R_ARM_THM_PC9 scripts/mod/modpost.c:1222:7: error: ‘R_ARM_THM_JUMP24’ undeclared (first use in this function); did you mean ‘R_ARM_THM_PC8’? case R_ARM_THM_JUMP24: ^~~~~~~~~~~~~~~~ R_ARM_THM_PC8 make[2]: *** [scripts/Makefile.host:133: scripts/mod/modpost.o] Error 1 make[1]: *** [/usr/src/kernels/linux-6.12.33/Makefile:1216: prepare0] Error 2 make[1]: *** 正在等待未完成的任务.... CC /usr/src/kernels/linux-6.12.33/tools/objtool/libsubcmd/help.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/libsubcmd/pager.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/libsubcmd/parse-options.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/libsubcmd/run-command.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/libsubcmd/sigchain.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/libsubcmd/subcmd-config.o LD /usr/src/kernels/linux-6.12.33/tools/objtool/libsubcmd/libsubcmd-in.o AR /usr/src/kernels/linux-6.12.33/tools/objtool/libsubcmd/libsubcmd.a CC /usr/src/kernels/linux-6.12.33/tools/objtool/weak.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/arch/x86/special.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/arch/x86/decode.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/arch/x86/orc.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/check.o LD /usr/src/kernels/linux-6.12.33/tools/objtool/arch/x86/objtool-in.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/special.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/builtin-check.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/elf.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/objtool.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/orc_gen.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/orc_dump.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/libstring.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/libctype.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/str_error_r.o CC /usr/src/kernels/linux-6.12.33/tools/objtool/librbtree.o LD /usr/src/kernels/linux-6.12.33/tools/objtool/objtool-in.o LINK /usr/src/kernels/linux-6.12.33/tools/objtool/objtool make: *** [Makefile:224: __sub-make] Error 2 是什么意思,该怎么解决
06-18
<think>我们遇到的是一个编译Linux内核模块时出现的错误。错误信息显示在编译`scripts/mod/modpost.c`文件时,出现了多个未声明的标识符,这些标识符都是ARM架构相关的重定位类型(如`R_ARM_MOVW_ABS_NC`、`R_ARM_MOVT_ABS`等)。错误原因分析:这些重定位类型通常在ARM架构的头文件中定义(例如在`<elf.h>`或特定架构的头文件中)。错误提示表明在编译过程中,这些符号没有被正确声明。可能的原因包括:1.编译环境缺少必要的头文件或头文件版本不匹配。2.内核配置中启用了ARM相关的选项,但当前编译环境可能是x86,导致没有包含ARM特定的头文件。3.内核源代码树中与ARM架构相关的头文件可能没有正确生成或包含。解决步骤:1.确保编译环境正确安装了交叉编译工具链(如果目标平台是ARM)。但注意,我们是在编译内核模块,而错误出现在`modpost`阶段,这个阶段通常是在主机上运行,并不需要交叉编译。然而,`modpost`需要处理不同架构的内核模块,因此可能需要对应架构的头文件。2.检查内核配置。可能在内核配置中启用了ARM相关的模块,但当前编译环境(可能是x86)没有ARM的头文件。我们可以尝试重新配置内核,关闭ARM架构的支持(如果不需要的话),或者确保已安装必要的交叉编译头文件。3.查看内核源代码中是否有相关的定义。在Linux内核源代码中,这些重定位类型通常在`arch/arm/include/asm/elf.h`中定义。我们可以检查该文件是否存在这些定义。4.如果确定这些定义应该存在,可能是由于某些配置选项没有正确包含该头文件。可以尝试在`modpost.c`中显式包含ARM架构的头文件,但这不是一个常规做法。5.另外,错误信息中提到的重定位类型(如`R_ARM_MOVW_ABS_NC`)在较新的ARM架构中定义,如果使用的内核版本较旧,可能没有这些定义。但根据路径(`linux-6.12.33`)这是一个较新的版本,应该支持。6.还有一种可能是编译工具链的问题,比如binutils版本过旧,不支持这些重定位类型。可以尝试更新binutils。然而,从错误信息中我们看到,编译是在`/usr/src/kernels/linux-6.12.33/`目录下进行的,这可能是内核头文件或内核源码包。我们需要确保这个目录下的源码是完整的,并且与当前编译环境兼容。具体解决方案:由于错误发生在编译`modpost`工具时,这个工具是主机架构(x86_64)上的程序,它需要处理ARM架构的模块,因此它需要ARM架构的重定位类型定义。在Linux内核源码中,`modpost`应该通过条件编译来处理多种架构。但是,如果当前配置没有正确设置,可能会导致这些定义未被包含。我们可以尝试以下方法:方法1:安装必要的交叉编译头文件(对于ARM架构)。在Ubuntu/Debian系统上,可以尝试安装:sudoapt-getinstallgcc-arm-linux-gnueabi方法2:在内核配置中明确指定架构。在编译之前,确保已经正确设置了架构和交叉编译工具链(如果需要)。例如,如果目标架构是x86,则应该使用:makeARCH=x86_64...(或根据情况)但是,我们注意到错误发生在编译主机工具(modpost)时,而不是目标内核。因此,我们可能需要确保主机工具在编译时能够获得所有架构的定义。这通常由内核构建系统自动处理。方法3:检查内核源码树中`include/linux/elf.h`和`arch/arm/include/asm/elf.h`是否存在,并检查这些头文件是否包含了所需的定义。如果没有,可能是源码不完整或版本不对。方法4:根据错误提示,在`modpost.c`中,这些重定位类型是在ARM架构的条件编译块中使用的(如`#ifdefCONFIG_ARM`)。如果我们的主机架构不是ARM,那么可能`CONFIG_ARM`没有被定义,因此这些重定位类型就不会被定义。但是,`modpost`需要支持多种架构,所以它应该包含所有架构的重定位类型。实际上,在`modpost.c`中,它包含了`arch_is_arm`等函数,并根据文件类型处理不同的架构。仔细查看错误,发现错误发生在`addend_arm_rel`函数中,这个函数显然是处理ARM重定位的。所以,即使主机是x86,这个函数也应该被编译,并且需要ARM重定位类型的定义。因此,问题可能在于`modpost.c`没有包含定义这些重定位类型的头文件。我们可以在`modpost.c`的开头添加包含ARM架构头文件的语句,例如:#include<asm/elf.h>#include<elf.h>但是,`asm/elf.h`是架构相关的头文件,它应该通过内核构建系统自动指向特定架构的头文件。可能在内核构建时,没有正确设置架构的头文件路径。方法5:尝试在内核的根目录执行`makescripts`,重新构建脚本相关的工具,有时可以解决问题。方法6:如果上述方法都不行,我们可以尝试在`modpost.c`中手动添加缺失的宏定义。从错误信息看,缺失的宏定义包括:R_ARM_MOVW_ABS_NCR_ARM_MOVT_ABSR_ARM_CALLR_ARM_JUMP24R_ARM_THM_MOVW_ABS_NCR_ARM_THM_MOVT_ABSR_ARM_THM_JUMP19R_ARM_THM_JUMP24我们可以从ARM架构的ELF标准中获取这些值,并添加到`modpost.c`中。但是,这不是一个推荐的做法,因为不同版本可能有不同的值。根据ARM架构的ELF规范(例如:https://github.com/ARM-software/abi-aa/blob/main/aaelf32/aaelf32.rst),这些重定位类型的值如下(注意:这些值在规范中是固定的,但不同Linux内核版本可能使用不同的值,最好参考当前内核源码中的定义):#defineR_ARM_MOVW_ABS_NC43#defineR_ARM_MOVT_ABS44#defineR_ARM_CALL28#defineR_ARM_JUMP2429#defineR_ARM_THM_MOVW_ABS_NC47#defineR_ARM_THM_MOVT_ABS48#defineR_ARM_THM_JUMP1951#defineR_ARM_THM_JUMP2430//注意:在规范中,R_ARM_THM_JUMP24的值是30,但内核中可能不同?但是,注意:在内核源码中,这些宏定义已经在`arch/arm/include/asm/elf.h`中定义。因此,我们应该检查这个头文件是否被正确包含。在`modpost.c`中,我们可以看到它包含了`<elf.h>`,但没有包含`<asm/elf.h>`。实际上,`modpost`是一个用户空间的程序,它不应该直接包含内核头文件。因此,内核构建系统可能通过其他方式提供这些定义。经过调查,在Linux内核源码中,`modpost`的处理方式是在编译时通过`-I`选项包含架构特定的头文件目录。因此,我们需要确保构建系统在编译`modpost`时添加了`-Iarch/arm/include`这样的选项。方法7:检查内核构建系统的Makefile,确保在编译`modpost`时包含了ARM架构的头文件目录。但通常,构建系统会根据配置自动处理。考虑到错误信息中显示的是绝对路径`/usr/src/kernels/linux-6.12.33/`,这可能是系统预装的内核头文件或源码。可能是这个源码包在构建时没有正确配置架构。我们可以尝试重新配置内核,指定当前架构(例如x86_64)并重新编译。重新配置内核的步骤:cd/usr/src/kernels/linux-6.12.33makecleanmakemrpropermakex86_64_defconfig#或者使用当前系统的配置make注意:如果之前编译过,请先清理。方法8:如果问题仍然存在,可以尝试在编译时指定架构:makeARCH=x86_64因为错误发生在编译主机工具(modpost)时,而主机是x86_64,所以明确指定架构可能会避免引入ARM相关的部分。但是,即使指定了x86_64架构,`modpost`工具仍然需要支持其他架构(因为模块可能来自其他架构)。所以,可能还是需要包含其他架构的头文件。方法9:更新binutils。因为binutils提供了对这些重定位类型的定义(在`elf/arm.h`中),更新到最新版本可能会解决问题。根据以上分析,我们推荐尝试以下步骤:步骤1:更新binutils(如果可能)。步骤2:确保已安装ARM交叉编译工具链(即使主机是x86,因为modpost需要处理ARM的重定位)。步骤3:重新配置内核,使用默认的x86_64配置,然后重新编译。如果上述步骤无效,我们可以尝试手动在`modpost.c`中添加缺失的宏定义(作为临时解决方案)。但请注意,这不是一个推荐的做法,因为可能会影响其他架构的处理。临时解决方案:在`scripts/mod/modpost.c`文件中,在包含`<elf.h>`之后,添加以下宏定义(值根据ARMELF规范):#ifndefR_ARM_MOVW_ABS_NC#defineR_ARM_MOVW_ABS_NC43#endif...(其他缺失的定义)但最好从内核的`arch/arm/include/asm/elf.h`中复制这些定义。我们可以查看该文件,找到这些定义的值。例如,在`arch/arm/include/asm/elf.h`中,我们可能找到:#defineR_ARM_NONE0#defineR_ARM_PC241...#defineR_ARM_CALL28#defineR_ARM_JUMP2429#defineR_ARM_MOVW_ABS_NC43#defineR_ARM_MOVT_ABS44#defineR_ARM_THM_JUMP2430#defineR_ARM_THM_MOVW_ABS_NC47#defineR_ARM_THM_MOVT_ABS48#defineR_ARM_THM_JUMP1951因此,我们可以在`modpost.c`中添加这些定义(在包含任何头文件之后,但在使用之前)。具体操作如下(临时解决方案):打开`scripts/mod/modpost.c`,在包含头文件部分(大约在开头)添加以下代码:```c#include<elf.h>/*临时添加缺失的ARM重定位类型定义*/#ifndefR_ARM_MOVW_ABS_NC#defineR_ARM_MOVW_ABS_NC43#endif#ifndefR_ARM_MOVT_ABS#defineR_ARM_MOVT_ABS44#endif#ifndefR_ARM_CALL#defineR_ARM_CALL28#endif#ifndefR_ARM_JUMP24#defineR_ARM_JUMP2429#endif#ifndefR_ARM_THM_MOVW_ABS_NC#defineR_ARM_THM_MOVW_ABS_NC47#endif#ifndefR_ARM_THM_MOVT_ABS#defineR_ARM_THM_MOVT_ABS48#endif#ifndefR_ARM_THM_JUMP19#defineR_ARM_THM_JUMP1951#endif#ifndefR_ARM_THM_JUMP24#defineR_ARM_THM_JUMP2430#endif```然后重新编译。注意:这是一个临时解决方案,最好还是通过修复构建环境来解决。由于问题复杂,请根据实际情况选择解决方案。最后,错误信息结尾的`make:***[Makefile:224:__sub-make]Error2`表示编译过程因前面的错误而终止。总结:这个问题通常是由于构建环境缺少ARM交叉编译头文件或内核配置不当引起的。建议先尝试安装交叉编译工具链,然后重新配置和编译内核。如果问题仍然存在,请检查内核源码的完整性。</think>### 编译错误分析与解决方案 这个错误发生在编译 Linux 内核时,具体是在构建 `modpost` 工具(模块后处理工具)阶段。错误信息显示 `modpost.c` 文件中引用了多个未定义的 ARM 架构重定位常量(如 `R_ARM_MOVW_ABS_NC`)。以下是详细分析和解决方案: #### 错误原因分析: 1. **交叉编译问题**: - 错误发生在处理 ARM 架构的重定位类型时 - 系统可能缺少 ARM 交叉编译工具链或相关头文件 - 内核配置可能启用了 ARM 支持但未正确设置交叉编译环境 2. **头文件缺失**: - ARM 特定的重定位常量定义在 `arch/arm/include/asm/elf.h` 头文件中 - 编译过程未能正确包含 ARM 架构的头文件 3. **工具链问题**: - Binutils 版本可能过旧(低于 2.31) - 缺少 ARM 架构的开发文件 #### 解决方案: ##### 方法1:安装ARM交叉编译工具链(推荐) ```bash # Ubuntu/Debian sudo apt-get install gcc-arm-linux-gnueabi binutils-arm-linux-gnueabi # Fedora/CentOS/RHEL sudo dnf install arm-linux-gnu-binutils arm-linux-gnu-gcc ``` ##### 方法2:在内核配置中禁用ARM支持 ```bash cd /usr/src/kernels/linux-6.12.33 make menuconfig ``` 导航到: ``` -> General setup -> Enable loadable module support (MODULES) [按Y启用] -> Architecture-specific settings (ARM) [按N禁用] ``` 保存配置后重新编译: ```bash make -j$(nproc) ``` ##### 方法3:手动添加缺失定义(临时方案) 编辑 `scripts/mod/modpost.c` 文件,在开头添加以下定义: ```c /* 手动添加缺失的ARM重定位常量 */ #define R_ARM_MOVW_ABS_NC 43 #define R_ARM_MOVT_ABS 44 #define R_ARM_CALL 28 #define R_ARM_JUMP24 29 #define R_ARM_THM_MOVW_ABS_NC 47 #define R_ARM_THM_MOVT_ABS 48 #define R_ARM_THM_JUMP19 51 #define R_ARM_THM_JUMP24 30 ``` ##### 方法4:更新Binutils工具链 ```bash # Ubuntu/Debian sudo apt-get install binutils # Fedora/CentOS/RHEL sudo dnf update binutils ``` #### 完整修复流程(推荐方案): ```bash # 1. 安装ARM交叉编译工具链 sudo apt-get install gcc-arm-linux-gnueabi binutils-arm-linux-gnueabi # 2. 清理构建环境 cd /usr/src/kernels/linux-6.12.33 make clean # 3. 指定ARM交叉编译器 export CROSS_COMPILE=arm-linux-gnueabi- # 4. 重新配置内核(可选) make menuconfig # 5. 重新编译 make -j$(nproc) ``` #### 错误信息解读: - **错误格式**:`undeclared identifier` 表示编译器找不到符号定义 - **错误位置**:`scripts/mod/modpost.c` 处理ARM重定位的函数 - **最终错误**:`Error 2` 表示编译过程因致命错误终止 - **工具链流程**: - `HOSTCC`:编译主机工具 - `INSTALL`:安装头文件 - `LD`:链接对象文件 - `AR`:创建静态库 此问题通常由架构支持配置不当或交叉编译环境不完整引起,安装ARM工具链或禁用ARM支持是最直接的解决方案。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值