前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >程序一定要从main函数开始运行吗?

程序一定要从main函数开始运行吗?

作者头像
程序员小猿
发布于 2021-01-19 06:48:30
发布于 2021-01-19 06:48:30
1.3K00
代码可运行
举报
文章被收录于专栏:程序IT圈程序IT圈
运行总次数:0
代码可运行

对于静态链接先提出两个问题:

Q:

每个目标文件都有好多个段,目标文件在被链接成可执行文件时,输入目标文件中的各个段如何被合并到输出文件?

A:

合并相似的段,将所有的.text段合并到输出文件的.text段,将所有的.data段合并到输出文件的.data段。

Q:

链接器如何为他们分配在输出文件中的空间和地址?

A:

这里涉及到程序链接的两个步骤:

  1. 空间与地址分配:扫描所有的输入目标文件,获得它们每个段的长度属性和位置,收集输入目标文件中的符号表中的所有符号定义和符号引用,统一放到一个全局符号表中,合并所有的段,计算出输出文件中各个段合并后的长度和位置,并建立映射关系。
  2. 符号解析与重定位:使用第一步收集到的所有信息,读取输入文件中段的数据及重定位信息,进行符号解析和重定位,调整代码中的地址,将每个段中需要重定位的指令和数据进行“修补”,使他们都指向正确的位置。

Tips:

外部符号指的是目标文件需要引用的符号,但是定义在其它目标文件中,链接前外部符号地址都是000000之类,链接后的可执行文件就可以看见这些外部符号都是有地址的。链接就是把相似的段放在一起,先找到段的偏移地址,再找出符号在段中的偏移,这样可以确定符号在整个可执行程序中的地址。

对于那些需要重定位的符号,都会放在重定位表里,也叫重定位段,即.rel.data、.rel.text等,如果.text段有被重定位的地方,就有.rel.text段,如果.data段有被重定位的地方,就有.rel.data段。

可以使用objdump查看目标文件的重定位表。

源代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int main() {
    printf("程序喵\n");
    return 0;
}
gcc -c test
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
objdump -r test.o

test.o:     file format elf64-x86-64

RELOCATION RECORDS FOR [.text]:
OFFSET           TYPE              VALUE
0000000000000007 R_X86_64_PC32     .rodata-0x0000000000000004
000000000000000c R_X86_64_PLT32    puts-0x0000000000000004


RELOCATION RECORDS FOR [.eh_frame]:
OFFSET           TYPE              VALUE
0000000000000020 R_X86_64_PC32     .text

使用nm也可以查看需要重定位的符号:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
nm -u test.o
                 U _GLOBAL_OFFSET_TABLE_
                 U puts

对于UND类型,这种未定义的符号都是因为该目标文件中有关于他们的重定位项,在链接器扫描完所有的输入目标文件后,所有这种未定义的符号都应该能在全局符号表中找到,否则报符号未定义错误。 注意:我们代码里明明用的是printf,为什么它却引用了puts的符号呢,因为编译器默认情况下会把只用一个字符串参数的printf替换成puts, 可以节省格式解析的时间,使用-fno-builtin会关闭这个内置函数优化选项,如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
~/test$ gcc -c -fno-builtin testlink.cc -o test.o
~/test$ nm test.o
                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
                 U printf

Tips:

现在的程序和库通常来讲都很大,一个目标文件可能包含成百上千个函数或变量,当需要用到某个目标文件的任意一个函数或变量时,就需要把它整个目标文件都链接进来,也就是说那些没有用到的函数也会被链接进去,这会导致链接输出文件变的很大,造成空间浪费。

有一个编译选项叫函数级别链接,可以使得某个函数或变量单独保存在一个段里面,都链接器需要用到某个函数时,就将它合并到输出文件中,对于没用到的函数则将他们抛弃,减少空间浪费,但这会减慢编译和链接过程,GCC编译器的编译选项是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
-ffunction-sections
-fdata-sections

可能很多人都会以为程序都是由main函数开始执行和结束的,但其实不是,在main函数调用之前,为了保证程序可以顺利进行,要先初始化进程执行环境,如堆分配初始化、线程子系统等,C++的全局对象构造函数也是这一时期被执行的,全局析构函数是main之后执行的。 Linux一般程序的入口是__start函数,程序有两个相关的段: init段:进程的初始化代码,一个程序开始运行时,在main函数调用之前,会先运行.init段中的代码。 fini段:进程终止代码,当main函数正常退出后,glibc会安排执行该段代码。 如何指定程序入口 在ld链接过程中使用-e参数可以指定程序入口,由于一段简短的printf函数其实都依赖了好多个链接库,我们也不太方便使用链接脚本将目标文件与所有这些依赖库进行链接,所以使用下面这段内嵌汇编的程序来打印一段字符串,这段程序不依赖任何链接库就可以打印出字符串内容,读者如果不懂其中的含义也不用担心,只需要了解下面介绍的链接知识就好。

