编译器背后的故事
关于可执行程序是如何被组装的
仿做学习材料“用gcc生成静态库和动态库.pdf”和“静态库.a与.so库文件的生成与使用.pdf”
学习材料“用gcc生成静态库和动态库.pdf”
第一步:编辑生成例子程序hello.c,hello.h,main.c
先创建一个作业目录,保存本次练习的文件
#mkdir test1
#cd test1
然后用vim、nano或gedit等文本编译器编辑生成所需要的3个文件。
- 编辑生成hello.h
2.编辑生成hello.c
3.编辑生成main.c
第二步:将hello.c编译成.o文件
这里生成.o文件也需要加上-fPIC
查看是否生成了hello.o文件
第三步:由.o文件创建静态库
并用ls命令查看
第四步:在程序中使用静态库
方法一:
#gcc -o hello main.c -L.-lmyhello
自定义的库时,main.c还可放在-L.和-lmyhello之间,但是不能放在它俩之后,否则会提示myhello没定义,但是是系统的库时,如 g++ -o main (-L/usr/lib)-lpthread main.cpp就不出错。
方法二:
#gcc main.c libmyhello.a -o hello
方法三:
先生成main.o:
gcc -c main.c
再生成可执行文件:
gcc -o hello main.o libmyhello.a
动态库连接时也可以这样做。
#./hello
Hello everyone!
我们删除静态库文件试试公用函数 hello是否真的连接到目标文件hello 中了。
#rm libmyhello.a
rm: remove regular file 'libmyhello.a"? y
#.hello
Hello cveryone!
程序照常运行,静态库中的公用函数已经连接到目标文件中了。
这里我直接采用最快捷的一种:方法二。
第五步:由.o文件创建动态库文件
注意,如果第二步生成.o文件时,命令中不加-fPIC的话,这里就会出现错误。
###### 第六步:在程序中使用动态库
当静态库和动态库同名时,gcc优先使用动态库
第一步:先删除以上情况下的除hello.c,hello.h,main.c的所有文件
第二步:创建同名的静态库和动态库:libmyhello.so
第三步:运行gcc命令来使用函数库myhello生成目
标文件 hello,并运行程序hello。
通过上述最后一条ls命令,可以发现静态库文件libmyhello.a和动态库文件libmyhello.so都已经生成,并都在当前目录中。然后,我们运行gcc命令来使用函数库myhello生成目标文hello,并运行程序hello。
#gcc -o hello main.c -L.-lmyhello
(动态库和静态库同时存在时,优先使用动态库,当然,如果直接#gcc main.c libmyhello.a -o hello的话,就是指定为静态库了)
#./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shar
ed object file: No such file or directory
从程序hello运行的结果中很容易知道,当静态库和动态库同名时,gcc命令将优先使用动态库,默认去连/usr/ib和/lib等目录中的动态库,将文件libmyhello.so复制到目录/usr/lib中即可。
学习材料“静态库.a与.so库文件的生成与使用.pdf”
第一步编辑生成所需的四个文件A1.c、A2.c、A.h、test.c
先创建一个作业目录,保存本次练习的文件
#mkdir test2
#cd test2
1.编辑生成A1.c
2.编辑生成A2.c
3.编辑生成A.h
4.编辑生成test.c
第二步:生成和使用静态.a文件
1.生成目标文件(.o)
2.生成静态.a文件
3.使用.a库文件
第三步:生成与使用共享库.so文件
1.生成目标文件
2.输出共享库.so文件
3.使用.so库文件,创建可执行文件
对第一次作业改编
第一次作业
第一步:扩展x2y
第二步:gcc分别编译为3个.o 目标文件;将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件, 然后用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序
第三步:记录文件大小
图中就是可执行文件的大小单位是字节。
说明gcc编译工具集中各软件的用途,了解EFF文件格式,汇编语言格式
仿做学习材料“Linux GCC常用命令.pdf”和“GCC编译器背后的故事.pdf”
学习材料“Linux GCC常用命令.pdf”
示例程序:
//test.c
#include <stdio.h>
int main (void)
{
printf(“Hello World! \n”);
return 0;
}
1.简单编译
直接编译
gcc test.c -o test
实质上,上述编译过程是分为四个阶段进行的,即预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。
分步——四个阶段
1.预处理
2.编译
3.汇编
4.连接
2.多个程序文件的编译
这里的a1.c文件对应得是前面第一次作业的sub1.c,这里的a2.c文件对应得是前面的main1.c,这里的a3.c文件对应得是前面的sub1.h,
如果同时处理的文件不止一个,GCC仍然会按照预处理、编译和链接的过程依次进行。如果深究起来,上面这条命令大致相当于依次执行如下三条命令:
gcc -c testl.c -o testi.o
gcc -c test2.c -o test2.o
gcc testl.o test2.o -o test
3.检错
先将之前写的test.c文件修改一下,制造一点错误。
然后使用检错命令检错
库文件连接
先将上一步骤对test.c的错误修改修正回来
第一步:编译生成可执行文件
第二步:链接
第三步:强制链接时使用静态链接库
默认情况下,GCC在链接时优先使用动态链接库,只有当动态链接库不存在时才考虑使用静态链接库,如果需要的话可以在编译时加上-static选项,强制使用静态链接库。
在/usrldevlmysqlib目录下有链接时所需要的库文件libmysqlclient.so和libmysqlclient.a,为了让GCC在链接时只用到静态链接库,可以使用下面的命令:
gcc -L /usr/dev/mysql/lib -static -lmysqlclient test.o -o test
静态库链接时搜索路径顺序:
1.ld会去找GCC命令中的参数-L
2.再找gcc的环境变量LIBRARY_PATH
3.再找内定目录/lib /usr/lib/usr/local/lib这是当初compile gcc时写在程序内的
动态链接时、执行时搜索路径顺序:
1.编译目标代码时指定的动态库搜索路径
2.环境变量LD_LIBRARY_PATH指定的动态库搜索路径
3.配置文件/etc/ld.so.conf中指定的动态库搜索路径
4.默认的动态库搜索路径/lib
5.默认的动态库搜索路径/usr/lib
学习材料“GCC编译器背后的故事.pdf”
准备工作
创建目录test0,编写一个Hello.c
编译过程
1.预处理
2.编译
3.汇编
4.链接
可以看出该执行文件链接了很多其他动态库
分析ELF文件
1.ELF文件的段
2.反汇编ELF
(1)使用objdump -D 对其进行反汇编如下:
(2)使用objdump -S 对其进行反汇编并且将其C语言源代码混合显示出来:
nasm汇编编译器的安装与使用
安装
安装命令
sudo apt-get install nasm
使用
用nasm汇编编译器编译生成执行程序
与C代码的编译生成的程序大小进行对比
显示nasm编译器下生成的可执行文件较小。
了解实际程序是如何借助第三方库函数完成代码设计
写出几个基本函数名称及功能
了解Linux 系统中终端程序最常用的光标库(curses)的主要函数功能,
举例如下
cbreak():调用cbreak函数后,除了"Del"和"Ctrl"键外,接受其他所有字符输入。
raw(和cbreak()两个函数都可以禁止行缓冲(line buffering。区别是:在raw()函数模式下,处理挂起(CTRLZ)、中断或退出(CTRLC)等控制字符时,将直接传送给程序去处理而不产生终端信号;而在cbreak(模式下,控制字符将被终端驱动程序解释成其它字符。
nl (O /nonl 0):输出时,换行是否作为回车字符。nl函数将换行作为回车符,而nonl函数相反。
noecho()/echo():关闭/打开输入回显功能。
intrflush(WINDOW *win,bool bf)win为标准输出。当bf为true时输入Break,可以加快中断的响应。但是,有可能会造成屏幕输出信息的混乱。
keypad (WINDOW *win,bool bf : win为标准输出。调用keypad函数后,将可以使用键盘上的一些特殊字符,如方向键,转化成curses.h中的特殊键。
refresh():重绘屏幕显示内容。在调用initscr函数后,第一次调用refresh函数会清除屏幕显示。
体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)
操作方法
在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口(windows+R),命令行输入telnet bbs.newsmth.net。
在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库,请说明头文件(比如curses.h)和库文件都被安装到哪些目录中
安装命令
sudo apt-get install libncurses5-dev
头文件(比如curses.h)和库文件都安装的目录
查找命令
whereis / -name 文件名
参考 “Linux 环境下C语言编译实现弹球游戏”,用gcc编译生成一个终端游戏
代码参考(https://blog.csdn.net/psc0606/article/details/9990981)