【发布时间】:2018-12-27 13:11:52
【问题描述】:
我正在尝试编写一个小程序,询问用户他们的姓名,对用户输入进行编码,然后将一条消息打印到标准输出,详细说明编码的输入。例如,用户输入姓名‘John’,它会在标准输出中打印“Your code name is: Red5”。
SECTION .data ; Section containing initialised data
RequestName: db "Please enter your name: "
REQUESTLEN: equ $-RequestName
OutputMsg: db "Your code name is: "
OUTPUTLEN: equ $-OutputMsg
SECTION .bss ; Section containing uninitialized data
EncodedName: resb ENCODELEN
ENCODELEN: equ 1024
我的输出消息的第一部分,“你的代号是:”,存储(开始)在内存地址“OutputMsg”,输出消息的第二部分,将是编码的用户输入“Red5 ”,存储在内存地址“EncodedName”。因此,要将所需的消息打印到标准输出,我使用以下代码将两者连接起来:
mov rdx,OUTPUTLEN ; Length of string 'OutputMsg'
add rdx,r8 ; r8 contains the number of bytes entered by the user
; the code name is always equ in length to user input
mov rax,4 ; sys_write
mov rbx,1 ; stdout
mov rcx,OutputMsg ; Offset of string to print to stdout
int 80h ; Make kernel call
这几乎可以按预期工作。但是,输出中缺少最后一个字符。因此,我得到的不是“你的代号是:Red5”,而是“你的代号是:Red5”。在调试器中检查内存时,在“OutputMsg”的末尾和“EncodedName”的偏移量之间错误地“放置”了一个空内存地址 (0x00)。
Address Binary ASCII
0x… 60012a 0x20 Space (This is the end of the data item ‘OutputMsg’)
0x… 60012b 0x00 NUL
0x… 60012c 0x52 R (The start of SECTION .bss / 'EncodedName')
我已经使用其他几个代码示例对此进行了测试,在内存中SECTION .data 的结尾和SECTION .bss 的开头之间似乎总是存在NUL 字符的“随机”位置。
1) 是什么导致了这个空地址空间,因为它没有包含在我的源代码中?
2) 在我查看的所有示例中,空地址空间出现在 SECTION .data 的末尾,因此我认为这是预期的行为。这个空地址空间的具体原因是什么,是为了“标记”一个部分的结尾和下一个部分的开头吗?为什么有必要这样做?
3) 空间大小是如何计算的。我发现根据程序和我正在查看的部分,有时这个空间是一个字节,有时是两个/三个;我如何在运行前知道这个空白空间有多少字节?
我可以解决这个问题。但是,我想了解发生了什么。我编写了将两个SECTIONS 之间的字符串连接起来的代码,以便打印到标准输出。我无法解释的意外空地址空间正在影响我的计算。
NASM 版本 2.11.08 架构 x86 | Ubuntu 16.04
【问题讨论】:
-
通常你不应该依赖于不同部分的相对位置。它们甚至可能不在给定的顺序中。它们之间的填充由对齐要求确定。此外,部分内容可能来自多个文件,然后由链接器合并。
-
好吧,根据the manual NASM 默认假设大多数部分是 4 字节对齐。以
0xb结尾的地址不是4 字节对齐的,而以0xc结尾的地址是。 -
对于较大的
.bss或较大的.data,它们可能会位于不同的页面中,它们之间会有一些未映射的页面。由链接器决定如何将节映射到 ELF 可执行段 (What's the difference of section and segment in ELF file format),以及是否将它们标记为映射或复制到内存中。 -
顺便说一句,What happens if you use the 32-bit int 0x80 Linux ABI in 64-bit code?。您应该将代码移植到
syscall,除非您出于某种特定原因使用 32 位int 0x80。 -
您可以使用
gcc -m32构建 32 位二进制文件,而不是在学习的同时尝试将示例移植到 x86-64。 (例如,gcc -m32 -static -nostdlib foo.s汇编 + 链接 32 位静态可执行文件)。顺便说一句,“x64”仅在 Windows 上使用。架构的实际名称是 x86-64。