第一篇万字博客:编码器背后的故事

关于可执行程序是如何被组装的

用 gcc 生成 .a 静态库和 .so 动态库

编辑生成例子程序 hello.h、hello.c 和 main.c

先创建一个作业目录,保存本次练习的文件。

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

然后用 vim编辑器编辑生成所需要的 3 个文件hello.h.hello.c,main.c

lee@ubuntu:~/test1$ vim hello.h
lee@ubuntu:~/test1$ vim hello.c
lee@ubuntu:~/test1$ vim main.c

hello.h代码

#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif //HELLO_H

hello…c代码

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

main.c代码

#include "hello.h"
int main()
{
hello("everyone");
return 0;
}

将 hello.c 编译成.o 文件

lee@ubuntu:~/test1$ gcc -c hello.c

由.o 文件创建静态库

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

在程序中使用静态库

先生成main.o,再生成可执行文件

lee@ubuntu:~/test1$ gcc -c main.c
lee@ubuntu:~/test1$ gcc -o hello main.o libmyhello.a

执行

lee@ubuntu:~/test1$ ./hello
Hello everyone!

删除静态库文件试试公用函数 hello 是否真的连接到目标文件 hello 中了

lee@ubuntu:~/test1$ rm libmyhello.a
lee@ubuntu:~/test1$ ./hello
Hello everyone!

程序照常运行,静态库中的公用函数已经连接到目标文件中了

由.o 文件创建动态库文件

lee@ubuntu:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o


在程序中使用动态库

lee@ubuntu:~/test1$ # gcc -o hello main.c -L. -lmyhello
lee@ubuntu:~/test1$  ./hello
Hello everyone!

在作业一的基础上扩展改编生成最终的可执行程序,记录文件的大小

编辑生成程序sub1.h,sub2.h,sub1.c,sub2.c,main1.c

先创建一个作业目录

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

然后使用vim编辑器编辑生成sub1.h,sub1.c,sub2.h,sub2.c,main1.c

lee@ubuntu:~/test1/test2$ vim sub1.h
lee@ubuntu:~/test1/test2$ vim sub1.c
lee@ubuntu:~/test1/test2$ vim sub2.h
lee@ubuntu:~/test1/test2$ vim sub2.c
lee@ubuntu:~/test1/test2$ vim main1.c

sub1.h代码

#ifndef SUB1_H
#define SUB1_H 
float x2x(int a, int b);
#endif //SUB1_H

sub1.c代码

#include"sub1.h"

float x2x(int a, int b){
	return a + b;	//相加
}

sub2.h代码

#ifndef SUB2_H
#define SUB2_H 
float x2y(int a, int b);
#endif //SUB2_

sub2.c代码

#include"sub2.h"

float x2y(int a, int b){
        return a * b;	//相乘
}

main1.c代码

#include<stdio.h>
#include"sub1.h"
#include"sub2.h"

int main(){
	int a = 2, b = 3;
	printf("%d + %d = %f\n", a, b, x2x(a, b));
	printf("%d × %d = %f\n", a, b, x2y(a, b));
	return 0;
}

将 sub1.c、sub2.c 编译成 .o文件

lee@ubuntu:~/test1/test2$ gcc -c sub1.c sub2.c

o文件创建静态库

lee@ubuntu:~/test1/test2$ ar -crv libsub1.a sub1.o
a - sub1.o
lee@ubuntu:~/test1/test2$ ar -crv libsub2.a sub2.o
a - sub2.o
lee@ubuntu:~/test1/test2$ ls
libsub1.a  libsub2.a  main1.c  sub1.c  sub1.h  sub1.o  sub2.c  sub2.h  sub2.o

在程序中使用静态库

lee@ubuntu:~/test1/test2$ gcc main1.c libsub1.a libsub2.a -o main1
lee@ubuntu:~/test1/test2$ ./main1
2 + 3 = 5.000000
2 × 3 = 6.000000

o文件创建动态库

lee@ubuntu:~/test1/test2$ gcc -shared -fPIC -o libsub1.so sub1.o
lee@ubuntu:~/test1/test2$ gcc -shared -fPIC -o libsub2.so sub2.o
lee@ubuntu:~/test1/test2$ ls
libsub1.a   libsub2.a   main1    sub1.c  sub1.o  sub2.h
libsub1.so  libsub2.so  main1.c  sub1.h  sub2.c  sub2.o

