我的任务是在NASM x86 Assembly中编写一个处理转义序列的子程序。我终于把它弄到了不会发生段错误的程度,但它不会对小写字符进行转换。例如,如果我键入Hello \ nWorld,它会打印HellonWorld,其中n应替换为10换行换行符。我正在使用查找表进行转换。我正在从需要重写的代码中删除一些部分,但这是我必须测试的小写
答案 0 :(得分:1)
错误:
mov ecx, buf ; Arg 2: address of buffer
mov edx, BUFLEN ; Arg 3: buffer length
mov [ecx + edx], byte 0 ; null terminate the read buffer
; out of bounds, overwriting newstr first byte
; you should do "dec edx" ahead of write (dec edx == sub edx,1)
; when not sure about last index, imagine buffer of size 1 (ofs 0)
lowercase:
cmp ax, 'a' ; is it a?
; you loaded "bl" with value, and it's 8bit, not 16bit
; so you should do cmp bl,'a' (and next cmp is wrong too)
mov ecx, [esi] ; move the char to ecx to index
; no, nope. Not at all. First the "esi" already points beyond "\n"
; you already did inc esi twice (L1_top: and handle_ESC: )
; and mov ecx,[esi] will fetch 4 bytes (dword), it will load ecx with 'Worl'
; you want to do movzx ecx,bl (zero extending byte "n" in bl to dword in ecx)
; or and ebx,0xFF (masking out the higher 3 bytes to zero) and work with ebx
mov ebx, mappings ; point to the mappings array
sub ecx, 97 ; convert the lower case letter to decimal
; I would suggest to write it as: sub ecx,'a' - to keep human intent readable
mov ax, [ebx + ecx] ; indexed addressing done here
; loading two bytes, while your table is byte-sized, plus your index is *1
; if you would create word-sized table
; you would have to do "shl ecx,1" (index*2) after sub 'a'
; but mov al,[ebx+ecx] should be enough
然后在call handle_ESC
之后你将斧头写入newstr,并将ed edi写入两次。
这是无效的逻辑。
对于"\0123"
(八进制),您只想写单个字节,但将esi提前5
对于"\\"
,你想要写单字节并前进2(类似于C的逃逸)
对于"\n"
,您想要写单字节并前进2
等
所以newstr可以比原始str短得多,而handle_ESC的结果总是单字节,我认为,你应该只存储al
(转换后的值),并且只提前edi
一次。
您应该重新考虑您的代码逻辑,以正确处理所有请求的案例。
要输出正确长度的newstr(或用' '
空格字符填充它以使剩余的缓冲区“不可见”,那么你实际上可以打印整个BUFLEN。)
这就是我通过快速查看找到的内容,我没有尝试编译或运行,使用您的调试器。
编辑问题编辑后(源代码的第2版)(顺便说一句,这也是SO政策,在回答之后编辑问题,但在这个特殊情况下我对此很好..也许你应该把原来扩展到显示编辑过的部分):
mov ebx, mappings ; point to the mappings array
movzx ecx, bl ; move the char into ecx for indexing
您是否意识到bl
是ebx
的一部分?因此,您使用mappings
地址的最低有效字节覆盖char值,从而丢失char值。在这种情况下,补救措施很简单,ecx
已在mov ebx, mappings
之前提供,因此您可以将movzx
移到其前面。
(我没有检查其余的更改)
关于调试器:您应该在输入系统调用之后放置断点并“运行”它。还要检查是否有一些设置应该使用哪个控制台/终端作为输入,在Linux调试器中经常为I / O打开全新的终端(我使用“edb”用于微小的asm程序,对我很有用,并让我想起来自Borland的1990年代以来的经典涡轮调试器,并在那里使用了新的终端窗口。
但实际上我会建议不同的东西,注释掉STDIN读取系统调用,而是将变量的硬编码设置,如mov [buf],'A\n!'
mov eax,4
...etc...
来模拟系统调用输出值。然后,您可以专注于在没有检查终端窗口的情况下单步进入调试器(输出除外)。
你可以准备几个版本的输入,并通过注释来切换它们。稍后您将意识到创建合理的API更容易,只需在一些提供的缓冲区上调用您的函数,将结果接收到其他缓冲区,并通过提供不同的源数据指针运行它几次。因此,您可以一次性调试多个测试输入,并通过简单易用的界面创建功能。