我刚开始学习组装,我的朋友和我对以下命令中究竟发生了什么有了不同的想法:
推 1234h
我们的问题是:第一个数字(12)将在堆栈中的哪个位置?
ss:[sp-2]?
ss:[sp-4]?
*sp =堆栈指针
我刚开始学习组装,我的朋友和我对以下命令中究竟发生了什么有了不同的想法:
推 1234h
我们的问题是:第一个数字(12)将在堆栈中的哪个位置?
ss:[sp-2]?
ss:[sp-4]?
*sp =堆栈指针
假设您正在谈论 x86 硬件(因为您指定了ss
寄存器),字节将被推高顺序(最重要)到最低顺序(最不重要)。在这种情况下,它将推送 byte 12h
,然后是 byte 34h
。由于 x86 堆栈指针在您推送项目时会减少,因此内存布局将如下所示:
[sp+1] = 12h
[sp] = 34h
如果您将 [sp] 作为一个单词(两个字节)访问,您将获得原始值:
[sp] = 1234h
信不信由你,但你们俩都可能是对的。字节顺序取决于您机器的字节顺序。
在实模式下(看起来你正在尝试为它编写程序),首先计算地址。公式很简单:
address = (segment<<4)+offset
然后这个地址减去你试图推送的元素的大小(word
:2字节,dword
:4字节,qword
:8字节)。第三步是在结果位置上写入数据。因此,如果SS
是0x30
和SP
0xFF
,那么:
address = (0x30<<4)+0xFF = 0x3FF
2
:position = 0x3FF - 2 = 0x3FD
*(unsigned char*)position = LOBYTE(0x1234)
,
*((unsigned char*)position+1) = HIBYTE(0x1234)
.所以,byte SS:[SP] = 0x34
和byte SS:[SP+1] = 0x12
。请注意,堆栈会向下增长/扩展,因此不会有SP-x
, 但是SP+x
.
在保护模式下,事情有点复杂(我将在这里只写使用段寄存器的过程):
首先,处理器获取全局描述符表描述符的地址(由LGDT
指令加载)。
处理器将段寄存器的值与 GDT 的大小进行比较(如果更大,则抛出异常)。它还检查段寄存器是否没有指向空描述符(SS!=0
)
SS*8
)。处理器必须检查一些事情:
4.1。分段存在吗?4.2. 我们是否有足够的权限访问段?4.3. 是代码段吗?4.4. 可写吗?4.5. 是ESP*granularity+base < limit
4.6。是系统段吗?
然后它通过 计算地址base_of_segment+ESP
。
之后它继续类似于实模式。