当前位置:网站首页 > C++编程 > 正文

devc++反编译(c+ 反编译)



    在使用C/C++开发程序时,编译链接是非常重要的步骤。编译器会将源代码转换为机器码,链接器会将编译器生成的目标文件和库文件组合成可执行文件。

 

    先使用gcc编译生成可执行文件helloworld,再运行helloworld,相关命令如下:
在这里插入图片描述
    gcc/g++会将源代码文件编译成中间代码文件(.o文件),然后将这些中间代码文件链接成可执行文件。在编译和链接过程中,我们可以指定不同的选项来控制编译和链接的行为。以下是简单的编译和链接过程的十一图(以helloworld.c为例)。
在这里插入图片描述

1.1预处理过程

    预处理阶段由cpp来完成,这里的cpp不是"c plus plus"的意思,而是"the C Preprocessor"的意思。cpp会在编译之前对源文件进行处理。它会对自定义和预定义的宏进行展开,把包含的头文件内容插入到当前源文件,并根据预处理指令包含不同的代码,或者设置设置传递给编译器的参数。
    我们可以使用gcc的-E和-v选项来查看整个预处理过程。其中,-E选项表示只对源文件做预处理,-v选项表示显示详细的预处理过程,-o选项表示将预处理后的结果输出到helloworld.i文件中

 

从下面输出可以看出,系统头文件搜索的路径。
在这里插入图片描述

1.2编译阶段

    编译阶段由编译器ccl把预处理后的文件内容转换成汇编程序。我们可以使用gcc的-S选项来实现编译的过程。-S选项便是只做编译处理,生成的汇编代码如下。

 

biocare@biocare:~/桌面/test$ cat helloworld.s
.file “helloworld.c”
.text
.section .rodata
.LC0:
.string “Yang Jian ate dog meat pot”
.LC1:
.string “%s hello world ”
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $32, %rsp
movl %edi, -20(%rbp)
movq %rsi, -32(%rbp)
leaq .LC0(%rip), %rax
movq %rax, -8(%rbp)
movq -8(%rbp), %rax
movq %rax, %rsi
leaq .LC1(%rip), %rdi
movl $0, %eax
call printf@PLT
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident “GCC: (Ubuntu 7.5.0-3ubuntu1~18.04) 7.5.0”
.section .note.GNU-stack,“”,@progbits

1.3 汇编阶段

    紧接着,汇编器as会把helloworld.s的内容翻译成机器指令,这些机器指令按照ELF(Executable and Linking Format)被打包成重定位的二进制目标文件。使用-c选项来实现目标文件的生成,file命令用于查看目标文件的信息。

 

在这里插入图片描述
    在file命令的输出中,“ELF 64-bit LSB relocatable”表示64位的、格式为ELF的可重定位文件,“not stripped”表示文件中的符号和调试信息未被删除
    ELF是一种常用的二进制文件格式,它支持动态链接和装载,可以用于生成可执行文件、共享库和可重定位文件等。64位的ELF是一种针对64位处理器的二进制文件格式。(ELF相关介绍移步到ELF文章中进行学习,ELF文件概述)
    文件中的符号和调试信息可以用于调试和分析程序。如果需要生成发布版本的程序,可以使用strip命令删除这些信息,以减小程序的大小。

1.4 链接阶段

    因为helloworld.c中引用了标准C库中的printf函数,所以在最后的链接阶段,ld链接器会把printf函数符号位于libc.so.6库中的重定位和符号表信息复制到最终的可执行文件helloworld 中。gcc链接时库的搜索路径在使用-v选项时会显示出来,libc.so.6库就是在这些路径中进行搜索的。

gcc -v -o helloworld helloworld.o

在这里插入图片描述

    在实际的C/C++工程项目中,一个程序通常由多个代码文件构成,应该如何编译工程项目的程序呢?假设我们的项目中有3个源文件和两个头文件,它们分别是print1.c、print2.c、main.c、print1.h、print2.h。

