linux的x64与x86_在Linux x86 64机器上链接

linux的x64与x86

Linking is the process of combining various pieces of code and files in order to construct a single file that can be loaded into memory and then executed.Linking can be performed at compile-time, when source code is being translated to machine code, or at load-time when the program is getting copied into the memory, or at run-time while the program is running, by the program itself.Linking plays a crucial job in software development because it enables separate compilation, which in turn enables structuring large projects in various small modules instead of one monolithic source file. In addition to begin organized in smaller more-manageable modules, this also accelerates compile-time because those modules can be compiled once and linked as many times as needed, and if one module gets an edit, we only need to recompile this particular file and then re-link the project.

链接是组合各种代码和文件以构建可以加载到内存中然后执行的单个文件的过程。链接可以在编译时,将源代码转换为机器代码时执行,也可以在链接时执行。链接程序在软件开发中起着至关重要的作用,因为它可以进行单独的编译 ,进而可以构造大型项目,这在软件开发中起着至关重要的作用。放在各种小模块中,而不是一个整体的源文件。 除了开始组织成较小的可管理性较小的模块外,这还加快了编译时间,因为这些模块可以编译一次并根据需要进行多次链接,并且如果一个模块得到编辑,我们只需要重新编译该特定文件并然后重新链接项目。

Linking can be useful to learn for a couple of reasons, despite being not very ostensible at the beginning.

尽管在开始时并不太容易理解,但出于几个原因,链接对于学习还是有用的。

  • While making large projects, it’s likely that you’ll face a linker error that states that the program is missing a module or library function, those can be somewhat not understandable and if you’re not familiar with linking and what are the techniques that the linker uses to resolve those symbols, those might be very perplexing and confounding.

    在制作大型项目时,您可能会遇到一个链接器错误,指出程序缺少模块或库函数,这些可能有些难以理解,并且如果您不熟悉链接以及该链接器有哪些技术?链接程序用来解析这些符号,这些符号可能非常令人困惑和困惑。
  • Understanding linking will let you avoid very nasty errors. Often while using multiple global variables in different modules, linkers will have to decide on which of those to resolve a reference to, this can horribly affect your program silently without any warnings. If you’re not familiar with the rules that linkers use to make such decisions, your programs can be vulnerable to those nasty errors and baffling run-times behaviors.

    了解链接将使您避免非常讨厌的错误。 通常,在不同模块中使用多个全局变量时,链接器将不得不决定要解析哪个全局变量,这会在无任何警告的情况下以无提示的方式严重影响您的程序。 如果您不熟悉链接程序用来制定此类决定的规则,则您的程序可能容易受到那些讨厌的错误和令人困惑的运行时行为的攻击。

  • Linking is very related to scoping rules. Learning linking will make you really understand how those scoping rules are exactly implemented, e.g., what is the difference between locals and globals, what does it mean to define a variable as a static or extern.

    链接与范围规则非常相关。 学习链接将使您真正了解那些作用域规则是如何正确实现的,例如,局部变量和全局变量之间的区别是什么,将变量定义为staticextern意味着什么。

编译过程 (Compilation process overveiw)

GNU compilation system provides us with a compiler driver (GCC) that is used to run a file (or a set of files) through a series of steps that essentially convert the source file from the ASCII format to an executable that can readily be run.these steps can be seen by running GCC with -v option.

GNU编译系统为我们提供了一个编译器驱动程序(GCC),该驱动程序用于通过一系列步骤来运行一个文件(或一组文件),这些步骤实际上将源文件从ASCII格式转换为易于运行的可执行文件。通过使用-v选项运行GCC可以看到这些步骤。

  • You’ll (optionally because, in some GCC versions, it’s integrated into the compiler) see it call the C preprocessor cpp with some arguments and file names.

    您会看到(可选,因为在某些GCC版本中,它已集成到编译器中),您会看到它使用一些参数和文件名调用C预处理程序cpp

  • Next, it will call the C compiler cc1 on the temp files generated by the preprocessor.

    接下来,它将对预处理器生成的临时文件调用C编译器cc1

  • Then it will call the assembler as that converts those assembly files into object code.

    然后,它会调用汇编as那些装配文件转换成目标代码。

  • Followed by the linker ld or an intermediary that is used as a driver for the linker.

    紧随其后的是链接程序ld或用作链接程序驱动程序的中介程序。

