论GCC和库函数们是如何组装执行程序的

一、阅读、理解和学习材料“用gcc生成静态库和动态库.pdf”请在Linux系统(Ubuntu)下如实仿做一遍。

1、首先创建一个作业目录,进入该目录。

wwyq0601@ubuntu:~$ mkdir test1
wwyq0601@ubuntu:~$ cd test1

2、创建hello.h,hello.c和main.c文件。

#include<stdio.h>
void hello(const char*name)
 { printf("Hello%s!\n",name); }
 
#include<stdio.h> 
void hello(const char*name)
 { printf("Hello%s!\n",name); }
 
 #include"hello.h"
   int main()
    {
    hello("everyone");
    return 0;
     }

3、将源程序hello.c通过gcc编译成.o文件,并用ls命令检查是否生成。

wwyq0601@ubuntu:~/test1$ gcc -c hello.c
wwyq0601@ubuntu:~/test1$ ls
hello.c  hello.h  hello.o  main.c

4、接下来创建静态库,用ar命令,创建静态库文件libmyhello.a,并用ls命令检查是否成功。

wwyq0601@ubuntu:~/test1$ ar -crv libmyhello.a hello.o
a - hello.o

wwyq0601@ubuntu:~/test1$ ls
hello.c  hello.h  hello.o  libmyhello.a  main.c

5、创建完成后进行静态库中的程序使用,结果如下。

wwyq0601@ubuntu:~/test1$ gcc -o hello main.c -L -lmyhello
wwyq0601@ubuntu:~/test1$ ./hello
Helloeveryone!

6、运行成功,再创建一个动态库文件看看,并用ls命令检查。

wwyq0601@ubuntu:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o
wwyq0601@ubuntu:~/test1$ ls
hello  hello.c  hello.h  hello.o  libmyhello.a  libmyhello.so  main.c

7、成功,在程序中运行动态库,首先用gcc命令生成目标文件,

wwyq0601@ubuntu:~/test1$ gcc -o hello main.c -L -lmyhello
wwyq0601@ubuntu:~/test1$ ./hello
Helloeveryone!
二、静态库.a与.so库文件的生成与使用

并在第一次作业的程序代码基础进行改编,除了x2x函数之外,再扩展写一个x2y函数(功能自定),main函数代码将调用x2x和x2y ;将这3个函数分别写成单独的3个 .c文件,并用gcc分别编译为3个.o 目标文件;将x2x、x2y目标文件用 ar工具生成1个 .a 静态库文件, 然后用 gcc将 main函数的目标文件与此静态库文件进行链接,生成最终的可执行程序,记录文件的大小。将x2x、x2y目标文件用 ar工具生成1个 .so 动态库文件, 然后用 gcc将 main函数的目标文件与此动态库文件进行链接,生成最终的可执行程序,记录文件的大小,并与之前做对比。
1、退出test1文件,创建新的test2文件。

wwyq0601@ubuntu:~/test1$ cd
wwyq0601@ubuntu:~$ mkdir test2
wwyq0601@ubuntu:~$ cd test2

2、用nano文本编辑器编辑生成所需文件:A1.c,A2.c,A.h,test.c,代码如下。

#include <stdio.h> 
void print1(int arg){
printf("A1 print arg:%d\n",arg);
}

#include <stdio.h>
 void print2(char *arg){
printf("A2 printf arg:%s\n", arg);
}

#ifndef A_H
 #define A_H 
 void print1(int);
void print2(char *);
#endif

#include <stdlib.h> 
#include "A.h"
int main()
{ print1(1); 
print2("test"); exit(0);
}

3、静态库.a文件的生成与使用
①生成目标文件,并检查

wwyq0601@ubuntu:~/test2$ gcc -c -fPIC A1.c A2.c
wwyq0601@ubuntu:~/test2$ ls
A1.c  A1.o  A2.c  A2.o  A.h  test.c

②生成静态库.a文件

wwyq0601@ubuntu:~/test2$ ar crv libafile.a A1.o A2.o
a - A1.o
a - A2.o

③使用.a库文件,创建可执行程序,结果如下。

wwyq0601@ubuntu:~/test2$ gcc -o test test.c libafile.a
wwyq0601@ubuntu:~/test2$ ./test
A1 print arg:1
A2 printf arg:test

4、共享库.so文件的生成与使用
①生成目标文件

wwyq0601@ubuntu:~/test2$ gcc -c -fPIC A1.c A2.c
wwyq0601@ubuntu:~/test2$ ls
A1.c  A1.o  A2.c  A2.o  A.h  libafile.a  test  test.c

②生成共享库.so文件

wwyq0601@ubuntu:~/test2$ gcc -shared *.o -o libsofile.so

③使用.so库文件创建可执行程序,得出结果。