print1.h

 

print1.c

 

print2.h

 

print2.c

 

main.c

 

    我们可以手动完成整个编译和链接的过程。
在这里插入图片描述

2.1 makefile

    在项目开发中,我们经常需要修改源代码文件,如果每次修改都需要手动进行编译和链接,显然非常耗时和低效。那么,有没有什么快捷的方式,使得我们能够根据代码文件的变更来完成程序的重新编译和链接呢?
    在Linux系统中,make命令是一个自动化编译工具,可以自动完成程序的编译和链接。我们只需要编写一个名为"makefile"的文件,在这个文件中包含整个程序的编译、链接规则,然后执行make命令,make命令就会在当前目录下搜索makefile文件并解析执行其中的编译、链接命令,从而完成整个程序的编译和链接。
    makefile文件在本质上就是一个编译、链接脚本,其中包含了所有的编译、链接规则,并且定义了程序中各个代码文件之间的依赖关系和编译、链接选项。make命令就是makefile这个脚本的解析器和执行器,make和 makefile的关系,同shell和 shell脚本的关系类似。make命令并不是每次都简单地重新执行makefile文件中的所有编译、链接命令,如果是这样的话,就和 shell脚本没有区别了。相反,make命令会根据源代码文件的变更情况,只重新编译必要的目标文件并重新链接生成可执行文件,从而提高了编译、链接的效率。如果修改了某个.c文件,那么对应的目标文件就会被重新编译生成。如果源代码文件没做任何变更,那么make命令不会执行makefile文件中的任何编译、链接命令,而只在终端输出当前的可执行文件是最新文件的提示。

2.2 makefile的基本语法

     自行了解

2.3 make命令的工作方式

make命令会在当前目录下查找名为“makefile”的文件,也可以使用-f选项指定特定的文件为makefile文件。

如果没有找到makefile文件,make命令就会报错,提示找不到makefile文件。如果找到了makefile文件,默认情况下,make命令会把makefile文件中出现的第一个target作为最终生成的目标。当然,我们也可以在执行make命令时,指定生成特定的target。

make命令会根据makefile文件中描述的依赖关系和最终所要生成的target来执行相应的命令,最后生成目标文件。具体来说,如果target是最新生成的,那么make命令不会执行makefile文件中的任何命令;如果target不存在或者target不是最新的,那么make命令会执行makefile文件中生成target所关联的命令,并根据需要递归地执行生成其他依赖文件的命令;如果 target关联的某些源代码文件被修改,或者target的某些依赖文件缺失,那么make命令会生成最新的依赖文件,并执行makefile文件中生成target所关联的命令。

make命令在执行编译和链接的过程中,不会理会关联命令的错误,而只处理依赖关系。如果依赖的文件无法生成,make命令会直接报错并退出;否则,make命令会根据makefile文件中描述的依赖关系,递归地执行关联的命令,直至生成最终的target。

三、常用的编译和链接选项

3.1 动态链接库选项

-l 选项
    -l选项指定要链接哪个动态库,假设我们在程序中使用了pthread系列的多线程函数,则在链接时需要指定-lpthread这个选项。
-L 选项
    -L选项指明了动态库所在的目录。-L选项几乎被用于指明第三方动态库或自定义动态库所在的目录,因为第三方的或自定义的动态库都有自己独立的存储目录。
-WI,-rpath 选项
    在“-Wl,-rpath”选项中,-WI表示给链接器传递参数,传递的参数为逗号后面的-rpath。-rpath为实际传递给链接器的参数,它用于指定运行时动态链接库优先的搜索路径,这条搜索路径的信息被保存在可执行文件中。
-Ⅰ头文件选项
    -Ⅰ头文件选项在工程项目中最为常见。当编译到引用了动态库符号的源代码时,就需要指定-Ⅰ头文件选项,用于指明引用的动态库符号声明的头文件所在的目录。

3.2 宏选项

    宏选项是编译的开关,通过这些开关,我们可以选择性地实现程序不同的功能。
