用gcc生成静态库.a和动态库.so
一、生成静态库.a
1.在作业1的基础上将代码进行改编,再扩展写一个x2y函数;
①sub1.c文件
代码如下:
#include<stdio.h>
float x2x(int a,int b)
{
return a/b;
}
②sub2.c文件
代码如下:
#include<stdio.h>
long x2y(int a,int b)
{
return(a*b);
}
2.主函数main.c文件
代码如下:
#include<stdio.h>
#include"sub1.c"
#include"sub2.c"
void main()
{
int a,b;
scanf("%d %d",&a,&b);
printf("%f\n",x2x(a,b));
printf("%d\n",x2y(a,b));
}
3.用gcc分别编译为3个.o 目标文件;
4.将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件;
5.用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序,记录文件的大小。
size test
二、生成动态库.so
1.将x2x、x2y目标文件用 ar工具生成1个 .so 动态库文件;
2.用 gcc将 main函数的目标文件与此动态库文件进行链接,生成最终的可执行程序,记录文件的大小,并与之前做对比。
size test
正常而言动态库.so的文件大小应该小于静态库.a的文件大小,这里应该是我自己的某一步操作错误导致两文件大小一致
gcc不是一个人在战斗
(一)准备工作
由于 GCC 工具链主要是在 Linux 环境中进行使用,因此本文也将以 Linux 系统作为工作环境。为了能够演示编译的整个过程,先创建一个工作目录 test0,然后用文本编辑器生成一个 C 语言编写的简单 Hello.c 程序为示例,其源代码如下所示:
#include<stdio.h>
int main(void)
{
printf("hello world!\n");
return 0;
}
(二)编译过程
1.预处理
输入指令:
gcc -E hello.c -o hello.i
// 将源文件 hello.c 文件预处理生成 hello.i
// GCC 的选项-E 使 GCC 在进行完预处理后即停止
2.编译
编译过程就是对预处理完的文件进行一系列的词法分析,语法分析,语义分析及 优化后生成相应的汇编代码。
输入指令:
gcc -S hello.i -o hello.s
// 将预处理生成的 hello.i 文件编译生成汇编程序 hello.s
// GCC 的选项-S 使 GCC 在执行完编译后停止,生成汇编程序
3.汇编
汇编过程调用对汇编代码进行处理,生成处理器能识别的指令,保存在后缀为.o的目标文件中。
输入指令:
gcc -c hello.s -o hello.o
// 将编译生成的 hello.s 文件汇编生成目标文件 hello.o
// GCC 的选项-c 使 GCC 在执行完汇编后停止,生成目标文件
4.链接
(1)静态链接,静态链接是指在编译阶段直接把静态库加入到可执行文件中去,这样可执行 文件会比较大;
gcc -static hello.c -o hello
使用size查看大小:
输入指令:ldd hello
说明没有链接动态库
(2) 动态链接,动态链接则是指链接阶段仅仅只加入一些描述信息,而程序执行时再从系统 中把相应动态库加载到内存中去。
gcc hello.c -o hello
使用size查看大小:
输入指令:ldd hello
可以看出该可执行文件链接了很多其他动态库,主要是 Linux 的 glibc 动态库
(三)分析ELF文件
1.ELF文件的段
一个典型的 ELF 文件包含下面几个段:
.text:已编译程序的指令代码段。
.rodata:ro 代表 read only,即只读数据(譬如常数 const)。
.data:已初始化的 C 程序全局变量和静态局部变量。
.bss:未初始化的 C 程序全局变量和静态局部变量。
.debug:调试符号表,调试器用此段的信息帮助调试。
可以使用 readelf -S 查看其各个 section 的信息如下:
readelf -S hello
2.反汇编ELF
由于 ELF 文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF 文件包 含的指令和数据,需要使用反汇编的法。
使用 objdump -D 对其进行反汇编如下:
objdump -D hello
使用 objdump -S 将其反汇编并且将其 C 语言源代码混合显示出来:
gcc -o hello -g hello.c //要加上-g 选项
objdump -S hello
(四)nasm汇编编译器
1.先按照LInux公社下载安装nasm编译器
输入指令:nasm -version
来查看是否安装成功,如果出现了nasm的版本信息则说明安装成功,否则还需进一步安装。
2.安装成功后,编写hello.asm代码
; hello.asm
section .data ; 数据段声明
msg db "Hello, world!", 0xA ; 要输出的字符串
len equ $ - msg ; 字串长度
section .text ; 代码段声明
global _start ; 指定入口函数
_start: ; 在屏幕上显示一个字符串
mov edx, len ; 参数三:字符串长度
mov ecx, msg ; 参数二:要显示的字符串
mov ebx, 1 ; 参数一:文件描述符(stdout)
mov eax, 4