库制作与原理
文章目录
- 库制作与原理
- 一、什么是库
- 四个总结
- 二、静态库
-
- [2.1 静态库生成](#2.1 静态库生成)
- [2.2 静态库使用](#2.2 静态库使用)
- 三、动态库
-
- [3.1 动态库生成](#3.1 动态库生成)
- [3.2 动态库使用](#3.2 动态库使用)
- [3.3 库运行搜索路径](#3.3 库运行搜索路径)
- 五、目标文件
- 六、ELF文件
- 七、ELF从形成到加载轮廓
-
- [7.1 ELF形成可执行](#7.1 ELF形成可执行)
- [7.2 ELF可执行文件加载](#7.2 ELF可执行文件加载)
- 八、理解链接与加载
-
- [8.1 静态链接](#8.1 静态链接)
- [8.2 ELF加载与进程地址空间](#8.2 ELF加载与进程地址空间)
-
- [8.2.1 虚拟地址/逻辑地址](#8.2.1 虚拟地址/逻辑地址)
- [8.2.2 重新理解进程虚拟地址空间](#8.2.2 重新理解进程虚拟地址空间)
- [8.3 动态链接与动态库加载](#8.3 动态链接与动态库加载)
-
- [8.3.1 进程如何看到动态库](#8.3.1 进程如何看到动态库)
- [8.3.2 进程间如何共享库](#8.3.2 进程间如何共享库)
- [8.3.3 动态链接](#8.3.3 动态链接)
-
- [8.3.3.1 概要](#8.3.3.1 概要)
- [8.3.3.2 我们的可执行程序被编译器动了手脚](#8.3.3.2 我们的可执行程序被编译器动了手脚)
- [8.3.3.3 动态库中的相对地址](#8.3.3.3 动态库中的相对地址)
- [8.3.3.4 我们的程序怎么和库具体映射起来的](#8.3.3.4 我们的程序怎么和库具体映射起来的)
- [8-3-3-5 我们的程序,怎么进⾏库函数调⽤](#8-3-3-5 我们的程序,怎么进⾏库函数调⽤)
- [8-3-3-6 全局偏移量表GOT(global offset table)](#8-3-3-6 全局偏移量表GOT(global offset table))
- [8-3-3-7 库间依赖](#8-3-3-7 库间依赖)
- [8.3.4 总结](#8.3.4 总结)
一、什么是库

预备工作,准备好历史封装的libc代码,在任意新增"库文件"
代码如下(示例):
c
// my_stdio.h
#pragma once
#define SIZE 1024
#define FLUSH_NONE 0
#define FLUSH_LINE 1
#define FLUSH_FULL 2
struct IO_FILE
{
int flag; // 刷新⽅式
int fileno; // ⽂件描述符
char outbuffer[SIZE];
int cap;
int size;
// TODO
};
typedef struct IO_FILE mFILE;
mFILE *mfopen(const char *filename, const char *mode);
int mfwrite(const void *ptr, int num, mFILE *stream);
void mfflush(mFILE *stream);
void mfclose(mFILE *stream);
// my_stdio.c
#include "my_stdio.h"
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
mFILE *mfopen(const char *filename, const char *mode)
{
int fd = -1;
if(strcmp(mode, "r") == 0)
{
fd = open(filename, O_RDONLY);
}
else if(strcmp(mode, "w")== 0)
{
fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666);
}
else if(strcmp(mode, "a") == 0)
{
fd = open(filename, O_CREAT|O_WRONLY|O_APPEND, 0666);
}
if(fd < 0) return NULL;
mFILE *mf = (mFILE*)malloc(sizeof(mFILE));
if(!mf)
{
close(fd);
return NULL;
}
mf->fileno = fd;
mf->flag = FLUSH_LINE;
mf->size = 0;
mf->cap = SIZE;
return mf;
}
void mfflush(mFILE *stream)
{
if(stream->size > 0)
{
// 写到内核⽂件的⽂件缓冲区中!
write(stream->fileno, stream->outbuffer, stream->size);
// 刷新到外设
fsync(stream->fileno);
stream->size = 0;
}
}
int mfwrite(const void *ptr, int num, mFILE *stream)
{
// 1. 拷⻉
memcpy(stream->outbuffer+stream->size, ptr, num);
stream->size += num;
// 2. 检测是否要刷新
if(stream->flag == FLUSH_LINE && stream->size > 0 && stream-
>outbuffer[stream->size-1]== '\n')
{
mfflush(stream);
}
return num;
}
void mfclose(mFILE *stream)
{
if(stream->size > 0)
{
mfflush(stream);
}
close(stream->fileno);
}
// my_string.h
#pragma once
int my_strlen(const char *s);
// my_string.c
#include "my_string.h"
int my_strlen(const char *s)
{
const char *end = s;
while(*end != '\0')end++;
return end - s;
}
四个总结

二、静态库

2.1 静态库生成
2.2 静态库使用

三、动态库

3.1 动态库生成
3.2 动态库使用

3.3 库运行搜索路径

五、目标文件

六、ELF文件
七、ELF从形成到加载轮廓
7.1 ELF形成可执行
注意:实际合并是在链接时进行的,但是并不是这么简单的合并,也会
涉及对库的合并!
7.2 ELF可执行文件加载
八、理解链接与加载
8.1 静态链接
8.2 ELF加载与进程地址空间
8.2.1 虚拟地址/逻辑地址

8.2.2 重新理解进程虚拟地址空间
ELF在被编译好之后,会把自己未来程序的入口地址记录在
ELF header的Entry字段中
8.3 动态链接与动态库加载
8.3.1 进程如何看到动态库

8.3.2 进程间如何共享库

8.3.3 动态链接
8.3.3.1 概要

8.3.3.2 我们的可执行程序被编译器动了手脚
8.3.3.3 动态库中的相对地址

8.3.3.4 我们的程序怎么和库具体映射起来的

8-3-3-5 我们的程序,怎么进⾏库函数调⽤

8-3-3-6 全局偏移量表GOT(global offset table)


8-3-3-7 库间依赖

8.3.4 总结