This whole process will result in an executable file that you can run in the shell using ./a.out. When the shell sees such a command, it invokes an operating system command called the loader, which is used to copy the executable to the memory and transfer the control to the beginning of its portion in the memory.

这整个过程将产生一个可执行文件,您可以使用./a.out在外壳中运行该文件。 当外壳程序看到这样的命令时,它会调用一个名为loader的操作系统命令,该命令用于将可执行文件复制到内存中,并将控件转移到内存中该部分的开头。

There are two main kinds of linking used in today’s systems, static linking, and dynamic linking. In this post I will be discussing static linking in general, and hopefully will tackle static library linking and dynamic library linking in subsequent ones.

在当今的系统中,主要使用两种链接: 静态链接动态链接。 在本文中,我将大体讨论静态链接,并希望在以后的文章中讨论静态库链接和动态库链接。

静态链接 (Static Linking)

The ld Linux program is a static linker, that takes in as inputs a bunch of relocatable object files, and produces a fully-linked executable file that can be subsequently loaded into memory and run.

ld Linux程序是一个静态链接器 ,它将一堆可重定位的目标文件作为输入,并生成一个完全链接的可执行文件,该文件随后可以加载到内存中并运行。

Those relocatable object files are the output of the compilation process until after going through the assembler. They consist of various sections that contain different things, e.g., a section that contains instructions, a section that contains initialized global variables, and a section that contains uninitialized global variables.

那些可重定位的目标文件是编译过程的输出,直到经过汇编器之后。 它们由包含不同内容的各个部分组成,例如,一个包含指令的部分,一个包含初始化的全局变量的部分以及一个包含未初始化的全局变量的部分。

Building the executable out of those object files requires two main phases.

从那些目标文件中构建可执行文件需要两个主要阶段。

The first of those is symbol resolution, in which the linker tries to identify all the symbols (which might be functions or global variables) referenced by the object files and try to resolve those symbol references to use exactly one symbol definition.

第一个是符号解析 ,其中链接器尝试标识目标文件引用的所有符号(可能是函数或全局变量),并尝试解析这些符号引用以仅使用一个符号定义。

The second of those is relocation. The output of the assembler is in the form of relocatable object files, this naming is because they start addresses from 0, and replace the value of each symbol that is not known at compile-time to 0, in the second phase of linking, namely, relocation, those object files are relocated by associating those 0’s to the actual address of the symbol that is resolved in the first phase.This relocation is done by the linker by the aid of relocation entries that the compiler and assembler generate, telling the linker that in the following instruction, there’s a symbol that needs relocation to the address of the correct symbol.

第二个是搬迁 。 汇编器的输出采用可重定位目标文件的形式,其命名是因为它们在链接的第二阶段中从0开始,并将编译时未知的每个符号的值替换为0,即,搬迁,这些目标文件是由那些0的关联到在第一phase.This搬迁解决由链接通过搬迁项目的帮助下,编译器和汇编完成的符号的实际地址,告诉链接器重新定位在以下说明中,有一个符号需要重定位到正确符号的地址。

目标文件 (Object Files)

There are three main types of object files, relocatable object files, executable object files, and shared object files.Relocatable object files are those output by the compiler and assembler.Executable object files are those processed by the linker and ready to be loaded to memory and get executed.Shared object files are a type of relocatable object files that can be dynamically linked at load-time or run-time.

目标文件主要有三种类型, 可重定位目标文件可执行目标文件共享目标文件。 可重定位目标文件是由编译器和汇编器输出的文件可执行目标文件是由链接器处理并准备好加载到内存的文件共享对象文件是一种可重定位的目标文件,可以在加载时或运行时动态链接

Different systems use different formats for object files, we will focus in this post on ELF-64(Executable and Linkable Format) which is used by modern Linux systems, although I’ve read that other formats are largely similar.

不同的系统对目标文件使用不同的格式 ,尽管我读过其他格式在很大程度上相似,但本文将重点介绍现代Linux系统使用的ELF-64(可执行和可链接格式)

ELF-64说明和部分 (ELF-64 Description and Sections)

The ELF-64 format consists of a number of sections that are merely contiguous bytes.The very first section is called the ELF header.It starts with some bits that describe the word size and byte ordering of the system (endianness). The rest of the ELF header has information about the type of the machine, e.g., x86–64, the offset of the section header table, and the object file type.

