Assembly Can';无法在Linux NASM中打印单个字符
我尝试制作一个程序,它接受一些输入,在字符串中找到奇数位置并打印相应的字符,因此您输入'somewords'并打印'oeod'。Assembly Can';无法在Linux NASM中打印单个字符,assembly,x86,nasm,Assembly,X86,Nasm,我尝试制作一个程序,它接受一些输入,在字符串中找到奇数位置并打印相应的字符,因此您输入'somewords'并打印'oeod'。 最后我做了一个循环,循环遍历字符串,然后将计数器除以2,如果余数不等于0,则在计数器的位置打印字符 它不打印单个字符,而不打印任何内容 完整代码: SECTION .bss inp: resb 255 SECTION .data msg db "Enter the string: ", 0h SECTION .text global _start _start:
最后我做了一个循环,循环遍历字符串,然后将计数器除以2,如果余数不等于0,则在计数器的位置打印字符 它不打印单个字符,而不打印任何内容 完整代码:
SECTION .bss
inp: resb 255
SECTION .data
msg db "Enter the string: ", 0h
SECTION .text
global _start
_start:
mov eax, msg
call stprint
mov edx, 255 ; take user input
mov ecx, inp
mov ebx, 0
mov eax, 3
int 80h
call findodd
mov ebx, 0
mov eax, 1
int 80h
findodd:
push eax
push ecx
push edx
push esi
push ebx
mov ecx, 0 ; counter
mov esi, 2 ; divider
.iterstring:
mov eax, inp ; move input to eax
cmp byte [eax+ecx], 0 ; check for end of the string in position
je .finish ; if equal, finish
inc ecx
push eax
mov eax, ecx ; move counter to eax
xor edx, edx ; divide it by 2
idiv esi
pop eax
cmp edx, 0 ; check the remainder
jnz .printchar ; print character if != 0
jmp .iterstring
.printchar:
push eax
push ebx
movzx ebx, byte [eax+ecx] ; move single byte to ebx
push ecx
mov ecx, ebx ; move ebx to print
mov edx, 1 ; print the character
mov ebx, 1
mov eax, 4
int 80h
pop ecx
pop eax
pop ebx
jmp .iterstring
.finish:
pop eax
pop ecx
pop edx
pop esi
pop ebx
ret
; print string function (taken from tutorial)
; if I try to print single character with it I get SEGFAULT
stprint:
push edx
push ecx
push ebx
push eax
call stlen
mov edx, eax
pop eax
mov ecx, eax
mov ebx, 1
mov eax, 4
int 80h
pop ebx
pop ecx
pop edx
ret
stlen:
push ebx
mov ebx, eax
nextch:
cmp byte [eax], 0
jz finish
inc eax
jmp nextch
finish:
sub eax, ebx
pop ebx
ret
我尝试使用bl、al和cl,但运气不佳。我还试着做了一些检查。例如,在.iterstring中打印计数器:
nasm-f elf lr3.asm&&ld-m elf_i386 lr3.o-o lr3&&l3
输入字符串:test
1.
2.
3.
4.
5.
所以看起来迭代工作得很好
我最幸运的是,我回答了类似的问题(),对代码做了这样的更改:
.printchar:
push eax
push ebx
push esi
mov eax, inp
movzx ebx, byte [eax+ecx]
mov esi, ecx ; see below
push ecx
push ebx
mov ecx, esp
mov edx, 1 ; print the character
mov ebx, 1
mov eax, 4
int 80h
pop ecx
pop ebx
pop eax
pop ebx
mov ecx, esi ; without this it just prints 1 character and ends
pop esi ; so ecx is not restored with pop for some reason?
jmp .iterstring
但它会打印除第一个字符以外的所有字符:
nasm-f elf lr3.asm&&ld-m elf_i386 lr3.o-o lr3&&l3
输入字符串:somewords
默兹
我陷入困境,无法理解我的错误是什么
编辑,最终代码:
findodd:
push eax
push ecx
push edx
push esi
push ebx
mov esi, 0 ; counter
mov eax, inp
.iterstring:
inc esi
cmp byte [eax+esi], 0
jz .finish
test esi,1
jz .iterstring
movzx ecx, byte [eax+esi]
push ecx
mov ecx, esp
mov edx, 1
mov ebx, 1
push eax
mov eax, 4
int 80h
pop eax
pop ecx
jmp .iterstring
.finish:
pop eax
pop ecx
pop edx
pop esi
pop ebx
ret
现在它按预期工作:
nasm -f elf lr3.asm && ld -m elf_i386 lr3.o -o lr3 && ./lr3
Enter the string: somewords
oeod
我不得不删除更多的push-pop指令,还必须将计数器移到esi中,因为按下然后弹出寄存器并不总是恢复它们在堆栈中的值,这对我来说很奇怪。当我尝试移动到
字节[eax+ecx]
的ecx地址时,它工作了,但当我将其更改为字节[eax+1]
时,它会发生故障,因为在pop后恢复eax会中断。当我按下ecx打印一条消息,然后弹出它回来时,结果是SEGFULT,而gdb在弹出消息后显示ecx内有垃圾代码。对于当前代码,它可以正常工作。这行代码不正确:
mov ecx, ebx ; move ebx to print
write
(int 80h/eax=4
)希望ecx
包含要写入的数据的地址(请参阅)。但是您正在传递数据本身
在修改后的代码中,您将字符放在堆栈上,然后在ecx
中传递其地址,因此这是正确的。
但是,当您到达.printchar
时,您已经增加了ecx
。这就是代码不打印第一个字符的原因
作为补充说明,您对偶数/奇数的检查是不必要的复杂。可以简化为:
test ecx,1 ; set EFLAGS based on ecx AND 1
jnz .printchar
您使用的是64位内核吗?@Joshua如果它是在没有
配置IA32\u仿真的情况下构建的,例如在WSL(Linux的Windows子系统)上,那么这将是一个问题。这些nasm+ld命令将创建以32位模式运行的32位可执行文件。因此,没有IA32支持的内核实际上根本不会运行它。64位内核不能解释这些症状,因为OP提供了一个很好的,而不仅仅是“不工作”:)如果在底部使用jz.iterstring
,而不是在相反的条件下跳过jmp
,那么iterstring
循环将更加有效。与惯用的do{}while()
循环风格相比,这是不必要的过度复杂。另外,使用div
除以2的幂也很可怕:使用AND获得余数,即低位。或者更好的方法是,使用test al,1
测试奇数/偶数,直接检查EAX的低位。或者更简单地说,将循环展开2,而不是在两种行为之间切换分支,您只需按顺序执行。首先只需检查0终止符(偶数字节),然后检查并打印奇数字节。然后循环重复。您不需要将数据复制到任何地方,只要让ECX指向数组即可。(虽然将奇数字符复制到tmp数组并对整个输出进行一次写入
系统调用会更有效。但使用SSE2psrlw xmm0,8
/packuswb
可以更有效地将奇数字节从2个16字节向量打包成一个。谢谢你,@peter cordes。我只知道基本知识,但会把sse2挖下来,谢谢你指出。