在程序中使用动态库

gcc main.c libsub1.so libsub2.so -o main2

获取root权限后

root@ubuntu:/home/lee/test1/test2# sudo mv libsub1.so /usr/lib
root@ubuntu:/home/lee/test1/test2# sudo mv libsub2.so /usr/lib
root@ubuntu:/home/lee/test1/test2# ./main2
2 + 3 = 5.000000
2 × 3 = 6.000000

两个可执行文件大小的比较

root@ubuntu:/home/lee/test1/test2# size main1
   text	   data	    bss	    dec	    hex	filename
   1741	    600	      8	   2349	    92d	main1
root@ubuntu:/home/lee/test1/test2# size main2
   text	   data	    bss	    dec	    hex	filename
   1969	    648	      8	   2625	    a41	main2

小结

静态库相比动态库浪费空间,且更新麻烦,随着时代进步,我认为动态库技术会成为主流

关于gcc编译工具集中各软件的用途

gcc常用命令练习

创建一个作业目录

lee@ubuntu:~$ mkdir test2
lee@ubuntu:~$ cd test2

用vim编辑器编辑生成一个text.c文件

lee@ubuntu:~/test2$ vim text.c

text.c代码

#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}

预处理

lee@ubuntu:~/test0$ gcc -E hello.c -o hello.i

编译

lee@ubuntu:~/test0$ gcc -S hello.i -o hello.s

汇编

lee@ubuntu:~/test0$ gcc -c hello.s -o hello.o
lee@ubuntu:~/test0$ as -c hello.s -o hello.o

链接

lee@ubuntu:~/test0$ gcc hello.c -o hello

执行

lee@ubuntu:~/test0$ ./hello
Hello World! 

分析ELF文件

ELF文件的段

lee@ubuntu:~/test0$ readelf -S hello
There are 33 section headers, starting at offset 0xcdb60:

节头:
  [] 名称              类型             地址              偏移量
       大小              全体大小          旗标   链接   信息   对齐
  [ 0]                   NULL             0000000000000000  00000000
       0000000000000000  0000000000000000           0     0     0
  [ 1] .note.ABI-tag     NOTE             0000000000400190  00000190
       0000000000000020  0000000000000000   A       0     0     4
  [ 2] .note.gnu.build-i NOTE             00000000004001b0  000001b0
       0000000000000024  0000000000000000   A       0     0     4
