编译器背后的故事

用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       
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值