Модель Кода x86-64

Эта статья описывает различия в OS X x86-64 модель кода пространства пользователя от модели кода, описанной в Двоичном интерфейсе приложений System V Дополнение Процессора Архитектуры AMD64, в http://www .x86-64.org/documentation.

x86-64 среда в OS X имеет только одну модель кода для кода пространства пользователя. Это является самым подобным маленькой модели PIC, определенной x86-64 System V ABI. Под Мужественным все статическое инициализированное хранение (и код и данные) должно соответствовать в Мужественном файле на 4 ГБ. Неинициализированный (заполняют нулями) данные, может быть любой размер, несмотря на то, что существует практический предел, наложенный OS X.

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

Перечисление 1 показывает, что выборка C код и соответствующий собирает код.

  Код перечисления 1 C и соответствующий ассемблерный код

 
extern int src[];
extern int dst[];
extern int* ptr;
 
static int lsrc[500];
static int ldst[500];
static int bsrc[500000];
static int bdst[500000];
static int* lptr;
 
dst[0] = src[0];        movq _src@GOTPCREL(%rip), %rax
                        movl (%rax), %edx
                        movq _dst@GOTPCREL(%rip), %rax
                        movl %edx, (%rax)
 
ptr = dst;              movq _dst@GOTPCREL(%rip), %rdx
                        movq _ptr@GOTPCREL(%rip), %rax
                        movq %rdx, (%rax)
                        ret
 
*ptr = src[0];          movq  _ptr@GOTPCREL(%rip), %rax
                        movq   (%rax), %rdx
                        movq   _src@GOTPCREL(%rip), %rax
                        movl   (%rax), %eax
                        movl   %eax, (%rdx)
                        ret
 
ldst[0] = lsrc[0];      movl _lsrc(%rip), %eax
                        movl %eax, _ldst(%rip)
 
lptr = ldst;            lea  _ldst(%rip), %rax
                        movq %rax, _lptr(%rip)
 
*lptr = lsrc[0];        movl _lsrc(%rip), %edx
                        movq _lptr(%rip), %rax
                        movl %edx, (%rax)
 
bdst[0] = bsrc[0];      movq _bsrc@GOTPCREL(%rip), %rax
                        movl (%rax), %edx
                        movq _bdst@GOTPCREL(%rip), %rax
                        movl %edx, (%rax)
 
lptr = bdst;            movq _bdst@GOTPCREL(%rip), %rax
                        movq %rax, _lptr(%rip)
 
*lptr = bsrc[0];        movq _bsrc@GOTPCREL(%rip), %rdx
                        movq _lptr(%rip), %rax
                        movl (%rdx), %edx

OS X x86-64 доступы модели генерации кода большие локальные данные через GOT, отличающийся от пути маленькая модель PIC, работает в System V x86-64 среда. Косвенность через GOT устраняет потребность в средней модели кода. Это поведение происходит от факта, что, когда компоновщик размечает данные, оно помещает данные, к которым получают доступ непосредственно (маленькие локальные данные и GOT сами) в 2 ГБ кода. Другие данные могут быть помещены дальше, потому что к этим данным получают доступ только через GOT. Это поведение позволяет большой (больше, чем 4 ГБ) и маленькие исполнимые программы быть созданным с помощью той же модели кода.

Модель кода для вызовов функции очень проста, как показано в Перечислении 2.

Перечисление 2  модель кода для вызовов функции

 
extern int foo();
static int bar();
 
foo();                    call _foo
 
bar();                    call _bar

Все прямые вызовы функции сделаны с помощью CALL rel32 инструкция.

Компоновщик ответственен за создание записей GOT (также известный как неленивые указатели), а также интерфейсные функции и ленивые указатели (также известный как записи таблицы загрузки программы или записи PLT) для вызовов к другому модулю связи. Так как компоновщик должен создать эти записи, это может также принять решение не создать их, когда это видит возможность. У компоновщика есть сложный ряд правил, которые диктуют, к каким символам нужно получить доступ косвенно (в зависимости от плоского по сравнению с двухуровневым пространством имен, слабым по сравнению с неслабыми определениями, видимостью символа, расстоянием от кода, и т.д.). Но в конечном счете существует много символов, к которым можно получить доступ непосредственно (не через GOT или записи PLT). Для этих символов компоновщик делает следующую оптимизацию:

  1. A CALL или JMP инструкция выполняет прямое, относительное PC ответвление к цели.

  2. Инструкция загрузки, выполняемая на записи GOT (например, movq _foo@GOTPCREL(%rip), %rxx) преобразовывается в a LEA вычисление (например, leaq _foo(%rip), %rxx). Эта трансформация удаляет одну запись GOT и сохраняет одну загрузку в память.

В обоих случаях специальные перемещения используются, которые позволяют компоновщику выполнять эту оптимизацию.