Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入了解 Go ELF 信息

深入了解 Go ELF 信息

作者头像
Jintao Zhang
发布于 2022-12-07 06:26:50
发布于 2022-12-07 06:26:50
83200
代码可运行
举报
文章被收录于专栏:MoeLoveMoeLove
运行总次数:0
代码可运行

大家好,我是张晋涛。

我们用 Go 构建的二进制文件中默认包含了很多有用的信息。例如,可以获取构建用的 Go 版本:

(这里我使用我一直参与的一个开源项目 KIND[1] 为例)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ go version ./bin/kind 
./bin/kind: go1.16

或者也可以获取该二进制所依赖的模块信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ go version -m ./bin/kind
./bin/kind: go1.16
        path    sigs.k8s.io/kind
        mod     sigs.k8s.io/kind        (devel)
        dep     github.com/BurntSushi/toml      v0.3.1
        dep     github.com/alessio/shellescape  v1.4.1
        dep     github.com/evanphx/json-patch/v5        v5.2.0
        dep     github.com/mattn/go-isatty      v0.0.12
        dep     github.com/pelletier/go-toml    v1.8.1  h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
        dep     github.com/pkg/errors   v0.9.1
        dep     github.com/spf13/cobra  v1.1.1
        dep     github.com/spf13/pflag  v1.0.5
        dep     golang.org/x/sys        v0.0.0-20210124154548-22da62e12c0c      h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
        dep     gopkg.in/yaml.v2        v2.2.8
        dep     gopkg.in/yaml.v3        v3.0.0-20210107192922-496545a6307b      h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
        dep     k8s.io/apimachinery     v0.20.2
        dep     sigs.k8s.io/yaml        v1.2.0

查看 KIND 代码仓库中的 go.mod文件,都包含在内了。

其实 Linux 系统中二进制文件包含额外的信息并非 Go 所特有的,下面我将具体介绍其内部原理和实现。当然,用 Go 构建的二进制文件仍是本文的主角。

Linux ELF 格式

ELF 是 Executable and Linkable Format 的缩写,是一种用于可执行文件、目标文件、共享库和核心转储(core dump)的标准文件格式。ELF 文件 通常 是编译器之类的输出,并且是二进制格式。以 Go 编译出的可执行文件为例,我们使用 file 命令即可看到其具体类型 ELF 64-bit LSB executable

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ file ./bin/kind 
./bin/kind: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped

本文中我们来具体看看 64 位可执行文件使用的 ELF 文件格式的结构和 Linux 内核源码中对它的定义。

使用 ELF 文件格式的可执行文件是由 ELF 头(ELF Header) 开始,后跟 程序头(Program Header) 或 节头(Section Header) 或两者均有组成的。

ELF 头

ELF 头始终位于文件的零偏移(zero offset)处(即:起点位置),同时在 ELF 头中还定义了程序头和节头的偏移量。

我们可以通过 readelf 命令查看可执行文件的 ELF 头,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ readelf -h ./bin/kind 
ELF Header:
  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF64
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           Advanced Micro Devices X86-64
  Version:                           0x1
  Entry point address:               0x46c460
  Start of program headers:          64 (bytes into file)
  Start of section headers:          400 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         6
  Size of section headers:           64 (bytes)
  Number of section headers:         15
  Section header string table index: 3

从上面的输出我们可以看到,ELF 头是以某个 Magic 开始的,此 Magic 标识了有关文件的信息,即:前四个 16 进制数,表示这是一个 ELF 文件。具体来说,将它们换算成其对应的 ASCII 码即可:

45 = E

4c = L

46 = F

7f 是其前缀,当然,也可以直接在 Linux 内核源码[2]中拿到此处的具体定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// include/uapi/linux/elf.h#L340-L343
#define ELFMAG0  0x7f  /* EI_MAG */
#define ELFMAG1  'E'
#define ELFMAG2  'L'
#define ELFMAG3  'F'

