Linux下的终端操作之C编译器
C编译器:GCC
gcc常用编译选项说明(gcc:即gnu c complier)
-
-o 产生可执行代码
-
-c 产生目标代码
-
-O1 一级优化编译
以下的操作是在CentOS7下操作的
单个源文件,转换成可执行代码
- 建立源程序(.c文件)
先在桌面建个文件夹(主要是为了方便找,也可以在其他地方建文件夹,但是要记住文件夹路径,便于在终端控制台上进行操作),例:建一个名为"file"的文件夹,在“file”文件夹下,创建了一个空文档,将该文档改名为hello.c
打开该文档输入hello.c文件内容,并保存。
简单的代码示例:
#include <stdio.h>
int main()
{
printf(“hello,world!\n”);
return 0;
}
- 生成可执行文件(默认可执行文件的名字是a.out,也可自己起名,文件名可以不带任何后缀)
启动terminal窗口,并切换到文件夹“file”,在terminal命令行下输入:“cd Desktop/file/” (桌面路径一般为Desktop)。输入gcc hello.c 输出的可执行代码名为a. out
或gcc –O1 -o hello hello.c 输出名为hello 也可以gcc -o hello hello.c
-
多个源文件,转换成可执行代码
1)建立源程序1(first.c文件)
打开文本编辑程序输入first.c文件内容。
2)生成源程序1的目标代码(first.o文件)
启动terminal窗口,并切换到文件夹“file”,输入
gcc –O1 -c first.c
3)建立源程序2(second.c文件)
打开文本编辑程序输入second.c文件内容。
3)生成可执行文件。
启动terminal窗口,并切换到文件夹“file”,
输入:
gcc –O1 –o third first.o second.c(可执行文件的名字third)
-
在终端界面执行可执行文件
- ./+可执行文件名
直接在命令行上输出结果
生成目标代码
gcc -O1 -c first.c
生成拓展名为.o
生成汇编代码文件
gcc -O1 -S first.c(文件名)
生成汇编指令
查看二值代码文件
用objdump
objdump -d 目标代码文件名/可执行代码文件名
例如:
objdump -d code.o
或
objdump -d a.out
进行反编译,查看机器码和汇编代码。
例如:
[zoey@zoeycentos7 桌面]$ objdump -d swap
swap: 文件格式 elf64-x86-64
Disassembly of section .init:
00000000004003a8 <_init>:
4003a8: 48 83 ec 08 sub $0x8,%rsp
4003ac: 48 8b 05 45 0c 20 00 mov 0x200c45(%rip),%rax # 600ff8 <__gmon_start__>
4003b3: 48 85 c0 test %rax,%rax
4003b6: 74 05 je 4003bd <_init+0x15>
4003b8: e8 33 00 00 00 callq 4003f0 <__gmon_start__@plt>
4003bd: 48 83 c4 08 add $0x8,%rsp
4003c1: c3 retq
Disassembly of section .plt:
00000000004003d0 <.plt>:
4003d0: ff 35 32 0c 20 00 pushq 0x200c32(%rip) # 601008 <_GLOBAL_OFFSET_TABLE_+0x8>
4003d6: ff 25 34 0c 20 00 jmpq *0x200c34(%rip) # 601010 <_GLOBAL_OFFSET_TABLE_+0x10>
4003dc: 0f 1f 40 00 nopl 0x0(%rax)
00000000004003e0 <__libc_start_main@plt>:
4003e0: ff 25 32 0c 20 00 jmpq *0x200c32(%rip) # 601018 <__libc_start_main@GLIBC_2.2.5>
4003e6: 68 00 00 00 00 pushq $0x0
4003eb: e9 e0 ff ff ff jmpq 4003d0 <.plt>
00000000004003f0 <__gmon_start__@plt>:
4003f0: ff 25 2a 0c 20 00 jmpq *0x200c2a(%rip) # 601020 <__gmon_start__>
4003f6: 68 01 00 00 00 pushq $0x1
4003fb: e9 d0 ff ff ff jmpq 4003d0 <.plt>
Disassembly of section .text:
0000000000400400 <_start>:
400400: 31 ed xor %ebp,%ebp
400402: 49 89 d1 mov %rdx,%r9
400405: 5e pop %rsi
400406: 48 89 e2 mov %rsp,%rdx
400409: 48 83 e4 f0 and $0xfffffffffffffff0,%rsp
40040d: 50 push %rax
40040e: 54 push %rsp
40040f: 49 c7 c0 90 05 40 00 mov $0x400590,%r8
400416: 48 c7 c1 20 05 40 00 mov $0x400520,%rcx
40041d: 48 c7 c7 0f 05 40 00 mov $0x40050f,%rdi
400424: e8 b7 ff ff ff callq 4003e0 <__libc_start_main@plt>
400429: f4 hlt
40042a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
中间省略........
0000000000400590 <__libc_csu_fini>:
400590: f3 c3 repz retq
Disassembly of section .fini:
0000000000400594 <_fini>:
400594: 48 83 ec 08 sub $0x8,%rsp
400598: 48 83 c4 08 add $0x8,%rsp
40059c: c3 retq
进入ROOT权限
需要权限才能进入调试
[zoey@zoeycentos7 ~]$ su
密码:
输入密码进入根权限
GDB调试器
程序编写后难免会出现各种错误。当程序完成编译时,隐藏的错误可能会使程序无法正常运行,或者不能实现预期的功能。简单的程序或浅显的错误可依赖程序员的经验判断出故障点,但现在的软件规模越来越大,调试起来也就越来越困难。调试器是帮助程序员修改错误的得力工具,常用的断点、单步跟踪等功能可帮助程序员快速找到故障点。
程序调试gdb及过程分析
通过gdb和objdump工具的使用,找到程序在栈中已存在的数据或者汇编代码本身的代码逻辑,加深对汇编代码的理解,加深理解对栈在程序运行中的重要作用。
Linux中最常用的调试工具是GDB, GDB调试器是GNU项目的子项目。该程序提供了所有常用调试功能,是Linux系统中最为简单快捷的调试工具。由于当前图形用户界面(GUI)普及,大量基于GUI的调试器被开发和运行在Linux上。它们大多是以GDB为核心配上GUI,即用户通过GUI发出命令,这些命令依次被传送给GDB,其中一个是DDD,意为数据显示调试器。在一些集成开发环境如Eclipse中,也提供了调试功能,并且以GDB为核心。
常见操作:
file<文件名> : 在GDB中打开执行文件
break : 设置断点,支持的形式有break行号、break函数名称、break行号/函数名称 if 条件
info : 查看和可执行程序相关的各种信息
kill : 终止正在调试的程序
print : 显示变量或表达式的值
set args : 设置调试程序的运行参数
delete : 删除设置的某个断点或观测点
clear : 删除设置在指定行号或函数上的断点
continue : 从断点处继续执行程序
list : 列出GDB中打开的可执行文件代码
watch : 在程序中设置观测点
run : 运行打开的可执行文件
next : 单步执行程序step进入所调用的函数内部,查看执行情况
whatis : 查看变量或函数类型,调用格式为"whatis 变量名/函数名”
ptype : 显示数据结构定义情况
make : 编译程序
quit : 退出GDB
gdb常用命令操作:
1) 进入gdb:
gdb 可执行文件名 例: gdb a.out
2) gdb中反汇编查看各函数
disas 函数名 例: disas main
3) gdb中连续运行程序
run
4)gdb中调试运行:
a) 设置断点break,可以设置多个断点:break *地址 或 break 函数名
例如:break swap break *0x4003fb (地址)
b) 设置断点后重新运行*,*这次运行会停在第一个断点处
run
c) 从断点处单步运行,每次执行一条指令就停下来
stepi
d) 从断点处开始连续运行,直到下一个断点,无断点则运行到程序结束
continue
gdb中查看寄存器以及存储单元的内容(注:调试过程中,查看可以随时进行)
a) 数据表示:默认10进制数,“/x”表示16进制数,“/t”表示2进制数
b) 查看单个寄存器的值:print 寄存器名,例如:print $eax print /x $eax 转换为16进制的 表示
c) 查看所有寄存器的值:info registers
d) 查看一个存储单元的内容:print *(类型 *) 地址
例如: print *(char *) 0x80483c8
print *(char *) ($esp + 8)
e) 查看多个存储单元的内容:x /个数及类型 地址
例如: x /2w 0xfffffff8
x /10b 0xffffff68
x /14b swap
示例: 对于给出的一个可执行文件tt(无源代码提供),通过gdb和objdump工具分析程序的行为.
[zoey@zoeycentos7 ~]$ cd 桌面/
[zoey@zoeycentos7 桌面]$ objdump -d tt
tt: 文件格式 elf32-i386
.......中间汇编代码省略,为缩减篇幅...
08048490 <ph1>:
8048490: 80 7c 24 04 68 cmpb $0x68,0x4(%esp)
8048495: 0f 94 c0 sete %al
8048498: 0f b6 c0 movzbl %al,%eax
804849b: c3 ret
0804849c <ph2>:
804849c: 8b 44 24 08 mov 0x8(%esp),%eax
80484a0: 03 44 24 04 add 0x4(%esp),%eax
80484a4: 83 f8 0a cmp $0xa,%eax
80484a7: 0f 94 c0 sete %al
80484aa: 0f b6 c0 movzbl %al,%eax
80484ad: c3 ret
080484ae <bomb>:
80484ae: 83 ec 1c sub $0x1c,%esp
80484b1: c7 04 24 20 86 04 08 movl $0x8048620,(%esp)
80484b8: e8 d3 fe ff ff call 8048390 <puts@plt>
80484bd: 83 c4 1c add $0x1c,%esp
80484c0: c3 ret
080484c1 <main>:
80484c1: 55 push %ebp
80484c2: 89 e5 mov %esp,%ebp
80484c4: 83 e4 f0 and $0xfffffff0,%esp
80484c7: 83 ec 20 sub $0x20,%esp
80484ca: a1 1c a0 04 08 mov 0x804a01c,%eax
80484cf: 89 04 24 mov %eax,(%esp)
80484d2: e8 a9 fe ff ff call 8048380 <_IO_getc@plt>
80484d7: 0f be c0 movsbl %al,%eax
80484da: 89 04 24 mov %eax,(%esp)
80484dd: e8 ae ff ff ff call 8048490 <ph1>
80484e2: 85 c0 test %eax,%eax
80484e4: 75 0c jne 80484f2 <main+0x31>
80484e6: e8 c3 ff ff ff call 80484ae <bomb>
80484eb: 90 nop
80484ec: 8d 74 26 00 lea 0x0(%esi,%eiz,1),%esi
80484f0: eb 53 jmp 8048545 <main+0x84>
80484f2: c7 04 24 30 86 04 08 movl $0x8048630,(%esp)
80484f9: e8 92 fe ff ff call 8048390 <puts@plt>
80484fe: 8d 44 24 1c lea 0x1c(%esp),%eax
8048502: 89 44 24 08 mov %eax,0x8(%esp)
8048506: 8d 44 24 18 lea 0x18(%esp),%eax
804850a: 89 44 24 04 mov %eax,0x4(%esp)
804850e: c7 04 24 28 86 04 08 movl $0x8048628,(%esp)
8048515: e8 a6 fe ff ff call 80483c0 <__isoc99_scanf@plt>
804851a: 8b 44 24 1c mov 0x1c(%esp),%eax
804851e: 89 44 24 04 mov %eax,0x4(%esp)
8048522: 8b 44 24 18 mov 0x18(%esp),%eax
8048526: 89 04 24 mov %eax,(%esp)
8048529: e8 6e ff ff ff call 804849c <ph2>
804852e: 85 c0 test %eax,%eax
8048530: 75 07 jne 8048539 <main+0x78>
8048532: e8 77 ff ff ff call 80484ae <bomb>
8048537: eb 0c jmp 8048545 <main+0x84>
8048539: c7 04 24 6c 86 04 08 movl $0x804866c,(%esp)
8048540: e8 4b fe ff ff call 8048390 <puts@plt>
8048545: c9 leave
8048546: c3 ret
8048547: 90 nop
8048548: 90 nop
8048549: 90 nop
804854a: 90 nop
804854b: 90 nop
804854c: 90 nop
804854d: 90 nop
804854e: 90 nop
804854f: 90 nop
注:该汇编代码不完全展示出来,为了简洁,好整理...
通过分析汇编代码,用gdb进行断点调试,分别在ph1函数,ph2函数,main函数中断点
[zoey@zoeycentos7 桌面]$ gdb tt
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-120.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-redhat-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /home/zoey/桌面/tt...(no debugging symbols found)...done.
(gdb) break ph1
Breakpoint 1 at 0x8048490
(gdb) break ph2
Breakpoint 2 at 0x804849c
(gdb) break *0x80484e6
Breakpoint 3 at 0x80484e6
(gdb) run
Starting program: /home/zoey/桌面/tt
h
Breakpoint 1, 0x08048490 in ph1 ()
Missing separate debuginfos, use: debuginfo-install glibc-2.17-317.el7.i686
(gdb) stepi
0x08048495 in ph1 ()
(gdb) stepi
0x08048498 in ph1 ()
(gdb) print *(char*) (4+$esp)
$1 = 104 'h'
(gdb) print $al
$2 = 1
(gdb) continue
Continuing.
phase 1 defused,congratulations!
how about the next one?
运行截图:
其中我遇到的问题:
- 文件权限问题,由于可执行文件从另一台终端编译产生的,且该终端的操作系统为32位操作系统
复制过来时,权限设置不一致.
-
其次在64位操作系统下运行32位程序会出现问题,运行不了.
出现:/lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录
解决方法安装软件:/lib/ld-linux.so.2: bad ELF interpreter解决
详细的解决操作