将 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
,这是未定义的行为。此外,a
、x,
和 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 long
或uint64_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 标准库,直到你掌握了它。 read
和 write
有太多陷阱。您将希望编写它们一次,然后将它们包含在一个小型库中,以便进行任何更多的组装工作。
我们可以通过从命令行中取出参数来跳过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 的 >>
运算符。想想它在算术上做了什么。该指令是shr
或sar
,具体取决于它应该签名还是未签名。以上是关于将 C 代码转换为 x86-64 位程序集?的主要内容,如果未能解决你的问题,请参考以下文章