接下来的数 02 是与 Class 字段相对应的,表示其体系结构,它可以是 32 位(=01) 或是 64 位(=02)的,此处显示 02 表示是 64 位的,再有 readelf 将其转换为 ELF64 进行展示。这里的取值同样可以在 Linux 内核源码[3]中找到:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// include/uapi/linux/elf.h#L347-L349
#define ELFCLASSNONE 0  /* EI_CLASS */
#define ELFCLASS32 1
#define ELFCLASS64 2

再后面的两个 01 01 则是与 Data 字段和 Version 字段相对应的,Data 有两个取值分别是 LSB(01)和 MSB(02),这里倒没什么必要展开。另外就是 Version 当前只有一个取值,即 01 。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// include/uapi/linux/elf.h#L352-L358
#define ELFDATANONE 0  /* e_ident[EI_DATA] */
#define ELFDATA2LSB 1
#define ELFDATA2MSB 2

#define EV_NONE  0  /* e_version, EI_VERSION */
#define EV_CURRENT 1
#define EV_NUM  2

接下来需要注意的就是我前面提到的关于偏移量的内容,即输出中的以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  Start of program headers:          64 (bytes into file)
  Start of section headers:          400 (bytes into file)
  Flags:                             0x0
  Size of this header:               64 (bytes)
  Size of program headers:           56 (bytes)
  Number of program headers:         6
  Size of section headers:           64 (bytes)
  Number of section headers:         15

ELF 头总是在起点,在此例中接下来是程序头(Program Header),随后是节头(Section Header),这里的输出显示程序头是从 64 开始的,所以节头的位置就是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
64 + 56 * 6 = 400

与上述输出符合,同理,节头的结束位置是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
400 + 15 * 64 = 1360

下一节内容中将用到这部分知识。

程序头

通过 readelf -l 可以看到其程序头,包含了若干段(Segment),内核看到这些段时,将调用 mmap syscall 来使用它们映射到虚拟地址空间。这部分不是本文的重点,我们暂且跳过有个印象即可。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ readelf -l ./bin/kind 

Elf file type is EXEC (Executable file)
Entry point 0x46c460
There are 6 program headers, starting at offset 64

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
  PHDR           0x0000000000000040 0x0000000000400040 0x0000000000400040
                 0x0000000000000150 0x0000000000000150  R      0x1000
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000333a75 0x0000000000333a75  R E    0x1000
  LOAD           0x0000000000334000 0x0000000000734000 0x0000000000734000
                 0x00000000002b3be8 0x00000000002b3be8  R      0x1000
  LOAD           0x00000000005e8000 0x00000000009e8000 0x00000000009e8000
                 0x0000000000020ac0 0x00000000000552d0  RW     0x1000
  GNU_STACK      0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000  RW     0x8
  LOOS+0x5041580 0x0000000000000000 0x0000000000000000 0x0000000000000000
                 0x0000000000000000 0x0000000000000000         0x8

 Section to Segment mapping:
  Segment Sections...
   00     
   01     .text 
   02     .rodata .typelink .itablink .gosymtab .gopclntab 
   03     .go.buildinfo .noptrdata .data .bss .noptrbss 
   04     
   05     

节头

使用 readelf -S 即可查看其节头,其结构如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// include/uapi/linux/elf.h#L317-L328
typedef struct elf64_shdr {
  Elf64_Word sh_name;  /* Section name, index in string tbl */
  Elf64_Word sh_type;  /* Type of section */
  Elf64_Xword sh_flags;  /* Miscellaneous section attributes */
  Elf64_Addr sh_addr;  /* Section virtual addr at execution */
  Elf64_Off sh_offset;  /* Section file offset */
  Elf64_Xword sh_size;  /* Size of section in bytes */
  Elf64_Word sh_link;  /* Index of another section */
  Elf64_Word sh_info;  /* Additional section information */
  Elf64_Xword sh_addralign; /* Section alignment */
  Elf64_Xword sh_entsize; /* Entry size if section holds table */
} Elf64_Shdr;

