ELF文件格式及程序加载执行过程总汇(3)sections

=========================== Sections =========================== 


一个object文件的section header table可以让我们定位所有的sections。 
section header table是个Elf32_Shdr结构的数组(下面描述)。一个section 
报头表(section header table)索引是这个数组的下标。ELF header table 
的e_shoff成员给出了section报头表的偏移量(从文件开始的计数)。e_shnum 
告诉我们section报头表中包含了多少个入口;e_shentsize 给出了每个 
入口的大小。 

一些section报头表索引是保留的;那些特别的索引在一个object文件中 
将没有与之对应sections。 

+ 图1-8: Special Section Indexes 

Name Value 
==== ===== 
SHN_UNDEF 0 
SHN_LORESERVE 0xff00 
SHN_LOPROC 0xff00 
SHN_HIPROC 0xff1f 
SHN_ABS 0xfff1 
SHN_COMMON 0xfff2 
SHN_HIRESERVE 0xffff 

* SHN_UNDEF 

该值表明没有定义,缺少,不相关的或者其他涉及到的无意义的section。 
例如,标号“defined”相对于section索引号SHN_UNDEF是一个没有被 
定义的标号。 

注意: 虽然索引0保留作为未定义的值,section报头表包含了一个索引0的 
入口。因此,假如ELF报头说一个文件的section报头表中有6个section入口 
的话,e_shnum的值应该是从0到5。最初的入口的内容以后在这个section中 
被指定。 

* SHN_LORESERVE 

该值指定保留的索引范围的最小值。 

* SHN_LOPROC through SHN_HIPROC 

该值包含了特定处理器语意的保留范围。 


* SHN_ABS 

该变量是相对于相应参考的绝对地址。 
例如,section号的标号是绝对地址,不被重定位影响。 

* SHN_COMMON 

该section的标号是一个公共(common)的标号,就象FORTRAN COMMON 
或者不允许的C扩展变量。 

* SHN_HIRESERVE 

该值指定保留的索引范围的上限。系统保留的索引值是从SHN_LORESERVE 
到SHN_HIRESERVE;该变量不涉及到section报头表(section header table)。 
因此,section报头表不为保留的索引值包含入口。 

sections包含了在一个object文件中的所有信息,除了ELF报头,程序报头 
表(program header table),和section报头表(section header table)。 
此外,object文件的sections满足几天条件: 

* 每个在object文件中的section都有自己的一个section的报头来描述它。 
section头可能存在但section可以不存在。 
* 每个section在文件中都占有一个连续顺序的空间(但可能是空的)。 
* 文件中的Sections不可能重复。文件中没有一个字节既在这个section中 
又在另外的一个section中。 
* object文件可以有"非活动的"空间。不同的报头和sections可以不覆盖到 
object文件中的每个字节。"非活动"数据内容是未指定的。 

一个section头有如下的结构。 

+ 图1-9: Section Header 

typedef struct { 
Elf32_Word sh_name; 
Elf32_Word sh_type; 
Elf32_Word sh_flags; 
Elf32_Addr sh_addr; 
Elf32_Off sh_offset; 
Elf32_Word sh_size; 
Elf32_Word sh_link; 
Elf32_Word sh_info; 
Elf32_Word sh_addralign; 
Elf32_Word sh_entsize; 
} Elf32_Shdr; 

* sh_name 

该成员指定了这个section的名字。它的值是section报头字符表section的 
索引。[看以下的“String Table”], 以NULL空字符结束。 

* sh_type 

该成员把sections按内容和意义分类。section的类型和他们的描述在下面。 

* sh_flags 

sections支持位的标记,用来描述多个属性。 
Flag定义出现在下面。 

* sh_addr 

假如该section将出现在进程的内存映象空间里,该成员给出了一个该section 
在内存中的位置。否则,该变量为0。 

* sh_offset 

该成员变量给出了该section的字节偏移量(从文件开始计数)。SHT_NOBITS 
类型的section(下面讨论)在文件中不占空间,它的sh_offset成员定位在 
文件中的概念上的位置。 

* sh_size 
该成员给你了section的字节大小。除非这个section的类型为SHT_NOBITS, 
否则该section将在文件中将占有sh_size个字节。SHT_NOBITS类型的section 
可能为非0的大小,但是不占文件空间。 

