汇编语言数据段变量值

Assembly Language data segment variable values

我正在努力学习汇编语言。我注意到它与 Java 等高级编程语言相比完全不同。

所以我读到数据传输指令遵循以下语法:

mnemonic destination, source

我认为是 destination = source 换句话说,就是给内存赋值。

我在书上看到了这个数据段声明的例子。

.data 
var1 SBYTE -4,-2,3,1 
var2 WORD 1000h,2000h,3000h,4000h 
var3 SWORD -16,-42 
var4 DWORD 1,2,3,4,5

怎么会有 > 1 个变量值? 这到底是什么意思?

如有任何解释,我将不胜感激。

谢谢。

要在汇编中定义多个 WORD 大小的变量,我们可以使用

var1 WORD 1000h
var2 WORD 2000h
var3 WORD 3000h
var4 WORD 4000h 

程序员通常不需要命名每个变量,只需命名第一个变量,然后使用指针算法找到其他变量。
在这种情况下我们可以使用

var1 WORD 1000h
     WORD 2000h
     WORD 3000h
     WORD 4000h 

当某些变量可能有不同的大小时,这尤其方便,否则关键字 WORD 的重复很烦人,可以简化为最终形式

var1 WORD 1000h, 2000h, 3000h, 4000h

这相当于第二种形式和第一种形式(除了名字)。

我什至不会在 Assembly 中经常使用单词 "variable"。

你当然可以这么想,而且大部分都能奏效,但从技术上讲,它的水平较低。

var1 DWORD 12345678h 会将值 12345678h 编译成四个字节(在 x86 little-endian 上字节将是 78 56 34 12),那些将 "land" 某处变成 .data段,OS加载executable后成为内存内容。 OS 会挑选一些空闲内存来加载它,所以它也会提供 .data 段的起始地址,并调整加载代码以反映二进制加载完成后的真实地址,然后执行它。

这意味着 78 56 34 12 字节将在某个特定地址可用(x86 上的内存可按字节寻址)。

var1将成为符号table中的"symbol",标记这四个中第一个字节的地址。

然后你可以写成mov cl,[var1]这样的汇编指令,意思是"load cl with byte from memory at address of symbol var1",这条指令在executable中被标记,OS会用real来调整符号 table 值,因此在执行期间它将指向加载 .data 段的正确内存。

或者像x86_64那样使用相对寻址时,mov指令编译为mov cl,[rip+offset_between_next_instruction_and_var1],那么OS不需要调整指令在全部,因为它可以在任何内存位置工作,所以偏移量是相对的。

从 BYTE 大小的地址 [var1] 加载内存内容将加载 78h - 非平凡的变量思维中断操作示例。从 [var1+1] 加载 WORD 将从内存中加载 WORD 值 3456h (在非 x86 平台上,未对齐的内存访问可能会导致执行崩溃,在 x86 上它会工作,只是性能会受到影响)。

然后 var1 DWORD 1,2,3,4 只是意味着你在 var 地址后面编译了更多的字节值,就像本例中的 01 00 00 00 02 00 00 00 .... 一样。您可以通过将它们寻址为 mov eax,[var1 + 2*4] 来使用它们 -> 会将 var1[2](Java 类似数组)值加载到 eax,即 3

请注意 var1 只是内存地址,因此如果向其添加正确的偏移量,您实际上可以通过它寻址来自 var2 的数据。因此在 Assembly 中无意中覆盖其他变量是多么容易,例如,错误地将 DWORD 写入 BYTE 变量就足够了,并且您已经覆盖了超出变量的一些内存的 3 个字节,可能被其他变量使用。

另请注意,在访问数组时,您必须始终手动执行索引的 *1、*2、*4、..!所以你必须时刻注意数组元素的大小。

两个大小的基本幂可以直接编码成x86指令的扩展寻址模式,比如mov eax,[ebx + esi*4 - 44]用"esi-11"索引寻址ebx地址的一些dword数组,所以Java-就像 eax = ebx[esi-11]; 一样,避免您单独计算乘法。

这是另一个常见的错误来源,忘记了 "index" 等于 "memory byte offset" 仅当数组元素为单字节大小时,在所有其他情况下,您必须将索引乘以元素为内存寻址获取字节偏移量的大小。

最后,当您将这些内容写入 .data 段时,它们会按顺序编译,作为连接的字节流(检查您的汇编程序规范以了解特定指令的任何自动填充,例如 dword,根据需要提前插入填充字节以对齐结果数据)。所以你实际上不需要任何 var1 ,如果你想硬核,自己计算所有偏移量,并通过 .data + offset 处理这些字节,这是可能的(这只是脑力练习向您展示在线上写 "more values" 是什么意思,而不是推荐 :)).


编辑:"irvine" ...所以您可能正在使用汇编程序 MASM?它不仅在编译期间保留地址符号,而且还记住第一个声明大小(如 "DWORD"),因此它将尝试使用更多 "variable-like" 编译方法来涵盖一些用例。

我个人建议您忽略它并仅将它们视为地址并避免所有 MASM 古怪语法,因为 1) 它在其他 x86 汇编程序中不起作用 2) 在较大的源代码中可能会非常混乱,一旦你习惯了低级组装。

我的意思是,在 MASM 中,mov eax,var1 被编译成机器码 mov eax,[address_of_var1](加载 eax 和 var1 的内存内容,即从人的角度来看 "load eax with variable var1")。

但是当我在没有可见 [] 的情况下读取源指令时,我习惯于认为它不是在访问内存并且只使用立即数(比如 mov eax,esimov eax,[esi]).即使在 MASM 中你可以写 mov eax,[var1],它也可以工作。但是要提取 var1 本身的地址 - 需要额外的语法糖,例如 mov eax,OFFSET var1 IIRC.


最后说明:那些 WORD 1,2,3,4 定义通常用在 Java 中你会使用数组的地方,比如 short wordArrayVar[] = {1, 2, 3, 4};。这是对那些多值定义的一种可能解释。但在某些情况下,它甚至在较低级别使用,只是在 .data 段中定义特定的字节值,甚至不用作数组,而是以某种不同的方式使用。

另一个常见的模式是"structure"实例的初始化,在Java中没有很好的例子,因为class成员变量不保证一个接一个地存储在内存中?但是在 C++ 中,所有写在源代码中的 class/struct 成员变量都可以想象成一个字节一个字节的内存,每个成员变量都有特定的偏移量和对齐方式,由它在源代码中的类型和位置决定。到那时,您可以通过在完整结构大小的块中为每个字节定义值来创建此类结构的预初始化实例。