我正在尝试学习汇编语言。我注意到它与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
有结果>变量的值是多少? 这究竟意味着什么?
我很感激任何解释。
感谢。
答案 0 :(得分: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
这相当于第一种形式的第二种形式和(名称旁边)。
答案 1 :(得分:0)
我甚至不会使用"变量"大会很多。
你当然可以这么想,而且它大部分会起作用,但从技术上讲它会更低级别。
var1 DWORD 12345678h
将值12345678h
编译成四个字节(在x86 little-endian上,字节将为78 56 34 12
),这些将" land"进入.data
段的某个地方,在通过操作系统加载可执行文件后将成为内存的内容。操作系统将选择一些空闲内存来加载它,因此它还将提供.data
段的起始地址,并在执行它之前调整加载的代码以反映加载二进制文件后的实际地址。
这意味着78 56 34 12
个字节在某个特定地址可用(x86上的内存可以按字节寻址)。
var1
将成为"符号"在符号表中,标记该四个字节中第一个字节的地址。
然后你可以在mov cl,[var1]
之类的汇编指令中写入,这意味着"在符号cl
"的地址处加载var1
来自内存的字节,这条指令是在可执行文件中标记,操作系统将使用实际符号表值对其进行调整,因此在执行期间它将指向加载.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个字节,可能被其他变量使用。< / p>
另请注意在手动访问数组时,如何始终执行索引的* 1,* 2,* 4,..所以你必须始终了解数组元素的大小。
两种尺寸的基本功能可以直接编码为x86指令的扩展寻址模式,如mov eax,[ebx + esi*4 - 44]
,用ebx地址处理某些双字阵列,其中包括&#34; esi-11&#34;索引,所以像Java一样类似eax = ebx[esi-11];
,这样可以省去单独计算乘法。
这是另一个常见的错误来源,忘记了&#34;索引&#34;等于&#34;内存字节偏移&#34;只有当数组元素是单字节大小时,在所有其他情况下,你必须将索引乘以元素大小以获得内存寻址的字节偏移量。
最后,当您将这些内容写入.data
段时,它们将按顺序编译,作为连续的字节流(检查汇编程序规范以了解特定指令的任何自动填充,如dword
,根据需要在前面插入填充字节以对齐结果数据)。所以你实际上并不需要任何var1
,如果你想去硬核,自己计算所有的偏移量,并通过.data + offset
来解决这些字节,它是可能的(这只是一种心理练习,可以向你展示写什么意思和更多的价值观,而不是推荐:))。
编辑:&#34; irvine&#34; ...所以你可能正在使用汇编程序MASM?那个不仅在编译期间保留了地址符号,而且还记住了第一个声明大小(例如&#34; DWORD&#34;),因此它将尝试用更多&#34;类似变量&#来覆盖一些用例34;编制方法。
我个人建议你忽略这一点,只考虑它们作为地址并避免所有MASM怪癖语法,因为1)它在其他x86汇编程序中不起作用2)在较大的源代码中可能会很混乱,一旦你习惯了低级别的装配。
我的意思是,在MASM中mov eax,var1
被编译为机器代码mov eax,[address_of_var1]
(内存内容为var1的加载eax,即。&#34;从变量var1加载eax&#34;从人的角度来看; )。
但是当我在没有可见[]
的情况下阅读源指令时,我曾经认为它不能访问内存并且只能立即使用(例如mov eax,esi
vs mov 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段中定义特定的字节值,甚至不能用作数组,而是以某种不同的方式。
另一种常见模式是&#34;结构&#34;的初始化。例如,在Java中没有很好的例子,因为类成员变量不能保证一个接一个地存储在内存中?但是在C ++中,所有在源代码中编写的类/结构成员变量都可以被想象为逐字节的内存,每个成员变量都有特定的偏移和对齐,由它在源代码中的类型和位置决定。此时,您可以通过在整个结构大小的块中定义每个字节的值来创建此类结构的预初始化实例。