Linux下的终端操作之C编译器加GDB调试

11 篇文章 1 订阅
本文详细介绍了在Linux环境下使用GCC编译器将C源代码转换为可执行文件的过程,包括-o选项、-c选项和-O1优化级别。同时,讲解了如何处理多个源文件、生成目标代码、汇编代码以及如何使用objdump查看二进制代码。此外,还探讨了使用GDB调试器进行程序调试的方法,包括设置断点、单步执行和查看内存内容等。文章最后通过一个实际的可执行文件tt,演示了如何通过GDB和objdump进行分析和调试。
摘要由CSDN通过智能技术生成

Linux下的终端操作之C编译器

C编译器:GCC

gcc常用编译选项说明(gcc:即gnu c complier)

  • -o 产生可执行代码

  • -c 产生目标代码

  • -O1 一级优化编译

以下的操作是在CentOS7下操作的

单个源文件,转换成可执行代码

  1. 建立源程序(.c文件)

先在桌面建个文件夹(主要是为了方便找,也可以在其他地方建文件夹,但是要记住文件夹路径,便于在终端控制台上进行操作),例:建一个名为"file"的文件夹,在“file”文件夹下,创建了一个空文档,将该文档改名为hello.c

打开该文档输入hello.c文件内容,并保存。

简单的代码示例:

#include  <stdio.h>

int main()
{
	printf(“hello,world!\n”);
    return 0;
}

  1. 生成可执行文件(默认可执行文件的名字是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)建立源程序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)
    
  2. 在终端界面执行可执行文件

  • ./+可执行文件名

直接在命令行上输出结果

生成目标代码

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?

运行截图:

在这里插入图片描述

其中我遇到的问题:

  1. 文件权限问题,由于可执行文件从另一台终端编译产生的,且该终端的操作系统为32位操作系统

复制过来时,权限设置不一致.

  1. 其次在64位操作系统下运行32位程序会出现问题,运行不了.

    出现:/lib/ld-linux.so.2: bad ELF interpreter: 没有那个文件或目录

    解决方法安装软件:/lib/ld-linux.so.2: bad ELF interpreter解决
    详细的解决操作

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值