wwyq0601@ubuntu:~/test2$ gcc -o test test.c libsofile.so
wwyq0601@ubuntu:~/test2$ ./test
A1 print arg:1
A2 printf arg:test

5、编写程序生成静态库文件及可执行程序,记录文件大小。
①创建四个所需文件如下,并分别输入代码。

wwyq0601@ubuntu:~$ nano main.h
wwyq0601@ubuntu:~$ nano sub1.c
wwyq0601@ubuntu:~$ nano sub2.c
wwyq0601@ubuntu:~$ nano main.c
#ifndef MAIN_H
#define MAIN_H
float x2x(int a,int b);
float x2y(int a,int b);
#endif

#include<stdio.h>
float x2x(int a,int b)
{
        float c=0;
        c=a+b;
        return c;
}

float x2y(int a,int b)
{
        float c=0;
        c=a/b;
        return c;
}

#include<stdio.h>
#include"main.h"
void main()
{
        int a=8,b=4;
        printf("%d\n",x2x(a,b));
        printf("%d\n",x2y(a,b));
}

②用gcc将sub1和sub2编译为.o文件,再将x2x,x2y目标文件用ar工具生成最后与静态库进行链接,生成可执行性文件,再用-la查看文件大小。

wwyq0601@ubuntu:~$ gcc -c sub1.c sub2.c

wwyq0601@ubuntu:~$ ar crv libsub.a sub1.o sub2.o
a - sub1.o
a - sub2.o


wwyq0601@ubuntu:~$ gcc -o main main.c libsub.a
wwyq0601@ubuntu:~$ ./main
4
4

wwyq0601@ubuntu:~$ ls -la
total 160
-rw-rw-r--  1 wwyq0601 wwyq0601 2752 Oct 15 08:54 libsub.a
drwx------  3 wwyq0601 wwyq0601 4096 Oct 15 05:25 .local
-rwxrwxr-x  1 wwyq0601 wwyq0601 8720 Oct 15 08:55 main
-rw-rw-r--  1 wwyq0601 wwyq0601  139 Oct 15 08:50 main.c
-rw-rw-r--  1 wwyq0601 wwyq0601   85 Oct 15 08:47 main.h

6、编写程序生成动态库文件及可执行程序,记录文件大小。
①生成动态库,使用.so库文件创建可执行程序。

wwyq0601@ubuntu:~$ gcc -shared -fPIC -o libsub.so sub1.o sub2.o

wwyq0601@ubuntu:~$ gcc -o main3 main.c libsub.so
main.c: In function ‘main’:

②此时报错提示找不到该文件,则需我们将对应的so文件拷贝到对应路径,用sudo实现

wwyq0601@ubuntu:~$ sudo cp libsub.so /usr/lib

③成功后执行./main3即可,结果如下

wwyq0601@ubuntu:~$ ./main3
4
4

④最后查看动态库生成文件的大小

wwyq0601@ubuntu:~$ ls -la
total 180
-rwxrwxr-x  1 wwyq0601 wwyq0601 7928 Oct 15 09:07 libsub.so
-rwxrwxr-x  1 wwyq0601 wwyq0601 8720 Oct 15 08:55 main
-rwxrwxr-x  1 wwyq0601 wwyq0601 8680 Oct 15 09:07 main3
-rw-rw-r--  1 wwyq0601 wwyq0601  139 Oct 15 08:50 main.c
-rw-rw-r--  1 wwyq0601 wwyq0601   85 Oct 15 08:47 main.h

7、对比发现libsub.a与libsun.so的文件大小相差很大,说明静态库生成的可执行性文件比动态库生成的要小,有一定区别。

三、Gcc不是一个人在战斗。

请说明gcc编译工具集中各软件的用途,了解EFF文件格式,汇编语言格式。阅读、理解和学习材料“Linux GCC常用命令.pdf”和“GCC编译器背后的故事.pdf”,如实仿做一遍。

