不一样的hello world

Linux 的系统调用

  • 通过glibc提供的库函数

    glibc 是 Linux 下使用的开源的标准 C 库,它是 GNU 发布的 libc 库,即运行时库。glibc 为程序员提供丰富的 API,除了例如字符串处理、数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。
  • 使用syscall直接调用

    该函数定义在 unistd.h 头文件中,函数原型如下:
    
    long int syscall (long int sysno, ...)
    
    1. sysno 是系统调用号,每个系统调用都有唯一的系统调用号来标识。
    2. ... 为剩余可变长的参数,为系统调用所带的参数。
    3. 返回值 该函数返回值为特定系统调用的返回值,在系统调用成功之后你可以将该返回值转化为特定的类型,如果系统调用失败则返回 -1。
  • 通过int指令陷入

    用户态程序通过软中断指令`int 0x80` 来陷入内核态,参数的传递是通过寄存器,eax 传递的是系统调用号,ebx、ecx、edx、esi和edi 来依次传递最多五个参数,当系统调用返回时,返回值存放在 eax 中。
    【系统调用号可在 /usr/include/asm/unistd_32.h(unistd_64.h)中查询 】

系统调用函数

write 调用

ssize_t write(int fd, const void *buf, size_t count);

write 调用的调用号为4,eax=4
fd 表示被写入的文件句柄,这里要向终端输出,文件句柄为1,ebx=1
buf 表示要写入的缓冲区地址,ecx=str 【hello world!字符串】
size 表示要写入的字节数,edx=13 【hello world!】

exit 调用

void exit(int status);

exit 调用的调用号为1。
status 表示进程退出码,如我们平时的main程序return的数值会返回给系统库,由系统库将该数值传递给exit系统调用。

有了上面的铺垫我们就可以做到,写一个不一样的hello world 程序,并且做到以下要求:

  • 不调用任何系统库
  • 不使用main函数【结束进程使用系统调用函数exit】

代码实现

GCC/GNU 编译器和 Clang/LLVM 编译器默认使用 AT&T/UNIX 汇编语法, GCC 可以通过加参数 -masm=intel 来使用 Intel 汇编语法,该参数并不适用于 Clang。

这里使用 Intel 语法 【AT&T/UNIX 语法 相应修改即可】

/* helloworld.c */   

char * str="hello world\n";

void print(){

	__asm(
"mov edx,13\n\t" //字符串长度
"mov ecx,str\n\t" //待显示字符串
"mov ebx,1\n\t" //文件描述符(stdout)
"mov eax,4\n\t" //write 系统调用号
"int 0x80\n\t" //软中断指令 进入系统调用
);
} void end(){ __asm(
"mov ebx,0\n\t" //进程退出码
"mov eax,1\n\t" //exit 系统调用号
"int 0x80\n\t"
);
} void run()
{
print();
end();
}

进行编译

# -masm=intel Intel 语法
# -fno-builtin 关闭gcc内置函数功能
# 生成可重定向文件 gcc helloworld.c -c -masm=intel -fno-builtin # -static 让ld使用静态链接方式链接程序
# -e 程序入口
# 生成可执行文件 ld -static -e run -o helloworld helloworld.o

可以看到成功生成可执行文件!

.text 保存的是程序的指令,它是只读的。
.rodata 保存的是字符串“HelloWorld!\n”,它也是只读的。
.data 保存的是Sir全局变量,看上去它是可读写的,但我们并没有在程序中改写该变量,所以实际上它也是只读的。
.comment保存的是编译器和系统版本信息,这些信息也是只读的。由于.comment里面保存的数据并不关键,对程序的运行没有作用,所以可以丢弃。 .eh_frame 可以通过把代码写入.eh_frame中(覆盖其原来的内容)可以实现binary大小基本没有变化。若存在该段,我们能够进行改写并无影响。
【但这个段也极大增加了elf文件的大小】

鉴于这些段的属性如此相似,原则上讲,我们可以把它们合并到一个段里面,该段的属性是可执行、可读的,包含程序的数据和指令。为了达到这个目的,我们使用Id链接脚本来控制链接过程。

ld链接脚本
/* tinyhelloworld.lds */

ENTRY(run)

SECTIONS
{
. = SIZEOF_HEADERS; /* = 旁的空格很关键,不然也会报错 */
.tinytext : {*(.text) *(.rodata) *(.data)}
/DISCARD/ : {*(.comment) *(.eh_frame)}
} # "."表示当前虚拟地址
# SIZEOF_HEADERS 输出文件头的大小
# tinytext: 新合并段名称
# /DISCARD/: 丢弃其中段

链接命令

ld -static -T tinyhelloworld.lds -o tinyhelloworld helloworld.o -s 

# 注意:这里 -T -o 的顺序不当,会引起报错
# -s 去除符号

可以看到,除了重定向表外,就只有.tinytext段了。并且可执行程序的大小为576字节

