Что такое «облицовка», которую компоновщик arm использует при вызове функции?

#c #assembly #arm #printf

Вопрос:

Я только что прочитал https://www.keil.com/support/man/docs/armlink/armlink_pge1406301797482.htm. но не могу понять, что такое шпон, который компоновщик arm вставляет между вызовами функций.

В документе «Стандарт вызова процедур для архитектуры ARM» говорится:,

5.3.1.1 Использование IP компоновщиком Как инструкции BL в состоянии ARM, так и в состоянии Thumb не могут адресовать полное 32 — разрядное адресное пространство, поэтому компоновщику может потребоваться вставить шпон между вызывающей подпрограммой и вызываемой подпрограммой. Виниры также могут потребоваться для поддержки взаимодействия РУКИ и большого пальца или динамического соединения. Любой вставленный шпон должен сохранять содержимое всех регистров, за исключением IP (r12) и флагов кода условия; соответствующая программа должна исходить из того, что шпон, изменяющий IP, может быть вставлен в любую инструкцию ветви, которая подвергается перемещению, поддерживающему взаимодействующие или длинные ветви. Примечание. R_ARM_CALL, R_ARM_JUMP24, R_ARM_PC24, R_ARM_THM_CALL, R_ARM_THM_JUMP24 и R_ARM_THM_JUMP19 являются примерами типов перемещения ELF с этим свойством. Смотрите [AAELF] для получения полной информации

Вот что я думаю, это что-то вроде этого ? : когда функция A вызывает функцию B и когда эти две функции находятся слишком далеко друг от друга, чтобы bl команда могла их выразить, компоновщик вставляет функцию C между функциями A и B таким образом, чтобы функция C была близка к функции B. Теперь функция A использует b инструкцию для перехода к функции C(копирование всех регистров между вызовом функции), а функция C использует bl инструкцию(копирование всех регистров тоже). Конечно, регистр r12 используется для хранения оставшихся битов адреса прыжка в длину. Это то, что означает шпон? (Я не знаю, почему arm не объясняет, что такое шпон, а только то, что обеспечивает шпон..)

Комментарии:

1. Шпон должен быть расположен близко к А, потому что В находится слишком далеко. A выполняет bl для шпона, и шпон устанавливает r12 в конечное место назначения (B) и выполняет bx r12. bx может охватывать все адресное пространство.

2. Вау, это все ясно объясняет. Почему бы вам не сделать свой комментарий ответом, чтобы я мог его выбрать. Спасибо!

3. Это был всего лишь набросок ответа. Вы можете написать полный ответ и принять его.

Ответ №1:

Это просто батут. Взаимодействие проще всего продемонстрировать, используя здесь gnu, но подразумевается, что у Киля тоже есть решение.

 .globl even_more
.type eve_more,%function
even_more:
    bx lr

.thumb

.globl more_fun
.thumb_func
more_fun:
    bx lr



extern unsigned int more_fun ( unsigned int x );
extern unsigned int even_more ( unsigned int x );
unsigned int fun ( unsigned int a )
{
    return(more_fun(a) even_more(a));
}
    
Unlinked object:

Disassembly of section .text:

00000000 <fun>:
   0:   e92d4070    push    {r4, r5, r6, lr}
   4:   e1a05000    mov r5, r0
   8:   ebfffffe    bl  0 <more_fun>
   c:   e1a04000    mov r4, r0
  10:   e1a00005    mov r0, r5
  14:   ebfffffe    bl  0 <even_more>
  18:   e0840000    add r0, r4, r0
  1c:   e8bd4070    pop {r4, r5, r6, lr}
  20:   e12fff1e    bx  lr

Linked binary (yes completely unusable, but demonstrates what the tool does)

Disassembly of section .text:

00001000 <fun>:
    1000:   e92d4070    push    {r4, r5, r6, lr}
    1004:   e1a05000    mov r5, r0
    1008:   eb000008    bl  1030 <__more_fun_from_arm>
    100c:   e1a04000    mov r4, r0
    1010:   e1a00005    mov r0, r5
    1014:   eb000002    bl  1024 <even_more>
    1018:   e0840000    add r0, r4, r0
    101c:   e8bd4070    pop {r4, r5, r6, lr}
    1020:   e12fff1e    bx  lr

00001024 <even_more>:
    1024:   e12fff1e    bx  lr

