关于可执行程序是如何被组装的
用 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
结果如下
写在最后
第一次写一万字博客,我舒服了