对照实际的命令输出,含义就很明显了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ readelf -S ./bin/kind 
There are 15 section headers, starting at offset 0x190:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .text             PROGBITS         0000000000401000  00001000
       0000000000332a75  0000000000000000  AX       0     0     32
  [ 2] .rodata           PROGBITS         0000000000734000  00334000
       000000000011f157  0000000000000000   A       0     0     32
  [ 3] .shstrtab         STRTAB           0000000000000000  00453160
       00000000000000a4  0000000000000000           0     0     1
  [ 4] .typelink         PROGBITS         0000000000853220  00453220
       00000000000022a0  0000000000000000   A       0     0     32
  [ 5] .itablink         PROGBITS         00000000008554c0  004554c0
       0000000000000978  0000000000000000   A       0     0     32
  [ 6] .gosymtab         PROGBITS         0000000000855e38  00455e38
       0000000000000000  0000000000000000   A       0     0     1
  [ 7] .gopclntab        PROGBITS         0000000000855e40  00455e40
       0000000000191da8  0000000000000000   A       0     0     32
  [ 8] .go.buildinfo     PROGBITS         00000000009e8000  005e8000
       0000000000000020  0000000000000000  WA       0     0     16
  [ 9] .noptrdata        PROGBITS         00000000009e8020  005e8020
       0000000000017240  0000000000000000  WA       0     0     32
  [10] .data             PROGBITS         00000000009ff260  005ff260
       0000000000009850  0000000000000000  WA       0     0     32
  [11] .bss              NOBITS           0000000000a08ac0  00608ac0
       000000000002f170  0000000000000000  WA       0     0     32
  [12] .noptrbss         NOBITS           0000000000a37c40  00637c40
       0000000000005690  0000000000000000  WA       0     0     32
  [13] .symtab           SYMTAB           0000000000000000  00609000
       0000000000030a20  0000000000000018          14   208     8
  [14] .strtab           STRTAB           0000000000000000  00639a20
       000000000004178d  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

Go 二进制文件探秘

本文中,我们重点关注名为 .go.buildinfo 的部分。 使用 objdump 查看其具体内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s -j .go.buildinfo ./bin/kind

./bin/kind:     file format elf64-x86-64

Contents of section .go.buildinfo:
 9e8000 ff20476f 20627569 6c64696e 663a0800  . Go buildinf:..
 9e8010 a0fc9f00 00000000 e0fc9f00 00000000  ................

这里我们按顺序来,先看到第一行的 16 个字节。

  • 前 14 个字节是魔术字节,必须为 \xff Go buildinf:
  • 第 15 字节表示其指针大小,这里的值为 0x08,表示 8 个字节;
  • 第 16 字节用于判断字节序是大端模式还是小端模式,非 0 为大端模式,0 为小端模式。

我们继续看第 17 字节开始的内容。

Go 版本信息

前面我们也看到了当前使用的字节序是小端模式,这里的地址应该是 0x009ffca0

我们来取出 16 字节的内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s --start-address 0x009ffca0 --stop-address 0x009ffcb0 ./bin/kind   

./bin/kind:     file format elf64-x86-64

Contents of section .data:
 9ffca0 f5027d00 00000000 06000000 00000000  ..}.............

这里前面的 8 个字节是 Go 版本的信息,后 8 个字节是版本所占的大小(这里表示占 6 个字节)。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s --start-address  0x007d02f5 --stop-address 0x007d02fb ./bin/kind

./bin/kind:     file format elf64-x86-64

Contents of section .rodata:
 7d02f5 676f31 2e3136                        go1.16

所以,如上所示,我们拿到了构建此二进制文件所用的 Go 版本的信息,是用 Go 1.16 进行构建的。

Go Module 信息

前面我们使用了 17~24 字节的信息,这次我们继续往后使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s --start-address  0x009ffce0 --stop-address 0x009ffcf0 ./bin/kind       

./bin/kind:     file format elf64-x86-64

Contents of section .data:
 9ffce0 5a567e00 00000000 e6020000 00000000  ZV~.............

与前面获取 Go 版本信息时相同,前 8 个字节是指针,后 8 个字节是其大小。也就是说从 0x007e565a 开始,大小为 0x000002e6 ,所以我们可以拿到以下内容:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
➜  kind git:(master) ✗ objdump -s --start-address  0x007e565a --stop-address 0x7e5940 ./bin/kind

