mirror of
https://github.com/0xAX/linux-insides.git
synced 2024-12-22 22:58:08 +00:00
Merge branch 'imiell-nits' of https://github.com/ianmiell/linux-insides into ianmiell-imiell-nits
This commit is contained in:
commit
6be6112c5b
@ -3,7 +3,7 @@ Introduction
|
|||||||
|
|
||||||
During the writing of the [linux-insides](http://0xax.gitbooks.io/linux-insides/content/) book I have received many emails with questions related to the [linker](https://en.wikipedia.org/wiki/Linker_%28computing%29) script and linker-related subjects. So I've decided to write this to cover some aspects of the linker and the linking of object files.
|
During the writing of the [linux-insides](http://0xax.gitbooks.io/linux-insides/content/) book I have received many emails with questions related to the [linker](https://en.wikipedia.org/wiki/Linker_%28computing%29) script and linker-related subjects. So I've decided to write this to cover some aspects of the linker and the linking of object files.
|
||||||
|
|
||||||
If we open page the `Linker` page on wikipidia, we can see the following definition:
|
If we open the `Linker` page on wikipidia, we will see following definition:
|
||||||
|
|
||||||
>In computer science, a linker or link editor is a computer program that takes one or more object files generated by a compiler and combines them into a single executable file, library file, or another object file.
|
>In computer science, a linker or link editor is a computer program that takes one or more object files generated by a compiler and combines them into a single executable file, library file, or another object file.
|
||||||
|
|
||||||
@ -12,7 +12,7 @@ If you've written at least one program on C in your life, you will have seen fil
|
|||||||
Linking process
|
Linking process
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
Let's create simple project with the following structure:
|
Let's create a simple project with the following structure:
|
||||||
|
|
||||||
```
|
```
|
||||||
*-linkers
|
*-linkers
|
||||||
@ -21,7 +21,7 @@ Let's create simple project with the following structure:
|
|||||||
*--lib.h
|
*--lib.h
|
||||||
```
|
```
|
||||||
|
|
||||||
And write there our example factorial program. Our `main.c` source code file contains:
|
Our `main.c` source code file contains:
|
||||||
|
|
||||||
```C
|
```C
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@ -140,14 +140,15 @@ Relocation is the process of connecting symbolic references with symbolic defini
|
|||||||
19: 89 c6 mov %eax,%esi
|
19: 89 c6 mov %eax,%esi
|
||||||
```
|
```
|
||||||
|
|
||||||
Note `e8 00 00 00 00` on the first line. The `e8` is the [opcode](https://en.wikipedia.org/wiki/Opcode) of the `call` instruction with a relative offset. So the `e8 00 00 00 00` contains a one-byte operation code followed by a four-byte address. Note that the `00 00 00 00` is 4-bytes, but why only 4-bytes if an address can be 8-bytes in the `x86_64`? Actually we compiled the `main.c` source code file with the `-mcmodel=small`. From the `gcc` man:
|
Note the `e8 00 00 00 00` on the first line. The `e8` is the [opcode](https://en.wikipedia.org/wiki/Opcode) of the `call`, and the remainder of the line is a relative offset. So the `e8 00 00 00 00` contains a one-byte operation code followed by a four-byte address. Note that the `00 00 00 00` is 4-bytes. Why only 4-bytes if an address can be 8-bytes in a `x86_64` (64-bit) machine? Actually we compiled the `main.c` source code file with the `-mcmodel=small`! From the `gcc` man page:
|
||||||
|
|
||||||
```
|
```
|
||||||
-mcmodel=small
|
-mcmodel=small
|
||||||
Generate code for the small code model: the program and its symbols must be linked in the lower 2 GB of the address space. Pointers are 64 bits. Programs can be statically or dynamically linked. This is the default code model.
|
|
||||||
|
Generate code for the small code model: the program and its symbols must be linked in the lower 2 GB of the address space. Pointers are 64 bits. Programs can be statically or dynamically linked. This is the default code model.
|
||||||
```
|
```
|
||||||
|
|
||||||
Of course we didn't pass this option to the `gcc` when we compiled the `main.c`, but it is default. We know that our program will be linked in the lower 2 GB of the address space from the quote from the `gcc` manual. With this code model, 4-bytes is enough to represent the address. So we have opcode of the `call` instruction and unknown address. When we compile `main.c` with all dependencies to the executable file and will look on the call of the factorial we will see:
|
Of course we didn't pass this option to the `gcc` when we compiled the `main.c`, but it is the default. We know that our program will be linked in the lower 2 GB of the address space from the `gcc` manual extract above. Four bytes is therefore enough for this. So we have opcode of the `call` instruction and an unknown address. When we compile `main.c` with all its dependencies to an executable file, and then look at the factorial call we see:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ gcc main.c lib.c -o factorial | objdump -S factorial | grep factorial
|
$ gcc main.c lib.c -o factorial | objdump -S factorial | grep factorial
|
||||||
@ -168,7 +169,7 @@ factorial: file format elf64-x86-64
|
|||||||
...
|
...
|
||||||
```
|
```
|
||||||
|
|
||||||
As we can see in the previous output, the address of the `main` function is `0x0000000000400506`. Why it does not starts from the `0x0`? You may already know that standard C programs are linked with the `glibc` C standard library unless `-nostdlib` is passed to `gcc`. The compiled code for a program includes constructors functions to initialize data in the program when the program is started. These functions need to be called before the program is started or in another words before the `main` function is called. To make the initialization and termination functions work, the compiler must output something in the assembler code to cause those functions to be called at the appropriate time. Execution of this program will starts from the code that is placed in the special section which is called `.init`. We can see it in the beginning of the objdump output:
|
As we can see in the previous output, the address of the `main` function is `0x0000000000400506`. Why it does not start from `0x0`? You may already know that standard C programs are linked with the `glibc` C standard library (assuming the `-nostdlib` was not passed to the `gcc`). The compiled code for a program includes constructor functions to initialize data in the program when the program is started. These functions need to be called before the program is started, or in another words before the `main` function is called. To make the initialization and termination functions work, the compiler must output something in the assembler code to cause those functions to be called at the appropriate time. Execution of this program will start from the code placed in the special `.init` section. We can see this in the beginning of the objdump output:
|
||||||
|
|
||||||
```
|
```
|
||||||
objdump -S factorial | less
|
objdump -S factorial | less
|
||||||
@ -182,23 +183,25 @@ Disassembly of section .init:
|
|||||||
4003ac: 48 8b 05 a5 05 20 00 mov 0x2005a5(%rip),%rax # 600958 <_DYNAMIC+0x1d0>
|
4003ac: 48 8b 05 a5 05 20 00 mov 0x2005a5(%rip),%rax # 600958 <_DYNAMIC+0x1d0>
|
||||||
```
|
```
|
||||||
|
|
||||||
Note that it starts at the `0x00000000004003a8` address relative to the `glibc` code. We can check it also in the resulted [ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format):
|
Not that it starts at the `0x00000000004003a8` address relative to the `glibc` code. We can check it also in the [ELF](https://en.wikipedia.org/wiki/Executable_and_Linkable_Format) output by running `readelf`:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ readelf -d factorial | grep \(INIT\)
|
$ readelf -d factorial | grep \(INIT\)
|
||||||
0x000000000000000c (INIT) 0x4003a8
|
0x000000000000000c (INIT) 0x4003a8
|
||||||
```
|
```
|
||||||
|
|
||||||
So, the address of the `main` function is the `0000000000400506` and it is offset from the `.init` section. As we can see from the output, the address of the `factorial` function is `0x0000000000400537` and binary code for the call of the `factorial` function now is `e8 18 00 00 00`. We already know that `e8` is opcode for the `call` instruction, the next `18 00 00 00` (note that address represented as little endian for the `x86_64`, in other words it is `00 00 00 18`) is the offset from the `callq` to the `factorial` function:
|
So, the address of the `main` function is `0000000000400506` and is offset from the `.init` section. As we can see from the output, the address of the `factorial` function is `0x0000000000400537` and binary code for the call of the `factorial` function now is `e8 18 00 00 00`. We already know that `e8` is opcode for the `call` instruction, the next `18 00 00 00` (note that address represented as little endian for `x86_64`, so it is `00 00 00 18`) is the offset from the `callq` to the `factorial` function:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
>>> hex(0x40051a + 0x18 + 0x5) == hex(0x400537)
|
>>> hex(0x40051a + 0x18 + 0x5) == hex(0x400537)
|
||||||
True
|
True
|
||||||
```
|
```
|
||||||
|
|
||||||
So we add `0x18` and `0x5` to the address of the `call` instruction. The offset is measured from the address of the following instruction. Our call instruction is 5-bytes size - `e8 18 00 00 00` and the `0x18` is the offset from the next after call instruction to the `factorial` function. A compiler generally creates each object file with the program addresses starting at zero. But if a program is created from multiple object files, all of them will be overlapped. Just now we saw a process which is called `relocation`. This process assigns load addresses to the various parts of the program, adjusting the code and data in the program to reflect the assigned addresses.
|
So we add `0x18` and `0x5` to the address of the `call` instruction. The offset is measured from the address of the following instruction. Our call instruction is 5-bytes long (`e8 18 00 00 00`) and the `0x18` is the offset of the call after the `factorial` function. A compiler generally creates each object file with the program addresses starting at zero. But if a program is created from multiple object files, these will overlap.
|
||||||
|
|
||||||
Ok, now we know a little about linkers and relocation. Time to link our object files and to know more about linkers.
|
What we have seen in this section is the `relocation` process. This process assigns load addresses to the various parts of the program, adjusting the code and data in the program to reflect the assigned addresses.
|
||||||
|
|
||||||
|
Ok, now that we know a little about linkers and relocation it is time to learn more about linkers by linking our object files.
|
||||||
|
|
||||||
GNU linker
|
GNU linker
|
||||||
-----------------
|
-----------------
|
||||||
|
Loading…
Reference in New Issue
Block a user