代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
const char* str = "hello";

void print() {
    asm("movl $13,%%edx \n\t"
        "movl str,%%ecx \n\t"
        "movl $0,%%ebx \n\t"
        "movl $4,%%eax \n\t"
        "int $0x80 \n\t"
        :
        :"r"(str):"edx", "ecx", "ebx");
}


void exit() {
    asm("movl $42,%ebx \n\t"
        "movl $1,%eax \n\t"
        "int $0x80 \n\t");
}

void nomain() {
    print();
    exit();
}

使用如下命令生成目标文件:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
gcc -c -fno-builtin test.cc

看下输出的test.o的符号:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
~/test$ nm -a test.o
0000000000000000 b .bss
0000000000000000 n .comment
0000000000000000 d .data
0000000000000000 d .data.rel.local
0000000000000000 r .eh_frame
0000000000000000 n .note.GNU-stack
0000000000000000 r .rodata
0000000000000000 t .text
0000000000000026 T _Z4exitv
0000000000000000 T _Z5printv
0000000000000039 T _Z6nomainv
0000000000000000 D str
0000000000000000 a test.cc

这里由于我的源文件是.cc结尾,所以是以c++方式编译的,所以符号变成了上面的形式,如果变成了test.c,符号如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
~/test$ gcc -c -fno-builtin test.c -o test.o
~/test$ nm -a test.o
0000000000000000 b .bss
0000000000000000 n .comment
0000000000000000 d .data
0000000000000000 d .data.rel.local
0000000000000000 r .eh_frame
0000000000000000 n .note.GNU-stack
0000000000000000 r .rodata
0000000000000000 t .text
0000000000000026 T exit
0000000000000039 T nomain
0000000000000000 T print
0000000000000000 D str
0000000000000000 a test.c

再使用-e指定入口函数符号:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
~/test$ ld -static -e nomain -o test test.o
~/test$ ./test
hello

如何使用自定义链接脚本实现自定义段的功能 在ld链接过程中使用-T参数可以指定链接脚本,通过ld -verbose可以查看默认的链接脚本,原文太长,这里简单截取了一部分:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
$ ld -verbose
GNU ld (GNU Binutils for Ubuntu) 2.30
  Supported emulations:
   elf_x86_64
   elf32_x86_64
   elf_i386
   elf_iamcu
   i386linux
   elf_l1om
   elf_k1om
   i386pep
   i386pe
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
/* Copyright (C) 2014-2018 Free Software Foundation, Inc.
   Copying and distribution of this script, with or without modification,
   are permitted in any medium without royalty provided the copyright
   notice and this notice are preserved.  */
OUTPUT_FORMAT("elf64-x86-64", "elf64-x86-64",
              "elf64-x86-64")
OUTPUT_ARCH(i386:x86-64)
ENTRY(_start)
SEARCH_DIR("=/usr/local/lib/x86_64-linux-gnu"); SEARCH_DIR("=/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu"); SEARCH_DIR("=/usr/lib/x86_64-linux-gnu64"); SEARCH_DIR("=/usr/local/lib64"); SEARCH_DIR("=/lib64"); SEARCH_DIR("=/usr/lib64"); SEARCH_DIR("=/usr/local/lib"); SEARCH_DIR("=/lib"); SEARCH_DIR("=/usr/lib"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib64"); SEARCH_DIR("=/usr/x86_64-linux-gnu/lib");
SECTIONS
{
  /* Read-only sections, merged into text segment: */
  PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); . = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;

  .init           :
  {
    KEEP (*(SORT_NONE(.init)))
  }
  .plt            : { *(.plt) *(.iplt) }
  .plt.got        : { *(.plt.got) }
  .plt.sec        : { *(.plt.sec) }
  .text           :
  {
    *(.text.unlikely .text.*_unlikely .text.unlikely.*)
    *(.text.exit .text.exit.*)
    *(.text.startup .text.startup.*)
    *(.text.hot .text.hot.*)
    *(.text .stub .text.* .gnu.linkonce.t.*)
    /* .gnu.warning sections are handled specially by elf32.em.  */
    *(.gnu.warning)
  }
  .fini           :
  {
    KEEP (*(SORT_NONE(.fini)))
  }
  .rodata         : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}

