Косвенная адресация

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

Когда файл использует данные, определяющиеся в другом файле, это создает ссылки символа. Ссылка символа идентифицирует файл, из которого символ импортируется и символ, на который ссылаются. Существует два типа ссылок символа: не ленивый и ленивый.

Следующие разделы описывают, как ссылки символа реализованы для 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