* sh_link 

该成员保存了一个section报头表的索引连接,它的解释依靠该section的 
类型。以下一个表描述了这些值。 

* sh_info 

该成员保存着额外的信息,它的解释依靠该section的类型。以下一个表描 
述了这些值。 

* sh_addralign 

一些sections有地址对齐的约束。例如,假如一个section保存着双字,系统 
就必须确定整个section是否双字对齐。所以sh_addr的值以sh_addralign的值 
作为模,那么一定为0。当然的,仅仅0和正的2的次方是允许的。值0和1意味 
着该section没有对齐要求。 

* sh_entsize 

一些sections保存着一张固定大小入口的表,就象符号表。对于这样一个 
section来说,该成员给出了每个入口的字节大小。如果该section没有 
保存着一张固定大小入口的表,该成员就为0。 

section头成员sh_type指出了section的语意。 

+ 图1-10: Section Types, sh_type 

Name Value 
==== ===== 
SHT_NULL 0 
SHT_PROGBITS 1 
SHT_SYMTAB 2 
SHT_STRTAB 3 
SHT_RELA 4 
SHT_HASH 5 
SHT_DYNAMIC 6 
SHT_NOTE 7 
SHT_NOBITS 8 
SHT_REL 9 
SHT_SHLIB 10 
SHT_DYNSYM 11 
SHT_LOPROC 0x70000000 
SHT_HIPROC 0x7fffffff 
SHT_LOUSER 0x80000000 
SHT_HIUSER 0xffffffff 

* SHT_NULL 

该值表明该section头是无效的;它没有相关的section。 
该section的其他成员的值都是未定义的。 

* SHT_PROGBITS 

该section保存被程序定义了的一些信息,它的格式和意义取决于程序本身。 

* SHT_SYMTAB and SHT_DYNSYM 

那些sections保存着一个符号表(symbol table)。一般情况下,一个 
object文件每个类型section仅有一个,但是,在将来,这个约束可能被放宽。 
典型的是,SHT_SYMTAB为连接器提供标号,当然它也有可能被动态连接时使用。 
作为一个完整的符号表,它可能包含了一些动态连接时不需要的标号。 
因此,一个object文件可能也包含了一个SHT_DYNSYM的section,它保存着 
一个动态连接时所需最小的标号集合来节省空间。 
看下面符号表“Symbol Table”的细节。 

* SHT_STRTAB 

该section保存着一个字符串表。一个object文件可以包含多个字符串表的 
section。看下面字符串表“String Table”的细节。 

* SHT_RELA 

该section保存着具有明确加数的重定位入口。就象object文件32位的 
Elf32_Rela类型。一个object文件可能有多个重定位的sections。具体细节 
看重定位``Relocation‘‘部分。 

* SHT_HASH 

该标号保存着一个标号的哈希(hash)表。所有的参与动态连接的object 
一定包含了一个标号哈希表(hash table)。当前的,一个object文件 
可能只有一个哈希表。详细细节看第二部分的哈希表"Hash Table"。 

* SHT_DYNAMIC 

该section保存着动态连接的信息。当前的,一个object可能只有一个动态 
的section,但是,将来这个限制可能被取消。详细细节看第二部分的动态 
section(“Dynamic Section”)。 

* SHT_NOTE 

该section保存着其他的一些标志文件的信息。详细细节看第二部分的“Note 
Section” 。 

* SHT_NOBITS 

该类型的section在文件中不占空间,但是类似SHT_PROGBITS。尽管该section 
不包含字节,sh_offset成员包含了概念上的文件偏移量。 

* SHT_REL 

该section保存着具有明确加数的重定位的入口。 
就象object文件32位类型Elf32_Rel类型。一个object文件可能有多个 
重定位的sections。具体细节看重定位``Relocation‘‘部分。 

* SHT_SHLIB 

该section类型保留但语意没有指明。包含这个类型的section的程序 
是不符合ABI的。 

* SHT_LOPROC through SHT_HIPROC 

在这范围之间的值为特定处理器语意保留的。 

* SHT_LOUSER 

该变量为应用程序保留的索引范围的最小边界。 

* SHT_HIUSER 

