读书-程序员的自我修养-链接、封装与库(11: 第四章:静态链接(4)--链接控制过程、ld默认链接脚本,TinyHelloWorld.c 和BFD库

读书-程序员的自我修养-链接、封装与库(11: 第四章:静态链接(4)--链接控制过程、ld默认链接脚本,TinyHelloWorld.c 和BFD库)

1. 链接控制过程

绝大部分情况下,我们使用链接器提供的默认链接器规则对目标文件进行链接。
但是对于一些特殊要求的程序,如OS内核,BIOS和一些没有OS的情况下运行程序,
往往需要对链接进行控制。

1.1 操作系统内核是啥?

操作系统内核,从本质上说,它本身也是一个程序。
如 windows的内核 ntoskrnl.exe 就是PE文件,它位于 C:\Windows\System32。
NT Kernel & System 其实它大小为 5.29 MB。
这就是真正的windows内核文件 ntoskrnl.exe,而不是很多文件组成的。

1.2 链接控制脚本

链接器一般都提供多种控制整个链接过程的方法,以用来产生用户所需的文件。
一般有如下三种方法:

  1. 使用命令传递参数,如 ld 的 -o -e等参数
  2. 将链接指令存放在目标文件里
  3. 使用连接控制脚本

1.2.1 ld 链接

由于各个链接器平台的链接控制过程各不相同,我们这里以 ld 链接器为例。
我们使用ld链接器的时候没有指定链接脚本,其实是默认使用了链接脚本。

ld -verbose 查看默认链接脚本
这也是标准链接脚本文件的格式。

root@ubuntu-admin-a1:/home# ld -verbose
GNU ld (GNU Binutils for Ubuntu) 2.26.1
Supported emulations:
elf_x86_64
	……
using internal linker script:
==================================================
/* Script for -z combreloc: combine and sort reloc sections */
/* Copyright (C) 2014-2015 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"); 
……
SECTIONS
{
/* Read-only sections, merged into text segment: */
PROVIDE (__executable_start = SEGMENT_START("text-segment", 0x400000)); 
. = SEGMENT_START("text-segment", 0x400000) + SIZEOF_HEADERS;
.interp         : { *(.interp) }
……
/DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}
==================================================
root@ubuntu-admin-a1:/home# 

1.3 最小的程序

最小的程序 TinyHelloWorld.c 不用printf库函数,不用main,使用linux 入口_start.

1.3.1 TinyHelloWorld.c 代码,编译链接,执行

char* str = "Hello World!\n";

void print(){
	asm( "movl $13,%%edx \n\t"
		"movl  %0,%%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();
}

root@ubuntu-admin-a1:/home/4Chapter# gcc -c -fno-builtin -m32 TinyHelloWorld.c 
root@ubuntu-admin-a1:/home/4Chapter# ld -static -m elf_i386 -e nomain -o TinyHelloWorld TinyHelloWorld.o
root@ubuntu-admin-a1:/home/4Chapter# ls
ab   a.o  b.o      TinyHelloWorld    TinyHelloWorld.o
a.c  b.c  libcRes  TinyHelloWorld.c
root@ubuntu-admin-a1:/home/4Chapter# ./TinyHelloWorld 
Hello World!
root@ubuntu-admin-a1:/home/4Chapter# 

参考
https://blog.csdn.net/lichkingyang/article/details/83660812

1.3.2 TinyHelloWorld.c 解释:

  1. -fno-builtin 不使用内置函数
    GCC编译器提供了很多内置函数,它会把一些常用的c库函数替换成编译器的内置函数,已达到优化的目的。
    如gcc会将只有字符串参数的printf替换成puts,以节省格式化解析时间。
    用 -fno-builtin 参数关闭GCC内置函数功能。

  2. -m32强制用32位API编译,就可以编译通过
    原因就是64位的系统不能编译32位的目标文件

  3. -static 使用静态链接的方式来链接程序
    默认是动态链接

  4. -e nomain 表示该程序的入口函数为nomain

  5. 退出码 42 的来源
    因为《银河系漫游指南》里面的终极电脑给出关于生命的终极答案是42.

1.4 ld 链接脚本-TinyHelloWorld.lds

TinyHelloWorld.lds

ENTRY(nomain)

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

验证

root@ubuntu-admin-a1:/home/4Chapter# gcc -c -fno-builtin -m32 TinyHelloWorld.c 
root@ubuntu-admin-a1:/home/4Chapter# ld -static -T TinyHelloWorld.lds -m elf_i386 -o TinyHelloWorld2 TinyHelloWorld.o
root@ubuntu-admin-a1:/home/4Chapter# ls
ab   a.o  b.o      TinyHelloWorld   TinyHelloWorld.c    TinyHelloWorld.o
a.c  b.c  libcRes  TinyHelloWorld2  TinyHelloWorld.lds
root@ubuntu-admin-a1:/home/4Chapter# ./TinyHelloWorld2
Hello World!
root@ubuntu-admin-a1:/home/4Chapter#

2. BFD库

BFD库(Binary File Descriptor Library)
它是一个GNU项目
它的目标就是希望通过一种统一的接口来处理不同的目标文件格式。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值