《程序员的自我修养》学习笔记——不一样的hello world【第四弹】的更多相关文章

  1. pwn学习日记Day19 《程序员的自我修养》读书笔记

    windows PE/COFF章总结 本章学习了windows下的可执行文件和目标文件格式PE/COFF.PE/COFF文件与ELF文件非常相似,它们都是基于段的结构的二进制文件格式.Windows下 ...

  2. pwn学习日记Day21 《程序员的自我修养》读书笔记

    Linux内核装载ELF过程 (1)bash进程调用fork()系统调用创建一个新的进程 (2)新的进程调用execve()系统调用执行指定的ELF文件,原先的bash进程继续返回等待刚才启动的新进程 ...

  3. pwn学习日记Day20 《程序员的自我修养》读书笔记

    可执行文件的装载与进程 覆盖装入和页映射是两种典型的动态装载方法 进程建立的三步 1.创建一个独立的虚拟地址空间 2.读取可执行文件头,并且建立虚拟空间与可执行文件的映射关系. 3.将CPU的指令寄存 ...

  4. 第八周读书笔记(人月神话X月亮与六便士)——到底什么才是一个程序员的自我修养?

    写了这么久的读书笔记,涉及到问题大多是一些如何把软件工程做好,如何把自己的职业生涯做好.但总感觉逻辑链上缺了一环,亦即:我们为什么要把软件工程做好,我们成为一名优秀的职业生涯的意义到底在于什么?我觉得 ...

  5. gcc ld 链接器相关知识,调试指令(程序员的自我修养----链接、装载与库)

    最近解决一个动态链接上的问题,因为以前从来没有接触过这方面的知识,所以恶补了一下,首先要了解gcc编译指令(makefile),ld链接器的选项(还有连接脚本section指定内存位置),熟悉查看连接 ...

  6. 程序员的自我修养(2)——计算机网络(转) good

    相关文章:程序员的自我修养——操作系统篇 几乎所有的计算机程序,都会牵涉到网络通信.因此,了解计算机基础网络知识,对每一个程序员来说都是异常重要的. 本文在介绍一些基础网络知识的同时,给出了一些高质量 ...

  7. 程序员的自我修养:高效使用Google解决问题

    如果票选近二十年最伟大的发明,我相信搜索引擎肯定会占据一个不容小觑的位置,它不单是一项发明,更是一项成就,最大程度消灭了信息的不平等.既然人人都可以接触到海量的信息,那么衡量信息财富多寡就只剩下技巧这 ...

  8. pwn学习日记Day18 《程序员的自我修养》读书笔记

    知识杂项 obj文件:当前源代码编译成二进制目标文件 exe文件:将.obj文件与库文件.lib等文件链接生成的可执行文件 一个现代编译器的主要工作流程如下: 源程序(source code)→ 预处 ...

  9. 《程序员的自我修养》读书笔记——系统调用、API

        系统调用 程序运行的时候,本身是没有权限访问多少系统资源的.系统资源有限,如果操作系统不进行控制,那么各个程序难免会产生冲突.线程操作系统都将可能产生冲突的系统资源保护起来,阻止程序直接访问. ...

  10. Java程序员的自我修养

    一.自我修养路线图 如图,这是笔者所走的路.且不论这路走的对不对,这个过程中行业环境会影响到你,大可不必钻牛角尖.附上这张图的目的是为了说,如果你想成为一个优秀的程序员,那么你一定要有规划.当然,别想 ...

随机推荐

  1. HCIP-进阶实验02-ISIS协议部署

    进阶实验02-ISIS协议部署 1 实验需求 设备 接口 IP地址 备注 R1 G0/0/0 10.1.123.1/24 R2 G0/0/0 10.1.123.2/24 G0/0/1 10.1.24. ...

  2. 阿里云 rocketMq 延时消息

    初始化消费者和生产者 生产者 设置rocketmq的accesskey 和secretkey 以及rocketmq的 binder server. 首先 编辑一个配置类,将关于配置rocketmq的东 ...

  3. 微信小程序 的 openid,appid,unionid

    openid: 用户在同一个小程序下的唯一表示,即同一个用户在不同的小程序下的openid是不同的 appid 小程序唯一凭证,即 AppID,每个小程序都有自己的一个appid unionid 如果 ...

  4. 在Unity3D中开发的Sketch Shader

    Pencil Sketch Shader 特点 此素描渲染风格的Shader是顶点片元Shader,由本人手动编写完成. 此素描渲染风格的Shader已经在移动设备真机上进行过测试,可以直接应用到您的 ...

  5. Epoll的使用例子

    本篇文章在上一篇的基础上,使用 epoll  实现了一个事件监听和回调处理的模块.如何编写一个使用该模块的例子呢? 监测什么类型的fd,监测什么类型的事件,监测到事件以后需要做什么?后来可以看看如何将 ...

  6. 创建一个Openpyxl

    from openpyxl import Workbook wb = Workbook(r'C:\Users\yjiang3\Desktop\23.xlsx') ws = wb.active from ...

  7. Git简介及使用

    1.Git简介 GIT= 版本控制 + 备份服务器 我们称用来存放上传档案的地方就做Repository.用中文来说,有点像是档案仓库的意思. 不过,通常我们还是使用Repository这个名词.当有 ...

  8. software engineering homework 1

    1. 回顾你过去将近3年的学习经历 当初你报考的时候,是真正喜欢计算机这个专业吗? 你现在后悔选择了这个专业吗? 你认为你现在最喜欢的领域是什么(可以是计算机的也可以是其它领域)? 答:一开始感觉编程 ...

  9. 创建sqlSession对象操作数据库

    1.加载核心配置文件 //加载mybatis核心配置文件,获取SqlSessionFactory String resource = "mybatis-config.xml"; I ...

  10. 第五天 pycharms 安装使用

    python全栈开发笔记第5天笔记pycharms使用 集成开发环境(IDE,Integratde Development Encironment ) VIM #linux下经典的文本编辑器 Emac ...