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