链接器的秘密

问题:源码被编译后生成目标文件,这些文件如何生成最终的可执行程序?
链接器的主要作用就是把各个模块之间的相互引用的部分处理好,使得各个模块之间能够正确的衔接
链接器上——链接器的秘密
源文件 func.c

#include <stdio.h>

int* g_pointer;

void func()
{
    g_pointer = (int*)"D.T.Software";

    return;
}

原文件:test.c

#include <stdio.h>

int g_global = 0;
int g_test = 1;

extern int* g_pointer;
extern void func();

int main(int argc, char *argv[])
{
    printf("&g_global = %p\n", &g_global);
    printf("&g_test = %p\n", &g_test);
    printf("&g_pointer = %p\n", &g_pointer);
    printf("g_pointer = %p\n", g_pointer);
    printf("&func = %p\n", &func);
    printf("&main = %p\n", &main);

    func();

    return 0;
}

原文件:

#include <stdio.h>
#include <stdlib.h>

int program()
{
    printf("D.T.Software\n");

    exit(0);
}

1目标文件的特点:

1.各个段没有具体的起始地址,只有段大小信息
2.各个标识符没有实际地址,只有段中的相对地址
3.段和标识符的实际地址需要链接器具体确定

2链接器的工作内容

1.将目标文件和库文件整合为最终的可执行程序。
2.合并各个目标文件中的段(.test, .data, .bsss)
3.确定各个段和段之间的标识符的最终地址(重定位)

3 main函数的位置

3.1 mian函数的位置

问题main函数是第一个被执行的函数吗?
默认情况下(对gcc 编译器):
1.程序加载后,_start()函数是第一个被调用执行的函数(_start()函数的入口地址就是代码段(.text)的起始地址
2._start()函数准好参数(main函数、初始化函数libc_csu_fini,终止函数libc_csu_init)后立即调用_libc_start_main()函数
3._libc_start_main()函数初始化运行环境后调用main()函数
4._start 函数的地址是真个代码段的起始地址

3.1 _libc_start_main() 函数的作用:

1.调用_libc_csu_init()函数(完成必要的初始化操作)
2.启动程序的第一个线程(主线程),main()为线程入口
3.注册_libc_csu_fini()函数(程序运行终止时被调用)

3.2程序的启动过程:

链接器上——链接器的秘密

4自定义程序入口函数

gcc 提供 -e 选项用于在链接时指定入口函数
自定义入口函数必须使用 -nostartfiles 选项进行链接

#include <stdio.h> 
#include <stdlib.h>
int program()       // entry function
{
    printf(" Hello BT \n");
    exit(0);
}

首先直接编译程序:
链接器上——链接器的秘密
报错,找不到main函数,默认情况下,连接器工作是会去链接系统总提供的启动文件,当而默认的启动文件中最终会调用main函数。
使用编译选项 gcc -e program -nostartfiles program.c 则编译运行正常。
思考:
链接选项 -nostartfiles 的意义是什么?
在通常情况下,链接时都会使用系统启动文,该编译选项指定不使用系统的启动文件。
链接器根据说明原则完成具体的工作?
链接脚本。

5 重要链接选项

-nostartfiles // 指定入口函数
-nodefaultlibs // 不使用默认库文件
-nostdlib // 不使用标准库函数