1、gcc命令下各选项的含义
-E:仅作预处理,不进行编译、汇编和链接
-S:仅编译到汇编语言,不进行汇编和链接
-c:编译、汇编到目标代码(也就是计算机可识别的二进制)
-o:执行命令后文件的命名
-g:生成调试信息
-w:不生成任何警告
-Wall:生成所有的警告
2、gcc编译的四个步骤
预处理:gcc -E Test.c -o Test.i
编译: gcc -S Test.i -o Test.s
汇编: gcc -c Test.s -o Test.o
链接生成可执行文件: gcc Test.o -o Test
3、gcc常用的编译代码
①ar
用于创建静态链接库。为了便于初学者理解,在此介绍动态库与静态库 的概念:
(1)如果要将多个.o 目标文件生成一个库文 件,则存 在两种类型的库,一种是静态库,另一种是动态库。
(2)在 windows 中静态库是 以 .lib 为 后缀 的文 件 ,共享库 是以 .dll 为 后缀 的 文 件 。在 linux 中静 态库是以 .a 为 后 缀 的 文 件 , 共 享 库 是 以 .so 为 后 缀 的文件。
(3) 静 态 库 和 动 态 库 的 不 同 点 在 于 代 码 被 载 入 的 时 刻 不 同 。 静 态 库 的 代 码 在 编 译 过 程 中 已 经 被 载 入 可 执 行 程 序 , 因 此 体 积 较 大 。 共 享 库 的 代 码 是 在 可 执 行 程 序 运 行 时 才 载 入 内 存 的 , 在 编 译 过 程 中 仅 简 单 的 引 用 , 因 此 代 码 体 积 较 小 。 在 Linux 系 统 中 , 可 以 用 ldd 命 令 查 看 一 个 可 执 行 程 序 依 赖 的 共 享 库。
(4)如 果 一 个 系 统 中 存 在 多 个 需 要 同 时 运 行 的 程 序 且 这 些 程 序 之 间 存 在 共 享 库,那么采用动态库的形式将更节省内存。
②ld
用于链接。
③as
用于汇编。
④ldd
可以用于查看一个可执行程序依赖的共享库。
⑤size
查看执行文件中各部分的大小。
⑥addr2line:用 来将程序 地址转 换成其所 对应的程 序源文 件及所对 应的代 码 行,也可以得到所对应的函数。该工具将帮助调试器在调试的过程中定位对 应的源代码位置。

四、as汇编编译器针对的是AT&T汇编代码风格,Intel风格的汇编代码则可以用nasm汇编编译器编译生成执行程序。

请在ubuntu中下载安装nasm,对示例代码“hello.asm”编译生成可执行程序,并与“hello world”C代码的编译生成的程序大小进行对比。
1、下载nasm

wwyq0601@ubuntu:~$ sudo apt install nasm

2、使用nasm对hello.asm文件编译生成可执行程序,输入代码。

wwyq0601@ubuntu:~$ nano 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       ; 系统调用号(sys_write)
        int 0x80         ; 调用内核功能
                         ; 退出程序
        mov ebx, 0       ; 参数一:退出代码
        mov eax, 1       ; 系统调用号(sys_exit)
        int 0x80         ; 调用内核功能

3、生成hello文件并运行。

wwyq0601@ubuntu:~$ nano hello.asm
wwyq0601@ubuntu:~$ nasm -f elf64 hello.asm
wwyq0601@ubuntu:~$ ld -s -o hello hello.o
wwyq0601@ubuntu:~$ ./hello
Hello, world!

4、与“hello world”C代码编译生成的程序大小进行对比

wwyq0601@ubuntu:~$ size hello
   text    data     bss     dec     hex filename
     34      14       0      48      30 hello
wwyq0601@ubuntu:~$ nano helloworld.c
wwyq0601@ubuntu:~$ gcc helloworld.c
wwyq0601@ubuntu:~$ ./a.out
Hello,worldwwyq0601@ubuntu:~$ size a.out
   text    data     bss     dec     hex filename
   1184     552       8    1744     6d0 a.out

对比得nasm编译得到的文件比gcc编译的文件小。

五、了解Linux 系统中终端程序最常用的光标库(curses)的主要函数功能,写出几个基本函数名称及功能

1、curse的主要函数功能
curses函数库能够优化光标的移动并最小化需要对屏幕进行的刷新,从而也减少了必须向字符终端发送的字符数目。
2、部分常用函数名称及功能
①从屏幕读取

//返回光标位置字符
int instr(char *string);  //读取字符到string所指向的字符串中
int innstr(char *string, int numbers);//读取numbers个字符到string所指向的字符串中

②移动光标

int move(int new_y, int new_x);    //移动stdcsr的光标位置
int leaveok(WINDOW *window_ptr,bool leave_flag);
//设置一个标志,用于控制在屏幕刷新后curses将物理光标

③窗口优化屏幕更新

int wnoutrefresh(WINDOW *window_ptr);
//The wnoutrefresh subroutine determines which parts of the terminal may need updating.
 
int doupdate(void);
//The doupdate subroutine sends to the terminal the commands
六、体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)。

①在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口
在这里插入图片描述
②命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS
在这里插入图片描述

七、在Ubuntu中用 sudo apt-get install libncurses5-dev 安装curses库,请说明 头文件(比如curses.h)和库文件都被安装到哪些目录中

1、安装curses库

sudo apt-get install libncurses5-dev

2、头文件(比如curses.h)和库文件都被安装到/usr/include和/usr/lib下。

八、用gcc编译生成终端游戏

首先建立一个文件,linux环境下C语言编写小游戏贪吃蛇,,参考以上代码,编译时使用该编译命令:gcc mysnake1.0.c -lcurses -o mysnake1.0,最后运行生成的可执行文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值