[英]How does the jump instruction in assembly work with multiple processes?
So, I am confused about how jump instructions work in an operating system. 所以,我对跳转指令在操作系统中的工作原理感到困惑。 I thought that the jump instruction set the value in the processor's program counter.
我认为跳转指令设置处理器程序计数器中的值。 But programs can be run in various locations in memory.
但程序可以在内存中的不同位置运行。 I see that in x86, there's the
JMP EAX
instruction, but my C++ code doesn't seem to use this. 我在x86中看到,有
JMP EAX
指令,但我的C ++代码似乎没有使用它。 I compiled some C++ code in VC++: 我在VC ++中编译了一些C ++代码:
int main()
{
int i = 0;
while (i < 10)
{
++i;
if (i == 7)
{
i += 1;
continue;
}
}
}
This translates to: 这意味着:
int main()
{
00411370 push ebp
00411371 mov ebp,esp
00411373 sub esp,0CCh
00411379 push ebx
0041137A push esi
0041137B push edi
0041137C lea edi,[ebp-0CCh]
00411382 mov ecx,33h
00411387 mov eax,0CCCCCCCCh
0041138C rep stos dword ptr es:[edi]
int i = 0;
0041138E mov dword ptr [i],0
while (i < 10)
00411395 cmp dword ptr [i],0Ah
00411399 jge main+47h (4113B7h)
{
++i;
0041139B mov eax,dword ptr [i]
0041139E add eax,1
004113A1 mov dword ptr [i],eax
if (i == 7)
004113A4 cmp dword ptr [i],7
004113A8 jne main+45h (4113B5h)
{
i += 1;
004113AA mov eax,dword ptr [i]
004113AD add eax,1
004113B0 mov dword ptr [i],eax
continue;
004113B3 jmp main+25h (411395h)
}
}
004113B5 jmp main+25h (411395h)
}
004113B7 xor eax,eax
004113B9 pop edi
004113BA pop esi
004113BB pop ebx
004113BC mov esp,ebp
004113BE pop ebp
004113BF ret
So I'm confused, for the command jmp 411395h
, does this imply the program is always loaded in the same spot in memory? 所以我很困惑,对于命令
jmp 411395h
,这是否意味着程序总是被加载到内存中的相同位置? Because that seems illogical. 因为那似乎不合逻辑。
No, there are two things possibly at play here - you don't specify an OS so I'm going to give a general answer. 不,这里有两件事情可以发挥 - 你没有指定操作系统所以我会给出一般答案。
The first is that an executable file is rarely in the final format. 首先,可执行文件很少采用最终格式。 As a simplification, compilation turns source into object files and linking combines object files into an executable.
作为简化,编译将源转换为目标文件,并且链接将对象文件组合成可执行文件。
But the executable has to be loaded into memory and, at that stage, there can be even more modifications done. 但是必须将可执行文件加载到内存中,在那个阶段,可以进行更多修改。 One of these modifications may be to fix up memory references within the executable to point to memory that has been loaded at different locations.
这些修改之一可以是修复可执行文件中的内存引用以指向已在不同位置加载的内存。
This can be acheived by the executable file containing a list of addresses within itself that need to be fixed up at run time. 这可以通过包含本身内部地址列表的可执行文件来实现,该列表需要在运行时修复。
There is also a disconnect between virtual memory and physical memory in many modern operating systems. 在许多现代操作系统中,虚拟内存和物理内存之间也存在脱节。
When your process starts, you get your own (4G for Windows 32bit, I believe) address space into which your process is loaded. 当您的流程开始时,您将获得自己的(用于Windows 32位的4G,我相信)加载进程的地址空间。 The addresses within this address space have little relationship to your actual physical memory addresses and the translation between the two is done by a memory management unit (MMU).
此地址空间中的地址与您的实际物理内存地址几乎没有关系,两者之间的转换由内存管理单元(MMU)完成。
In fact, your process could be flying all over the physical address space as it's paged out and in. The virtual addresses will not change however. 实际上,您的进程可能会遍布物理地址空间,因为它已被分页并进入。但虚拟地址不会改变。
As other people wrote, there are relative jump and relative call instructions which essentially add a fixed value to eip
and therefore do not depend on the program's location in memory; 正如其他人所写的那样,有相对跳转和相对调用指令基本上为
eip
添加一个固定值,因此不依赖于程序在内存中的位置; compilers prefer to use these whenever possible. 编译器喜欢尽可能使用这些。 You can look at the code bytes to see what exact instructions your compiler used.
您可以查看代码字节以查看编译器使用的确切指令。 However, I assume you are asking about jumps/calls to absolute addresses.
但是,我假设你在询问跳转/调用绝对地址。
When the linker generates an executable, it generates absolute addresses supposing a particular base address ; 当链接器生成可执行文件时,它会生成假定特定基址的绝对地址 ; Microsoft linker usually uses
400000h
. Microsoft链接器通常使用
400000h
。 When OS loads an executable or a dll, it "fixes up" all absolute addresses by adding the difference between the address at which the executable was actually loaded and the address at which the linker based it. 当OS加载可执行文件或dll时,它通过添加实际加载可执行文件的地址与链接器基于它的地址之间的差异来“修复”所有绝对地址。 All executable formats except
.com
specify some sort of fixup table, which lists all locations in the executable which have to be patched up in this way. 除
.com
之外的所有可执行格式都指定了某种类型的修正表,其中列出了必须以这种方式修补的可执行文件中的所有位置。 Therefore, after the OS loads your executable into memory at base address, say, 1500000h
, your jump will look like jmp 1511395h
. 因此,在OS将您的可执行文件加载到基本地址(例如
1500000h
,您的跳转将看起来像jmp 1511395h
。 You can check this by looking at actual code bytes with a debugger. 您可以通过使用调试器查看实际代码字节来检查这一点。
Older Windows systems preferred to load executables at the base address used by the linker; 较旧的Windows系统倾向于在链接器使用的基址处加载可执行文件; this created a security risk, because an attacker would know in advance what is where in memory.
这造成了安全风险,因为攻击者会提前知道内存中的内容。 This is why newer systems use base address randomization.
这就是为什么较新的系统使用基地址随机化的原因。
The memory locations are relative to the process. 内存位置与进程有关。
main
is always at the same spot in memory, relative to the beginning of the program. 相对于程序的开头,
main
始终位于内存中的相同位置。
No. On x86 (and other architectures, too), most jump instructions are IP-relative : the binary machine codes for the instructions represent an offset from the current instruction pointer. 在x86(以及其他体系结构)上,大多数跳转指令都是IP相关的 :指令的二进制机器代码表示与当前指令指针的偏移量。 So, no matter what virtual address the code gets loaded at, the jump instructions function correctly.
因此,无论代码加载到哪个虚拟地址,跳转指令都能正常工作。
Relative jumps take the address of the current machine instruction (called instruction pointer) and add an offset to compute the address to be jumped to. 相对跳转采用当前机器指令的地址(称为指令指针)并添加偏移量来计算要跳转到的地址。
If you look at your code 如果你看看你的代码
004113B3 jmp main+25h (411395h)
004113B5 jmp main+25h (411395h)
004113B7 xor eax,eax
you'll note that the jmp instruction is 2 bytes long (1 byte for jmp, 1 byte for offset), and cannot possibly store an absolute 4-byte address. 你会注意到jmp指令长2个字节(jmp为1个字节,偏移为1个字节),并且不能存储绝对的4字节地址。
Relative jumps are basic functionality of CPUs (from what I know about 65xx, Z80, 8086, 68000), and are not related to such advanced features as virtual memory, memory mapping or address space randomization. 相对跳转是CPU的基本功能(据我所知65xx,Z80,8086,68000),与虚拟内存,内存映射或地址空间随机化等高级功能无关。
大多数芯片具有相对跳跃(相对于当前位置)和虚拟寻址。
int main()
{
00411370 push ebp
00411371 mov ebp,esp
00411373 sub esp,0CCh
00411379 push ebx
0041137A push esi
0041137B push edi
0041137C lea edi,[ebp-0CCh]
00411382 mov ecx,33h
00411387 mov eax,0CCCCCCCCh
0041138C rep stos dword ptr es:[edi]
int i = 0,int j=0;
0041138E mov dword ptr [i][j],0
while (i < 10)
00411395 cmp dword ptr [i][j[,0Bh
00411399 jge main+47h (4113B7h)
{
++i;
0041139B mov eax,dword ptr [i][j]
0041139E add eax,1
004113A1 mov dword ptr [i][j],eax '
if (i == 7)
004113A4 cmp dword ptr [i][j],7
004113A8 jne main+45h (4113B5h)
{
i += 1;
004113AA mov eax,ebx,dword ptr [i][j]
004113AD add eax,1
004113B0 mov dword ptr [i][j],ebx
continue;
004113B3 jmp main+25h (411395h)
}
}
004113B5 jmp main+25h (411395h)
}
004113B7 xor eax,ebx
004113B9 pop edi
004113BA pop esi
004113BB pop ecx
004113BC mov esp,ebp
004113BE pop ebp
004113BF ret
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.