Косвенная адресация
Косвенная адресация является именем метода генерации кода, позволяющего символам, определенным в одном файле ссылаться от другого файла, не требуя, чтобы файл ссылки имел явно заданные знания расположения файла, определяющего символ. Поэтому файл определения может быть изменен независимо от файла ссылки. Косвенная адресация минимизирует число расположений, которые должны быть изменены динамическим компоновщиком, упрощающим совместное использование кода и улучшающим производительность.
Когда файл использует данные, определяющиеся в другом файле, это создает ссылки символа. Ссылка символа идентифицирует файл, из которого символ импортируется и символ, на который ссылаются. Существует два типа ссылок символа: не ленивый и ленивый.
Когда модуль загружается, неленивые ссылки символа разрешены (связанный с их определениями) динамическим компоновщиком.
Неленивая ссылка символа является по существу указателем символа — часть размера указателя данных. Компилятор генерирует неленивые ссылки символа для символов данных или функциональных адресов.
Ленивые ссылки символа разрешены динамическим компоновщиком в первый раз, когда они используются (не во время загрузки). Последующие вызовы к символу, на который ссылаются, переходят непосредственно к определению символа.
Ленивые ссылки символа составлены из указателя символа и тупика символа, мелкой суммы кода, непосредственно разыменовывающего и переходящего через указатель символа. Компилятор генерирует ленивые ссылки символа, когда он встречается с вызовом к функции, определяемой в другом файле.
Следующие разделы описывают, как ссылки символа реализованы для PowerPC и архитектуры IA-32. Для получения дальнейшей информации на PowerPC и тупиках символа IA-32, посмотрите Ссылку Ассемблера OS X.
Ссылки символа PowerPC
В архитектуре PowerPC, при генерации вызовов к функциям, определяющимся в других файлах, компилятор создает тупик символа и ленивый указатель символа. Ленивый указатель символа является адресом, первоначально установленным склеить код, вызывающий функцию связующего звена компоновщика dyld_stub_binding_helper
. Эти вызовы функции связующего звена динамическая функция компоновщика, выполняющая фактическую работу привязки тупика. По возврату из
dyld_stub_binding_helper
, ленивый указатель указывает на исполнительный адрес внешней функции.
Простой пример кода в Перечислении 1 мог бы произвести два различных типов тупиков символа, в зависимости от того, компилируется ли это с позиционно-независимой генерацией кода. Перечисление 2 показывает косвенную адресацию без позиционно-независимого кода, и Перечисление 3 показывает и косвенную адресацию и позиционно-независимый код.
Пример кода перечисления 1 C для косвенных вызовов функции
extern void bar(void); |
void foo(void) |
{ |
bar(); |
} |
Пример перечисления 2 косвенного вызова функции
.text |
; The function foo |
.align 2 |
.globl _foo |
_foo: |
mflr r0 ; move the link register into r0 |
stw r0,8(r1) ; save the link register value on the stack |
stwu r1,-64(r1) ; set up the frame on the stack |
bl L_bar$stub ; branch and link to the symbol stub for _bar |
lwz r0,72(r1) ; load the link register value from the stack |
addi r1,r1,64 ; removed the frame from the stack |
mtlr r0 ; restore the link register |
blr ; branch to the link register to return |
.symbol_stub ; the standard symbol stub section |
L_bar$stub: |
.indirect_symbol _bar ; identify this symbol stub for the |
; symbol _bar |
lis r11,ha16(L_bar$lazy_ptr) ; load r11 with the high 16 bits of the |
; address of bar’s lazy pointer |
lwz r12,lo16(L_bar$lazy_ptr)(r11) ; load the value of bar’s lazy pointer |
; into r12 |
mtctr r12 ; move r12 to the count register |
addi r11,r11,lo16(L_bar$lazy_ptr) ; load r11 with the address of bars lazy |
; pointer |
bctr ; jump to the value in bar’s lazy pointer |
.lazy_symbol_pointer ; the lazy pointer section |
L_bar$lazy_ptr: |
.indirect_symbol _bar ; identify this lazy pointer for symbol |
; _bar |
.long dyld_stub_binding_helper ; initialize the lazy pointer to the stub |
; binding helper address |
Пример перечисления 3 позиционно-независимого, косвенного вызова функции
.text |
; The function foo |
.align 2 |
.globl _foo |
_foo: |
mflr r0 ; move the link register into r0 |
stw r0,8(r1) ; save the link register value on the stack |
stwu r1,-80(r1) ; set up the frame on the stack |
bl L_bar$stub ; branch and link to the symbol stub for _bar |
lwz r0,88(r1) ; load the link register value from the stack |
addi r1,r1,80 ; removed the frame from the stack |
mtlr r0 ; restore the link register |
blr ; branch to the link register to return |
.picsymbol_stub ; the standard pic symbol stub section |
L_bar$stub: |
.indirect_symbol _bar ; identify this symbol stub for the symbol _bar |
mflr r0 ; save the link register (LR) |
bcl 20,31,L0$_bar ; Use the branch-always instruction that does not |
; affect the link register stack to get the |
; address of L0$_bar into the LR. |
L0$_bar: |
mflr r11 ; then move LR to r11 |
; bar’s lazy pointer is located at |
; L0$_bar + distance |
addis r11,r11,ha16(L_bar$lazy_ptr-L0$_bar); L0$_bar plus high 16 bits of |
; distance |
mtlr r0 ; restore the previous LR |
lwz r12,lo16(L_bar$lazy_ptr-L0$_bar)(r11); ...plus low 16 of distance |
mtctr r12 ; move r12 to the count register |
addi r11,r11,lo16(L_bar$lazy_ptr-L0$_bar); load r11 with the address of bar’s |
; lazy pointer |
bctr ; jump to the value in bar’s lazy |
; pointer |
.lazy_symbol_pointer ; the lazy pointer section |
L_bar$lazy_ptr: |
.indirect_symbol _bar ; identify this lazy pointer for symbol bar |
.long dyld_stub_binding_helper ; initialize the lazy pointer to the stub |
; binding helper address. |
Как Вы видите, __picsymbol_stub
код в Перечислении 3 напоминает позиционно-независимый код, сгенерированный для Перечисления 2. Для любого позиционно-независимого Мужественного файла тупики символа должны, очевидно, быть независимой позицией, также.
Статический компоновщик выполняет две оптимизации при записи выходных файлов:
Это удаляет тупики символа для ссылок на символы, определяющиеся в том же модуле, изменяя команды перехода, вызывавшие через тупики для ветвления непосредственно к вызову.
Это удаляет копии того же тупика символа, обновляя команды перехода по мере необходимости.
Обратите внимание на то, что подпрограмма, переходящая косвенно к другой подпрограмме, должна сохранить цель вызова в GPR11 или GPR12. Стандартизация регистров, используемых компилятором для хранения целевого адреса, позволяет оптимизировать динамическую генерацию кода. Поскольку цель удовлетворяет потребности, которые будут сохранены в регистре при любых обстоятельствах, это соглашение стандартизирует что регистр использовать. Подпрограммы, которые, возможно, вызвали непосредственно, не должны зависеть от значения GR12, потому что в случае прямого вызова не определяется его значение.
Ссылки символа IA-32
В архитектуре IA-32 ссылки символа реализованы как тупик символа и ленивый указатель символа, объединенный в один JMP
инструкция. Первоначально, такие инструкции указывают динамическому компоновщику. Когда динамический компоновщик встречается с такой инструкцией, она определяет местоположение символа, на который ссылаются, и изменяет JMP
инструкция для указания непосредственно на этот символ. Поэтому последующее выполнение JMP
инструкция переходит непосредственно к символу, на который ссылаются.
Перечисление 4 и Перечисление 5 показывают простую программу C, и блок IA-32 генерировал выделение символа тупиковый и неленивый указатель для импортированного символа.
Программа перечисления 4 C с помощью импортированного символа
#include <stdio.h> |
main( int arc, char *argv[]) |
{ |
fprintf(stdout, "hello, world!\n") ; |
} |
Ссылка символа перечисления 5 IA-32 в блоке
.cstring |
LC0: |
.ascii "hello, world!\12\0" |
.text |
.globl _main |
_main: |
pushl %ebp |
movl %esp, %ebp |
subl $24, %esp |
movl L___sF$non_lazy_ptr, %eax |
addl $88, %eax |
movl %eax, 12(%esp) |
movl $14, 8(%esp) |
movl $1, 4(%esp) |
movl $LC0, (%esp) |
call L_fwrite$stub ; call to imported symbol |
leave |
ret |
.section __IMPORT,__jump_table,symbol_stubs,self_modifying_code+pure_instructions,5 |
L_fwrite$stub: ; symbol stub |
.indirect_symbol _fwrite |
hlt ; hlt ; hlt ; hlt ; hlt |
.section __IMPORT,__pointers,non_lazy_symbol_pointers |
L___sF$non_lazy_ptr: ; nonlazy pointer |
.indirect_symbol ___sF |
.long 0 |
.subsections_via_symbols |
Ссылки Символа x86-64
В этом разделе описываются отклонения от System V x85-64 среда в области ссылок символа.
Статический компоновщик ответственен за генерацию всех интерфейсных функций, тупиковых функций помощника, ленивых и неленивых указателей, а также косвенной таблицы символов, необходимой динамическому загрузчику (dyld
).
Для ссылки Перечисление 6 показывает, как сгенерированы тупик, помощник и ленивый указатель.
Перечисление 6 , Генерирующее тупик, помощника и ленивый указатель
_foo$stub: jmp *_foo$lazy_pointer(%rip) |
_foo$stub_helper: leaq _foo$lazy_pointer(%rip),%r11 |
jmp dyld_stub_binding_helper |
_foo$lazy_pointer: .quad _foo$stub_helper |