ELF-64格式由许多部分组成,这些部分只是连续的字节。第一部分称为ELF标头,它以一些位开始,这些位描述了系统的字长和字节顺序(字节序)。 ELF标头的其余部分包含有关计算机类型的信息,例如x86–64, 节标头表的偏移量和目标文件类型。

  • Section header table: Has the locations(offsets) of each of the subsequent sections.

    节头表 :具有每个后续节的位置(偏移)。

  • .text: Contains the machine code of the program.

    .text :包含程序的机器代码。

  • .rodata: Read-only data, such as those allocated in the following manner in C:char *str = “Hello World.;” and switch statement jump tables.

    .rodata :只读数据,例如以下列方式在C中分配的那些数据: char *str = “Hello World.;” 和switch语句跳转表。

  • .data: Contains initialized global and static variables.

    .data :包含初始化的全局和静态变量。

  • .bss: Contains uninitialized global and static variables, and any global or static variable that is initialized to zero, this does not take space for variables and is used as a placeholder. At runtime, those variables are loaded into memory as zero.

    .bss :包含未初始化的全局变量和静态变量,以及任何初始化为零的全局变量或静态变量,这不占用变量空间,并且用作占位符。 在运行时,这些变量将以零加载到内存中。

  • .symtab: Acts as a symbol table.

    .symtab :用作符号表。

  • .rel.text: Contains relocation entries for the .text section, i.e., for global functions that are called and need to be resolved to a symbol.

    rel.text :包含.text节的重定位条目,即,被调用并需要解析为符号的全局函数。

  • .rel.data: Contains relocation entries for the .data section, i.e., for global variables that are referenced and need to be resolved to a symbol.

    rel.data :包含.data节的重定位条目,即,引用的全局变量和需要解析为符号的全局变量。

  • .debug_*: Typically more than one section that contains information required for debugging, e.g., a symbol table for local variables, line mapping between machine code and original source code, etc.

    debug_ * :通常,一个以上的部分包含调试所需的信息,例如,局部变量的符号表,机器代码与原始源代码之间的线映射等。

  • .strtab: String table that contains names of symbols in .symtab and .debug section and section names, this is typically null-terminated strings.

    .strtab :包含.symtab.debug节中的符号名称以及节名称的字符串表,通常为以空字符结尾的字符串。

The .symtab section is always existent in the linked executable, while debug sections are only present if gcc was run with the -g flag, this implies that only function-local symbols are not included if not compiled in debug mode. to get rid of the symbol table section, run the strip program in Linux, which can be used to strip any sections from the object file.

.symtab节始终存在于链接的可执行文件中,而调试节仅在使用-g标志运行gcc时才存在,这意味着如果未在调试模式下编译,则仅不包括局部函数符号。 要摆脱符号表部分,请在Linux中运行strip程序,该程序可用于从目标文件中剥离任何部分。

符号类型 (Symbols types)

There are three main kinds of symbols that the linker has to deal with in each object module.Global Symbols, External Symbols, and Local Symbols.Global Symbols are symbols that are defined by the object module and can be referenced by other modules.External Symbols are symbols that are defined in another object module and referenced by the current module.Local Symbols are symbols that are defined and referenced only by the object module, those correspond to static variables and functions in C. Those symbols are invisible outside the module that defines them.

链接程序在每个对象模块中必须处理的符号主要有三种:全局符号,外部符号和局部符号。 全局符号是由对象模块定义的符号,可以被其他模块引用。 外部符号是在另一个对象模块中定义并由当前模块引用的符号。 局部符号是仅由对象模块定义和引用的符号,它们对应于C 静态变量和函数。 这些符号在定义它们的模块之外不可见。

Local symbol does not refer to a function-local variable, those are handled at runtime using registers and the stack frame. Local symbols in the context of linking are those which are defined using the static keyword.

局部符号不涉及函数局部变量,而是在运行时使用寄存器和堆栈帧处理的。 链接上下文中的局部符号是使用static关键字定义的。

Function-local variables are not handled at runtime in the stack frame, they’re used as local variables and the compiler allocates space for them either in the .data or the .bss segments, thats why those static variables are presistent over the life span of the program, not deallocated when their scope ends.