该变量为应用程序保留的索引范围的最大边界。在SHT_LOUSER和HIUSER的 
section类型可能被应用程序使用,这和当前或者将来系统定义的section 
类型是不矛盾的。 

其他section类型的变量是保留的。前面提到过,索引0(SHN_UNDEF)的section 
头存在的,甚至索引标记的是未定义的section引用。这个入口保存着以下的 
信息。 

+ 图1-11: Section Header Table Entry: Index 0 

Name Value Note 
==== ===== ==== 
sh_name 0 No name 
sh_type SHT_NULL Inactive 
sh_flags 0 No flags 
sh_addr 0 No address 
sh_offset 0 No file offset 
sh_size 0 No size 
sh_link SHN_UNDEF No link information 
sh_info 0 No auxiliary information 
sh_addralign 0 No alignment 
sh_entsize 0 No entries 

一个section报头(section header table)的sh_flags成员保存着1位标记, 
用来描述section的属性。以下是定义的值;其他的值保留。 

+ 图1-12: Section Attribute Flags, sh_flags 

Name Value 
==== ===== 
SHF_WRITE 0x1 
SHF_ALLOC 0x2 
SHF_EXECINSTR 0x4 
SHF_MASKPROC 0xf0000000 

假如在sh_flags中的一个标记位被设置,该section相应的属性也被打开。 
否则,该属性没有被应用。未明的属性就设为0。 

* SHF_WRITE 

该section包含了在进程执行过程中可被写的数据。 

* SHF_ALLOC 

该section在进程执行过程中占据着内存。一些控制section不存在一个 
object文件的内存映象中;对于这些sections,这个属性应该关掉。 

* SHF_EXECINSTR 

该section包含了可执行的机器指令。 

* SHF_MASKPROC 

所有的包括在这掩码中的位为特定处理语意保留的。 

在section报头中,两个成员sh_link和sh_info的解释依靠该section的类型。 

+ 图1-13: sh_link and sh_info Interpretation 

sh_type sh_link sh_info 
======= ======= ======= 
SHT_DYNAMIC The section header index of 0 
the string table used by 
entries in the section. 
SHT_HASH The section header index of 0 
the symbol table to which the 
hash table applies. 
SHT_REL, The section header index of The section header index of 
SHT_RELA the associated symbol table. the section to which the 
relocation applies. 
SHT_SYMTAB, The section header index of One greater than the symbol 
SHT_DYNSYM the associated string table. table index of the last local 
symbol (binding STB_LOCAL). 
other SHN_UNDEF 0 


Special Sections 特殊的Sections 

不同的sections保存着程序和控制信息。下面列表中的section被系统使用, 
指示了类型和属性。 

+ 图1-14: Special Sections 

Name Type Attributes 
==== ==== ========== 
.bss SHT_NOBITS SHF_ALLOC+SHF_WRITE 
.comment SHT_PROGBITS none 
.data SHT_PROGBITS SHF_ALLOC+SHF_WRITE 
.data1 SHT_PROGBITS SHF_ALLOC+SHF_WRITE 
.debug SHT_PROGBITS none 
.dynamic SHT_DYNAMIC see below 
.dynstr SHT_STRTAB SHF_ALLOC 
.dynsym SHT_DYNSYM SHF_ALLOC 
.fini SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR 
.got SHT_PROGBITS see below 
.hash SHT_HASH SHF_ALLOC 
.init SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR 
.interp SHT_PROGBITS see below 
.line SHT_PROGBITS none 
.note SHT_NOTE none 
.plt SHT_PROGBITS see below 
.rel SHT_REL see below 
.rela SHT_RELA see below 
.rodata SHT_PROGBITS SHF_ALLOC 
.rodata1 SHT_PROGBITS SHF_ALLOC 
.shstrtab SHT_STRTAB none 
.strtab SHT_STRTAB see below 
.symtab SHT_SYMTAB see below 
.text SHT_PROGBITS SHF_ALLOC+SHF_EXECINSTR 

* .bss 

该sectiopn保存着未初始化的数据,这些数据存在于程序内存映象中。 
通过定义,当程序开始运行,系统初始化那些数据为0。该section不占 
文件空间,正如它的section类型SHT_NOBITS指示的一样。 

* .comment 

该section保存着版本控制信息。 

* .data and .data1 
这些sections保存着初始化了的数据,那些数据存在于程序内存映象中。 

