将 C 代码转换为 x86-64 位程序集?

Posted

技术标签:

【中文标题】将 C 代码转换为 x86-64 位程序集?【英文标题】:Converting C code to x86-64 bit Assembly? 【发布时间】:2020-11-11 02:11:38 【问题描述】:

我正在尝试为我的字符串创建一个随机字母生成器,我已经获得了一个 C 代码,并且必须将它转换为我的程序的汇编语言。我在 x86-64 位 NASM 汇编语言 中执行此操作。我应该只使用系统调用而不是 C/C++ 函数调用。 这是我必须转换的 C/C++ 代码:

int genran(int x,int y)

    int a = 0;
    a = a + x * 1103515245 + 12345;
    return (unsigned int)(a / 65536) % (y + 1); 

我是 Assembly 的新手,如果有任何帮助,我将不胜感激,这就是我目前所得到的。我知道它有些错误,但我会努力改进它:

            section .data
    string          db  "The random string is generated below: "
    len_string      equ  $-string
    a               dd  0
    x               dd  ?
    y               dd  ?
    rem             dd  0
            section .bss
    string_buff     resb    21  ;Our string's length is 20 characters
            section .txt
            global main
    main:
        mov rax, 1
        mov rdi, 1
        mov rsi, string     
        mov rdx, len_string
        syscall

        mov rax, a
        mov rbx, x      ;we have to come up with a value of x?
        mul rbx, 1103515245
        add rbx, 12345
        mov rax, rbx
        div rax, 65536
        mov rdx, y      ;we have to come up with a value of y?
        add rdx, 1
        ;mod rax, rdx
        ;ret
exit: 
      mov rax, 60
      xor rdi, rdi
      syscall

【问题讨论】:

注意int是x86-64调用约定中的32位类型;无需使用 64 位寄存器,只需 32 位。 C 甚至明确地使用unsigned 来使包装定义明确。尽管它应该一直使用 unsigned ; x * 1103515245 + 12345 依赖于 32 位环绕,但在 C 中,对于已签名的 int,这是未定义的行为。此外,ax,y 是局部变量,而不是全局/静态变量,因此它们应该只在寄存器中,而不是标签和 dd。 (另外mov reg, symbol 将地址放在 NASM 语法中的寄存器中,因此如果您确实想要全局变量,则需要 [a]。)还有其他错误。 不幸的是,分配指令提到它是在 64 位而不是 32 位中完成的。我将尝试使用 [a] 而不是 a 来存储数据而不是其地址 @Peter 你知道 x 和 y 应该输入什么值吗?我觉得 x 可能是 ASCII 字符值,而 y 可能是 20,即字符串的最大限制,对吗? 您确定它们是指 64 位整数,而不仅仅是它必须是 64 位代码吗?但即便如此,[x] 仍然与 C 不匹配;您的 C 没有任何静态存储。 godbolt.org/z/Ebrdra。查看编译器输出示例,使用unsigned long longuint64_t。 (另外,dd 是 4 个字节,一个 64 位寄存器是 8 个字节,如果你打算使用静态存储)。 虽然 C 可能应该使用static uint64_t a 来保持对这个LCG 的调用的状态?否则x 应该是之前的随机值,将其反馈给函数以生成另一个。 y 显然是要生成的随机数范围,这就是为什么他们将其用作 random_number % (y+1) 以从 [0..y] 中获取随机数,并且分布几乎均匀。 (不完美,除非y+1 正好除以 2^32。) 【参考方案1】:
    mov rax, rbx
    div rax, 65536

请阅读说明;这不存在。 div 是一个只接受一个参数的操作码。代码应该是这样的

    mov rax, rbx
    xor rdx, rdx ; Div takes rdx:rax as an implicit 128 bit argument.
    mov rcx, 65536
    div rcx

在这个技能水平上,我不建议尝试使用read 或编写系统调用。相反,我非常推荐调用 C 标准库,直到你掌握了它。 readwrite 有太多陷阱。您将希望编写它们一次,然后将它们包含在一个小型库中,以便进行任何更多的组装工作。

我们可以通过从命令行中取出参数来跳过read,如下所示:

    mov rbx, [rsp + 16] ; argv[1]

除非我非常错误,否则堆栈上的第一件事是argc,然后是argv[0],然后是argv[1],...

【讨论】:

OP 写的是main,而不是_start,所以EDI=argc,RSI=char **argv。没有堆栈参数。 mov rdx, [rsi+8] 会将argv[1] 加载到RDX 中,准备用movzx eax, byte [rdx] 取消引用以获取argv[1][0],第一个arg 的第一个字节。 (假设argc>1,否则argv[1] 为NULL。) 哦,我明白了,我误解了 div 类似于 add。另外,我的其他东西是正确的还是只是需要更正的 div? @Joshua 我相信有 20 个字符,另外 1 个用于新行,即 y string_buff 为 21 [编辑:Nvm,我没有得到 argc 部分] div 没有直接形式,所以 div 65536 也不起作用。但无论哪种方式,在除以 2 的恒定幂时使用 div 都是愚蠢的;改用右移。 不幸的是没有跳转或循环,也没有推送、弹出、ret 功能。 wdym 右移 ?@Nate 像 C 的 >> 运算符。想想它在算术上做了什么。该指令是shrsar,具体取决于它应该签名还是未签名。

以上是关于将 C 代码转换为 x86-64 位程序集?的主要内容,如果未能解决你的问题,请参考以下文章

x86 32位操作码,x86-x64不同或完全删除

汇编语言-X86处理器架构-64位x86处理器

将 scanf 与 x86-64 GAS 程序集一起使用

使用 g++ -march=x86-64 构建的代码可以在 32 位操作系统上运行吗?

python学习之计算机基础详解

x86-64的条件跳转如何工作?