readelf:警告: [ 3]: Link field (0) should index a symtab section.
  [ 3] .rela.plt         RELA             00000000004001d8  000001d8
       0000000000000228  0000000000000018  AI       0    20     8
  [ 4] .init             PROGBITS         0000000000400400  00000400
       0000000000000017  0000000000000000  AX       0     0     4
  [ 5] .plt              PROGBITS         0000000000400418  00000418
       00000000000000b8  0000000000000000  AX       0     0     8
  [ 6] .text             PROGBITS         00000000004004d0  000004d0
       000000000008f690  0000000000000000  AX       0     0     16
  [ 7] __libc_freeres_fn PROGBITS         000000000048fb60  0008fb60
       0000000000001523  0000000000000000  AX       0     0     16
  [ 8] __libc_thread_fre PROGBITS         0000000000491090  00091090
       000000000000108f  0000000000000000  AX       0     0     16
  [ 9] .fini             PROGBITS         0000000000492120  00092120
       0000000000000009  0000000000000000  AX       0     0     4
  [10] .rodata           PROGBITS         0000000000492140  00092140
       000000000001926c  0000000000000000   A       0     0     32
  [11] .stapsdt.base     PROGBITS         00000000004ab3ac  000ab3ac
       0000000000000001  0000000000000000   A       0     0     1
  [12] .eh_frame         PROGBITS         00000000004ab3b0  000ab3b0
       000000000000a510  0000000000000000   A       0     0     8
  [13] .gcc_except_table PROGBITS         00000000004b58c0  000b58c0
       000000000000009e  0000000000000000   A       0     0     1
  [14] .tdata            PROGBITS         00000000006b6120  000b6120
       0000000000000020  0000000000000000 WAT       0     0     8
  [15] .tbss             NOBITS           00000000006b6140  000b6140
       0000000000000040  0000000000000000 WAT       0     0     8
  [16] .init_array       INIT_ARRAY       00000000006b6140  000b6140
       0000000000000010  0000000000000008  WA       0     0     8
  [17] .fini_array       FINI_ARRAY       00000000006b6150  000b6150
       0000000000000010  0000000000000008  WA       0     0     8
  [18] .data.rel.ro      PROGBITS         00000000006b6160  000b6160
       0000000000002d94  0000000000000000  WA       0     0     32
  [19] .got              PROGBITS         00000000006b8ef8  000b8ef8
       00000000000000f8  0000000000000000  WA       0     0     8
  [20] .got.plt          PROGBITS         00000000006b9000  000b9000
       00000000000000d0  0000000000000008  WA       0     0     8
  [21] .data             PROGBITS         00000000006b90e0  000b90e0
       0000000000001af0  0000000000000000  WA       0     0     32
  [22] __libc_subfreeres PROGBITS         00000000006babd0  000babd0
       0000000000000048  0000000000000000  WA       0     0     8
  [23] __libc_IO_vtables PROGBITS         00000000006bac20  000bac20
       00000000000006a8  0000000000000000  WA       0     0     32
  [24] __libc_atexit     PROGBITS         00000000006bb2c8  000bb2c8
       0000000000000008  0000000000000000  WA       0     0     8
  [25] __libc_thread_sub PROGBITS         00000000006bb2d0  000bb2d0
       0000000000000008  0000000000000000  WA       0     0     8
  [26] .bss              NOBITS           00000000006bb2e0  000bb2d8
       00000000000016f8  0000000000000000  WA       0     0     32
  [27] __libc_freeres_pt NOBITS           00000000006bc9d8  000bb2d8
       0000000000000028  0000000000000000  WA       0     0     8
  [28] .comment          PROGBITS         0000000000000000  000bb2d8
       0000000000000029  0000000000000001  MS       0     0     1
  [29] .note.stapsdt     NOTE             0000000000000000  000bb304
       00000000000014cc  0000000000000000           0     0     4
  [30] .symtab           SYMTAB           0000000000000000  000bc7d0
       000000000000a950  0000000000000018          31   678     8
  [31] .strtab           STRTAB           0000000000000000  000c7120
       00000000000068c4  0000000000000000           0     0     1
  [32] .shstrtab         STRTAB           0000000000000000  000cd9e4
       0000000000000176  0000000000000000           0     0     1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
  L (link order), O (extra OS processing required), G (group), T (TLS),
  C (compressed), x (unknown), o (OS specific), E (exclude),
  l (large), p (processor specific)

反汇编ELF

lee@ubuntu:~/test0$  objdump -D hello
			....
			.....
			.....
			   1478:	41 00 00             	add    %al,(%r8)
    147b:	00 03                	add    %al,(%rbx)
    147d:	00 00                	add    %al,(%rax)
    147f:	00 73 74             	add    %dh,0x74(%rbx)
    1482:	61                   	(bad)  
    1483:	70 73                	jo     14f8 <catch_hook+0x14a0>
    1485:	64 74 00             	fs je  1488 <catch_hook+0x1430>
    1488:	9d                   	popfq  
    1489:	6f                   	outsl  %ds:(%rsi),(%dx)
    148a:	48 00 00             	rex.W add %al,(%rax)
    148d:	00 00                	add    %al,(%rax)
    148f:	00 ac b3 4a 00 00 00 	add    %ch,0x4a(%rbx,%rsi,4)
	...
    149e:	00 00                	add    %al,(%rax)
    14a0:	6c                   	insb   (%dx),%es:(%rdi)
    14a1:	69 62 63 00 75 6e 6d 	imul   $0x6d6e7500,0x63(%rdx),%esp
    14a8:	61                   	(bad)  
    14a9:	70 5f                	jo     150a <catch_hook+0x14b2>
    14ab:	63 6f 6d             	movslq 0x6d(%rdi),%ebp
    14ae:	70 6c                	jo     151c <catch_hook+0x14c4>
    14b0:	65 74 65             	gs je  1518 <catch_hook+0x14c0>
    14b3:	00 2d 38 40 2d 31    	add    %ch,0x312d4038(%rip)        # 312d54f1 <_end+0x30c18af1>
    14b9:	32 38                	xor    (%rax),%bh
    14bb:	28 25 72 62 70 29    	sub    %ah,0x29706272(%rip)        # 29707733 <_end+0x2904ad33>
    14c1:	20 38                	and    %bh,(%rax)
    14c3:	40 25 72 62 78 00    	rex and $0x786272,%eax
    14c9:	00 00                	add    %al,(%rax)


