我有一个inputfile.txt,如下所示:3 4 2 0 8 1 5 3
我试图在outputfile.txt
内写入输入文件的每个字符,加1。
所以在outputfile.txt里面我应该看到4 5 3 1 9 2 6 4
。
我试着写这段代码,但我有几个疑问。
.section .data
buff_size: .long 18
.section .bss
.lcomm buff, 18
.section .text # declaring our .text segment
.globl _start # telling where program execution should start
_start:
popl %eax # Get the number of arguments
popl %ebx # Get the program name
popl %ebx # Get the first actual argument - file to read
# open the file
movl $5, %eax # open
movl $0, %ecx # read-only mode
int $0x80
# read the file
movl $0, %esi
movl %eax, %ebx # file_descriptor
analyzecharacter: #here I want to read a single character
movl $3, %eax
movl $buff, %edi
leal (%esi,%edi,1), %ecx
movl $1, %edx
int $0x80
add $1, %esi #this point is not clear to me, what I'd like to do is to increment the index of the buffer in order to be positioned on the next cell of buffer array, I've added 1 but I think is not correct
cmp $8, %esi # if I've read all 8 characters then I'll exit
je exit
openoutputfile:
popl %ebx # Get the second actual argument - file to write
movl $5, %eax # open
movl $2, %ecx # read-only mode
int $0x80
writeinoutputfile:
#increment by 1 and write the character to STDOUT
movl %eax, %ebx # file_descriptor
movl $4, %eax
leal (%esi,%edi,1), %ecx
add $1, %ecx #increment by 1
movl $1, %edx
int $0x80
jmp analyzecharacter
exit:
movl $1, %eax
movl $0, %ebx
int $0x80
我有两个问题/怀疑:
1-我首先怀疑的是这条指令:add $1, %esi
。这是通过buffer
数组的正确方法吗?
2-第二个疑问是:当我分析每个角色时,我是否应该始终调用openoutputfile
标签?我认为通过这种方式我重新打开文件并覆盖以前的内容。
实际上,如果我运行该程序,我只会看到一个字符\00
(垃圾字符,由此指令中%esi的值引起,我猜:leal (%esi,%edi,1), %ecx
)。
我希望我的问题很清楚,我对装配很陌生,而且我花了好几个小时。
FYI:
I'm using GAS Compiler and the syntax is AT&T.
Moreover I'm on Ubuntu 64 bit and Intel CPU.
答案 0 :(得分:2)
那么,我将如何做代码...... 考虑到这一点,我已经习惯了英特尔的语法,我无法在网上写出AT& T来源而没有错误(而且我实在太懒了事情和调试它),所以我会尽量避免完全编写指令,只是描述过程,让你填写说明。
因此,让我们决定你要通过我的来源的第1版char来做char:
start:
; verify the command line has enough parameters, if not jump to exitToOs
; open both input and output files at the start of the code
processingLoop:
; read single char
; if no char was read (EOF?), jmp finishProcessing
; process it
; write it
jmp processingLoop
finishProcessing:
; close both input and output files
exitToOs:
; exit back to OS
通过我的清单后,这个设计存在一个微妙的问题,它没有严格检查文件系统错误,例如无法打开任何一个文件,或者写字符(但是你的来源)也不关心)。否则我认为它应该运作良好。
因此,让我们在版本2中扩展它以更接近真实的ASM指令(星号标记的指令是由我来的,因此可能使用混乱的语法,由您来决定这些指令的最终版本):
start:
; verify the command line has enough parameters, if not jump to exitToOs
popl %eax # Get the number of arguments
* cmpl $3,eax ; "./binary fileinput fileoutput" will have $3 here?? Debug!
* jnz exitToOs
; open both input and output files at the start of the code
movl $5, %eax # open
popl %ebx # Get the program name
; open input file first
popl %ebx # Get the first actual argument - file to read
movl $0, %ecx # read-only mode
int $0x80
cmpl $-1, %eax ; valid file handle?
jz exitToOs
* movl %eax, ($varInputHandle) ; store input file handle to memory
; open output file, make it writable, create if not exists
movl $5, %eax # open
popl %ebx # Get the second actual argument - file to write
* ; next two lines should use octal numbers, I hope the syntax is correct
* movl $0101, %ecx # create flag + write only access (if google is telling me truth)
* movl $0666, %edx ; permissions for out file as rw-rw-rw-
int $0x80
cmpl $-1, %eax ; valid file handle?
jz exitToOs
movl %eax, ($varOutputHandle) ; store output file handle to memory
processingLoop:
; read single char to varBuffer
movl $3, %eax
movl ($varInputHandle), %ebx
movl $varBuffer, %ecx
movl $1, %edx
int $0x80
; if no char was read (EOF?), jmp finishProcessing
cmpl $0, %eax
jz finishProcessing ; looks like total success, finish cleanly
;TODO process it
* incb ($varBuffer) ; you wanted this IIRC?
; write it
movl $4, %eax
movl ($varOutputHandle), %ebx # file_descriptor
movl $varBuffer, %ecx ; BTW, still set from char read, so just for readability
movl $1, %edx ; this one is still set from char read too
int $0x80
; done, go for the next char
jmp processingLoop
finishProcessing:
movl $0, ($varExitCode) ; everything went OK, set exit code to 0
exitToOs:
; close both input and output files, if any of them is opened
movl ($varOutputHandle), %ebx # file_descriptor
call closeFile
movl ($varInputHandle), %ebx
call closeFile
; exit back to OS
movl $1, %eax
movl ($varExitCode), %ebx
int $0x80
closeFile:
cmpl $-1, %ebx
ret z ; file not opened, just ret
movl $6, %eax ; sys_close
int $0x80
; returns 0 when OK, or -1 in case of error, but no handling here
ret
.data
varExitCode: dd 1 ; no idea about AT&T syntax, "dd" is "define dword" in NASM
; default value for exit code is "1" (some error)
varInputHandle: dd -1 ; default = invalid handle
varOutputHandle: dd -1 ; default = invalid handle
varBuffer: db ? ; (single byte buffer)
哇,我其实是完全写的? (当然它需要语法检查+清除星号,";"用于评论等...)
但是我的意思是,版本1的评论已经非常详细,每个都只需要少量的ASM指令,所以并不困难(虽然现在我看到我确实在53分钟前提交了第一个答案,所以这是关于〜1小时为我工作(包括谷歌搜索和其他地方的一些其他事情))。
我绝对不知道有些人可能想要使用AT& T语法,这是如此荒谬冗长。我很容易理解为什么GCC正在使用它,对于编译器来说这非常好。
但是也许你应该检查NASM,这是人类"面向(尽可能少写语法糖,并专注于指令)。 NASM的主要问题(或我认为的优势)是英特尔语法,例如: MOV eax,ebx
将数字ebx
放入eax
,这是Intels错误,从其他微处理器制造商处获取LD
语法,忽略 LD = load 含义,并将其更改为 MOV = move 以不公开复制指令集。
然后,我完全不知道为什么ADD $1,%eax
是AT& T中的正确方式(而不是eax,1
顺序),我甚至不想知道,但它对我没有任何意义(由于英特尔MOV
语法的LD
来源,反向MOV
至少有一定意义。
OTOH我可以与cmp $number,%reg
有关,因为我开始使用" yoda"在C ++中格式化以避免if
中的变量值变化(比较:if (0 = variable)
与if (variable = 0)
,两者都有拼写错误=
而不是想要的==
.. " yoda"即使有警告OFF也不会编译。)
但是......哦......这是我本周的最后一次AT& T ASM答案,它让我感到很生气。 (我知道这是个人偏好,但所有额外的$
和%
使我感到烦恼,就像颠倒的顺序一样。)
拜托,我花了很多时间写这篇文章。尽量花时间研究它,并试图理解它。如果感到困惑,请在评论中提问,但如果你完全错过了这一点并且没有从中学到任何有用的东西,那将是可怜的浪费我们的时间。 :)所以继续。
最后的注意事项:并努力寻找一些调试器,找到适合自己的东西(可能是一些视觉上适合你的东西" TD"来自Borland的DOS时代对于新人来说会非常好),但它&# 39; s 绝对必要让您快速改进,能够通过指令逐步指令,并观察寄存器和内存内容如何改变值。真的,如果你能够调试自己的代码,你很快就会意识到你正在从%ebx
中的错误文件句柄中读取第二个字符......(至少我希望如此)。
答案 1 :(得分:0)
早期清除1):add $1, %esi
确实等同于inc %esi
。
当你学习汇编程序时,我会选择inc
变体,所以你不要忘记它的存在并习惯它。回到286-586次,它的执行速度也会更快,今天使用add
代替 - 因为微架构(μops)的复杂性,其中inc
对于CPU而言更加复杂(将其翻译为添加μops我猜,但你不应该在学习基础知识的同时担心这一点,而是要考虑源的“人类”可读性,不要有任何性能技巧。
这是正确的方法吗?
那么,首先应该决定是否要为每个字符解析它(或者更确切地说是byte
,因为字符现在通常是utf8字形,其大小可以是1到6或者字节数; I “甚至不确定”或者用缓冲区处理它。
这两者的混合使代码中的其他错误变得容易。
从快速看,我看到了:
这可能是你所做的所有重大错误,但实际上这么多,我建议你从头开始。
我将尝试在下一个答案中快速完成我的版本(因为这个版本有点长),向您展示我将如何做到这一点。但首先请尝试(努力)找到我在上面强调的所有要点,并了解您的代码是如何工作的。如果你完全理解你的指令是做什么的,以及为什么他们真的做了我描述的错误,你将有更多的时间来设计你的下一个代码,再加上调试它。因此,您将真正找到并充分理解的点越多,对您来说就越好。
“BTW笔记”:
我从未做过linux asm编程(我现在很想在阅读你的努力之后做点什么),但是从一些关于系统调用的wiki中我读到了:
系统调用期间保留所有寄存器。
当然,eax
中的返回值除外。
请记住这一点,如果您正确地对系统调用进行分组,它可能会在调用之前重复注册设置,从而为您节省一些麻烦。