编译器背后的故事
学习目标
1.请说明可执行程序是如何被组装的?
(1)阅读、理解和学习材料“用gcc生成静态库和动态库.pdf”和“静态库.a与.so库文件的生成与使用.pdf”,请在Linux系统(Ubuntu)下如实仿做一遍。
(2)在第一次作业的程序代码基础进行改编,除了x2x函数之外,再扩展写一个x2y函数(功能自定),main函数代码将调用x2x和x2y ;将这3个函数分别写成单独的3个 .c文件,并用gcc分别编译为3个.o 目标文件;将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件, 然后用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序,记录文件的大小。
(3)将x2x、x2y目标文件用 ar工具生成1个 .so 动态库文件, 然后用 gcc将 main函数的目标文件与此动态库文件进行链接,生成最终的可执行程序,记录文件的大小,并与之前做对比。
2.Gcc不是一个人在战斗。请说明gcc编译工具集中各软件的用途,了解EFF文件格式,汇编语言格式。
(1)阅读、理解和学习材料“Linux GCC常用命令.pdf”和“GCC编译器背后的故事.pdf”,如实仿做一遍。
(2)as汇编编译器针对的是AT&T汇编代码风格,Intel风格的汇编代码则可以用nasm汇编编译器编译生成执行程序。请在ubuntu中下载安装nasm,对示例代码“hello.asm”编译生成可执行程序,并与“hello world”C代码的编译生成的程序大小进行对比。
3.每一个程序背后都站着一堆优秀的代码库。了解实际程序是如何借助第三方库函数完成代码设计。
(1)了解Linux 系统中终端程序最常用的光标库(curses)的主要函数功能,写出几个基本函数名称及功能;
(2)在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)。
(3)在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库,请说明 头文件(比如curses.h)和库文件都被安装到哪些目录中;
(4)请参考 “Linux 环境下C语言编译实现贪吃蛇游戏”(http://www.linuxidc.com/Linux/2011-08/41375.htm)或者弹球游戏(https://blog.csdn.net/psc0606/article/details/9990981),用gcc编译生成一个终端游戏,体会curses库如何被链接和使用。
一、可执行文件的组装
1. gcc生成静态库/动态库
进入linux终端,先建立一个作业目录test1,如下图
然后用vim写如下代码
由.o文件生成静态库libmyhello.a,如下图
由.o文件创建动态库libmyhello.so,如下图
2. 静态库文件链接(实践)
已建好3个文件main.c,sub1.c,sub2.c,代码如下
//main.c
#include <stdio.h>
#include "sub1.c"
#include "sub2.c"
int main()
{
int a = 3;
int b = 5;
float c;
float d;
c = x2x(a, b);
d = x2y(a, b);
printf("%f \n", c);
printf("%f \n", d);
return 0;
}
//sub1.c
#include <stdio.h>
float x2x(int a, int b)
{
float c;
c = a + b;
return c;
}
//sub2.c
#include <stdio.h>
float x2y(int a, int b)
{
float d;
d = a - b;
return d;
}
用gcc编译为.o文件
将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件libsub.a
main函数与此静态库文件进行链接,并运行main文件,正确
3. 动态库文件链接(实践)
创建动态库文件libsub.so
将main函数与此动态库文件进行链接,并运行main1文件,正确
main1文件大小如下,并与main文件比较,两者一样大
二、gcc编译工具
1. gcc常用命令
创建一个文件夹testgcc,写一个简单程序test,一步到位编译如下
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译 (Compilation)、汇编 (Assembly)和连接(Linking)。
预处理
gcc -E test.c -o test.i
编译为汇编代码(.s文件)
gcc -S test.i -o test.s
汇编为目标文件(.o文件)
gcc -c test.s -o test.o
连接
gcc test.o -o test
2. nasm初步使用
在ubuntu18.04中安装nasm
在终端输入如下命令:
sudo apt-get install nasm
在testgcc文件夹下创建文件hello.asm代码如下
;hello.asm
section .dara ;
msg db "Hwllo,world!",0xA ;
len equ $ - msg ;
section .text ;
global _start ;
_start: ;
mov edx, len ;
mov ecx, msg ;
mov ebx, 1 ;
mov eax, 4 ;
int 0x80 ;
;
mov ebx, 0 ;
mov eax, 1 ;
int 0x80 ;
对hello.asm编译生成可执行程序,并运行
hello与test文件大小比较,显然nasm编译的文件要小
在ubuntu中下载安装nasm
安装
ubuntu11.10下默认没有安装curses函数库,(CentOS下默认已经安装,可以直接在usr/include下查看是否有该头文件来确定)使用
>sudo apt-get install ncurses-dev
安装curses库,这样在curses函数库的头文件和库文件就被分别安装在/usr/include/和/usr/lib/下,在编译程序时,直接使用命令:
gcc program.c -o program -lcurses
完成编译,运行。
curses函数库头文件安装位置:/usr/include/
curses函数库库文件安装位置:/usr/lib/