【问题标题】:Assembly print variables and values程序集打印变量和值
【发布时间】:2015-12-11 20:16:19
【问题描述】:

我有这个代码

global start

section .text

start:
mov rax,0x2000004
mov rdi,1
mov rsi,msg
mov rdx,msg.len
syscall

mov rax,0x2000004
mov rdi,2
mov rsi,msgt
mov rdx,msgt.len
syscall

mov rax,0x2000004
mov rdi,3
mov rsi,msgtn
mov rdx,msgtn.len
syscall

mov rax,0x2000001
mov rdi,0
syscall

section .data

msg: db "This is a string",10
.len: equ $ - msg

var: db 1

msgt: db "output of 1+1: "
.len: equ $ - msgt

msgtn: db 1
.len: equ $ - msg

我想打印变量msgtn。我试过msgt: db "output of 1+1", var 但是 NASM 汇编器失败了:

second.s:35: error: Mach-O 64-bit format does not support 32-bit absolute addresses

我也试过"output of 1+1", [1+1]而不是变量,但我得到了:

second.s:35: error: expression syntax error

我也试过了,没有括号,没有数字,只有字符串“1+1”。

我用来组装我的程序的命令是:

/usr/local/Cellar/nasm/*/bin/nasm -f macho64 second.s && ld -macosx_version_min 10.7.0 second.o second.o

nasm -v 显示:

NASM version 2.11.08 compiled on Nov 27 2015

采用 Intel core i5(x86_64 程序集)的 OS X 10.9.5

【问题讨论】:

  • 你能告诉我们你用来组装和链接你的程序的命令吗?知道您使用的是什么版本的 NASM 也很好。 (nasm -v 应该给出版本)。编辑您的问题以提供确切的错误也会有所帮助。
  • 首先,我建议不要使用 2.11.08。它有一些令人讨厌的问题(错误)。获取旧版本或更新版本。使用正确版本的 NASM 命令,如 nasm -f macho64 -o second.o second.sld second.o -o second 应该可以工作。我确实在您的更新中注意到您的链接看起来不寻常。你的输入对象和输出文件都是second.o
  • 但我认为主要问题是您想将数字打印为字符串。您不能直接使用 sys_write 系统调用来做到这一点。您需要将数字转换为字符串并将该字符串的地址传递给 sys_write。或者,您可以链接到 c 库并使用 printf
  • @MichaelPetch 我编辑了我的问题。我实际上正在使用 2.11.08,因为我今天 brew 安装了它。如果没有 printf,我如何将该数字转换为字符串?
  • 一种方法是将一个数连续除以 10(直到被除数为 0),并将每个除法的余数(通过添加 ASCII 字符 '0` 转换为字符)存储到缓冲区相反的顺序。如果你谷歌你应该能够找到这种方法的一些例子。

标签: macos assembly nasm x86-64


【解决方案1】:

db 指令允许您将汇编时间常数字节放入目标文件(通常在数据部分)。您可以使用表达式作为参数,让汇编器在汇编时为您做一些数学运算。任何需要在运行时发生的事情都需要通过你编写的指令来完成,然后运行。它不像 C++,其中一个全局变量可以有一个在启动时在后台运行的构造函数。


msgt: db "output of 1+1", var

将放置这些 ascii 字符,后跟(低字节?)var 的绝对地址。你会使用这种东西(使用dddq)来做这样的事情 C:int var; int *global_ptr = &var;,你有一个全局/静态指针变量,它开始初始化为指向另一个全局/静态变量.我不确定 MacOS X 是否允许使用 64 位指针,或者它是否只是拒绝对 32 位地址进行重定位。但这就是你得到的原因:

second.s:35: error: Mach-O 64-bit format does not support 32-bit absolute addresses

请注意,指针的数值取决于代码在虚拟地址空间中的加载位置。所以地址不是严格来说是一个汇编时常量。链接器需要标记需要运行时重定位的内容,例如将mov 的那些 64 位立即常数地址放入寄存器 (mov rsi,msg)。请参阅this answer 了解有关它和lea rsi, [rel msg] 之间的区别的一些信息,以使用 RIP 相对方法将地址放入寄存器。 (该答案包含指向更详细信息的链接, wiki 也是如此)。


你尝试使用db [1+1]:你到底在期待什么? NASM 语法中的[] 表示内存引用。 First:生成的字节必须是汇编时常量。我不确定是否有一种简单的语法可以复制其他地址的任何内容,但这不是。 (我只是定义一个宏并在两个地方都使用它。)第二2 不是有效地址。


msgt: db "output of 1+1: ",   '0' + 1 + 1,    10

会将 ASCII 字符:output of 1+1: 2\n 放在目标文件中的那个位置。 10 是 ASCII 换行符的十进制值。 '0' 是 0x30 的一种写法,即编码字符 '0' 的 ASCII 码。 2 字节不是可打印的 ASCII 字符。你这样做的版本会在那里打印一个 2 字节,但你不会注意到,除非你将输出传送到 hexdump (或 od -t x1c 或其他东西,IDK OS X 提供的东西。od 不是非常好,但它被广泛使用。)

请注意,此字符串不是以空值结尾的。如果你想将它传递给期望一个隐式长度字符串的东西(比如fputs(3)strchr(3),而不是write(2)memchr(3)),附加一个额外的, 0 以在所有内容之后添加一个零字节否则。


如果您想在运行时进行数学运算,您需要将数据放入寄存器,添加它,然后将数字的字符串表示形式存储到某处的缓冲区中。 (或者一次打印一个字节,但这太可怕了。)

最简单的方法是调用printf,以轻松打印一个常量字符串并替换一些东西。花时间为需要手动调整的代码部分编写asm,而不是重新实现库功能。

在 cmets 中有一些关于 int-to-string 的讨论。


你的链接命令看起来很有趣:

ld -macosx_version_min 10.7.0 second.o second.o

您确定要两次相同的.o


当您不需要符号扩展到 64 位寄存器时,您可以只通过 moving 到 32 位寄存器来节省一些代码字节。例如mov edi,2 而不是 mov rdi,2 节省了一个字节(REX 前缀),除非 NASM 很聪明并且无论如何都会这样做(实际上,确实如此)。

lea rsi, [rel msg](或使用default rel)是比mov r64, imm64 更短的指令。 (AT&T 的助记符是 movabs,但 Intel 语法仍然称它为 mov。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多