./bin/kind:     file format elf64-x86-64

Contents of section .rodata:
 7e565a 3077 af0c9274 080241e1 c107e6d6 18e6 0w...t..A.......
 7e566a 7061 74680973 6967732e 6b38732e 696f path.sigs.k8s.io
 7e567a 2f6b 696e640a 6d6f6409 73696773 2e6b /kind.mod.sigs.k
 7e568a 3873 2e696f2f 6b696e64 09286465 7665 8s.io/kind.(deve
 7e569a 6c29 090a6465 70096769 74687562 2e63 l)..dep.github.c
 7e56aa 6f6d 2f427572 6e745375 7368692f 746f om/BurntSushi/to
 7e56ba 6d6c 0976302e 332e3109 0a646570 0967 ml.v0.3.1..dep.g
 7e56ca 6974 6875622e 636f6d2f 616c6573 7369 ithub.com/alessi
 7e56da 6f2f 7368656c 6c657363 61706509 7631 o/shellescape.v1
 7e56ea 2e34 2e31090a 64657009 67697468 7562 .4.1..dep.github
 7e56fa 2e63 6f6d2f65 76616e70 68782f6a 736f .com/evanphx/jso
 7e570a 6e2d 70617463 682f7635 0976352e 322e n-patch/v5.v5.2.
 7e571a 3009 0a646570 09676974 6875622e 636f 0..dep.github.co
 7e572a 6d2f 6d617474 6e2f676f 2d697361 7474 m/mattn/go-isatt
 7e573a 7909 76302e30 2e313209 0a646570 0967 y.v0.0.12..dep.g
 7e574a 6974 6875622e 636f6d2f 70656c6c 6574 ithub.com/pellet
 7e575a 6965 722f676f 2d746f6d 6c097631 2e38 ier/go-toml.v1.8
 7e576a 2e31 0968313a 314e6638 336f7270 726b .1.h1:1Nf83orprk
 7e577a 4a79 6b6e5436 68377a62 75454755 456a JyknT6h7zbuEGUEj
 7e578a 6379 566c4378 53554754 454e6d4e 4352 cyVlCxSUGTENmNCR
 7e579a 4d3d 0a646570 09676974 6875622e 636f M=.dep.github.co
 7e57aa 6d2f 706b672f 6572726f 72730976 302e m/pkg/errors.v0.
 7e57ba 392e 31090a64 65700967 69746875 622e 9.1..dep.github.
 7e57ca 636f 6d2f7370 6631332f 636f6272 6109 com/spf13/cobra.
 7e57da 7631 2e312e31 090a6465 70096769 7468 v1.1.1..dep.gith
 7e57ea 7562 2e636f6d 2f737066 31332f70 666c ub.com/spf13/pfl
 7e57fa 6167 0976312e 302e3509 0a646570 0967 ag.v1.0.5..dep.g
 7e580a 6f6c 616e672e 6f72672f 782f7379 7309 olang.org/x/sys.
 7e581a 7630 2e302e30 2d323032 31303132 3431 v0.0.0-202101241
 7e582a 3534 3534382d 32326461 36326531 3263 54548-22da62e12c
 7e583a 3063 0968313a 56777967 55726e77 396a 0c.h1:VwygUrnw9j
 7e584a 6e38 38633475 38474433 725a5162 7172 n88c4u8GD3rZQbqr
 7e585a 502f 74676173 38387450 55624278 5172 P/tgas88tPUbBxQr
 7e586a 6b3d 0a646570 09676f70 6b672e69 6e2f k=.dep.gopkg.in/
 7e587a 7961 6d6c2e76 32097632 2e322e38 090a yaml.v2.v2.2.8..
 7e588a 6465 7009676f 706b672e 696e2f79 616d dep.gopkg.in/yam
 7e589a 6c2e 76330976 332e302e 302d3230 3231 l.v3.v3.0.0-2021
 7e58aa 3031 30373139 32393232 2d343936 3534 0107192922-49654
 7e58ba 3561 36333037 62096831 3a683871 446f 5a6307b.h1:h8qDo
 7e58ca 7461 4550754a 4154724d 6d573034 4e43 taEPuJATrMmW04NC
 7e58da 7767 37763232 61484832 38777770 6175 wg7v22aHH28wwpau
 7e58ea 5568 4b394f6f 3d0a6465 70096b38 732e UhK9Oo=.dep.k8s.
 7e58fa 696f 2f617069 6d616368 696e6572 7909 io/apimachinery.
 7e590a 7630 2e32302e 32090a64 65700973 6967 v0.20.2..dep.sig
 7e591a 732e 6b38732e 696f2f79 616d6c09 7631 s.k8s.io/yaml.v1
 7e592a 2e32 2e30090a f9324331 86182072 0082 .2.0...2C1.. r..
 7e593a 4210 4116d8f2                        B.A...          

