-
- 一、用gcc生成静态库和动态库
-
- (1)编辑生成子程序hello.h、hello.c、main.c
- (2)将hello.c编译成.o文件
- (3)由.o文件创建静态库
- (4)在程序中使用静态库
- (5)由.o文件创建动态库文件
- (6)在程序中使用动态库
- 二、linux下静态库.a和.so库文件的生成和使用
-
- (1)静态库.a文件的生成与使用
- (2)共享库.so文件的生成和使用
- 三、GCC常用命令
-
- (1)C语言程序编译
- (2)多个程序文件的编译
- (3)检错
- (4)链接
- (5)ELF文件
- (6)使用nasm汇编编译器编译hello.asm
- 四、第三方库函数curses
-
- 用C语言编写一个贪吃蛇游戏
一、用gcc生成静态库和动态库
在创建库函数之前,先来准备举例用的源程序,并将函数库的源程序编译成.o文件。
(1)编辑生成子程序hello.h、hello.c、main.c
先创建一个目录,用来保存用到的文件
xh@ubuntu:~$ mkdir test1
xh@ubuntu:~$ cd test1
xh@ubuntu:~/test1$
然后用vim、nano或gedit等文本编辑器生成所需要的3个文件。
xh@ubuntu:~/test1$ vim hello.c
#include<stdio.h>
void hello(const char *name)
{
printf("Hello %s!\n",name);
}
利用vim编写完hello.c文件程序后,按esc
键,输入:wq
保存并退出;
在终端里输入nano hello.h
编辑生成hello.h文件,写完之后按ctrl+X
保存退出即可;
xh@ubuntu:~/test1$ nano hello.h
#ifndef HELLO_H
#define HELLO_H
void hello(const char *name);
#endif//HELLO_H
接着在终端里输入gedit main.c
,创建编辑main.c文件;
xh@ubuntu:~/test1$ gedit main.c
#include "hello.h"
int main()
{
hello("everyone");
return 0;
}
终端里面输入ls
就可以看到我们刚刚建立的三个文件。
xh@ubuntu:~/test1$ ls
hello.c hello.h main.c
(2)将hello.c编译成.o文件
利用gcc命令编译hello.c
xh@ubuntu:~/test1$ gcc -c hello.c
在unbuntu终端运行ls
命令看看是否生成了hello.o文件
xh@ubuntu:~/test1$ ls
hello.c hello.h hello.o main.c
可以看到已经生成了hello.o文件
(3)由.o文件创建静态库
静态库文件名的命名规范是以lib为前缀,紧接着跟静态库名,扩展名为.a。例如:我们将创建的静态库名为myhello,则静态库名就是libmyhello.a。在创建静态库时,需要注意这点。创建静态库用ar命令。在终端下输入ar -crv libmyhello.a hello.o
将创建静态库文件libmyhello。
xh@ubuntu:~/test1$ ar -crv libmyhello.a hello.o
a - hello.o
同样在终端运行ls
命令查看结果
xh@ubuntu:~/test1$ ls
hello.c hello.h hello.o libmyhello.a main.c
(4)在程序中使用静态库
先生成目标程序hello,生成目标程序hello可有3种方式:
方法一:
xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello
终端输入ls
命令可以查看到目标程序hello已生成
xh@ubuntu:~/test1$ ls
hello hello.c hello.h hello.o libmyhello.a main.c
自定义的库时,main.c还可放在-L.和-lmyhello之间,但是不能放在他俩之后,否则会提示myhello没定义。
方式二:
终端输入gcc main.c libmyhello.a -o hello
xh@ubuntu:~/test1$ gcc main.c libmyhello.a -o hello
方式三:
先在终端输入gcc -c main.c
生成main.o;
xh@ubuntu:~/test1$ gcc -c main.c
xh@ubuntu:~/test1$ ls
hello hello.c hello.h hello.o libmyhello.a main.c main.o
再输入gcc -o hello mian.o libmyhello.a
生成可执行文件。
xh@ubuntu:~/test1$ gcc -o hello main.o libmyhello.a
终端里输入./hello
就可以运行hello程序;
xh@ubuntu:~/test1$ ./hello
Hello everyone!
我们删除静态文件试试公用函数hello是否真的连接到目标文件hello中了。
xh@ubuntu:~/test1$ rm libmyhello.a
xh@ubuntu:~/test1$ ls
hello hello.c hello.h hello.o main.c main.o
xh@ubuntu:~/test1$ ./hello
Hello everyone!
由上可以看出,程序照常运行,静态库中的公用函数已经连接到目标文件中了。
(5)由.o文件创建动态库文件
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀lib,但其文件名扩展为.so。例如:我们将创建的动态库命名为myhello,则动态库文件名就是libmyhello.so。用gcc来创建动态库。
在系统命令提示符下输入gcc -shared -fPIC -o libmyhello.so hello.o
得到动态库文件libmyhello.so;
xh@ubuntu:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o
/usr/bin/ld: hello.o: relocation R_X86_64_32 against `.rodata' can not be used when making a shared object; recompile with -fPIC
hello.o: error adding symbols: Bad value
collect2: error: ld returned 1 exit status
可以看到这里出现了错误,这时的解决方法是将前面的gcc -c hello.c
换成gcc -fPIC -c hello.c
;
xh@ubuntu:~/test1$ gcc -fPIC -c hello.c
xh@ubuntu:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o
xh@ubuntu:~/test1$ ls
hello hello.c hello.h hello.o libmyhello.so main.c main.o
由上述命令结果可知,这时再执行前面生成动态文件的命令将不会出错。
(6)在程序中使用动态库
先用gcc命令生成目标文件
xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello
然后输入./hello
运行程序。
xh@ubuntu:~/test1$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
哦!出错了。看看错误提示,原来是找不到动态文件libmyhello.so。程序在运行时,会在/usr/lib和/lib等目录中查找需要的动态库文件。若找到,则载入动态库,否则提示错误并终止程序运行。我们将文件libmyhello.so复制到目录/usr/lib中,再试试。
xh@ubuntu:~/test1$ mv libmyhello.so /usr/lib
mv: cannot move 'libmyhello.so' to '/usr/lib/libmyhello.so': Permission denied
可以看到又出现了问题,这时我们可以尝试在终端输入sudo su
,输入密码,切换到root用户下,再输入cp libmyhello.so /usr/lib
命令,将libmyhello.so复制到/usr/lib目录之下,最后输入./hello
执行程序。
xh@ubuntu:~/test1$ sudo su
[sudo] password for xh:
root@ubuntu:/home/xh/test1# cp libmyhello.so /usr/lib
root@ubuntu:/home/xh/test1# ./hello
Hello everyone!
可以看到,这时已经成功输出结果。
回过头来,我们可以发现使用静态库和动态库编译生成目标程序使用的gcc命令完全一样,那当静态库和动态库同名时,gcc命令会使用哪个库文件呢?
首先我们先删除除.c和.h外的所有文件,利用rm 文件名
命令来实现;
xh@ubuntu:~/test1$ rm libmyhello.so
xh@ubuntu:~/test1$ ls
hello.c hello.h main.c main.o
xh@ubuntu:~/test1$ rm main.o
xh@ubuntu:~/test1$ ls
hello.c hello.h main.c
再来创建静态库文件libmyhello.a和动态文件libmyhello.so.
xh@ubuntu:~/test1$ ls
hello.c hello.h main.c
xh@ubuntu:~/test1$ gcc -fPIC -c hello.c
xh@ubuntu:~/test1$ ls
hello.c hello.h hello.o main.c
xh@ubuntu:~/test1$ ar -cr libmyhello.a hello.o
xh@ubuntu:~/test1$ ls
hello.c hello.h hello.o libmyhello.a main.c
xh@ubuntu:~/test1$ gcc -shared -fPIC -o libmyhello.so hello.o
xh@ubuntu:~/test1$ ls
hello.c hello.h hello.o libmyhello.a libmyhello.so main.c
接着,我们运行gcc命令来使用函数库myhello生成目录文件hello,并运行程序hello。
xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello
xh@ubuntu:~/test1$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
可以看到上诉程序运行时有误,可以根据错误提示得出,当静态库和动态库同名时,gcc命令将优先使用动态库,默认去连/usr/lib
和/lib
等目录的动态库,将文件libmyhello.so复制到目录/usr/lib中即可。
xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello
xh@ubuntu:~/test1$ ./hello
./hello: error while loading shared libraries: libmyhello.so: cannot open shared object file: No such file or directory
xh@ubuntu:~/test1$ sudo su
root@ubuntu:/home/xh/test1# cp libmyhello.so /usr/lib
root@ubuntu:/home/xh/test1# exit
exit
xh@ubuntu:~/test1$ gcc -o hello main.c -L. -lmyhello
xh@ubuntu:~/test1$ ls
hello hello.c hello.h hello.o libmyhello.a libmyhello.so main.c
xh@ubuntu:~/test1$ ./hello
Hello everyone!
二、linux下静态库.a和.so库文件的生成和使用
首先创建一个目录
xh@ubuntu:~/test1$ mkdir test2
xh@ubuntu:~/test1$ cd test2
然后用vim、nano或gedit等文本编辑器编辑生成所需要的四个文件x2x.c、x2y.c、x2.h、main.c。
xh@ubuntu:~/test1/test2$ vim x2x.c
#include<stdio.h>
float x2x(int a,int b)
{
float sum;
sum=a+b;
print("a+b=%.2f\n",sum);
}
#include<stdio.h>
float x2y(int m,int n)
{
float mul;
mul=m*n;
printf("m*n=%.2f\n",mul);
}
#ifndef x2_H
#define x2_H
float x2x(int,int);
float x2y(int,int);
#endif
#include<stdio.h>
#include "x2.h"
int main()
{
x2x(3,4);
x2y(5,6);
exit(0);
}
(1)静态库.a文件的生成与使用
首先生成目标文件,即.o文件;
xh@ubuntu:~/test2$ gcc -c x2x.c x2y.c
xh@ubuntu:~/test2$ ls
main.c x2.h x2x.c x2x.o x2y.c x2y.o
然后生成静态库.a文件;
xh@ubuntu:~/test2$ ar crv libafile.a x2x.o x2y.o
a - x2x.o
a - x2y.o
xh@ubuntu:~/test2$ ls
libafile.a main.c x2.h x2x.c x2x.o x2y.c x2y.o
使用.a文件,创建可执行程序;
root@ubuntu:/home/xh/test2# gcc -o main main.c libafile.a
root@ubuntu:/home/xh/test2# ./main
a+b=7.00
m*n=30.00
在终端命令行里输入ls -l main
即可查看生成的目标程序main的大小。
root@ubuntu:/home/xh/test2# ls -l main
-rwxr-xr-x 1 root root 8720 Oct 16 17:02 main
(2)共享库.so文件的生成和使用
首先生成目标文件,即.o文件;
root@ubuntu:/home/xh/test2# gcc -c -fPIC x2x.c x2y.c
生成共享库.so文件;
xh@ubuntu:~/test2$ gcc -shared -fpic -o libsofile.so x2x.o x2y.o
xh@ubuntu:~/test2$ ls
libafile.a libsofile.so main main.c main.o x2.h x2x.c x2x.o x2y.c x2y.o
使用.so库文件,创建可执行程序;
xh@ubuntu:~/test2$ gcc -o main main.c libsofile.so
xh@ubuntu:~/test2$ ./main
./main: error while loading shared libraries: libsofile.so: cannot open shared object file: No such file or directory
这里出现了错误,运行ldd main
,查看情况;
xh@ubuntu:~/test2$ ldd mainlinux-vdso.so.1 => (0x00007fff83bdb000)libsofile.so => not foundlibc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f0787635000)/lib64/ld-linux-x86-64.so.2 (0x00007f07879ff000)
发现确实找不到.so文件。这是由于linux自身系统设定的原因,其只在/lib和/usr/lib下搜索对应的.so文件,只需将.so文件复制到其路径下即可;
xh@ubuntu:~/test2$ sudo su
[sudo] password for xh:
root@ubuntu:/home/xh/test2# cp libsofile.so /usr/lib
root@ubuntu:/home/xh/test2# ./main
a+b=7.00
m*n=30.00
可以看到程序现在能正常运行了。
在终端命令行里输入ls -l main
即可查看生成的目标程序main的大小。
root@ubuntu:/home/xh/test2# ls -l main
-rwxrwxr-x 1 xh xh 8624 Oct 16 17:29 main
将动态库生成的可执行程序和静态库相比较,发现动态库生成的可执行程序要小一些。
三、GCC常用命令
GCC即GNU C Compiler,GCC不仅支持C语言,它还支持Ada语言、C++语言、Java语言等。
(1)C语言程序编译
下面举一个C语言例子:
//test5.c
#include<stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
这个程序一步到位的命令是gcc test5.c -o test5
实质上,上述编译过程是分四个阶段进行的,即预处理(也称为预编译)、编译、汇编和连接。
使用gcc -E test5.c -o test5.i
或gcc -E test5.c
进行预编译;
xh@ubuntu:~$ gcc -E test5.c -o test5.i
xh@ubuntu:~$ ls
a.out Downloads hello main1.c mian Pictures sub1.o test1 test5 test.c
Desktop examples.desktop hello.c main1.o mkefile Public sub.c test2 test5.c Videos
Documents func.h main makefile Music sub1.c Templates test3 test5.i
此时生成test5.i文件
预处理之后,可直接对生成的test5.i文件编译,生成汇编代码:
xh@ubuntu:~$ gcc -S test5.i -o test5.s
xh@ubuntu:~$ ls
a.out Downloads hello main1.c mian Pictures sub1.o test1 test5 test5.s
Desktop examples.desktop hello.c main1.o mkefile Public sub.c test2 test5.c test.c
Documents func.h main makefile Music sub1.c Templates test3 test5.i Videos
gcc的-S选项,表示在程序编译期间,在生成汇编代码之后,停止,-o输出汇编代码文件。
将生产的汇编代码test5.s,用gas汇编器将其编译成目标文件:
xh@ubuntu:~$ gcc -c test5.s -o test5.o
xh@ubuntu:~$ ls
a.out Downloads hello main1.c mian Pictures sub1.o test1 test5 test5.o Videos
Desktop examples.desktop hello.c main1.o mkefile Public sub.c test2 test5.c test5.s
Documents func.h main makefile Music sub1.c Templates test3 test5.i test.c
gcc连接器是gas提供的,负责将程序中的目标文件与所需的所以附加文件的目标文件连接起来,最终生成可执行文件。
xh@ubuntu:~$ gcc test5.o -o test5
xh@ubuntu:~$ ls
a.out Downloads hello main1.c mian Pictures sub1.o test1 test5 test5.o Videos
Desktop examples.desktop hello.c main1.o mkefile Public sub.c test2 test5.c test5.s
Documents func.h main makefile Music sub1.c Templates test3 test5.i test.c
运行可执行文件。
xh@ubuntu:~$ ./test5
Hello World!
(2)多个程序文件的编译
假设有一个由test6.c和test7.c两个源文件组成的程序,为了对他们进行编译,并最终生成可执行程序test,可使用gcc test6.c test7.c -o test
命令。
(3)检错
gcc -pedantic illcode.c -o illcode
-pedantic 编译选项并不能保证被编译程序与 ANSI/ISO C 标准的完全兼容,它仅仅只能用来帮助
Linux 程序员离这个目标越来越近。或者换句话说,-pedantic 选项能够帮助程序员发现一些不符合
ANSI/ISO C 标准的代码,但不是全部,事实上只有 ANSI/ISO C 语言标准中要求进行编译器诊断的
那些情况,才有可能被 GCC 发现并提出警告。
除了-pedantic 之外,GCC 还有一些其它编译选项也能够产生有用的警告信息。这些选项大多以-W
开头,其中最有价值的当数-Wall 了,使用它能够使 GCC 产生尽可能多的警告信息。
gcc -Wall illcode.c -o illcode
GCC 给出的警告信息虽然从严格意义上说不能算作错误,但却很可能成为错误的栖身之所。一个优
秀的 Linux 程序员应该尽量避免产生警告信息,使自己的代码始终保持标准、健壮的特性。所以将
警告信息当成编码错误来对待,是一种值得赞扬的行为!所以,在编译程序时带上-Werror 选项,那
么 GCC 会在所有产生警告的地方停止编译,迫使程序员对自己的代码进行修改,如下:
gcc -Werror test.c -o test
(4)链接
静态库链接时搜索路径顺序:
- ld 会去找 GCC 命令中的参数-L
- 再找 gcc 的环境变量 LIBRARY_PATH
- 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初 compile gcc 时写在程序内的
动态链接时、执行时搜索路径顺序:
- 编译目标代码时指定的动态库搜索路径
- 环境变量 LD_LIBRARY_PATH 指定的动态库搜索路径
- 配置文件/etc/ld.so.conf 中指定的动态库搜索路径
- 默认的动态库搜索路径/lib
- 默认的动态库搜索路径/usr/lib
(5)ELF文件
1、ELF文件的段
ELF 文件位于 ELF Header 和 Section Header Table 之间的都
是段(Section)。一个典型的 ELF 文件包含下面几个段:
.text:已编译程序的指令代码段。
.rodata:ro 代表 read only,即只读数据(譬如常数 const)。
.data:已初始化的 C 程序全局变量和静态局部变量。
.bss:未初始化的 C 程序全局变量和静态局部变量。
.debug:调试符号表,调试器用此段的信息帮助调试。
可以使用 readelf -S 查看其各个 section 的信息如下:
xh@ubuntu:~$ readelf -S hello
There are 31 section headers, starting at offset 0x19d8:Section Headers:[Nr] Name Type Address OffsetSize EntSize Flags Link Info Align[ 0] NULL 0000000000000000 000000000000000000000000 0000000000000000 0 0 0[ 1] .interp PROGBITS 0000000000400238 00000238000000000000001c 0000000000000000 A 0 0 1[ 2] .note.ABI-tag NOTE 0000000000400254 000002540000000000000020 0000000000000000 A 0 0 4[ 3] .note.gnu.build-i NOTE 0000000000400274 000002740000000000000024 0000000000000000 A 0 0 4[ 4] .gnu.hash GNU_HASH 0000000000400298 00000298000000000000001c 0000000000000000 A 5 0 8[ 5] .dynsym DYNSYM 00000000004002b8 000002b80000000000000060 0000000000000018 A 6 1 8[ 6] .dynstr STRTAB 0000000000400318 00000318000000000000003d 0000000000000000 A 0 0 1[ 7] .gnu.version VERSYM 0000000000400356 000003560000000000000008 0000000000000002 A 5 0 2[ 8] .gnu.version_r VERNEED 0000000000400360 000003600000000000000020 0000000000000000 A 6 1 8[ 9] .rela.dyn RELA 0000000000400380 000003800000000000000018 0000000000000018 A 5 0 8[10] .rela.plt RELA 0000000000400398 000003980000000000000030 0000000000000018 AI 5 24 8[11] .init PROGBITS 00000000004003c8 000003c8000000000000001a 0000000000000000 AX 0 0 4[12] .plt PROGBITS 00000000004003f0 000003f00000000000000030 0000000000000010 AX 0 0 16[13] .plt.got PROGBITS 0000000000400420 000004200000000000000008 0000000000000000 AX 0 0 8[14] .text PROGBITS 0000000000400430 000004300000000000000182 0000000000000000 AX 0 0 16[15] .fini PROGBITS 00000000004005b4 000005b40000000000000009 0000000000000000 AX 0 0 4[16] .rodata PROGBITS 00000000004005c0 000005c00000000000000010 0000000000000000 A 0 0 4[17] .eh_frame_hdr PROGBITS 00000000004005d0 000005d00000000000000034 0000000000000000 A 0 0 4[18] .eh_frame PROGBITS 0000000000400608 0000060800000000000000f4 0000000000000000 A 0 0 8[19] .init_array INIT_ARRAY 0000000000600e10 00000e100000000000000008 0000000000000000 WA 0 0 8[20] .fini_array FINI_ARRAY 0000000000600e18 00000e180000000000000008 0000000000000000 WA 0 0 8[21] .jcr PROGBITS 0000000000600e20 00000e200000000000000008 0000000000000000 WA 0 0 8[22] .dynamic DYNAMIC 0000000000600e28 00000e2800000000000001d0 0000000000000010 WA 6 0 8[23] .got PROGBITS 0000000000600ff8 00000ff80000000000000008 0000000000000008 WA 0 0 8[24] .got.plt PROGBITS 0000000000601000 000010000000000000000028 0000000000000008 WA 0 0 8[25] .data PROGBITS 0000000000601028 000010280000000000000010 0000000000000000 WA 0 0 8[26] .bss NOBITS 0000000000601038 000010380000000000000008 0000000000000000 WA 0 0 1[27] .comment PROGBITS 0000000000000000 000010380000000000000035 0000000000000001 MS 0 0 1[28] .shstrtab STRTAB 0000000000000000 000018cc000000000000010c 0000000000000000 0 0 1[29] .symtab SYMTAB 0000000000000000 000010700000000000000648 0000000000000018 30 47 8[30] .strtab STRTAB 0000000000000000 000016b80000000000000214 0000000000000000 0 0 1
Key to Flags:W (write), A (alloc), X (execute), M (merge), S (strings), l (large)I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)O (extra OS processing required) o (OS specific), p (processor specific)
2、反编译ELF
由于 ELF 文件无法被当做普通文本文件打开,如果希望直接查看一个 ELF 文件包
含的指令和数据,需要使用反汇编的方法。
使用 objdump -D 对其进行反汇编如下:
xh@ubuntu:~$ objdump -S hellohello: file format elf64-x86-64Disassembly of section .init:00000000004003c8 <_init>:4003c8: 48 83 ec 08 sub $0x8,%rsp4003cc: 48 8b 05 25 0c 20 00 mov 0x200c25(%rip),%rax # 600ff8 <_DYNAMIC+0x1d0>4003d3: 48 85 c0 test %rax,%rax4003d6: 74 05 je 4003dd <_init+0x15>4003d8: e8 43 00 00 00 callq 400420 <__libc_start_main@plt+0x10>4003dd: 48 83 c4 08 add $0x8,%rsp4003e1: c3 retq Disassembly of section .plt:00000000004003f0 <puts@plt-0x10>:4003f0: ff 35 12 0c 20 00 pushq 0x200c12(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>4003f6: ff 25 14 0c 20 00 jmpq *0x200c14(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>4003fc: 0f 1f 40 00 nopl 0x0(%rax)0000000000400400 <puts@plt>:400400: ff 25 12 0c 20 00 jmpq *0x200c12(%rip) # 601018 <_GLOBAL_OFFSET_TABLE_+0x18>400406: 68 00 00 00 00 pushq $0x040040b: e9 e0 ff ff ff jmpq 4003f0 <_init+0x28>0000000000400410 <__libc_start_main@plt>:400410: ff 25 0a 0c 20 00 jmpq *0x200c0a(%rip) # 601020 <_GLOBAL_OFFSET_TABLE_+0x20>400416: 68 01 00 00 00 pushq $0x140041b: e9 d0 ff ff ff jmpq 4003f0 <_init+0x28>Disassembly of section .plt.got:0000000000400420 <.plt.got>:400420: ff 25 d2 0b 20 00 jmpq *0x200bd2(%rip) # 600ff8 <_DYNAMIC+0x1d0>400426: 66 90 xchg %ax,%axDisassembly of section .text:0000000000400430 <_start>:400430: 31 ed xor %ebp,%ebp400432: 49 89 d1 mov %rdx,%r9400435: 5e pop %rsi400436: 48 89 e2 mov %rsp,%rdx400439: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp40043d: 50 push %rax40043e: 54 push %rsp40043f: 49 c7 c0 b0 05 40 00 mov $0x4005b0,%r8400446: 48 c7 c1 40 05 40 00 mov $0x400540,%rcx40044d: 48 c7 c7 26 05 40 00 mov $0x400526,%rdi400454: e8 b7 ff ff ff callq 400410 <__libc_start_main@plt>400459: f4 hlt 40045a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)0000000000400460 <deregister_tm_clones>:400460: b8 3f 10 60 00 mov $0x60103f,%eax400465: 55 push %rbp400466: 48 2d 38 10 60 00 sub $0x601038,%rax40046c: 48 83 f8 0e cmp $0xe,%rax400470: 48 89 e5 mov %rsp,%rbp400473: 76 1b jbe 400490 <deregister_tm_clones+0x30>400475: b8 00 00 00 00 mov $0x0,%eax40047a: 48 85 c0 test %rax,%rax40047d: 74 11 je 400490 <deregister_tm_clones+0x30>40047f: 5d pop %rbp400480: bf 38 10 60 00 mov $0x601038,%edi400485: ff e0 jmpq *%rax400487: 66 0f 1f 84 00 00 00 nopw 0x0(%rax,%rax,1)40048e: 00 00 400490: 5d pop %rbp400491: c3 retq 400492: 0f 1f 40 00 nopl 0x0(%rax)400496: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)40049d: 00 00 00 00000000004004a0 <register_tm_clones>:4004a0: be 38 10 60 00 mov $0x601038,%esi4004a5: 55 push %rbp4004a6: 48 81 ee 38 10 60 00 sub $0x601038,%rsi4004ad: 48 c1 fe 03 sar $0x3,%rsi4004b1: 48 89 e5 mov %rsp,%rbp4004b4: 48 89 f0 mov %rsi,%rax4004b7: 48 c1 e8 3f shr $0x3f,%rax4004bb: 48 01 c6 add %rax,%rsi4004be: 48 d1 fe sar %rsi4004c1: 74 15 je 4004d8 <register_tm_clones+0x38>4004c3: b8 00 00 00 00 mov $0x0,%eax4004c8: 48 85 c0 test %rax,%rax4004cb: 74 0b je 4004d8 <register_tm_clones+0x38>4004cd: 5d pop %rbp4004ce: bf 38 10 60 00 mov $0x601038,%edi4004d3: ff e0 jmpq *%rax4004d5: 0f 1f 00 nopl (%rax)4004d8: 5d pop %rbp4004d9: c3 retq 4004da: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)00000000004004e0 <__do_global_dtors_aux>:4004e0: 80 3d 51 0b 20 00 00 cmpb $0x0,0x200b51(%rip) # 601038 <__TMC_END__>4004e7: 75 11 jne 4004fa <__do_global_dtors_aux+0x1a>4004e9: 55 push %rbp4004ea: 48 89 e5 mov %rsp,%rbp4004ed: e8 6e ff ff ff callq 400460 <deregister_tm_clones>4004f2: 5d pop %rbp4004f3: c6 05 3e 0b 20 00 01 movb $0x1,0x200b3e(%rip) # 601038 <__TMC_END__>4004fa: f3 c3 repz retq 4004fc: 0f 1f 40 00 nopl 0x0(%rax)0000000000400500 <frame_dummy>:400500: bf 20 0e 60 00 mov $0x600e20,%edi400505: 48 83 3f 00 cmpq $0x0,(%rdi)400509: 75 05 jne 400510 <frame_dummy+0x10>40050b: eb 93 jmp 4004a0 <register_tm_clones>40050d: 0f 1f 00 nopl (%rax)400510: b8 00 00 00 00 mov $0x0,%eax400515: 48 85 c0 test %rax,%rax400518: 74 f1 je 40050b <frame_dummy+0xb>40051a: 55 push %rbp40051b: 48 89 e5 mov %rsp,%rbp40051e: ff d0 callq *%rax400520: 5d pop %rbp400521: e9 7a ff ff ff jmpq 4004a0 <register_tm_clones>0000000000400526 <main>:400526: 55 push %rbp400527: 48 89 e5 mov %rsp,%rbp40052a: bf c4 05 40 00 mov $0x4005c4,%edi40052f: e8 cc fe ff ff callq 400400 <puts@plt>400534: b8 00 00 00 00 mov $0x0,%eax400539: 5d pop %rbp40053a: c3 retq 40053b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)0000000000400540 <__libc_csu_init>:400540: 41 57 push %r15400542: 41 56 push %r14400544: 41 89 ff mov %edi,%r15d400547: 41 55 push %r13400549: 41 54 push %r1240054b: 4c 8d 25 be 08 20 00 lea 0x2008be(%rip),%r12 # 600e10 <__frame_dummy_init_array_entry>400552: 55 push %rbp400553: 48 8d 2d be 08 20 00 lea 0x2008be(%rip),%rbp # 600e18 <__init_array_end>40055a: 53 push %rbx40055b: 49 89 f6 mov %rsi,%r1440055e: 49 89 d5 mov %rdx,%r13400561: 4c 29 e5 sub %r12,%rbp400564: 48 83 ec 08 sub $0x8,%rsp400568: 48 c1 fd 03 sar $0x3,%rbp40056c: e8 57 fe ff ff callq 4003c8 <_init>400571: 48 85 ed test %rbp,%rbp400574: 74 20 je 400596 <__libc_csu_init+0x56>400576: 31 db xor %ebx,%ebx400578: 0f 1f 84 00 00 00 00 nopl 0x0(%rax,%rax,1)40057f: 00 400580: 4c 89 ea mov %r13,%rdx400583: 4c 89 f6 mov %r14,%rsi400586: 44 89 ff mov %r15d,%edi400589: 41 ff 14 dc callq *(%r12,%rbx,8)40058d: 48 83 c3 01 add $0x1,%rbx400591: 48 39 eb cmp %rbp,%rbx400594: 75 ea jne 400580 <__libc_csu_init+0x40>400596: 48 83 c4 08 add $0x8,%rsp40059a: 5b pop %rbx40059b: 5d pop %rbp40059c: 41 5c pop %r1240059e: 41 5d pop %r134005a0: 41 5e pop %r144005a2: 41 5f pop %r154005a4: c3 retq 4005a5: 90 nop4005a6: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)4005ad: 00 00 00 00000000004005b0 <__libc_csu_fini>:4005b0: f3 c3 repz retq Disassembly of section .fini:00000000004005b4 <_fini>:4005b4: 48 83 ec 08 sub $0x8,%rsp4005b8: 48 83 c4 08 add $0x8,%rsp4005bc: c3 retq
(6)使用nasm汇编编译器编译hello.asm
在终端命令行内输入sudo apt-get install nasm
安装nasm编译器。
编写一个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 ; 调用内核功能
使用nasm -f win64 hello.asm
命令进行编译;
进而生成可执行程序进行运行;
xh@ubuntu:~$ touch hello.asm
xh@ubuntu:~$ gedit hello.asm
xh@ubuntu:~$ nasm -f elf64 hello.asm
xh@ubuntu:~$ ld -s -o hello hello.o
xh@ubuntu:~$ ./hello
Hello, world!
利用size+文件名
即可查看其文件大小。
xh@ubuntu:~$ size hellotext data bss dec hex filename34 14 0 48 30 hello
再查看c代码里面编译生成的目标程序的大小:
xh@ubuntu:~$ size hellotext data bss dec hex filename1182 552 8 1742 6ce hello
可以发现用nasm汇编编译器生成的目标文件更节省空间。
四、第三方库函数curses
curses 函数库的名称来自它所提供的功能,它能够优化光标的移动并减少需要对屏幕进行的刷新,减少了必须向字符终端发送的字符数目。
当对使用curse函数库的程序进行编译时,必须在程序中包含头文件curses.h,需要在命令行中用-lcurses选项对curses函数库进行链接,所有的curses程序必须从初始函数initscr开始,以函数endwin结束。函数initscr在一个程序中只能调用一次。
在 win10 系统中,“控制面板”–>“程序”—>“启用或关闭Windows功能”,启用 “telnet client” 和"适用于Linux的Windows子系统"(后面会使用)。 然后打开一个cmd命令行窗口,命令行输入 telnet bbs.newsmth.net,以游客身份体验一下即将绝迹的远古时代的 BBS (一个用键盘光标控制的终端程序)。
在ubuntu中安装cueses库,需要用到sudo apt-get install libncurses5-dev
命令。安装完成后我们可以用find / -name curses.h
命令查看头文件和库文件所在的目录:
xh@ubuntu:~$ sudo su
root@ubuntu:/home/xh# find / -name curses.h
/usr/include/curses.h
find: ‘/run/user/1000/gvfs’: Permission denied
xh@ubuntu:~$ find / -name term.h
find: ‘/etc/cups/ssl’: Permission denied
find: ‘/etc/ssl/private’: Permission denied
find: ‘/etc/polkit-1/localauthority’: Permission denied
find: ‘/proc/tty/driver’: Permission denied
find: ‘/proc/1/task/1/fd’: Permission denied
find: ‘/proc/1/task/1/fdinfo’: Permission denied
find: ‘/proc/1/task/1/ns’: Permission denied
find: ‘/proc/1/fd’: Permission denied
find: ‘/proc/1/map_files’: Permission denied
find: ‘/proc/1/fdinfo’: Permission denied
find: ‘/proc/1/ns’: Permission denied
find: ‘/proc/2/task/2/fd’: Permission denied
find: ‘/proc/2/task/2/fdinfo’: Permission denied
/usr/include/term.h
有许多目录因为权限的原因无法遍历。
那么我们可以换一下试试,用whereis
代替find:
xh@ubuntu:~$ whereis / -name curses.h
: /usr/bin/. /usr/sbin/. /usr/lib/. /bin/. /sbin/. /etc/. /lib/. /lib64/. /usr/games/. /usr/local/bin/. /usr/local/sbin/. /usr/local/etc/. /usr/local/lib/. /usr/local/games/. /usr/include/. /usr/local/. /usr/libexec/. /usr/share/. /usr/share/man/hy/. /usr/share/man/bs/. /usr/share/man/cy/. /usr/share/man/shn/. /usr/share/man/sl/. /usr/share/man/nb/. /usr/share/man/ms/. /usr/share/man/kk/. /usr/share/man/man2/. /usr/share/man/ps/. /usr/share/man/man9/. /usr/share/man/fr.ISO8859-1/. /usr/share/man/fr_CA/. /usr/share/man/man8/. /usr/share/man/si/. /usr/share/man/pl/. /usr/share/man/eo/. /usr/share/man/el/. /usr/share/man/uk/. /usr/share/man/my/. /usr/share/man/ku/. /usr/share/man/bg/. /usr/share/man/lt/. /usr/share/man/ro/. /usr/share/man/tr/. /usr/share/man/sq/. /usr/share/man/zh_HK/. /usr/share/man/man4/. /usr/share/man/io/. /usr/share/man/man1/. /usr/share/man/es/. /usr/share/man/et/. /usr/share/man/fo/. /usr/share/man/ta/. /usr/share/man/hu/. /usr/share/man/ne/. /usr/share/man/gl/. /usr/share/man/eu/. /usr/share/man/gd/. /usr/share/man/zh_TW/. /usr/share/man/pt_BR/. /usr/share/man/fa/. /usr/share/man/it/. /usr/share/man/hi/. /usr/share/man/pa/. /usr/share/man/ce/. /usr/share/man/lv/. /usr/share/man/sv/. /usr/share/man/ca/. /usr/share/man/man3/. /usr/share/man/fy/. /usr/share/man/se/. /usr/share/man/he/. /usr/share/man/bn/. /usr/share/man/sr/. /usr/share/man/ug/. /usr/share/man/uz/. /usr/share/man/man7/. /usr/share/man/bo/. /usr/share/man/ru/. /usr/share/man/en_GB/. /usr/share/man/fr.UTF-8/. /usr/share/man/id/. /usr/share/man/pt/. /usr/share/man/az/. /usr/share/man/th/. /usr/share/man/ar/. /usr/share/man/man5/. /usr/share/man/ca@valencia/. /usr/share/man/en_CA/. /usr/share/man/cs/. /usr/share/man/ko/. /usr/share/man/vi/. /usr/share/man/oc/. /usr/share/man/fi/. /usr/share/man/mhr/. /usr/share/man/de/. /usr/share/man/nn/. /usr/share/man/zh_CN/. /usr/share/man/ast/. /usr/share/man/hr/. /usr/share/man/sk/. /usr/share/man/fr/. /usr/share/man/km/. /usr/share/man/te/. /usr/share/man/nl/. /usr/share/man/en_AU/. /usr/share/man/ml/. /usr/share/man/da/. /usr/share/man/be/. /usr/share/man/man6/. /usr/share/man/ja/. /usr/share/info/. /usr/src/linux-headers-4.15.0-120-generic/.tmp_versions /usr/src/linux-headers-4.15.0-120-generic/.config /usr/src/linux-headers-4.15.0-120-generic/. /usr/src/linux-headers-4.15.0-112-generic/.tmp_versions /usr/src/linux-headers-4.15.0-112-generic/.config /usr/src/linux-headers-4.15.0-112-generic/. /usr/src/linux-headers-4.15.0-112/. /usr/src/linux-headers-4.15.0-120/.
可以看到,whereis
可以访问。
用C语言编写一个贪吃蛇游戏
完整代码:http://www.linuxidc.com/Linux/2011-08/41375.htm
将代码在linux ubuntu中写入.c文件,在终端输入gcc snake.c -lcurses -o snake
命令生成目标可执行程序;
最后输入./snake
即可运行。
总结:gcc编译器使用广泛,命令也比较多样,静态库和动态库在linux里面都是比较重要的一个概念,关于curses应用比较复杂,要想搞懂,还需要多费一些时间。在编写命令过程中也是在不断尝试,遇到报错也不要慌,遇到了就是赚到,这次想办法解决了,下次就能轻松解决。