* .debug 

该section保存着为标号调试的信息。该内容是未指明的。 

* .dynamic 

该section保存着动态连接的信息。该section的属性将包括SHF_ALLOC位。 
是否需要SHF_WRITE是跟处理器有关。第二部分有更详细的信息。 

* .dynstr 

该section保存着动态连接时需要的字符串,一般情况下,名字字符串关联着 
符号表的入口。第二部分有更详细的信息。 

* .dynsym 

该section保存着动态符号表,如“Symbol Table”的描述。第二部分有更 
详细的信息。 

* .fini 

该section保存着可执行指令,它构成了进程的终止代码。 
因此,当一个程序正常退出时,系统安排执行这个section的中的代码。 

* .got 

该section保存着全局的偏移量表。看第一部分的“Special Sections”和 
第二部分的“Global Offset Table”获得更多的信息。 

* .hash 

该section保存着一个标号的哈希表。看第二部分的“Hash Table”获得更多 
的信息。 

* .init 

该section保存着可执行指令,它构成了进程的初始化代码。 
因此,当一个程序开始运行时,在main函数被调用之前(c语言称为main), 
系统安排执行这个section的中的代码。 

* .interp 

该section保存了程序的解释程序(interpreter)的路径。假如在这个section 
中有一个可装载的段,那么该section的属性的SHF_ALLOC位将被设置;否则, 
该位不会被设置。看第二部分获得更多的信息。 

* .line 

该section包含编辑字符的行数信息,它描述源程序与机器代码之间的对于 
关系。该section内容不明确的。 

* .note 

该section保存一些信息,使用“Note Section”(在第二部分)中提到的格式。 

* .plt 

该section保存着过程连接表(Procedure Linkage Table)。看第一部分的 
``Special Sections‘‘和第二部分的“Procedure Linkage Table”。 

* .rel and .rela 

这些section保存着重定位的信息,看下面的``Relocation‘‘描述。 
假如文件包含了一个可装载的段,并且这个段是重定位的,那么该section的 
属性将设置SHF_ALLOC位;否则该位被关闭。按照惯例,由重定位适用 
的section来提供。因此,一个重定位的section适用的是.text,那么该名字 
就为.rel.text或者是.rela.text。 

* .rodata and .rodata1 

这些section保存着只读数据,在进程映象中构造不可写的段。看第二部分的 
``Program Header‘‘获得更多的资料。 

* .shstrtab 

该section保存着section名称。 

* .strtab 

该section保存着字符串,一般地,描述名字的字符串和一个标号的入口相关 
联。假如文件有一个可装载的段,并且该段包括了符号字符串表,那么section 
的SHF_ALLOC属性将被设置;否则不设置。 

* .symtab 

该section保存着一个符号表,正如在这个section里``Symbol Table‘‘的 
描述。假如文件有一个可装载的段,并且该段包含了符号表,那么section 
的SHF_ALLOC属性将被设置;否则不设置。 

* .text 

该section保存着程序的``text‘‘或者说是可执行指令。 


前缀是点(.)的section名是系统保留的,尽管应用程序可以用那些保留的 
section名。应用程序可以使用不带前缀的名字以避免和系统的sections 
冲突。object文件格式可以让一个定义的section部分不出现在上面的列 
表中。一个object文件可以有多个同样名字的section。 

为处理器体系保留的section名的形成是通过置换成一个体系名的缩写。 
该名字应该取自体系名,e_machine使用的就是。例如,.Foo.psect就是 
在FOO体系上定义的名字。 

现存的扩展名是历史遗留下来的。 

Pre-existing Extensions 
======================= 
.sdata .tdesc 
.sbss .lit4 
.lit8 .reginfo 
.gptab .liblist 
.conflict 