我们成功的拿到了其所依赖的 Modules 相关的信息,这与我们在文章开头执行 go version -m ./bin/kind 是可以匹配上的,只不过这里的内容相当于是做了序列化。

具体实现

在前面的内容中,关于如何使用 readelf 和 objdump 命令获取二进制文件的的 Go 版本和 Module 信息就已经涉及到了其具体的原理。这里我来介绍下 Go 代码的实现。

节头的名称是硬编码在代码中的

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//src/cmd/go/internal/version/exe.go#L106-L110
 for _, s := range x.f.Sections {
  if s.Name == ".go.buildinfo" {
   return s.Addr
  }
 }

同时,魔术字节也是通过如下定义:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var buildInfoMagic = []byte("\xff Go buildinf:")

获取 Version 和 Module 相关信息的逻辑如下,在前面的内容中也已经基本介绍过了,这里需要注意的也就是字节序相关的部分了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 ptrSize := int(data[14])
 bigEndian := data[15] != 0
 var bo binary.ByteOrder
 if bigEndian {
  bo = binary.BigEndian
 } else {
  bo = binary.LittleEndian
 }
 var readPtr func([]byte) uint64
 if ptrSize == 4 {
  readPtr = func(b []byte) uint64 { return uint64(bo.Uint32(b)) }
 } else {
  readPtr = bo.Uint64
 }
 vers = readString(x, ptrSize, readPtr, readPtr(data[16:]))
 if vers == "" {
  return
 }
 mod = readString(x, ptrSize, readPtr, readPtr(data[16+ptrSize:]))
 if len(mod) >= 33 && mod[len(mod)-17] == '\n' {
  // Strip module framing.
  mod = mod[16 : len(mod)-16]
 } else {
  mod = ""
 }

总结

我在这篇文章中分享了如何从 Go 的二进制文件中获取构建它时所用的 Go 版本及它依赖的模块信息。如果对原理不感兴趣的话,直接通过 go version -m 二进制文件 即可获取相关的信息。

具体实现还是依赖于 ELF 文件格式中的相关信息,同时也介绍了 readelf 和 objdump 工具的基本使用,ELF 格式除了本文介绍的这种场景外,还有很多有趣的场景可用,比如为了安全进行逆向之类的。

另外,你可能会好奇从 Go 的二进制文件获取这些信息有什么作用。最直接的来说,可以用于安全漏洞扫描,比如检查其依赖项是否有安全漏洞;或是可以对依赖进行分析(主要指:接触不到源代码的场景下)会比较有用。


参考资料

[1]KIND 项目地址: https://github.com/kubernetes-sigs/kind

[2]ELF Magic 内核源码: https://github.com/torvalds/linux/blob/2c85ebc57b3e1817b6ce1a6b703928e113a90442/include/uapi/linux/elf.h#L340-L343

