【问题标题】:A program in assembly to make an input string to lowercase汇编程序将输入字符串转换为小写
【发布时间】:2021-11-21 14:29:06
【问题描述】:

我正在尝试用汇编 x86-64 为英特尔 64 位处理器编写程序。该程序应使用 gas(GNU 汇编器)编译并在 Linux 上运行。问题是编写一个名为 lowercase 的程序,它接受输入字符串并打印该字符串的小写字母。 应该这样编译:

$> echo "STRING" | ./lowercase
   string
$>

我编写了程序,但问题是它会无限打印空格。谁能帮我理解为什么下面的代码会这样?

.section .bss
.comm buf, 1

.section .text
.globl _start

_start:
        mov $65,        %bh
        mov $97,        %ch
        mov $0,         %dh

Loop:
        mov $0,         %rax                    # syscall number for read
        mov $0,         %rdi                    # where to read from: stdin
        mov $buf,       %rsi                    # buffer adr
        mov $1,         %rdx                    # length of the buffer in bytes
        syscall

        cmpb %dh,       buf                     # if read returns 0 (EOF) or less then 0 exit
        jle Exit
        cmpb %bh,       buf                     # if the character is less than 65 (Char A) print it
        jl Write
        cmpb %ch,       buf                     # if the charcter is less than 97 make it lowercase
        jl ToLowercase


Write:
        mov $1,         %rax                    # system call for write
        mov $1,         %rdi                    # file handle for stdout
        mov $buf,       %rsi                    # address of string to output
        mov $1,         %rdx                    # number of bytes
        syscall
        jmp Loop

ToLowercase:
        addb $32,       buf                     # Make the character lowercase
        jmp Write                               # And go back to output it

Exit:
        mov   $60,      %rax                    # system call for exit
        movb  $0,       %dil                    # return code
        syscall

【问题讨论】:

  • 您是否尝试过单步调试并检查每条指令是否符合您的预期?如果没有,这就是去这里的方式。如果您有,请在此处包含有关哪些指令未达到您的期望的信息。调试是程序员的一项基本技能,尤其是对于汇编而言。

标签: assembly x86-64


【解决方案1】:

这一行:

cmpb %dh,       buf                     # if read returns 0 (EOF) or less then 0 exit

您使用系统调用的 count 参数修改了 %rdx(因此为 %dh)。另外,syscall 没有保存 %rdx 的合约,所以这个检查是无效的。

另外,来自系统调用(linux,其他)的返回值在 %rax 中,所以您正在使用 buf 检查未定义的值(%dh)?有点像

cmp $1, %rax
jlt Exit

将测试读取的返回。那你需要看看你是不是在'A'..'Z':

...
mov buf, %dl
cmp $'A', %dl
jl  write
cmp $'Z', %dl
jle ToLowerCase
...

【讨论】:

  • syscall 在 Linux 上将保留 rdx 和大多数其他寄存器,因此 rdx 在系统调用之后仍将包含 1 (但当然这不是 OP 想要的 dh 值,他们已经覆盖了)。然而,它将覆盖rcx 和r11,并且OP 在rcx(特别是ch)中确实具有重要值。
  • 从我的观点来看,当你不需要时,你不会将某些东西绑定到特定的实现。 Rdx 是易失性的,因此不应指望;除了它是错误的寄存器来查看这一事实之外;同样 %rcx 肯定被破坏了......
  • @mevets 非常感谢您的清晰解释。它解决了问题!
  • RDX 在 Linux 上的 function 调用约定中是不稳定的。它在 Linux 上的 syscall 调用约定中保留调用,如 x86-64 SysV ABI 的附录中所述(其中的要点 2 在 What are the calling conventions for UNIX & Linux system calls (and user-space functions) on i386 and x86-64 的早期版本中引用 - 当前版本有我的编辑更清楚地表达它。)也如内核中的 nolibc 示例中所述,请参阅lore.kernel.org/lkml/…
  • 不要将函数调用与系统调用混淆。它们在方便和效率方面是相似的,但 RCX 和 R11 在内核获得控制之前被 syscall 指令本身 破坏,而不是因为它想要被内核任意破坏。事实上,这就是 为什么 arg 传递 reg 必须不同的原因:RCX 不能仍然是第 4 个 arg 传递 reg,所以他们在 R10 中替换。
猜你喜欢
  • 2014-04-30
  • 1970-01-01
  • 2022-07-02
  • 1970-01-01
  • 2014-03-30
  • 2017-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多