Как инструкция перехода в сборке работает с несколькими процессами? - PullRequest
6 голосов
/ 13 октября 2009

Итак, я не понимаю, как работают инструкции перехода в операционной системе. Я думал, что инструкция перехода устанавливает значение в программном счетчике процессора. Но программы могут быть запущены в разных местах памяти. Я вижу, что в x86 есть инструкция JMP EAX, но мой код на C ++, похоже, не использует ее. Я скомпилировал немного кода C ++ в VC ++:

int main()
{
    int i = 0;
    while (i < 10)
    {
        ++i;
        if (i == 7)
        {
            i += 1;
            continue;
        }
    }
}

Это означает:

    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              

Так что я запутался, для команды jmp 411395h это означает, что программа всегда загружается в одно и то же место в памяти? Потому что это кажется нелогичным.

Ответы [ 7 ]

6 голосов
/ 13 октября 2009

Как писали другие люди, существуют инструкции относительного перехода и относительного вызова, которые по существу добавляют фиксированное значение к eip и, следовательно, не зависят от расположения программы в памяти; компиляторы предпочитают использовать их, когда это возможно. Вы можете посмотреть на байты кода, чтобы увидеть, какие именно инструкции использовал ваш компилятор. Однако я предполагаю, что вы спрашиваете о переходах / вызовах на абсолютные адреса.

Когда компоновщик генерирует исполняемый файл, он генерирует абсолютные адреса, предполагая конкретный базовый адрес ; Microsoft linker обычно использует 400000h. Когда ОС загружает исполняемый файл или dll, она «исправляет» все абсолютные адреса, добавляя разницу между адресом, по которому исполняемый файл был фактически загружен, и адресом, на котором его основал компоновщик. Во всех исполняемых форматах, кроме .com, указывается какая-то таблица исправлений, в которой перечислены все местоположения в исполняемом файле, которые должны быть исправлены таким образом. Поэтому после того, как ОС загрузит ваш исполняемый файл в память по базовому адресу, скажем, 1500000h, ваш переход будет выглядеть как jmp 1511395h. Вы можете проверить это, посмотрев фактические байты кода с помощью отладчика.

Старые системы Windows предпочитали загружать исполняемые файлы по базовому адресу, используемому компоновщиком; это создало угрозу безопасности, потому что злоумышленник заранее знал, что находится в памяти. Вот почему более новые системы используют рандомизацию базовых адресов.

6 голосов
/ 13 октября 2009

Нет, здесь могут быть две вещи - вы не указываете ОС, поэтому я собираюсь дать общий ответ.

Во-первых, исполняемый файл редко находится в окончательном формате. Для упрощения компиляция превращает исходный код в объектные файлы, а компоновка объединяет объектные файлы в исполняемый файл.

Но исполняемый файл должен быть загружен в память, и на этом этапе может быть сделано еще больше изменений. Одна из этих модификаций может заключаться в исправлении ссылок на память в исполняемом файле, чтобы указывать на память, которая была загружена в разных местах.

Это может быть достигнуто с помощью исполняемого файла, содержащего список адресов внутри себя, которые необходимо исправить во время выполнения.

Существует также разрыв между виртуальной памятью и физической памятью во многих современных операционных системах.

Когда ваш процесс запускается, вы получаете свое собственное (4G для Windows 32bit, я полагаю) адресное пространство, в которое загружается ваш процесс. Адреса в этом адресном пространстве мало связаны с вашими фактическими адресами физической памяти, и преобразование между ними осуществляется блоком управления памятью (MMU).

Фактически, ваш процесс может летать по всему физическому адресному пространству, когда он выгружается и разворачивается. Однако виртуальные адреса не изменятся.

3 голосов
/ 13 октября 2009

Относительные переходы берут адрес текущей машинной инструкции (так называемый указатель инструкции) и добавляют смещение для вычисления адреса, к которому нужно перейти.

Если вы посмотрите на свой код

004113B3  jmp         main+25h (411395h) 
004113B5  jmp         main+25h (411395h) 
004113B7  xor         eax,eax 

вы заметите, что инструкция jmp имеет длину 2 байта (1 байт для jmp, 1 байт для смещения) и не может хранить абсолютный 4-байтовый адрес.

Относительные скачки являются основной функциональностью процессоров (из того, что я знаю о 65xx, Z80, 8086, 68000) и не связаны с такими расширенными функциями, как виртуальная память, отображение памяти или рандомизация адресного пространства.

3 голосов
/ 13 октября 2009

Нет. На x86 (и других архитектурах тоже) большинство инструкций перехода являются IP-относительными : двоичные машинные коды для команд представляют смещение от текущего указателя инструкции. Таким образом, независимо от того, по какому виртуальному адресу загружается код, инструкции перехода работают правильно.

3 голосов
/ 13 октября 2009

Места памяти относятся к процессу. main всегда находится в одном и том же месте в памяти относительно начала программы.

2 голосов
/ 13 октября 2009

Большинство чипов имеют относительные переходы (относительно текущего местоположения) и виртуальную адресацию.

0 голосов
/ 12 мая 2014
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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...