上一篇:ELF 详解2 – Section Header & Section

ELF Symbol Table

Symbol Table 包含了一组 Symbol。这些 Symbol 在程序中,要么表示定义,要么表示引用,它们的作用是在编译和链接的过程中,进行定位或者重定位(后面会讲到)。

先查看它在 Section Header 列表中的信息

$ readelf -SW program.o
...
Section Headers:[Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
...[12] .symtab           SYMTAB          0000000000000000 000130 000180 18     13  10  8
...

由上可知 Symbol Table Section:

  1. 名字叫 “.symtab”
  2. 类型是 SYMTAB
  3. offset = 0x130 = (1 * 16 + 3) * 16 = 304 bytes
  4. size = 0x180 = (1 * 16 + 8) * 16 = 386 bytes
  5. 每个 Symbol 的 size = 0x18 = 1 * 16 + 8 = 24
  6. Symbol 数为: 386 / 24 = 16

ELF Symbol

看一下 Symbol 的定义

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;typedef struct {Elf64_Word  st_name; // 4 B (B for bytes)unsigned char  st_info; // 1 Bunsigned char    st_other; // 1 BElf64_Half  st_shndx; // 2 BElf64_Addr  st_value; // 8 BElf64_Xword st_size; // 8 B
} Elf64_Sym; // total size = 24 B

我们只关注 Elf64_Sym (64位系统的定义)。
可以看到 Elf64_Sym 的大小为24字节。

用 readelf 查看一下 Symbol Table

# 注意这里是小写的 s,代表 Symbols,大写的 S,代表 Sections/Section Headers
$ readelf -sW program.oSymbol table '.symtab' contains 16 entries:Num:    Value          Size Type    Bind   Vis      Ndx Name0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS program.c2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 4: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 5: 0000000000000000     1 OBJECT  LOCAL  DEFAULT    3 d6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 8: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 9: 0000000000000000     0 SECTION LOCAL  DEFAULT    7 10: 0000000000000008    10 OBJECT  GLOBAL DEFAULT  COM c11: 0000000000000008     8 OBJECT  GLOBAL DEFAULT    3 f12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND function13: 0000000000000000    81 FUNC    GLOBAL DEFAULT    1 main14: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND a15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

这里包含了 program.o 中出现的所有 Symbol。其中 Num=0(index=0) 的 Symbol 用作 undefined Symbol,它的 index 有个特别的名字,叫做 STN_UNDEF

接下来,我们将选取 Num=5,也就是 Name=d 的 Symbol,从字节级别解读 Symbol 的定义。

上面已经知道了 Symbol Table 在对象文件中的 offset=304,每个 Symbol 的 size=24,那么 Num=5 的 Symbol(Num 从0开始计算,Num=5表示前面有5个 Symbol),它的 offset = 304 + 24 * 5 = 424。

$ hexdump -C -s424 -n24 program.o
000001a8  0b 00 00 00 01 00 03 00  00 00 00 00 00 00 00 00  |................|
000001b8  01 00 00 00 00 00 00 00                           |........|
000001c0
st_name

Symbol 名字的索引,类似于 Section Header 的 sh_name,只不过这个索引使用的 String table 跟 Section Header sh_name 使用的 String table 不是同一个。后面会讲到。

数据类型:Elf64_Word (4 bytes)

$ hexdump -C -s424 -n4 program.o
000001a8  0b 00 00 00                                       |....|
000001ac

值为:“0b 00 00 00” + little endian = 0xb = 11

st_info

这是个组合字段。它的高4位表示 Symbol Binding,低4位表示 Symbol Type。

数据类型:unsigned char (1 byte)

$ hexdump -C -s428 -n1 program.o
000001ac  01                                                |.|
000001ad

值为:1,二进制表示为 “0000 0001”
Symbol Binding: 1 >> 4 = 0
Symbol Type: 1 & 0xf = 1

Symbol Binding 值的含义:(只需要关注红框部分)

STB_LOCAL: 对象文件外部不可见。
STB_GLOBAL:所有合并到一起的对象文件都可见。对于同一个 Global Symbol,它的定义可以满足其他地方对它的引用。
STB_WEAK:类似于 STB_GLOBAL,但匹配时优先级较低。
(后面会详细讲到)

Symbol Type 值的含义:(只需要关注红框部分)

STT_NOTYPE:类型未指定,也可以认为这个类型的 Symbol,在当前的对象文件中没找到其定义,需要外部其他对象文件来提供它的定义。
STT_OBJECT:关联到一个数据对象,比如数组,变量等。
STT_FUNC:关联到一个函数或者其他可执行的代码。
STT_SECTION:关联到可以重定位的 Section。
STT_FILE:给出了这个对象文件的源文件名,譬如 program.o 的源文件就是 program.c。它的 Section index 为 SHN_ABS。(后面会讲到)
STT_COMMON:标识了未初始化的公共块。后面会详细讲到。
STT_TLS:指定了线程本地存储的实体。(本章不讨论多线程,所以先跳过)

st_other

指定了 Symbol 的可见性。当前只有最低2位被使用。

数据类型:unsigned char (1 byte)

$ hexdump -C -s429 -n1 program.o
000001ad  00                                                |.|
000001ae

值为:0
可见性:0 & 0x3 = 0

通常情况下,可见性都为 STV_DEFAULT,也就是使用默认规则(后面会讲到)。
其他可见性先跳过。

st_shndx

不同的上下文有不同的含义:

  1. 当 st_shndx = Section Header index: st_value(Symbol 的值)指向某个 Section 中的某个位置。
  2. 当 st_shndx = SHN_ABS:表示该 Symbol 不应该被重定位。比如类型为 STT_FILE 的 Symbol。
  3. 当 st_shndx = SHN_COMMON:该 Symbol 标记的是一个还没分配的公共块,st_value 表示对齐约束,即(连接编辑器为这个 Symbol 分配的地址 % st_value = 0),而 st_size(Symbol 的大小)则表示该 Symbol 至少需要多少字节。
  4. 当 st_shndx = SHN_UNDEF:表示当前对象文件引用了该 Symbol,但是它的定义存在于其他对象文件中。比如对象文件 A 引用了 Symbol b(没有 b 的定义),对象文件 B 有 b 的定义,那么链接编辑器将 A 和 B 进行合并时,A 的 Symbol b 就会链接到 B 中 b 的定义。
  5. 当 st_shndx = SHN_XINDEX:Section Header index 过大时需要做特殊解析,当前不考虑这种情况。

数据类型:Elf64_Half (2 bytes)

$ hexdump -C -s430 -n2 program.o
000001ae  03 00                                             |..|
000001b0

值为:“03 00” + little endian = 0x3 = 3,即关联到的 Section Header index 为3。

st_value

根据不同的上下文,有不同的定义

  1. 对于 Relocatable file,如果 st_shndx 的值为 SHN_COMMON,那么 st_value 表示对齐约束。(上面 st_shndx = SHN_COMMON 已经提及)
  2. 对于 Relocatable file,如果 st_shndx 的值为关联的 Section Header index,那么 st_value 表示从该 Section 起始位置开始的 offset。(上面 st_shndx = Section Header index 已经提及)
  3. 对于 Executable file 和 Shared object file,st_value 则是已经计算好的虚存地址,这是为了方便 dynamic linker(动态链接器)。

数据类型:Elf64_Addr (8 bytes)

$ hexdump -C -s432 -n8 program.o
000001b0  00 00 00 00 00 00 00 00                           |........|
000001b8

值为:0

st_size

表示 Symbol 的大小,例如数据对象占据多少字节。如果 Symbol 没有大小或者大小未知,则值为0。

数据类型:Elf64_Xword (8 bytes)

$ hexdump -C -s440 -n8 program.o
000001b8  01 00 00 00 00 00 00 00                           |........|
000001c0

值为:“01 00 00 00 00 00 00 00” + little endian = 0x1,即占据1个字节。

可以把各字段的信息对照 Symbol Table 中 Num=5 的输出,从而加深理解。

“.symtab” 的 sh_link & sh_info

首先回顾一下 “.symtab” 的 Section Header 输出:

$ readelf -SW program.o
...
Section Headers:[Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
...[12] .symtab           SYMTAB          0000000000000000 000130 000180 18     13  10  8[13] .strtab           STRTAB          0000000000000000 0002b0 000024 00      0   0  1
...

由上可知,".symtab" 的 sh_link = 13,sh_info = 10。
前面 Section Header 的章节已经提到过,sh_link 和 sh_info 在不同的上下文有不同的含义。

对于 “.symtab”,
sh_link:表示关联到的 String table 的 Section Header index。在这里,这个 String table 就是 “.strtab”。Symbol 的 st_name 就是对应到 “.strtab” 的 index。
sh_info:1 + 最后一个 local Symbol (Binding = STB_LOCAL) 在 Symbol Table 中的 index,也就是说从这个 index 开始的 Symbol 不再是 local。

查看一下 “.strtab” 的内容

$ hexdump -C -s0x2b0 -n36 program.o
000002b0  00 70 72 6f 67 72 61 6d  2e 63 00 64 00 66 75 6e  |.program.c.d.fun|
000002c0  63 74 69 6f 6e 00 6d 61  69 6e 00 61 00 70 72 69  |ction.main.a.pri|
000002d0  6e 74 66 00                                       |ntf.|
000002d4

从上面查看过的字节信息可知,Symbol d 的 st_name = 11,在这里对应的就是 “d”。

以下是打印所有 Symbol 名字的程序,类似于前面打印所有 Section Header 名字的程序(为了方便理解,其实就是换了变量名和对应的数值)。

// print_symbol_names.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>int main() {off_t symbolTableOffset = 0x130;size_t symbolTableSize = 0x180;size_t symbolSize = 0x18;off_t stringTableOffset = 0x2b0;size_t stringTableSize = 0x24;size_t nameSize = 4;int symbolNum = symbolTableSize / symbolSize;char content[stringTableSize];union {char b[4];int off;} symbolName;int fd = open("program.o", O_RDONLY);if (fd == -1)exit(EXIT_FAILURE);if (lseek(fd, stringTableOffset, SEEK_SET) == -1)exit(EXIT_FAILURE);if (read(fd, content, stringTableSize) != stringTableSize)exit(EXIT_FAILURE);int i;int currSymbolOffset = symbolTableOffset;char *start;for (i = 0; i < symbolNum; ++i) {if (lseek(fd, currSymbolOffset, SEEK_SET) == -1)exit(EXIT_FAILURE);if (read(fd, symbolName.b, 4) != 4)exit(EXIT_FAILURE);start = content + symbolName.off;printf("[%2d]: off: 0x%02x,  name: %s\n", i, symbolName.off, start);currSymbolOffset += symbolSize;}if (close(fd) == -1)exit(EXIT_FAILURE);return 0;
}

输出的结果和 “readelf -sW program.o” 是一致的。

$ gcc -o print_symbol_names print_symbol_names.c
$ ./print_symbol_names
[ 0]: off: 0x00,  name:
[ 1]: off: 0x01,  name: program.c
[ 2]: off: 0x00,  name:
[ 3]: off: 0x00,  name:
[ 4]: off: 0x00,  name:
[ 5]: off: 0x0b,  name: d
[ 6]: off: 0x00,  name:
[ 7]: off: 0x00,  name:
[ 8]: off: 0x00,  name:
[ 9]: off: 0x00,  name:
[10]: off: 0x09,  name: c
[11]: off: 0x22,  name: f
[12]: off: 0x0d,  name: function
[13]: off: 0x16,  name: main
[14]: off: 0x1b,  name: a
[15]: off: 0x1d,  name: printf

再来看 sh_info 的含义。

$ readelf -sW program.oSymbol table '.symtab' contains 16 entries:Num:    Value          Size Type    Bind   Vis      Ndx Name0: 0000000000000000     0 NOTYPE  LOCAL  DEFAULT  UND 1: 0000000000000000     0 FILE    LOCAL  DEFAULT  ABS program.c2: 0000000000000000     0 SECTION LOCAL  DEFAULT    1 3: 0000000000000000     0 SECTION LOCAL  DEFAULT    3 4: 0000000000000000     0 SECTION LOCAL  DEFAULT    5 5: 0000000000000000     1 OBJECT  LOCAL  DEFAULT    3 d6: 0000000000000000     0 SECTION LOCAL  DEFAULT    6 7: 0000000000000000     0 SECTION LOCAL  DEFAULT    8 8: 0000000000000000     0 SECTION LOCAL  DEFAULT    9 9: 0000000000000000     0 SECTION LOCAL  DEFAULT    7
# --------------------------------------------------------------------10: 0000000000000008    10 OBJECT  GLOBAL DEFAULT  COM c11: 0000000000000008     8 OBJECT  GLOBAL DEFAULT    3 f12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND function13: 0000000000000000    81 FUNC    GLOBAL DEFAULT    1 main14: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND a15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf

由上可知,Num = 10 的 Symbol 是 c,在它之前的 Symbol,Binding 都是 LOCAL,从它开始往后的都是 GLOBAL。

接下来将查看 program.c 中出现的各个 Symbol 在对象文件中是如何定义的。

Symbol 示例

Num=5, Name=d
// program.c
...
static char d = 'd';
...
$ readelf -sW program.o
...Num:    Value          Size Type    Bind   Vis      Ndx Name
...5: 0000000000000000     1 OBJECT  LOCAL  DEFAULT    3 d
...# 查看关联到的 Section Header (Ndx=3)
$ readelf -SW program.o
...
Section Headers:[Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
...[ 3] .data             PROGBITS        0000000000000000 000098 000010 00  WA  0   0  8
...# 打印 ".data" 的内容
$ hexdump -C -s0x98 -n16 program.o
00000098  64 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |d...............|
000000a8

对于 Symbol d:

  1. 在程序中声明为 static,所以它的 Symbol Binding 是 LOCAL
  2. 用于储存数据,所以它的 Symbol Type 是 STT_OBJECT
  3. 拥有初始值 ‘d’,所以它被分配在 “.data” Section 中
  4. 从上面 st_value 的定义可知,因为 d 关联到 “.data” Section,所以它的 st_value 表示它在 “.data” 中的 offset,在这里 offset = 0
  5. 数据类型为 char,因此占据1个字节,所以它的 Symbol st_value = 1;从 “.data” 中 offset = 0 的地方提取到的1字节为 0x64,其 ascii 就是 ‘d’。
Num=10,Name=c
// program.c
...
char c[10];
...
$ readelf -sW program.o
...Num:    Value          Size Type    Bind   Vis      Ndx Name
...10: 0000000000000008    10 OBJECT  GLOBAL DEFAULT  COM c
...

对于 Symbol c:

  1. 在程序中是个全局变量(没有声明 static),所以它的 Symbol Binding 是 GLOBAL
  2. 用于储存数据,所以它的 Symbol Type 是 STT_OBJECT
  3. 没有初始化,因此它的 Symbol st_shndx 为 SHN_COMMON(注意,这里并没有把它分配到 “.bss”,后面会详细讲到)
  4. 从上面 st_value 的定义可知,当 st_shndx = SHN_COMMON 时,st_value 表示对齐约束;这里 st_value = 8,表示它被分配到的地址会是8的整数倍
  5. 它是个长度为10的 char 数组,所以它的 Symbol st_size = 10
Num=11,Name=f
// library.h
int function(int);// program.c
#include <stdio.h>
#include "library.h"
...
char* f = (char*) function;
...
$ readelf -sW program.o
...Num:    Value          Size Type    Bind   Vis      Ndx Name
...11: 0000000000000008     8 OBJECT  GLOBAL DEFAULT    3 f
...# 打印 ".data" 的内容
$ hexdump -C -s0x98 -n16 program.o
00000098  64 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |d...............|
000000a8

对于 Symbol f:

  1. 在程序中是个全局变量(没有声明 static),所以它的 Symbol Binding 是 GLOBAL
  2. 用于储存数据(存储的是地址),所以它的 Symbol Type 是 STT_OBJECT
  3. 初始值为函数 function 的地址,所以它被分配在 “.data” Section 中
  4. 从上面 st_value 的定义可知,因为 f 关联到 “.data” Section,所以它的 st_value 表示它在 “.data” 中的 offset,在这里 offset = 8
  5. 数据类型为 char*,因此占据8个字节,所以它的 Symbol st_value = 8;从 “.data” 中 offset = 8 的地方提取到的8字节为 “00 00 00 00 00 00 00 00”,这是因为当前无法确定 function 的地址
Undefined Symbol

Num=12, Name=function
Num=14, Name=a (声明为 extern)
Num=15, Name=printf
它们都是外部引用(对外部 Symbol 的引用)

// program.c
...
extern int a;
...
char* f = (char*) function;int main() {...printf("a: %d\n", a);printf("result: %d\n", d);
}
$ readelf -sW program.o
...Num:    Value          Size Type    Bind   Vis      Ndx Name
...12: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND function
...14: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND a15: 0000000000000000     0 NOTYPE  GLOBAL DEFAULT  UND printf
...

对于这几个 Symbol:

  1. 在程序中是外部引用,所以它们的 Symbol Binding 是 GLOBAL
  2. 没有找到它们的定义,所以 Section Header index 为 SHN_UNDEF, Symbol Type 为 STT_NOTYPE,st_size = 0, st_value = 0

Ndx = SHN_UNDEF 的 Symbol 都需要在链接期间进行 Symbol 解析。

Num=13, name=main
...
int main() {int d = function(100) + a;printf("a: %d\n", a);printf("result: %d\n", d);
}
$ readelf -sW program.o
...Num:    Value          Size Type    Bind   Vis      Ndx Name
...13: 0000000000000000    81 FUNC    GLOBAL DEFAULT    1 main
...# 查看关联到的 Section Header(Ndx=1)
$ readelf -SW program.o
...
Section Headers:[Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
...[ 1] .text             PROGBITS        0000000000000000 000040 000051 00  AX  0   0  1
...# 查看 ".text" 的内容
$ hexdump -C -s0x40 -n81 program.o
00000040  55 48 89 e5 48 83 ec 10  bf 64 00 00 00 e8 00 00  |UH..H....d......|
00000050  00 00 89 c2 8b 05 00 00  00 00 01 d0 89 45 fc 8b  |.............E..|
00000060  05 00 00 00 00 89 c6 bf  00 00 00 00 b8 00 00 00  |................|
00000070  00 e8 00 00 00 00 8b 45  fc 89 c6 bf 00 00 00 00  |.......E........|
00000080  b8 00 00 00 00 e8 00 00  00 00 b8 00 00 00 00 c9  |................|
00000090  c3                                                |.|
00000091# 查看 ".text" 的反汇编内容
$ objdump -d program.o
...
Disassembly of section .text:0000000000000000 <main>:0:   55                      push   %rbp1:   48 89 e5                mov    %rsp,%rbp
...4f:  c9                      leaveq 50:  c3                      retq

对于 Symbol main:

  1. 在程序中是个全局函数(没有声明 static),所以它的 Symbol Binding 是 GLOBAL,Symbol Type 为 STT_FUNC
  2. 包含了可执行的代码,所以被分配在 “.text” Section 中
  3. 从上面 st_value 的定义可知,因为 main 关联到 “.text” Section,所以它的 st_value 表示它在 “.text” 中的 offset,在这里 offset = 0;而 st_size 表示代码占据多少字节,在这里 st_size = 81(注意,这里的81是10进制,而 “.text” 的 size 是16进制: 0x51 => 5 * 16 + 1 = 81)

下一篇:ELF 详解4 – 深入 Symbol

Linux ELF 详解3 -- Symbol Table Symbol相关推荐

  1. Linux ELF 详解2 -- Section Header Section

    上一篇:ELF 详解1 – ELF Header ELF Section Header & Section 先看 Section Header 的定义 typedef struct {Elf3 ...

  2. Linux proc详解

    本文转自:http://hi.baidu.com/beyond907/blog/item/250f41249c9fbb7435a80fd2.html Linux procfs详解 1.0 proc文件 ...

  3. Linux系统结构 详解

    Linux系统结构 详解 标签: 产品产品设计googleapple互联网 2011-01-07 14:14 31038人阅读 评论(6) 收藏 举报 分类: Linux(21) 版权声明:本文为博主 ...

  4. linux /proc 详解

    linux /proc 详解 本文整理了一下 linux /proc下的几个常用的目录和文件,可供查阅,之后在学习工作中有别的用到的话会再补充. /proc 简介 Linux系统上的/proc目录是一 ...

  5. Linux: 系统结构详解

    Linux系统一般有4个主要部分: 内核.shell.文件系统和应用程序.内核.shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序.管理文件并使用系统.部分层次结构如图1-1所 ...

  6. Linux 系统结构详解——新手上路

    Linux 系统结构详解 Linux系统一般有4个主要部分: 内核.shell.文件系统和应用程序.内核.shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序.管理文件并使用系统 ...

  7. Linux系统调用详解(实现机制分析)

    为什么需要系统调用   linux内核中设置了一组用于实现系统功能的子程序,称为系统调用.系统调用和普通库函数调用非常相似,只是系统调用由操作系统核心提供,运行于内核态,而普通的函数调用由函数库或用户 ...

  8. 《Linux命令详解手册》——Linux畅销书作家又一力作

    关注IT,更要关心IT人,让系统管理员以及程序员工作得更加轻松和快乐.鉴于此, 图灵公司引进了国外知名出版社John Wiley and Sons出版的Fedora Linux Toolbox: 10 ...

  9. Linux系统详解 系统的启动、登录、注销与开关机

    Linux系统详解 第六篇:系统的启动.登录.注销与开关机 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://johncai.blo ...

  10. 每天一个linux命令(25):linux文件属性详解

    每天一个linux命令(25):linux文件属性详解 Linux 文件或目录的属性主要包括:文件或目录的节点.种类.权限模式.链接数量.所归属的用户和用户组.最近访问或修改的时间等内容.具体情况如下 ...

最新文章

  1. 剑指offer:左旋转字符串
  2. DataSet和List 泛型之间互相转换 (转载, 作者写的很好)
  3. appfog mysql_appfog java jdbc mysql连接
  4. SPL 关联优化技巧
  5. java数据库防火墙,数据库centos7防火墙导致java程序访问mongodb3.0.1时报错的问题分析...
  6. HTTPS|SSL笔记-SSL分手过程(Encrypted Alert)
  7. 在有空字符串的有序字符串数组中查找(找给定字符串)
  8. arcmap加载GPS定位终端数据
  9. visio2013画图相关
  10. 常见条形码的用法和格式
  11. ARM架构下常用GNU汇编程序伪指令介绍(Assembler Directive)
  12. 闰年c语言循环计算方法,C语言计算有多少闰年(答案原创)
  13. C#邮件过滤系统(论文+可执行程序+源码+外文翻译+程序操作演示录像)
  14. ICTCLAS代码学习笔记之CSpan类
  15. python 图片二值化后 判断图片是白底黑字,还是黑底白字
  16. OneNET麒麟座应用开发之三:获取温湿度数据
  17. Python编程语言好学吗 怎么能学好Python开发
  18. 跨平台开发 uni-app
  19. 常熟理工学院计算机专业排名2015,2019年常熟理工学院优势专业排名及分数线
  20. C++ 实现磁盘初始化

热门文章

  1. BLUES吉他学习笔记007 bluesrv[11]
  2. 变量undefined详解
  3. 智商情商哪个重要_你认为哪个更重要,情商还是智商?为什么?
  4. 支付宝实现JS调起支付你必须知道的坑(40004 ACQ.INVALID_PARAMETER)
  5. 不要随便点这个网站,你偷偷下载的小电影,他们全都知道!
  6. 【GNN报告】微软亚洲研究院郑书新:图神经网络迈入Transformer时代
  7. 北京内推 | 微软亚洲研究院自然语言计算组招聘NLP研究实习生
  8. Nero 2014 Platinum 白金版 V 15.0.02200 官方版
  9. encode()和decode()
  10. 没有卑微的工作,只有卑微的工作态度