00001028 <more_fun>:
    1028:   4770        bx  lr
    102a:   46c0        nop         ; (mov r8, r8)
    102c:   0000        movs    r0, r0
    ...

00001030 <__more_fun_from_arm>:
    1030:   e59fc000    ldr r12, [pc]   ; 1038 <__more_fun_from_arm 0x8>
    1034:   e12fff1c    bx  r12
    1038:   00001029    .word   0x00001029
    103c:   00000000    .word   0x00000000
 

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

Функция even_more находится в том же режиме (ARM), поэтому нет необходимости в батуте/фанере.

Для ограничения расстояния bl позвольте мне посмотреть. Вау, это было легко, и gnu тоже назвала это фанерой:

 .globl more_fun
.type more_fun,%function
more_fun:
    bx lr

extern unsigned int more_fun ( unsigned int x );
unsigned int fun ( unsigned int a )
{
    return(more_fun(a) 1);
}

MEMORY
{
    bob : ORIGIN = 0x00000000, LENGTH = 0x1000
    ted : ORIGIN = 0x20000000, LENGTH = 0x1000
}
SECTIONS
{
    .some   : { so.o(.text*)       } > bob
    .more   : { more.o(.text*)      } > ted
}

Disassembly of section .some:

00000000 <fun>:
   0:   e92d4010    push    {r4, lr}
   4:   eb000003    bl  18 <__more_fun_veneer>
   8:   e8bd4010    pop {r4, lr}
   c:   e2800001    add r0, r0, #1
  10:   e12fff1e    bx  lr
  14:   00000000    andeq   r0, r0, r0

00000018 <__more_fun_veneer>:
  18:   e51ff004    ldr pc, [pc, #-4]   ; 1c <__more_fun_veneer 0x4>
  1c:   20000000    .word   0x20000000

Disassembly of section .more:

20000000 <more_fun>:
20000000:   e12fff1e    bx  lr
 

Оставаясь в том же режиме, он не нуждался в bx.

Альтернативой является то, что вы заменяете каждую инструкцию bl во время компиляции более сложным решением на случай, если вам потребуется выполнить вызов far. Или, поскольку смещение bl/немедленное вычисляется во время соединения, вы можете во время соединения вставить батут/шпон, чтобы изменить режимы или покрыть расстояние.

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

Редактировать

Поймите, что цепочки инструментов различаются и даже внутри цепочки инструментов, gcc 3.x.x был первым, кто поддержал thumb, и я не знаю, видел ли я это тогда. Обратите внимание, что компоновщик является частью binutils, которая является отдельной разработкой от gcc. Вы упомянули «компоновщик arm», ну, у arm есть своя собственная цепочка инструментов, затем они купили Kiel и, возможно, заменили Kiel на свой собственный или нет. Затем есть gnu, clang/llvm и другие. Таким образом, это не тот случай, когда «компоновщик arm» делает то или иное, это случай, когда компоновщик цепочек инструментов делает то или иное, и каждая цепочка инструментов, во-первых, может свободно использовать любое соглашение о вызовах, которое они хотят, нет мандата на использование рекомендаций ARM, во-вторых, они могут реализовать это или нет или просто дать вам предупреждение, и вам придется иметь дело с этим (вероятно, на языке ассемблера или с помощью указателей функций).

ARM не нужно объяснять это, или, скажем, это четко объясняется в Справочном руководстве по архитектуре (посмотрите инструкцию bl, инструкцию bx, найдите слова, взаимодействующие и т. Д. Все довольно четко объяснено) для конкретной архитектуры. Так что нет причин объяснять это снова. Особенно для общего заявления, где охват bl варьируется, и каждая архитектура имеет разные функции взаимодействия, это будет длинный набор абзацев или короткая глава, чтобы объяснить то, что уже четко задокументировано.

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

В любом случае, прежде чем связывать, вы должны скомпилировать и собрать, и люди из набора инструментов полностью поймут правила архитектуры. РУКА здесь не является чем-то особенным.

Комментарии:

1. Спасибо! Я прочитаю его полностью как-нибудь позже (по причине более неотложных дел.. ах, слишком много вещей, чтобы понять.. 🙂 ).

Ответ №2:

Это комментарий Раймонда Чена :

Шпон должен быть расположен близко к А, потому что В находится слишком далеко. A выполняет bl для облицовки, а облицовка устанавливает r12 в конечный пункт назначения(B) и выполняет bx r12. bx может охватывать все адресное пространство.

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