-D 宏选项
    -D宏选项在编译阶段使用,用于影响预处理的结果。它能够在编译阶段就动态选择程序实现的功能。

 

    下面我们来看看在使用和不使用-D选项的情况下,程序的运行结果有何不同。
在这里插入图片描述
-DTEST=VALUE

    从上面的运行结果中可以看出,当添加-DTEST 选项时,宏的定义会被传递到源代码文件defineTest.c中。当然,我们也可以像在程序中给宏指定值一样,在编译参数中给宏指定值。例如,如果想要指定TEST宏的值为2,那么可以使用-DTEST=2选项。
-DNDEBUG 宏选项
    -DNDEBUG选项用于关闭assert断言函数。当我们使用-DNDEBUG选项进行编译时,代码中的assert 断言函数都会失效,不会进行任何检查。
    assert断言函数通常用于在程序中进行调试和错误处理。它会检查一个条件是否成立,如果这个条件不成立,则输出一条错误信息,并终止程序的执行。在开发阶段,我们通常会开启 assert 断言函数,以便及早发现程序中的问题。但在发布程序时,我们通常会关闭assert 断言函数,以提高程序的性能。

3.3 调试与告警选项

    在程序开发过程中,我们难免需要对程序进行调试,并且需要编译器帮助我们检查程序中的错误。此时,调试与告警选项就派上用场了。
-g 选项
    -g 选项用于在编译时生成调试信息。这些调试信息可以被调试工具(如gdb)用来进行程序调试。在使用gdb 进行调试时,我们可以查看程序的源代码、变量的值、函数的调用栈等信息,从而更方便地进行调试。
    需要注意的是,使用-g 选项会增大程序的大小,因为编译器会将调试信息嵌入可执行文件。因此,在发布程序时,我们通常会关闭-g 选项,以减小程序的大小。

-Wall 选项
    -Wall选项是用于生成所有告警信息的编译选项。它可以帮助我们发现可能隐藏的问题,从而提高程序的质量。
    具体来说,-Wall选项会生成所有的告警信息,包括一些常见的编程错误,如未声明的变量、未使用的变量、类型不匹配等。这些告警信息可以帮助我们发现代码中的潜在问题,从而及早地进行修复,避免在后期出现更严重的问题。
-Wextra 选项
    只打开-Wall选项是不够严格的,-Wextra选项是对-Wall选项的补充。
    -Wall选项虽然能够生成大量的告警信息,但并不包括所有的告警类型。这时,我们可以使用-Wextra选项来对-Wal1选项进行补充。
    具体来说,-Wextra选项包括一些没有被-Wall选项包含在内的告警类型,如未使用的函数、未使用的参数、类型转换等。使用-Wextra选项可以帮助我们发现更多潜在的问题,从而提高程序的质量。

声明:禁止转载等其他用途,仅供学习,以上内容如有雷同,请私信本作者,进行修改。

到此这篇devc++反编译(c+ 反编译)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • 怎么连接cp1025打印机(cp2506dn plus连接无线)2025-03-09 11:00:06
  • cnn是监督还是非监督的(cnn是美国的还是英国的)2025-03-09 11:00:06
  • pilow和pillow区别(pillow和cushion的区别)2025-03-09 11:00:06
  • nisc认证(sni 认证)2025-03-09 11:00:06
  • convlstm怎么读(convolk怎么读)2025-03-09 11:00:06
  • mouse2joystick下载(mouse 下载)2025-03-09 11:00:06
  • nonetype翻译(nonesence翻译)2025-03-09 11:00:06
  • 读取pcap文件(读取pcap文件 存储到mbuf)2025-03-09 11:00:06
  • 如何配置dhcp自动获取ip上网(dhcp设置自动获取ip的时效)2025-03-09 11:00:06
  • vs怎么用c++语言(vs怎么运行c++语言代码)2025-03-09 11:00:06
  • 全屏图片