[3]ELF Class 内核定义: https://github.com/torvalds/linux/blob/2c85ebc57b3e1817b6ce1a6b703928e113a90442/include/uapi/linux/elf.h#L347-L350

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-06-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 MoeLove 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
OpenHarmony 内核源码分析(ELF格式篇) | 应用程序入口并不是main
先说明,本篇很长,也很枯燥,若不是绝对的技术偏执狂是看不下去的.将通过一段简单代码去跟踪编译成ELF格式后的内容.看看ELF究竟长了怎样的一副花花肠子,用readelf命令去窥视ELF的全貌,最后用objdump命令反汇编ELF.找到了大家熟悉main函数.
小帅聊鸿蒙
2025/03/23
1620
OpenHarmony 内核源码分析(ELF格式篇) | 应用程序入口并不是main
扒一扒ELF文件
  在介绍ELF文件之前,我们先看下,一个.c程序是如何变成可执行目标文件的。下面举个例子。
嵌入式与Linux那些事
2021/04/20
8590
ELF文件格式简介
  可执行与可链接格式 (Executable and Linkable Format,ELF),常被称为 ELF格式,是一种用于可执行文件、目标代码、共享库和核心转储(core dump)的标准文件格式,一般用于类Unix系统,比如Linux,Macox等。ELF 格式灵活性高、可扩展,并且跨平台。比如它支持不同的字节序和地址范围,所以它不会不兼容某一特别的 CPU 或指令架构。这也使得 ELF 格式能够被运行于众多不同平台的各种操作系统所广泛采纳。   ELF文件一般由三种类型的文件:
全栈程序员站长
2022/11/17
2.4K0
ELF文件格式简介
[操作系统] ELF文件从形成到加载轮廓
编译和链接这两个步骤,在Windows下被IDE封装的很完美,我们一般是使用一键编译并运行,但是当链接出错的话我们就束手无措了。在Linux下有gcc/g++编译器,可以直接展示出编译链接的过程。
DevKevin
2025/03/08
3900
[操作系统] ELF文件从形成到加载轮廓
万字连载(上):如何 Bringup SoC 芯片
我一直认为战略上蔑视技术,战术上重视技术是很有必要的学习态度。这是一篇 Bringup SoC 芯片的指导手册,更是一篇了解整个系统流程的地图。不会深入了解每个模块的细节,但提供了整个系统的宏观描述,让你站在上帝视角俯视每个知识点,为了对读者更负责,我打算以付费的方式和大家见面,对技术细节有需求的小伙伴欢迎加我微信(rrjike)交流,保证超有所值。 系统在启动的时候,无论是 ROM 加载 Uboot(SPL + Bootloader),还是 Uboot 加载 Kernel,都是把相关的镜像放到对应的内存不
刘盼
2022/05/09
1.9K0
万字连载(上):如何 Bringup SoC 芯片
认识目标文件结构
目标文件是源代码编译但未链接的中间文件(Windows的.obj和Linux的.o),Windows的.obj采用 PE 格式,Linux 采用 ELF 格式,两种格式均是基于通用目标文件格式(COFF,Common Object File Format)变化而来,所以二者大致相同。本文以 Linux 的 ELF 格式的目标文件为例,进行介绍。
恋喵大鲤鱼
2019/06/22
1.3K0
编译、链接到载入、运行的大致过程----2.链接
编译完成之后,需要的步骤就是 链接.编译仅仅转换源代码到二进制的机器码,但是并没有把程序运行需要的所有资源整合到一起,所以编译后的"目标文件"是没办法直接运行的;在实际的项目中,通常是由多个源代码文件,每个源代码文件都可以进行编译后生成"目标文件“. 这些目标文件 和需要的其他资源被整合到一起,最终才生成我们常见的程序(典型的比如windows下的各种exe文件,linux 下的elf LSB executable 文件,linux 下的elf LSB shared object 等). 这个整合的过程就是“链接”.
qsjs
2020/06/08
8930
【linux命令讲解大全】054.readelf:展示ELF格式文件信息的工具
readelf命令用来显示一个或者多个elf格式的目标文件的信息,可以通过它的选项来控制显示哪些信息。这里的elf-file(s)就表示那些被检查的文件。可以支持32位,64位的elf格式文件,也支持包含elf文件的文档(这里一般指的是使用ar命令将一些elf文件打包之后生成的例如lib*.a之类的“静态库”文件)。
全栈若城
2024/03/02
8300
详解共享库的动态加载
这边文章不是一个如何引导,尽管它确实展示了如何编译和调试共享库和可执行文件。为了解动态加载的内部工作方式进行了优化。写这篇文章是为了消除我在该主题上的知识欠缺,以便成为一名更好的程序员。我希望它也能帮助您变得更好。
后场技术
2021/05/18
3.5K0
详解共享库的动态加载
ELF文件分析
e_ident : 十六个字节,可通过这个字段对ELF文件进行识别,其中包括五个部分:
mingjie
2022/05/12
1.4K0
ELF文件分析
深入浅出编译链接
本章对编译、链接相关基础知识进行回顾,温故而知新,可以为师矣。下面是两段示例代码:
麦克马
2025/05/07
1870
深入浅出编译链接
程序一定要从main函数开始运行吗?
每个目标文件都有好多个段,目标文件在被链接成可执行文件时,输入目标文件中的各个段如何被合并到输出文件?
程序员小猿
2021/01/19
1.4K0
程序一定要从main函数开始运行吗?
万字图文 | 你写的代码是如何跑起来的?
我们在写完代码后,进行简单的编译,然后在 shell 命令行下就可以把它启动起来。
开发内功修炼
2022/12/07
6590
万字图文 | 你写的代码是如何跑起来的?
ELF文件格式的详解
ELF的英文全称是The Executable and Linking Format,最初是由UNIX系统实验室开发、发布的ABI(Application Binary Interface)接口的一部分,也是Linux的主要可执行文件格式。
bigmagic
2020/10/10
6.7K2
ELF文件格式的详解
elf格式分析
近期研究了一下elf文件格式,发现好多资料写的都比較繁琐,可能会严重打击学习者的热情,我把自己研究的结果和大家分享,希望我的描写叙述可以简洁一些。
全栈程序员站长
2022/07/13
8910
elf格式分析
鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程
本篇将通过一个完整的小工程来阐述ELF编译,链接过程,并分析.o和bin文件中各区,符号表之间的关系.从一个崭新的视角去看中间过程.
小帅聊鸿蒙
2025/03/24
980
鸿蒙内核源码分析(静态链接篇) | 完整小项目看透静态链接过程
Linux 程序编译过程的来龙去脉
大家肯定都知道计算机程序设计语言通常分为机器语言、汇编语言和高级语言三类。高级语言需要通过翻译成机器语言才能执行,而翻译的方式分为两种,一种是编译型,另一种是解释型,因此我们基本上将高级语言分为两大类,一种是编译型语言,例如C,C++,Java,另一种是解释型语言,例如Python、Ruby、MATLAB 、JavaScript。
刘盼
2018/09/25
3.1K0
Linux 程序编译过程的来龙去脉
深入了解GOT,PLT和动态链接
之前几篇介绍exploit的文章, 有提到return-to-plt的技术. 当时只简单介绍了 GOT和PLT表的基本作用和他们之间的关系, 所以今天就来详细分析下其具体的工作过程.
evilpan
2023/02/12
1.7K0
深入了解GOT,PLT和动态链接
OpenHarmony 内核源码分析(编译过程篇) | 简单案例窥视编译全过程
编译过程要经过:源文件 --> 预处理 --> 编译(cc1) --> 汇编器(as) --> 链接器(ld) --> 可执行文件(PE/ELF)
小帅聊鸿蒙
2025/03/25
1240
OpenHarmony 内核源码分析(编译过程篇) | 简单案例窥视编译全过程
hook的几种方式及原理学习
对于大型的工程项目,依赖许多人的配合,包含大量不同的代码库与服务,有的我们能够访问程序的源代码,有的可以访问程序的可重定位文件,有的可以访问到可执行文件及其环境,假如我们想在在不同的层面改变或者添加一些逻辑,操作系统、编译器以及程序语言、代码库等都提供了 一些机制使得 开发者可以 方便的 增加或替换代码逻辑,对于逻辑调试、测试、性能分析、版本兼容等都有比较好的效果。
changan
2020/11/04
2.1K0
hook的几种方式及原理学习
相关推荐
OpenHarmony 内核源码分析(ELF格式篇) | 应用程序入口并不是main
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验