by xueyan(2005年06月11日,22时56

 

xueyan 05-06-24 03:03

elf文件格式-- 2 
elf文件格式-- 2
=================== String Table 字符串表========================= 


String table sections 保存着以NULL终止的一系列字符,一般我们称为字 
符串。object文件使用这些字符串来描绘符号和section名。一个字符串的 
参考是一个string table section的索引。第一个字节,即索引0,被定义保 
存着一个NULL字符。同样的,一个string table的最后一个字节保存着一个 
NULL字符,所有的字符串都是以NULL终止。索引0的字符串是没有名字或者说 
是NULL,它的解释依靠上下文。一个空的string table section是允许的; 
它的section header的成员sh_size将为0。对空的string table来说,非0的 
索引是没有用的。 

一个 settion 头的 sh_name 成员保存了一个对应于该 setion 头字符表部分 
的索引(就象ELF头的 e_shstrndx 成员所特指的那样。下表列出了一个有 25 字节 
的字符串表(这些字符串和不同的索引相关联): 


Index +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 
===== == == == == == == == == == == 
0 n a m e . V a r 
10 i a b l e a b l e 
20 x x 


+ Figure 1-15: String Table Indexes 

Index String 
===== ====== 
0 none 
1 "name." 
7 "Variable" 
11 "able" 
16 "able" 
24 null string 


如上所示,一个字符串表可能涉及该 section 中的任意字节。一个字符串可能 
引用不止一次;引用子串的情况是可能存在的;一个字符串也可能被引用若干次;而 
不被引用的字符串也是允许存在的。 

==================== Symbol Table 符号表========================= 


一个object文件的符号表保存了一个程序在定位和重定位时需要的定义和引用的信息。 
一个符号表索引是相应的下标。0表项特指了该表的第一个入口,就象未定义的符号 
索引一样。初始入口的内容在该 section 的后续部分被指定。 

Name Value 
==== ===== 
STN_UNDEF 0 

一个符号表入口有如下的格式: 

+ Figure 1-16: Symbol Table Entry 

typedef struct { 
Elf32_Word st_name; 
Elf32_Addr st_value; 
Elf32_Word st_size; 
unsigned char st_info; 
unsigned char st_other; 
Elf32_Half st_shndx; 
} Elf32_Sym; 

* st_name 

该成员保存了进入该object文件的符号字符串表入口的索引(保留了符号名的表达字符)。 
如果该值不为 0 ,则它代表了给出符号名的字符串表索引。否则,该符号无名。 

注意:External C 符号和object文件的符号表有相同的名称。 

* st_value 

该成员给出了相应的符号值。它可能是绝对值或地址等等(依赖于上下文); 
细节如下所述。 

* st_size 

许多符号和大小相关。比如,一个数据对象的大小是该对象所包含的字节数目。 
如果该符号的大小未知或没有大小则这个成员为 0 。 

* st_info 

成员指出了符号的类型和相应的属性。相应的列表如下所示。下面的代码说明了 
如何操作该值。 

#define ELF32_ST_BIND(i) ((i)>>4) 
#define ELF32_ST_TYPE(i) ((i)&0xf) 
#define ELF32_ST_INFO(b, t) (((b)<<4)+((t)&0xf)) 

* st_other 

该成员目前为 0 ,没有含义。 

* st_shndx 

每一个符号表的入口都定义为和某些 section 相关;该成员保存了相关的 section 
头索引。就象 Figure 1-8 和相关的文字所描述的那样,某些 section 索引 
指出了特殊的含义。 

一个符号的属性决定了可链接性能和行为。 

+ Figure 1-17: Symbol Binding, ELF32_ST_BIND 

Name Value 
==== ===== 
STB_LOCAL 0 
STB_GLOBAL 1 
STB_WEAK 2 
STB_LOPROC 13 
STB_HIPROC 15 

* STB_LOCAL 

在包含了其定义的object文件之外的局部符号是不可见的。不同文件中的具有相同 
名称的局部符号并不相互妨碍。 

* STB_GLOBAL 

全局符号是对所有的object目标文件可见的。一个文件中的全局符号的定义可以 
满足另一个文件中对(该文件中)未定义的全局符号的引用。 

* STB_WEAK 

弱符号相似于全局符号,但是他们定义的优先级比较低一些。 

* STB_LOPROC through STB_HIPROC 

其所包含范围中的值由相应的处理器语义所保留。 

全局符号和弱符号的区别主要在两个方面。 

* 当链接器链接几个可重定位的目标文件时,它不允许 STB_GLOBAL 符号的同名 
多重定义。另一方面,如果一个全局符号的定义存在则具有相同名称的弱符号名不会 
引起错误。链接器将认可全局符号的定义而忽略弱符号的定义。与此相似,如果有一个 
普通符号(比如,一个符号的 st_shndx 域包含 SHN_COMMON),则一个同名的弱符号 
不会引起错误。链接器同样认可普通符号的定义而忽略弱符号。 

* 当链接器搜索档案库的时候,它选出包含了未定义的全局符号的存档成员。该成员 
的定义或者是全局的或者是一个弱符号。链接器不会为了解决一个未定义的弱符号 
选出存档成员。未定义的弱符号具有 0 值。 

在每一个符号表中,所有具有 STB_LOCAL 约束的符号优先于弱符号和全局符号。 
就象上面 "sections" 中描述的那样,一个符号表部分的 sh_info 头中的成员 
保留了第一个非局部符号的符号表索引。 

符号的类型提供了一个为相关入口的普遍分类。 

+ Figure 1-18: Symbol Types, ELF32_ST_TYPE 

Name Value 
==== ===== 
STT_NOTYPE 0 
STT_OBJECT 1 
STT_FUNC 2 
STT_SECTION 3 
STT_FILE 4 
STT_LOPROC 13 
STT_HIPROC 15 

* STT_NOTYPE 

该符号的类型没有指定。 

* STT_OBJECT 

该符号和一个数据对象相关,比如一个变量、一个数组等。 

* STT_FUNC 

该符号和一个函数或其他可执行代码相关。 

* STT_SECTION 

该符号和一个 section 相关。这种类型的符号表入口主要是为了重定位,一般的 
具有 STB_LOCAL 约束。 


* STT_FILE 

按惯例而言,该符号给出了和目标文件相关的源文件名称。一个具有 STB_LOCAL 
约束的文件符号,其 section 索引为 SHN_ABS ,并且它优先于当前对应该文件的 
其他 STB_LOCAL 符号。 

* STT_LOPROC through STT_HIPROC 

该范围中的值是为处理器语义保留的。 

共享文件中的函数符号(具有 STT_FUNC 类型)有特殊的意义。当其他的目标文件 
从一个共享文件中引用一个函数时,链接器自动的为引用符号创建一个链接表。除了 
STT_FUNC 之外,共享的目标符号将不会自动的通过链接表引用。 


如果一个符号涉及到一个 section 的特定定位,则其 section 索引成员 st_shndx 
将保留一个到该 section 头的索引。当该 section 在重定位过程中不断 
移动一样,符号的值也相应变化,而该符号的引用在程序中指向同样的定位。某些 
特殊的 section 索引有其他的语义。 


* SHN_ABS 

该符号有一个不会随重定位变化的绝对值。 

* SHN_COMMON 

该符号标识了一个没有被分配的普通块。该符号的值给出了相应的系统参数,就象 
一个 section 的 sh_addralign 成员。也就是说,链接器将分配一个地址给 
该符号,地址的值是 st_value 的倍数。该符号的大小指出了需要的字节数。 


* SHN_UNDEF 

该 section 表索引表明该符号是未定义的。当链接器将该目标文件和另一个定义 
该符号的文件相装配的时候,该文件内对该符号的引用将链接到当前实际的定义。 

如上所述,符号表的 0 索引(STN_UNDEF)是保留的,它包含了如下内容: 

+ Figure 1-19: Symbol Table Entry: Index 0 

Name Value Note 
==== ===== ==== 
st_name 0 No name 
st_value 0 Zero value 
st_size 0 No size 
st_info 0 No type, local binding 
st_other 0 
st_shndx SHN_UNDEF No section 


Symbol Values(符号值) 

符号表入口对于不同的目标文件而言对 st_value 成员有一些不同的解释。 

* 在可重定位文件中, st_value 保存了 section 索引为 SHN_COMMON 符号 
的强制对齐值。 

* 在可重定位文件中, st_value 保存了一个符号的 section 偏移。也就是说, 
st_value 是从 st_shndx 定义的 section 开头的偏移量。 

* 在可执行的和可共享的目标文件中, st_value 保存了一个虚拟地址。为了使 
这些文件符号对于动态链接器更为有效,文件层面上的 section 偏移让位于内存 
层面上的虚拟地址( section 编号无关的)。 


尽管符号表值对于不同的目标文件有相似的含义,相应的程序还是可以有效地访问数据。 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值