thinkphp企业网站系统,河南省住房城乡和建设厅网站,网站如何做反链,电商网站开发的底层架构本文将综合以下4篇文章#xff0c;学习如何写出不依赖libc库的程序#xff1a; 【软件开发底层知识修炼】九 链接器-可重定位文件与可执行文件【软件开发底层知识修炼】十 链接器-main函数不是第一个被执行的函数【软件开发底层知识修炼】十一 链接器-链接脚本【软件开发底层… 本文将综合以下4篇文章学习如何写出不依赖libc库的程序 【软件开发底层知识修炼】九 链接器-可重定位文件与可执行文件【软件开发底层知识修炼】十 链接器-main函数不是第一个被执行的函数【软件开发底层知识修炼】十一 链接器-链接脚本【软件开发底层知识修炼】十二 C/C语言中内嵌汇编语言asm 如果没有看上面4篇文章的建议先按照顺序学习上述4篇文章再来看这篇文章不然有些地方会很突兀。 文章目录1 本篇文章的目的2 解决方案设计3 拓展4、总结 1 本篇文章的目的
那么本篇文章的目的是什么呢
我们想编写一个体积非常小的可执行程序。通过makefile完成代码编译运行后在屏幕上打印“D.T.SoftWare”
但凡是学习过C语言基础语法的人都能写出来这个程序 这不就是一个hello word 程序么就像下面这样.
hello.c
#include stdio.hint main(){printf(D.T.SoftWare\n);return 0;
}我们编译上述hello.c得到可执行程序hello可以看出hello的大小是
竟然有7135字节这其实是因为虽然我们只写了个printf函数但是实际上在编译链接的过程链接器将一大堆库函数都与我们的hello程序进行链接包括一些入口函数啊进程初始化函数以及printf这个库函数等等看起来只有一个printf库函数但是实际上一与libc库进行链接就会有一大堆东西这些东西是啥吗请参看上述四篇文章。
所以我们的目的并不是简单的写出上面的hello.c程序。我们想写一个程序进行编译链接后体积达到最小该如何做到
我们的分析思路大概如下图
我们知道main函数执行前还有一大堆的初始化函数需要调用我们不依赖于libc库进行编译链接以及编写自定义入口函数的链接脚本可以实现不依赖libc库打印字符串的话就直接进行系统调用直接调用sys_write函数而不是调用printf函数直接调用sys_write函数的方法的话就是使用内嵌汇编语言进行调用
学过我上面篇文章的朋友就一定会发现上述的几点都在那4篇文章中实现过。所以我们可以很轻松的进行代码的编写。
2 解决方案设计
上面已经给出了基本的程序设计思路现在我们来给出更加具体的程序设计思路。
通过内嵌汇编自定义打印函数和退出函数具体来说就是使用INT 0X80指令通过编写自定义的链接脚本来指定我们自己的入口函数不依赖任何库和GCC的任何内置功能删除可执行程序的无用信息比如无用的调试信息和段信息。这是通过链接脚本指定的
那么我们先来通过内嵌汇编设计一下打印函数和退出函数这里要参考【软件开发底层知识修炼】十二 C/C语言中内嵌汇编语言asm。
打印函数print
void print(const char* s, int l)
{asm volatile (movl $4, %%eax\n // sys_write系统函数相关movl $1, %%ebx\nmovl %0, %%ecx\nmovl %1, %%edx\nint $0x80 \n //通过80H进行系统调用:: r(s), r(l) // print的参数: eax, ebx, ecx, edx // 保留列表);
}上述打印函数在【软件开发底层知识修炼】十二 C/C语言中内嵌汇编语言asm这篇文章中一讲非常详细。
退出函数 exit
void exit(int code)
{asm volatile (movl $1, %%eax\n //sys_exitmovl %0, %%ebx\nint $0x80 \n:: r(code) //参数: eax, ebx);
}上述退出函数也在【软件开发底层知识修炼】十二 C/C语言中内嵌汇编语言asm这篇文章中一讲非常详细。
链接脚本设计 program.lds
上述链接脚本中已经注解的非常详细当然还是需要参考【软件开发底层知识修炼】十一 链接器-链接脚本这篇文章先学习以下链接脚本的语法与概念最好。
那么上述程序设计基本上完成下面我们给出完成的代码
program.c 源文件
void print(const char* s, int l);
void exit(int code);void program()
{print(D.T.Software\n, 13);exit(0);
}void print(const char* s, int l)
{asm volatile (movl $4, %%eax\nmovl $1, %%ebx\nmovl %0, %%ecx\nmovl %1, %%edx\nint $0x80 \n:: r(s), r(l): eax, ebx, ecx, edx);
}void exit(int code)
{asm volatile (movl $1, %%eax\nmovl %0, %%ebx\nint $0x80 \n:: r(code): eax, ebx);
}program.lds 链接脚本
ENTRY(program)SECTIONS
{.text 0x08048000 SIZEOF_HEADERS :{*(.text)*(.rodata)}/DISCARD/ :{*(*)}
}当然为了编译方便我还给出了makefile文件方便我们程序的编译 CC : gcc
LD : ld
RM : rm -frTARGET : program.out
SRC : $(TARGET:.out.c)
OBJ : $(TARGET:.out.o)
LDS : $(TARGET:.out.lds).PHONY : rebuild clean all$(TARGET) : $(OBJ) $(LDS)$(LD) -static -T $(LDS) -o $ $echo Target File $$(OBJ) : $(SRC)$(CC) -fno-builtin -o $ -c $^rebuild : clean allall : $(TARGET)clean :$(RM) $(TARGET) $(OBJ)上述makefile可能大多数人看不懂这个无所谓它只是一种类似于脚本语言的语言辅助我们编译程序的我们将上述三个文件makefile program.lds program.c这三个文件放到linux系统下的同一个目录下然后输入命令make即可完成代码的编译生成可执行文件。 就像下面这样我们队我们的程序进行编译 运行可执行程序 ./program.out 很明显我们实现了我们最开始的功能。 我们看看我们写的这个可执行程序的大小 上面的是hello的大小下面的是我们自己的可执行程序的大小只有582字节远远小于hello的大小。这正是我们所希望看到的。
3 拓展
如果有详细看上述的makefile文件我们会发现在我们编译我们的源文件的时候使用了静态链接。现在在这里介绍一些链接时的一些选项 ld 命令 GNU的链接器将目标文件链接为可执行文件 ld -static 表示ld使用静态链接的方式来产生最终的可执行程序而不是默认的动态链接。至于什么是静态链接什么是动态链接后面肯定会有文章详细学习。 gcc -fno-builtin -fno-builtin 用于关闭GCC内部函数功能 GCC提供了很多内置函数Built-in Function,它会把一些常用的C库函数替换成编译器内置的函数以达到优化程序的目的。在上述我们的makefile中就用到了这个选项以防止编译器的优化
4、总结 对于资源受限制的嵌入式设备需要考虑可执行程序的大小当然目前各种设备内存都足够大不必担心这一点 通过在C/C语言中内嵌汇编语言可以避免使用库函数而直接使用系统函数 可以通过如下方式来控制可执行程序的大小 最小化库的使用必要的时候可以自己实现相关函数自定义链接脚本删除无用的段信息
本文参考狄泰软件学院相关课程 想学习的可以加狄泰软件学院群 群聊号码199546072
学习探讨加个人可以免费帮忙下载CSDN资源 qq1126137994 微信liu1126137994