nasm

安装nasm编译器

在linux浏览器中打开网站
https://www.nasm.us/pub/nasm/releasebuilds/2.14rc16/nasm-2.14rc16.tar.gz
在文件夹“下载”中解压文件
安装

lee@ubuntu:~$ cd 下载
lee@ubuntu:~/下载$ cd nasm-2.14rc16/
lee@ubuntu:~/下载/nasm-2.14rc16$ ./configure
lee@ubuntu:~/下载/nasm-2.14rc16$ make
lee@ubuntu:~/下载/nasm-2.14rc16$ sudo make install

查看安装状态

lee@ubuntu:~/下载/nasm-2.14rc16$ nasm -version
NASM version 2.14rc16 compiled on Oct 14 2020

编译汇编hello.asm文件

lee@ubuntu:~/下载/nasm-2.14rc16$ vim 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         ; 调用内核功能


编译

lee@ubuntu:~/下载/nasm-2.14rc16$ nasm -f elf64 hello.asm

链接

lee@ubuntu:~/下载/nasm-2.14rc16$  ld -s -o hello hello.o

执行

lee@ubuntu:~/下载/nasm-2.14rc16$ ./hello
Hello, world!

汇编与C代码的编译生成的可执行程序大小对比

lee@ubuntu:~/下载/nasm-2.14rc16$ size hello
   text	   data	    bss	    dec	    hex	filename
     34	     14	      0	     48	     30	hello
lee@ubuntu:~/下载/nasm-2.14rc16$ cd ~/test0
lee@ubuntu:~/test0$ size hello
   text	   data	    bss	    dec	    hex	filename
 743329	  20876	   5984	 770189	  bc08d	hello
lee@ubuntu:~/test0$ 

关于实际程序如何借助第三方函数完成代码设计

curses几个基本函数

输出到屏幕

int addch(const chtype char_to_add);
int addchstr(chtype *const string_to_add);    //当前位置添加字符(串)

int printw(char *format, ...);   //类似与printf
int refresh(void);    //刷新物理屏幕
int box(WINDOW *win_ptr, chtype vertical, chtype horizontal);   //围绕窗口绘制方框
int insch(chtype char_to_insert);   //插入一个字符(已有字符后移)
int insertln(void);   //插入空白行
int delch(void);
int deleteln(void);   //删除字符和空白行

int beep(void);   //终端响铃
int flash(void);   //闪烁

从屏幕读取字符

1 chtype inch(void);  //返回光标位置字符
2 int instr(char *string);  //
3 int innstr(char *string, int numbers);  //将返回内容写入字符数组中

清除屏幕

int erase(void);   //在屏幕的每个位置写上空白字符
int clear(void);    //使用一个终端命令来清除整个屏幕,内部调用了clearok来执行清屏操作,(在下次调用refresh时可以重现屏幕原文)
int clrtobot(void);   //清除光标位置到屏幕结尾的内容
int clrtoeol(void);    //清除光标位置到该行行尾的内容

安装curses

lee@ubuntu:~$ sudo apt-get install libncurses5-dev

查看库文件和头文件安装目录

lee@ubuntu:~$ whereis libcurses.*
libcurses: /usr/lib/x86_64-linux-gnu/libcurses.a /usr/lib/x86_64-linux-gnu/libcurses.so
lee@ubuntu:~$ whereis curses.h
curses: /usr/include/curses.h

linux环境实现贪吃蛇游戏

建立一个文件夹

mkdir testSnake
cd testSnake

编译一个程序mysnake.c

lee@ubuntu:~$ vim mysnake.c

mysnake.c代码来源于http://www.linuxidc.com/Linux/2011-08/41375.htm

编译链接生成可执行文件

lee@ubuntu:gcc mysnake.c -lcurses -o mysnake

执行

lee@ubuntu:./mysnake

结果如下
在这里插入图片描述

写在最后

第一次写一万字博客,我舒服了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值