这里自定义一个简单的链接脚本test.lds

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ENTRY(nomain)

SECTIONS
{
    . = 0x8048000 + SIZEOF_HEADERS;
    tinytext : { *(.text) *(.data) *(.rodata) }
    /DISCARD/ : { *(.comment) }
}

再使用-T指定链接脚本:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
~/test$ ld -static -T test.lds -e nomain -o test test.o
~/test$ ./test
hello

上面的tinytext一行是指将.text段、.data段、.rodata段的内容都合并到tinytext段中,使用readelf查看段的信息。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
~/test$ readelf -S test
~/test$ There are 6 section headers, starting at offset 0x482a0:

Section Headers:
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .eh_frame         PROGBITS         00000000080480b0  000480b0
       0000000000000078  0000000000000000   A       0     0     8
  [ 2] tinytext          PROGBITS         0000000008048128  00048128
       0000000000000066  0000000000000000 WAX       0     0     8
  [ 3] .shstrtab         STRTAB           0000000000000000  0004826e
       000000000000002e  0000000000000000           0     0     1
  [ 4] .symtab           SYMTAB           0000000000000000  00048190
       00000000000000c0  0000000000000018           5     4     8
  [ 5] .strtab           STRTAB           0000000000000000  00048250
       000000000000001e  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), l (large)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

工具小贴士

关于静态链接库:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ar rcs libxxx.a xx1.o xx2.o 打包静态链接库
ar -t libc.a 查看静态链接库里都有什么目标文件
ar -x libc.a 会解压所有的目标文件到当前目录
gcc --verbose 可以查看整个编译链接步骤

关于objdump:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
objdump -i 查看本机目标架构
objdump -f 显示文件头信息
objdump -d 反汇编程序
objdump -t 显示符号表入口,每个目标文件都有什么符号
objdump -r 显示文件的重定位入口,重定位表
objdump -x 显示所有可用的头信息,等于-a -f -h -r -t
objdump -H 帮助

关于分析ELF文件格式:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
readelf -h 列出文件头
readelf -S 列出每个段
readelf -r 列出重定位表
readelf -d 列出动态段

关于查看目标文件符号信息:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
nm -a 显示所有的符号
nm -D 显示动态符号
nm -u 仅显示没有定义的外部符号
nm -defined-only 仅显示定义的符号

关于符号的说明: 如果符号类型是小写的,表明符号是局部符号,大写表示符号是全局符号。

A:该符号的值是绝对的,在以后的链接过程中,不允许进行改变。这样的符号值,常常出现在中断向量表中,例如用符号来表示各个中断向量函数在中断向量表中的位置。 B:该符号的值出现在.bss段中,未初始化的全局和静态变量。 C:该符号的值在COMMON段中,里面的都是弱符号。 D:该符号位于数据段中。 I:该符号对另一个符号的间接引用 N:debug符号 R:该符号位于只读数据区 T:该符号位于代码段 U:该符号在当前文件未定义,定义在别的文件中 ?:该符号类型没有定义 参考资料

https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/

《程序员的自我修养》

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

本文分享自 程序员小猿 微信公众号,前往查看

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
认识目标文件结构
目标文件是源代码编译但未链接的中间文件(Windows的.obj和Linux的.o),Windows的.obj采用 PE 格式,Linux 采用 ELF 格式,两种格式均是基于通用目标文件格式(COFF,Common Object File Format)变化而来,所以二者大致相同。本文以 Linux 的 ELF 格式的目标文件为例,进行介绍。
恋喵大鲤鱼
2019/06/22
1.3K0
[操作系统] ELF文件从形成到加载轮廓
编译和链接这两个步骤,在Windows下被IDE封装的很完美,我们一般是使用一键编译并运行,但是当链接出错的话我们就束手无措了。在Linux下有gcc/g++编译器,可以直接展示出编译链接的过程。
DevKevin
2025/03/08
1950
[操作系统] ELF文件从形成到加载轮廓
程序的编译、链接、装载与运行
在Linux操作系统中,一段C程序从被写下到最终被CPU执行,要经过一段漫长而又复杂的过程。下图展示了这个过程
shengjk1
2019/12/03
1.4K0
OpenHarmony 内核源码分析(ELF格式篇) | 应用程序入口并不是main
先说明,本篇很长,也很枯燥,若不是绝对的技术偏执狂是看不下去的.将通过一段简单代码去跟踪编译成ELF格式后的内容.看看ELF究竟长了怎样的一副花花肠子,用readelf命令去窥视ELF的全貌,最后用objdump命令反汇编ELF.找到了大家熟悉main函数.
小帅聊鸿蒙
2025/03/23
860
OpenHarmony 内核源码分析(ELF格式篇) | 应用程序入口并不是main
叙述 C语言编译
工作原因有时候会用python写写测试工具,感受到其快速实现应用的便利,但由于偏底层开发,主力语言依然是C。对于开发语言没有什么优劣概念,在特定的情景下哪种实现更佳就用哪种,工具合适才是最好的。
orientlu
2018/09/13
1.8K0
叙述 C语言编译
扒一扒ELF文件
  在介绍ELF文件之前,我们先看下,一个.c程序是如何变成可执行目标文件的。下面举个例子。
