CS:APP--Chapter07 : Linking
CS:APP--Chapter07 : Linking
标签(空格分隔): CS:APP
prologue
So far what I have learnt is to comprehend the relationship between my code/program and hardward for example how to translate my code into assembly code. From this point forward, We can shift to another perspective to inspect our program, that's the relationship to operation system. Undoubtedly, OS is a significant interface between user and computer resource. But the structure of computer shown in chapter 4 simply enable us to have a closer look at how way complicate it is, OS can simplify the operation , which forces programmer who want make effective use of computer to have a better understand the mechansim under the hood when our program is being executed from source code to executable file.
link static linking ,relocatable object file, shared object file, executable object file
Linker takes as input a collection of relocatable object files and generates a single full-linked executable object file.
1. compiler drivers
As we said before, our source code such as hello.c is executed only when it is converted from source file to executable code which is loaded into memory and then executed by processor.
one diagram :
We perform compile driver in order to invoke pre-processor, compiler, assembler, linker to generate executable file which eventually is loaded into memory by loader and executed by CPU.
Now we move on the command line to process our code.
Note: Because my own computer only has windows OS, many demonstrations on the book cannot be implemented now but poosible later and otherwise I just look up for another solution to accomplish these concepts.
GCC provides some options:
- v Display the programs invoked by the compiler.
- E Preprocess only; do not compile, assemble or link.
- S Compile only; do not assemble or link.
- c Compile and assemble, but do not link.
- o < file > Place the output into < file >.
computer is always performing works until there is transferring control over and interruped.
2. static linking
From a big point of view, dynamic linking seems more advanced and prevailing than static linking in terms of processing and simplicity. static linking is a kind of method we can come up with in a short period.
It just merges a collection of relocalable object files and command-line arguments and then generates a full linked executable file.
2.1 a glance at static linking
relocatable object file comprises of instructions and data segments, which are a contigous sequence of bytes. According to the features of C, these bytes are functions and global and locak variables. What's more, these functios are stored in one block in memory, initialied global variables are in another section and uninitialized variables are in yet another section.
In this case, there are two steps need to be done to get executable file by linker which performs linking:
- symbol resolution
several relocatable object files defines and references variables within their text such as function, global variables and static variables[1].
The relocatable onject file has organized these variable into different sections and thus linker can re-organize these sections.
- relocation
Because these relocatable files start from address 0, the location of most instructions and data must be modified during linking. In this case, linker should relocate these sections by associating a new memory location with each definition and then modifying all references to these symbols.
compiler and assembler do most jobs of generating data section and instruction section based on the system where this program runs.
3. object files
there are three types of object files:
name | description |
---|---|
relocatable object file | before linking |
executable object file | after linking |
shared object file | often used in dynamic linking at load or run time |
the standard of each file varies from system to system, just takeing linux for example, we focus on Executable and Linkable Format.
4. relocatable object file
relocatable object file starts with a ELF header of 16-byte length with the word size and byte ordering of the system, and ends up with the section header table where the address and size of each section are stored.
As for the parts sandwiched between them, they are sections named as this figure shows.
name | description |
---|---|
.text | the machine code of program |
.rodata | read-only data |
.data | initalised global and static variable |
.bss | uninitialised global and static variables |
.symyab | a table where the information of variables defined and referenced are stores |
.rel.text | record information of instruction for linker later |
.rel.data | record information of data later |
.debug | ? |
.line | a mapping between line number in source code and instruction in machine code in .text section |
.strtab |
the local unstatic variables are maintained by run-time stack and won't appear in either .data and .bss. By contrast, local static variables are maintained in either .data or .bss depending on whether or not there are initialized value.
As for .symtab section, all the symbols that are defined or referenced in the module.
5. symbol and symbol tables
symbols can be divided into three classes:
- external(referenced in module m but defined in another module)
- global(defined in the module m but referenced in another module)
- local(defined and referenced within module m)
It's kind of straightforward as we just resolute local symbol, it just is definedd with the module.
For global variables in symbol tables,
-
It will leave a linker symbol table entry as linker proceeds, if linker cannot find the definition in each of symbol table in all relocatable object files, it will print a error information and terminate.
-
when there are multiple global variables, the linker will either print error information or choose one of them and then discard the rest. detail
symbol tables consist of a array of entries organized in the format below:
typedef struct {
int name; /* String table offset */
char type:4, /* Function or data (4 bits) */
binding:4; /* Local or global (4 bits) */
char reserved; /* Unused */
short section; /* Section header index */
long value; /* Section offset or absolute address */
long size; /* Object size in bytes */
} Elf64_Symbol;
Provided the scenrio in which we cannot determine the defination of some variables referenced in this module and so on, three types of pesudosection are given to indicate the section field in entry
- ABS : the symbol that cannot be modified
- UNDEF : the symbol referenced in m but defined in other module
- COMMAN : the uninitialised symbol that have not been yet allocated (placeholder)
Note: the difference between .bss and COMMON is only unintialised global variables are restored with COMMON pesudosection and others are in .bss.
5.1 resolute duplicate symbols
assembler generates symbol tables with strong and weak information from compiler :
- strong : function and initialized varibales
- weak : uninitialized global and static variable
three rules:
- error when there are two and more than two strong symbols
- choose strong and then discard weak
- randomly choose one if there are all weak symbols
5.2 linking with static library
It appreas to be there is one different point from taking all relocatable object files as input is the type of file.
GCC just packages a set of related object modules into a single file called static library as a input on command line. Without any operation from pre-processor to generating relocatable object file, linker just scans left to right and takes all static libraries as input to resolute symbol referenced in object modules, using an algorithm shown below:
Algorithm for static library:
set E for all relocatable object files.
set U for all unresolved symbol referenced in modules
set D for all defined symbols in previous inout files
determine whether input f is object file,
- if f is an object file, then f is added into E and then update U and D.
- if f is an archieve, then every symbol is matched against each symbol in f. If matched, then update E and U. the linker won't proceed until the E and U don't change any more.
- if all arguments on command line are run out, the set E isn't empty. the linker will print an error infrmation and terminates.Otherwise, the linker just merge them together and reallocate them to generate a executable object file.
6. symbol relocation
Until now, every symbol has been associated with exactly one definition in its relocatable object files on the command line. And we also know the size and address of each section. But assembler generates each relocatable object file starting from address 0. In order to assgin run-time address to each function and symbol, symbol relocation must be executed to generate a single executable object file.
The whole process of symbol resolution is divided into two parts :
- relocate fsection and symbols following gathering all section of same type together to a new aggregate section (personally it just puts some assistant information in section header section for further loading), and then the linker associates each of symbol and section with one particular run-time address.
- modifying references so that they can point to the eventual run-time address of reference. Because assembler has no idea of the eventual location of each symbol, linker cannot determine it at first. During symbol resolution, each symbol that the linker cannot determine its eventual location forces the linker generates an relocatable entry restored in .rel.text if symbol is funcrion or in .rel.data f symbol is variable.
the format of relocation entry is shown below :
typedef struct {
long offset; /* Offset of the reference to relocate */
long type:32, /* Relocation type */
symbol:32; /* Symbol table index */
long addend; /* Constant part of relocation expression */
} Elf64_Rela;
we need look at the way the linker determine the eventual address of the symbol by using type :
6.1 two methods for new address of each symbol
type file defines how to modify the reference :
PC relative
(unsigned) (ADDR(r.symbol) + r.addend - refaddr)
PC absolute
(unsigned) (ADDR(r.symbol) + r.addend)
7. Executable object file
ELF executable file is designated to load easily to memory, with a mapping from the chunks of ELF execuable file to the blocks of memory segment based on Vrtual Memory, and this mapping is restored in segment header table.
8. loading ELF executable
I merely have a glance at it because of its simple introduction here which cannot explain it clear.
For simplicity, there is no information of gap between data and code segment for alignment requirement.
When loader called by execve load the ELF executable file from disk to memory and it transfers the control to the beginning of instruction.
Actually, the beginning of instruction is called entry point,always the address of the __start function defined in system object file crt1.o.
9. dynamic linking with shared libraries
Although static linking copes with many problem of organizing a large collection of module, it also brings on some disadvantages such as an inefficicent use of scarce memory despite the actual size of it and many mangement headache for memory as the version is updated.
The overall principle of dynamic linking is generating a shared library to be shared to all relocatable object files or to all different running processes.
the linker just does some of the linking statically to gengerate a partially linked executable object file by coping some relocation and symbol information , then one programm called dynamic linker modifies the shared libraries and each symbol defined in dynamic libraries to point to the actual address.
10. dynamic linking when application is running
later
one variable is declared with static, which is invisable from external modules and object files. ↩︎