功能局部变量没有在堆栈帧运行时处理,他们作为局部变量,要么在.data为他们的编译器分配空间或存在.bss段,这就是为什么那些静态变量presistent在寿命程序的范围,在范围结束时不释放。

int f() {
static int x = 0;
return x;
}int g() {
static int x = 1;
return x;
}

In the previous code snippet, the compiler would allocate space for two different versions of the x variable in the .data segment, one for fand one for g and might give them a suffix to distinguish them from each other, e.g.,x.1 and x.2and will, of course, treat them as two local symbols.

在前面的代码片段中,编译器将在.data段中为x变量的两个不同版本分配空间,一个用于f和一个用于g并可能给它们提供后缀以将它们彼此区分开,例如x.1x.2 ,当然会将它们视为两个局部符号。

符号表 (Symbol Tables)

Symbol tables are built by assemblers as a section in the ELF-64 object files, those tables consist of entries that have information about the symbols.

符号表由汇编器作为ELF-64目标文件中的一部分构建,这些表由具有有关符号信息的条目组成。

Those Entries are structured more or less contain the following fields: name, type, binding, section, value, and size.

这些条目的结构或多或少包含以下字段: 名称类型绑定部分大小

struct ELF64_symbol {int name;char type: 4, 
binding: 4,short section;long value;size_t size;
};
  • name: Contains a byte-offset to the null-terminated name of the index in the .strtab section in ELF64

    name :包含一个字节偏移量,该偏移量是ELF64中.strtab节中索引的以空值终止的名称

  • type: Indicates whether the symbol belongs to a function or a variable.

    type :指示符号属于函数还是变量。

  • binding: Indicates whether the symbol is global or local.

    binding :指示符号是全局符号还是局部符号。

  • section: Indicates the ELF64 section in which the symbol is allocated, essentially an index into the section header table.

    section :指示在其中分配了符号的ELF64节,本质上是节标题表的索引。

  • value: For relocatable object files, it contains the symbol offset in the section, whereas in executable object files, it contains the absolute address of the object of the associated symbol.

    value :对于可重定位目标文件,它包含该部分中的符号偏移量,而在可执行目标文件中,它包含相关符号的对象的绝对地址。

  • size: stores the size in bytes of the object.

    size :存储对象的大小(以字节为单位)。

There are three pseudosections that have no entries in the section header table. those are COMMON, UNDEF, and ABS.

在节标题表中没有条目的三个节。 它们是COMMONUNDEFABS

  • COMMON: Contains symbols for uninitialized global variables that are not yet allocated.

    COMMON :包含尚未分配的未初始化全局变量的符号。

  • UNDEF: Contains symbols that are not defined by the source file associated with the object file.

    UNDEF :包含未由与目标文件关联的源文件定义的符号。

  • ABS: Contains symbols that do not need relocation.

    ABS :包含不需要重定位的符号。

GCC assigns symbols to .bss or COMMON sections using the following convention:

GCC使用以下约定将符号分配给.bssCOMMON节:

COMMON: Uninitialized global variables.

COMMON :未初始化的全局变量。

.bss: Uninitialized static variables and global and static variables initialized to zero.

.bss :未初始化的静态变量以及初始化为零的全局变量和静态变量。

Example, Consider the following code:

示例,请考虑以下代码:

static int a;
int b;static int c = 0;
int d = 0;static int e = 12;
int f = 13;int main() {
func();
return 0;
}

Compiling and assembling the above code to produce the relocatable object file, and inspecting the program using readelf usinggcc -c a.c && readelf --all a.o

编译并汇编以上代码以生成可重定位的目标文件,并使用readelf使用gcc -c ac && readelf --all ao检查程序

We can see the following in the .symtab section.

我们可以在.symtab部分中看到以下内容。

