第26部分- Linux x86 64位汇编 字符串操作反转实例

本文介绍了在Linux x86_64环境下使用AT&T和Intel汇编语法实现字符串反转的方法。通过详细解析代码,展示了如何计算字符串长度、反转字符串并输出结果,为学习汇编语言提供了实用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第26部分- Linux x86 64位汇编 字符串操作反转实例

有了上面几个深入的语法知识后,我们继续来学习一个例子。

我们出两个版本的例子,实现字符串的反转效果。

AT&T汇编实现

将上诉的Intel汇编程序,我们改写成AT&T语法后汇编如下,这里将原来的mov rdi, $ + 15地方进行的修改,变成jmp命令了,jmp过去jmp回来,就不用涉及到栈中的函数地址了。

因为NASM 提供特殊的变量($ 和 $$ 变量)来操作位置计数器。在 GAS 中,无法操作位置计数器,必须使用标签计算下一个存储位置(数据、指令等等)。

.section .data
    .equ SYS_WRITE,1
    .equ STD_OUT,1
    .equ SYS_EXIT,60
    .equ EXIT_CODE,0
    .equ NEW_LINE,0xa
    INPUT: .ascii " Hello world!\n"
.section .bss;;//定义buffer缓存
		.lcomm OUTPUT,12
.global _start
.section .text
_start:
		movq $INPUT ,%rsi;//将字符串地址赋值为rsi
		xor %rcx, %rcx;//清零
		cld;//清空df标志位0
		
		jmp calculateStrLength;//调用函数计算字符串长度
reversePre:
		xor %rax, %rax;//清空rax
		xor %rdi, %rdi;//清空rdi
		jmp reverseStr;//跳转到reverseStr函数进行翻转

calculateStrLength:;//计算INPUT字符串的长度,并保存在rcx中。
		cmpb $0 ,(%rsi);//与0对比,字符串最后一个’\0’字符串结束标志
		je exitFromRoutine;//函数结束
		lodsb;//从rds:rsi处加载一个字节到al,并增加rsi
		pushq %rax;/将刚获取到的al进行压栈,不过这会破坏了原本返回地址的位置。返回使用通过push rdi来解决。
		inc %rcx;//增加计数器
		jmp calculateStrLength;//循环
exitFromRoutine:;//返回到_start主线。
		pushq %rdi;//压栈return地址到栈中

		jmp reversePre;
reverseStr:
		cmpq $0,%rcx;//对比0,rcx已保存的是字符串的长度
		je printResult;//相等则跳转到printResult
		popq %rax;//从栈中弹出一个字符给rax
		movq %rax,OUTPUT(%rdi);//写到[OUTPUT+rdi]对应的地址
		dec %rcx;//写一个少一个
		inc %rdi;//写一个地址加一个
		jmp reverseStr;//返回循环
printResult:
		mov %rdi, %rdx;//输出的长度赋值到rdx
		mov $SYS_WRITE,%rax;//调用号
		mov $STD_OUT, %rdi;//输出句柄
		mov $OUTPUT, %rsi;//输出字符串地址
                syscall;//调用sys_write输出OUTPUT保存的反向字符串。
		jmp printNewLine;//调用函数输出换行符

printNewLine:
		mov $SYS_WRITE, %rax;//调用号
		mov $STD_OUT, %rdi;//输出句柄
		mov $NEW_LINE, %rsi;//输出字符串地址
		mov $1,%rdx;//输出的长度赋值到rdx
		syscall
		jmp exit

exit:
		mov $SYS_EXIT ,%rax 
		mov $EXIT_CODE ,%rdi 
		syscall

编译:

as -g -o reverse_att.o reverse_att.s

ld  -o reverse_att reverse_att.o

Intel汇编实现

section .data
		SYS_WRITE equ 1
		STD_OUT   equ 1
		SYS_EXIT  equ 60
		EXIT_CODE equ 0

		NEW_LINE db 0xa
		INPUT db "Hello world!"
section .bss;;//定义buffer缓存
		OUTPUT resb 12
global _start
section .text
_start:
		mov rsi, INPUT;//将字符串地址赋值为rsi
		xor rcx, rcx;//清零
		cld;//清空df标志位0
		mov rdi, $ + 15;//预处理rdi寄存器,在calculateStrLength函数中会用到。$是表示当前指令的地址。$$是当前段的地址。通过$+15可以计算得到命令xor rax,rax这条指令的地址(可以通过objdump -D命令来数出来),将该地址赋值给rdi进行了保存。在calculateStrLength函数中可以直接获取rdi地址来返回到xor rax,rax命令。
		call calculateStrLength;//调用函数计算字符串长度
		xor rax, rax;//清空rax
		xor rdi, rdi;//清空rdi
		jmp reverseStr;//跳转到reverseStr函数进行翻转

calculateStrLength:;//计算INPUT字符串的长度,并保存在rcx中。
		cmp byte [rsi], 0;//与0对比,字符串最后一个’\0’字符串结束标志
		je exitFromRoutine;//函数结束
		lodsb;//从rds:rsi处加载一个字节到al,并增加rsi
		push rax;/将刚获取到的al进行压栈,不过这会破坏了原本返回地址的位置。返回使用通过push rdi来解决。
		inc rcx;//增加计数器
		jmp calculateStrLength;//循环
exitFromRoutine:;//返回到_start主线。
		push rdi;//压栈return地址到栈中
		ret
reverseStr:
		cmp rcx, 0;//对比0,rcx已保存的是字符串的长度
		je printResult;//相等则跳转到printResult
		pop rax;//从栈中弹出一个字符给rax
		mov [OUTPUT + rdi], rax;写到[OUTPUT+rdi]对应的地址
		dec rcx;//写一个少一个
		inc rdi;//写一个地址加一个
		jmp reverseStr;//返回循环

printResult:
		mov rdx, rdi;//输出的长度赋值到rdx
		mov rax, SYS_WRITE;//调用号
		mov rdi, STD_OUT;//输出句柄
		mov rsi, OUTPUT;//输出字符串地址
                syscall;//调用sys_write输出OUTPUT保存的反向字符串。
		jmp printNewLine;//调用函数输出换行符

printNewLine:
		mov rax, SYS_WRITE;//调用号
		mov rdi, STD_OUT;//输出句柄
		mov rsi, NEW_LINE;//输出字符串地址
		mov rdx, 1;//输出的长度赋值到rdx
		syscall
		jmp exit

exit:
		mov rax, SYS_EXIT
		mov rdi, EXIT_CODE
		syscall

编译和链接:

nasm -g -f elf64 -o reverse.o reverse.asm

ld -o reverse reverse.o

执行后输出如下:

# ./reverse

!dlrow olleH

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值