嵌入式与Linux那些事
2021/04/20
8050
Linux 程序编译过程的来龙去脉
大家肯定都知道计算机程序设计语言通常分为机器语言、汇编语言和高级语言三类。高级语言需要通过翻译成机器语言才能执行,而翻译的方式分为两种,一种是编译型,另一种是解释型,因此我们基本上将高级语言分为两大类,一种是编译型语言,例如C,C++,Java,另一种是解释型语言,例如Python、Ruby、MATLAB 、JavaScript。
刘盼
2018/09/25
3K0
Linux 程序编译过程的来龙去脉
OpenHarmony 内核源码分析(ELF解析篇) | 内核加载
ELF,它实在是太重要了,内核加载的就是它,不说清楚它怎么去说清楚应用程序运行的过程呢.看到下面这一坨一坨的,除了.text,.bss,.data听过见过外,其他的咱也没啥交情。
小帅聊鸿蒙
2025/03/23
760
Linux命令(63)——nm令
nm命令是GNU Binutils二进制工具集的一员,用于显示目标文件中的符号。如果没有为nm命令指出目标文件,则nm假定目标文件是a.out。
恋喵大鲤鱼
2019/04/18
5.3K0
main函数真的是C程序的开始吗?
我们在学习和编写C程序时,都是从main函数开始,main函数作为入口函数已经深深地印在我们的脑海中,那么main函数真的是C程序的入口函数吗?带着这个问题我们先来看下面一段代码。 1. 实验程序 示例代码 #include <stdlib.h> #include <stdio.h> static void __attribute__ ((constructor)) beforeMain(void) { printf("Before main...\n"); } int main(void)
Linux兵工厂
2023/02/28
5640
main函数真的是C程序的开始吗?
【linux命令讲解大全】054.readelf:展示ELF格式文件信息的工具
readelf命令用来显示一个或者多个elf格式的目标文件的信息,可以通过它的选项来控制显示哪些信息。这里的elf-file(s)就表示那些被检查的文件。可以支持32位,64位的elf格式文件,也支持包含elf文件的文档(这里一般指的是使用ar命令将一些elf文件打包之后生成的例如lib*.a之类的“静态库”文件)。
全栈若城
2024/03/02
7410
OpenHarmony 内核源码分析(重定位篇) | 与国际接轨
重定位就是把程序的逻辑地址空间变换成内存中的实际物理地址空间的过程。它是实现多道程序在内存中同时运行的基础。重定位有两种,分别是动态重定位与静态重定位。
小帅聊鸿蒙
2025/03/24
730
OpenHarmony 内核源码分析(重定位篇) | 与国际接轨
ELF文件格式简介
  可执行与可链接格式 (Executable and Linkable Format,ELF),常被称为 ELF格式,是一种用于可执行文件、目标代码、共享库和核心转储(core dump)的标准文件格式,一般用于类Unix系统,比如Linux,Macox等。ELF 格式灵活性高、可扩展,并且跨平台。比如它支持不同的字节序和地址范围,所以它不会不兼容某一特别的 CPU 或指令架构。这也使得 ELF 格式能够被运行于众多不同平台的各种操作系统所广泛采纳。   ELF文件一般由三种类型的文件:
全栈程序员站长
2022/11/17
2.3K0
ELF文件格式简介
hook的几种方式及原理学习
对于大型的工程项目,依赖许多人的配合,包含大量不同的代码库与服务,有的我们能够访问程序的源代码,有的可以访问程序的可重定位文件,有的可以访问到可执行文件及其环境,假如我们想在在不同的层面改变或者添加一些逻辑,操作系统、编译器以及程序语言、代码库等都提供了 一些机制使得 开发者可以 方便的 增加或替换代码逻辑,对于逻辑调试、测试、性能分析、版本兼容等都有比较好的效果。
changan
2020/11/04
2K0
hook的几种方式及原理学习
RT-thread finsh移植到linux平台
在一次项目中, 需要进行嵌入式操作系统选型, 需求就是选择一款OS,既能满足当下项目的需要,又要考虑公司未来对物联网应用的扩展能力,对比了目前市面上流行的开源操作系统,诸如FreeRTOS,RTX,UCOS,RT-Thread,contiki等, 最终确定了一款IoT OS:RT-Thread(遵循 Apache License 2.0 开源许可协议)。事实证明,这款操作系统也为公司物联网产品设计提供了很大便利,这里介绍其中一个我认为非常有用的组件FinSH,也正是深刻体会到了FinSH在程序应用开发中的便利, 使我下定决心将其移植到Linux平台,为我在linux平台的项目添上一个炫酷的操作接口,在此对整个过程进行总结。
全栈程序员站长
2022/08/31
3.3K0
RT-thread finsh移植到linux平台
含大量图文解析及例程 | Linux下的ELF文件、链接、加载与库(上)
常用工具 我们首先列出一些在接下来的介绍过程中会频繁使用的分析工具,如果从事操作系统相关的较底层的工作,那这些工具应该再熟悉不过了。不熟悉的读者可以先看一下这里的简单的功能介绍,我们会在后文中介绍一些详细的参数选项和使用场景。 另外,建议大家在遇到自己不熟悉的命令时,通过 man 命令来查看手册,这是最权威的、第一手的资料。 ELF文件详解 ELF文件的三种形式 在Linux下,可执行文件/动态库文件/目标文件(可重定向文件)都是同一种文件格式,我们把它称之为ELF文件格式。虽然它们三个都是ELF文件格式
刘盼
2022/07/27
3.8K0
含大量图文解析及例程 | Linux下的ELF文件、链接、加载与库(上)
ELF文件格式
1999年86open项目选择ELF作为x86处理器上Unix和类Unix系统的标准二进制文件格式。使用ELF的原因包括:灵活性、可扩展性、对不同字节序格式支持、跨平台支持地址size。
mingjie
2022/05/12
1.6K0
ELF文件格式
嵌入式链接脚本(LINK SCRIPT)介绍
每一个链接过程都由链接脚本(linkerscript, 一般以lds作为文件的后缀名)控制. 链接脚本主要用于规定如何把输入文件内的section放入输出文件内, 并控制输出文件内各部分在程序地址空间内的布局. 但你也可以用连接命令做一些其他事情. 连接器有个默认的内置连接脚本, 可用ld--verbose查看. 连接选项-r和-N可以影响默认的连接脚本(如何影响). -T选项用以指定自己的链接脚本, 它将代替默认的连接脚本。你也可以使用<暗含的连接脚本>以增加自定义的链接命令. 以下没有特殊说明,连接器指的是静态连接器.
心跳包
2020/08/31
2.5K0
用GCC开发STM32入门二
之前从网上下载了一份用GCC开发stm32的程序,也是用的stm32的库函数编程,启动文件是startup_stm32f10x_hd.s,链接脚本文件是从gcc_ride7中拷贝出的stm32f10x_flash_extsram.ld,做了些简单修改。但是编译了一下,出现了一大堆的错误。于是干脆不用这些文件,从网上查资料,自己写启动文件和链接脚本。仔细看了下startup_stm32f10x_hd.s,这个文件,发现也很简单,无非是定义了一些中断向量表和完成数据段的搬移和.bss段的清零等工作,并把程序跳转到main()函数。然后链接脚本文件告知链接器,把所有目标文件相应的段连接到一起,并把目标文件中的“变量地址”“函数地址”重定位至正确的地址空间; 编写前需要知道C程序编译后的典型内存布局 ,单片机的启动流程以及链接脚本文件的作用和编写等知识。部分知识,摘自网络。
杨永贞
2020/08/04
1.9K0
用GCC开发STM32入门二
《程序员的自我修养》第三章学习笔记
1, 编译器编译源代码生成的文件叫做目标文件。 从结构上说,是编译后的可执行文件,只不过还没有经过链接 3.1 目标文件的格式 1,可执行文件的格式: Windows下的PE  和   Linux下的ELF 2,从广义上说,目标文件与可执行文件的格式几乎是一样的,所以广义上可以将目标文件与可执行文件看成是一种类型的文件。 3,可执行文件,动态链接库,静态链接库都按照可执行文件格式存储(Windows下是 PE-COFF格式,Linux下是ELF格式)。 4,Linux下命令: $: file   ***  
xcywt
2018/01/11
1.2K0
《程序员的自我修养》第三章学习笔记
相关推荐
认识目标文件结构
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档