Symbol table '.symtab' contains 17 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FILE LOCAL DEFAULT ABS a.c
2: 0000000000000000 0 SECTION LOCAL DEFAULT 1
3: 0000000000000000 0 SECTION LOCAL DEFAULT 3
4: 0000000000000000 0 SECTION LOCAL DEFAULT 4
5: 0000000000000004 4 OBJECT LOCAL DEFAULT 4 a
6: 0000000000000008 4 OBJECT LOCAL DEFAULT 4 c
7: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 e
8: 0000000000000000 0 SECTION LOCAL DEFAULT 6
9: 0000000000000000 0 SECTION LOCAL DEFAULT 7
10: 0000000000000000 0 SECTION LOCAL DEFAULT 5
11: 0000000000000004 4 OBJECT GLOBAL DEFAULT COM b
12: 0000000000000000 4 OBJECT GLOBAL DEFAULT 4 d
13: 0000000000000004 4 OBJECT GLOBAL DEFAULT 3 f
14: 0000000000000000 21 FUNC GLOBAL DEFAULT 1 main
15: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _GLOBAL_OFFSET_TABLE_
16: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND func

The column Ndx specifies the index of the section in the symbol header table. The ones of interest here are:

Ndx列指定符号标题表中该部分的索引。 这里感兴趣的是:

3: .data segment.

3: .data段。

4: .bss segment.

4: .bss段。

If we investigate each of the variables we defined in the global scope:

如果我们调查在全局范围内定义的每个变量:

  • a: section 4, i.e., .bss section, because it’s an uninitialized static.

    a :第4节,即.bss节,因为它是未初始化的静态变量。

  • b: COMMON pseudosection, because it’s an uninitialized global.

    b :COMMON伪节,因为它是未初始化的全局变量。

  • c: section 4, i.e., .bss section, because it’s a static initialized to zero.

    c :第4节,即.bss节,因为它是静态初始化为零的。

  • d: section 4, i.e., .bss section, because it’s a global initialized to zero.

    d :第4节,即.bss节,因为它是全局初始化为零的。

  • e: section 3, i.e., .data section, decause it’s initialized static.

    e :第3节,即.data节,因为它已初始化为static。

  • f: section 3, i.e., .data section, because it’s an initialized global.

    f :第3节,即.data节,因为它是初始化的全局变量

  • func: UND, for UNDEF, because it’s referenced in this module but has to be declared in another module.

    funcUND ,对于UNDEF ,因为它是在此模块中引用的,但必须在另一个模块中声明。

符号解析度: (Symbol Resolution:)

Linker has to associate every symbol reference with only one symbol definition from the symbol tables of the relocatable object files.

链接器必须将每个符号引用仅与可重定位目标文件的符号表中的一个符号定义相关联。

In case of a module-local variable, which is defined and referenced in the same module, It is pretty straightforward, because the compiler does not allow having more than one definition of a static variable. This definition is used every time the variable is referenced.

对于在同一模块中定义和引用的模块局部变量,这非常简单,因为编译器不允许对静态变量进行多个定义。 每次引用变量时都会使用此定义。

In the case of global symbols, there are some rules that GCC follows to resolve symbol references across files.

对于全局符号, GCC遵循一些规则来解析文件中的符号引用。

The compiler and assembler associate each global variable with a strong or weak label, and this label is encoded in the symbol table for the linker to use it.

编译器和汇编器将每个全局变量与一个标签或标签相关联,并且此标签被编码在符号表中,以供链接程序使用。

  • A strong symbol is either an initialized variable or a function.

    符号是初始化变量或函数。

  • A weak symbol is an uninitialized variable.

    符号是未初始化的变量。

If there are multiple symbols that have the same name, the compiler will choose only one for all the references using the following rules:

如果有多个具有相同名称的符号,则编译器将使用以下规则为所有引用选择一个:

  • If there are more than one strong symbol of the same name, this is not allowed and will result in a compilation error.

    如果有多个相同名称的符号,则不允许这样做,这将导致编译错误。

  • If there are only one strong symbol and one or more weak symbols, the linker will choose the strong symbol.

    如果只有一个符号和一个或多个符号,则链接器将选择强符号。

  • If there are only one or more weak symbols, the linker will choose any of them.

    如果只有一个或多个符号,则链接器将选择其中任何一个。

Example:

例:

// a.c
#include <stdio.h>int i = 10;int main() {
printf("%d", i);
}// b.c
int i = 12;

compiled with gcc a.c b.c will give an error because there are 2 strong symbols called i. However, if one of them was uninitialized, the linker would have chosen the initialized one immediately.

gcc ac bc编译会产生错误,因为有2个称为i的符号。 但是,如果其中一个未初始化,则链接器将立即选择已初始化的一个。

