程序集打印变量和值

Assembly print variables and values

我有这个代码

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 assembler 失败了:

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”。

我用来 assemble 我的程序的命令是:

/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

OS X 10.9.5 与英特尔酷睿 i5(x86_64 组装)

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


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

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

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

请注意,指针的数值取决于代码在虚拟地址 space 中的加载位置。所以地址 不是 严格意义上的 assemble 时间常数。 linker 需要标记需要 运行 时间重定位的东西,比如那些你 mov 到寄存器 (mov rsi,msg) 的 64 位立即常量地址。请参阅 for some information on the difference between that and lea rsi, [rel msg] to get the address into a register using a RIP-relative method. (That answer has links to more detailed info, and so do the wiki)。


您尝试使用 db [1+1]:您到底在期待什么? NASM语法中的[]表示内存引用。 First:结果字节必须是 assemble 时间常数。我不确定是否有一种简单的语法可以复制其他地址中的任何内容,但事实并非如此。 (我只是定义一个宏并在两个地方都使用它。)其次2 不是有效地址。


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

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

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


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

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

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


您的 link 命令看起来很有趣:

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。)