每个人的第一步--没有依赖的"Hello world!"

相传,每一个程序员学习每一门语言的时候,都会先做同一件事情—在某种输出终端上打印出“Hello world!”。鉴于这种做法是如此深入人心,所以我也想在开始自己的博客时延续这一传统,用C语言来输出“Hello world!”,而为了体现现在和四年前在学校上C语言课时的不同,这一段程序将不使用C依赖库,转而使用内嵌汇编来实现必要的底层操作,并对其中(我觉得)有意思的地方作出讨论。

代码如下

char * str = "Hello world!\n";
void print()
{
    asm(
        "movl $13, %%edx \n\t"//string lenght:13
        "movl %0, %%ecx \n\t" //
        "movl $0, %%ebx \n\t" //stdout:0
        "movl $4, %%eax \n\t" //write system call: 4
        "int $0x80      \n\t" 
        ::"r"(str)
       );
}

void exit()
{
    asm(
        "movl $42, %ebx \n\t"
        "movl $1, %eax \n\t"
        "int $0x80     \n\t"
       );
}

void mymain()
{
    print();
    exit();
}

这里使用了一些AT&T汇编(对,我是在Linux下写的),内嵌实现了两个函数printfexit 来被mymain 调用,由于没有使用GLIBC等C库(不知道你注意到没有,这一段小程序也没有main),也就没有start.S 来调用main函数,所以需要在链接时指定入口函数。两种指定方法在makefile中如下所示:

C_FLAGS := -c -fno-builtin
LD_FLAGS := -static -e mymain
#LD_FLAGS := -static -T hello_world.lds

TARGET = hello_world

$(TARGET):
    gcc $(C_FLAGS) hello_world.c
    ld $(LD_FLAGS) -o $@  hello_world.o

clean:
    rm *.o $(TARGET)

分别可以使用编译选项-e mymain 和链接脚本-T hello_world.lds 来实现,链接脚本中指定入口函数的相关语句是ENTRY(mymain) ,在此不再赘述。如果使用这部分代码和makefile可以编译通过的话,你应该会得到一个很小而且不依赖动态装载的ELF文件;如果编译不过,请检查你的Linux系统是否是64位的,碰巧是的话,在gcc和ld时分别加上-m32-m elf_i386 也许可以解决问题。


一些讨论

至此,已经实现了一个比较简单的程序来输出那句耳熟能详的话,对于其中的一些较深层次的东西,还想做出一些讨论。现在是自由发挥时间,所以会想到哪写到哪。
1. 代码的最开始,声明了一个字符串并指向str ,作为一个已初始化的全局变量,会保存在ELF的.data段中。为了证明这一点,使用objdump -D hello_world 会得到下面的结果,其中的mov 0x8049160,%eax 将字符串的地址传递给系统调用。

 ...
 8048095:   89 e5                   mov    %esp,%ebp
 8048097:   53                      push   %ebx
 8048098:   a1 60 91 04 08          mov    0x8049160,%eax
 804809d:   ba 0d 00 00 00          mov    $0xd,%edx
 80480a2:   89 c1                   mov    %eax,%ecx
 ...

Disassembly of section .data:

08049160 <str>:
 8049160:   d6                      (bad)  
 8049161:   80                      .byte 0x80
 8049162:   04 08               add    $0x8,%al

此时如果不退出函数(exit();前加上while(1);。当然,实现sleep函数是比较好的做法,但是要包含signal.h,作为一个强迫症~),后台运行此程序记住PID号并执行cat /proc/xxxx/maps,会看到08049000-0804a000 rw-p 00000000 08:01 950559 这样的信息,代表这个变量保存在可读可写段。为了提高内存页的使用效率,将可执行文件装载到进程虚拟内存空间时权限相同的段将会一起映射,所以现在也许不应该再称之为Section(段),而是Segment(节)。而如果将这个字符串的声明由char * str 改为char * const str (使用const char * str 会如何呢),使用和刚刚相同的方法查看ELF文件结构和进程内存分布会发现,运行时这个字符串将会和代码段一起保存在类似08048000-08049000 r-xp 00000000 08:01 950559 的虚拟内存地址中,虽然str 并不具有可执行权限。
2. print();函数,通过软中断指令int $0x80来进入内核态调用4号(sys_write)系统调用,往标准输出stdout:0 写数据来实现打印。都是比较普通的做法,在此不再赘述。
3. exit(); 函数,与上面类似,调用1号(sys_exit)系统调用来退出程序。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码下载:完整代码,可直接运行 ;运行版本:2022a或2019b或2014a;若运行有问题,可私信博主; **仿真咨询 1 各类智能优化算法改进及应用** 生产调度、经济调度、装配线调度、充电优化、车间调度、发车优化、水库调度、三维装箱、物流选址、货位优化、公交排班优化、充电桩布局优化、车间布局优化、集装箱船配载优化、水泵组合优化、解医疗资源分配优化、设施布局优化、可视域基站和无人机选址优化 **2 机器学习和深度学习方面** 卷积神经网络(CNN)、LSTM、支持向量机(SVM)、最小二乘支持向量机(LSSVM)、极限学习机(ELM)、核极限学习机(KELM)、BP、RBF、宽度学习、DBN、RF、RBF、DELM、XGBOOST、TCN实现风电预测、光伏预测、电池寿命预测、辐射源识别、交通流预测、负荷预测、股价预测、PM2.5浓度预测、电池健康状态预测、水体光学参数反演、NLOS信号识别、地铁停车精准预测、变压器故障诊断 **3 图像处理方面** 图像识别、图像分割、图像检测、图像隐藏、图像配准、图像拼接、图像融合、图像增强、图像压缩感知 **4 路径规划方面** 旅行商问题(TSP)、车辆路径问题(VRP、MVRP、CVRP、VRPTW等)、无人机三维路径规划、无人机协同、无人机编队、机器人路径规划、栅格地图路径规划、多式联运运输问题、车辆协同无人机路径规划、天线线性阵列分布优化、车间布局优化 **5 无人机应用方面** 无人机路径规划、无人机控制、无人机编队、无人机协同、无人机任务分配 **6 无线传感器定位及布局方面** 传感器部署优化、通信协议优化、路由优化、目标定位优化、Dv-Hop定位优化、Leach协议优化、WSN覆盖优化、组播优化、RSSI定位优化 **7 信号处理方面** 信号识别、信号加密、信号去噪、信号增强、雷达信号处理、信号水印嵌入提取、肌电信号、脑电信号、信号配时优化 **8 电力系统方面** 微电网优化、无功优化、配电网重构、储能配置 **9 元胞自动机方面** 交通流 人群疏散 病毒扩散 晶体生长 **10 雷达方面** 卡尔曼滤波跟踪、航迹关联、航迹融合

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值