If both were uninitialized, the linker would’ve essentially chosen arbitrarily between them, but here, and because both will be COMMON symbols, both will be initialized to zero at load-time, so the value printed will be 0 in both cases where the linker chooses either the first or the second definition.

如果两个都未初始化,则链接器实际上将在它们之间进行任意选择,但是在这里,由于两个都将是COMMON符号,因此两者在加载时都将初始化为零,因此在两种情况下打印的值都将为0。链接器选择第一个或第二个定义。

Another example that will give an often perplexing behavior:

另一个例子通常会令人困惑:

// a.cint x = 10; void func();int main() {
func();
printf("%d", x);return 0;
}// b.cint x;void func() {
x = 15;
}

The intended behavior would most probably be that calling func() would edit x that exists in b.c, but according to the rules that linker will use, all references to the x symbol will reference the version in a.c because it’s a strong symbol and the one in b.c is a weak symbol.

预期的行为很可能是调用func()将编辑bc中存在的x ,但是根据链接程序将使用的规则,所有对x符号的引用将引用ac的版本,因为它是一个符号,而一个bc是一个弱符号。

This will often happen completely silently, no warnings will fire and this behavior will not be reported. Understanding linking is the way to save yourself from getting into such behavior.

这通常会完全无声地发生,不会发出警告,并且不会报告此行为。 了解链接是使自己免于陷入这种行为的方式。

Another very baffling result is when the data types are of different sizes, linkers have very little knowledge about data types, so it will just write any size of data in any place:

另一个非常令人困惑的结果是,当数据类型的大小不同时,链接器对数据类型的了解很少,因此它将在任何地方写入任何大小的数据:

// a.c 
#include <stdio.h>int x = 10;int y = 11;void func();int main() {
func();
printf("%d %d", x, y);return 0;
}double x;void func() {
x = 0.0;
}

Fortunately, compiling the two files will run with a warning, if ignored, calling the function func will write the binary form of 0.0 in the memory that both x and y inhabit.

幸运的是,编译这两个文件时将发出警告,如果忽略该警告,调用函数func将在xy所处的内存中写入二进制形式的0.0。

This program will output 0 0, because 0.0 has the binary form of all zeros, so the memory that x and y are in will be filled in with zeros.

该程序将输出0 0 ,因为0.0具有全零的二进制形式,因此xy所在的内存将用零填充。

Those bugs can often be avoided with GCC flag -fno-common, which means do not arbitrarily select one of a number of variables defined with the same name, simply raise an error, or use the flag -Werror which will treat warnings as errors, and any warnings will stop the compilation process.

通常可以使用GCC标志-fno-common来避免这些错误,这意味着不要随意选择以相同名称定义的多个变量之一,不要简单地引发错误,或者使用标志-Werror将警告视为错误,并且任何警告将停止编译过程。

One thing to note is that how linker resolves symbols are the reason why there’s this distinction between .bss and COMMON sections.Essentially, COMMON symbols will be transferred to .bss at load time, but the compiler knows very little on how the linker will choose the definition of symbol to resolve references to.

需要注意的一件事是链接器如何解析符号是.bssCOMMON节之间存在这种区别的原因。 本质上COMMON符号将在加载时转移到.bss ,但是编译器对链接器如何选择知之甚少要解析引用的符号的定义。

  • If there is more than one strong symbol with the same name, this will raise an error.

    如果有多个同名的符号,则将引发错误。

  • If there is only one strong symbol, this will be chosen and assigned to the .bss section.

    如果只有一个符号,它将被选择并分配给.bss部分。

  • If there are more than one weak symbols, compiler explicitly mark those as COMMON, the linker can afterward choose only one and move it to .bss section.

    如果存在多个弱符号,则编译器将其显式标记为COMMON ,链接器随后只能选择一个并将其移至.bss节。

Similarly, static variables are never assigned to COMMON because, by definition, static variables cause no trouble to linker to choose from, each static variable will be tied to the module which defines it, so they’re confidently assigned to .bss or .data.

同样,静态变量永远也不会分配给COMMON,因为根据定义,静态变量不会给链接程序带来麻烦,每个静态变量都将绑定到定义它的模块,因此可以放心地将它们分配给.bss.data。

翻译自: https://medium.com/@mahmoudabdalghany/linking-on-linux-x86-64-machines-933a17419ceb

linux的x64与x86

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值