Подтвердить что ты не робот

Arm/Thumb: использование BX в коде Thumb, вызов функции Thumb или переход к команде Thumb в другой функции

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

  • Как использовать BX для JUMP для существующего кода прошивки THUMB, из моего кода THUMB.
  • Как использовать BX для вызова существующей функции THUMB (должен сначала установить LR), из моего кода THUMB.

Я понимаю, что процессор смотрит на бит lsb (бит 0), и я должен убедиться, что для него установлено значение 1, чтобы сохранить состояние процессора в "состоянии большого пальца". Поэтому я думаю, что мне нужно добавить ADD 1, чтобы установить бит lsb в 1.

Итак... скажем, я хочу просто JUMP до 0x24000 (в середине какого-то существующего кода THUMB)

LDR R6, =0x24000
ADD R6, #1       @ (set lsb to 1)
BX R6

Я думаю, что это правильно?

Теперь скажите, что я хочу ЗВОНОТЬ существующую функцию большого пальца, используя BX, и я хочу, чтобы она вернулась ко мне, поэтому мне нужно установить LR туда, где я хочу, чтобы она вернулась.

Давайте скажем, что функция, которую я хочу вызвать, равна 0x24000 Было предложено :

ldr r2, =0x24000
mov lr, pc
bx r2

Вот что я не понимаю:

  • адрес в R2 не имеет набора бит lsb... так что не будет bx r2 переключить режим в режим ARM

  • LR.. У ПК есть адрес (начало текущей инструкции, + 4), мне сказали. В обоих Thumb и Arm каждый адрес инструкции должен быть выровнен (16 бит или 32 бит), поэтому у него не будет бит LSB, установленный в 1. Только нечетные числа имеют бит lsb, равный 1.

Итак, в приведенном выше коде я устанавливаю LR на (PC), адрес, для которого не установлен бит lsb 1. Поэтому, когда вызываемая мной функция приходит к ней эпилогом и делает BX LR,... uhmmm.. как это может работать, чтобы вернуться к моему коду THUMB? Мне что-то не хватает...

Обычно BL используется для вызова функций. В руководстве говорится, что инструкция BL устанавливает LR в следующую строку кода... Значит ли это, что команда (обычно используется) BL THUMB автоматически устанавливает LR в return addr + 1?

4b9b3361

Ответ 1

Вау, спасибо, что вызвал меня на этом. Я знаю, что я пробовал код qemu в http://github.com/dwelch67/yagbat и думал, что XPUT32 вызывает PUT32 так, как вы описываете, и это сработало. Но он НЕ работает. Я создал ряд экспериментов и довольно удивлен, это не то, чего я ожидал. Теперь я понимаю, почему gnu компоновщик делает то, что он делает. Извините, это длинный ответ, но я думаю, что это очень ценно. Это запутанная тема, я знаю, что в течение многих лет я ошибался, думая, что компьютер перетаскивает бит режима, но это не так.

Прежде чем начать с экспериментов ниже, если вы собираетесь это сделать:

LDR R6, =0x24000
ADD R6, #1       @ (set lsb to 1)
BX R6

потому что вам известно, что 0x24000 - это код большого пальца, просто сделайте это вместо:

LDR R6, =0x24001
BX R6

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

если вы не знаете адрес, но знаете имя адреса

ldr r6,=something
bx r6

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

.thumb
ping:
    ldr r0,=pong
    bx r0
.code 32
pong:
    ldr r0,=ping
    bx r0


d6008148 <ping>:
d6008148:   4803        ldr r0, [pc, #12]   ; (d6008158 <pong+0xc>)
d600814a:   4700        bx  r0

d600814c <pong>:
d600814c:   e59f0008    ldr r0, [pc, #8]    ; d600815c <pong+0x10>
d6008150:   e12fff10    bx  r0

d6008158:   d600814c    strle   r8, [r0], -ip, asr #2
d600815c:   d6008148    strle   r8, [r0], -r8, asr #2

который не работал, понг хотел вытащить большой палец с 0xD600815C, но получил адрес руки.

это все gnu сборщик btw, для других инструментов вам, возможно, придется что-то сделать. Для газа вам нужно поставить .thumb_func перед меткой, которую вы хотите объявить как метку большого пальца (термин func подразумевает, что функция вводит в заблуждение, не беспокойтесь о том, что .thumb_func означает, что это просто игра ассемблера/компоновщика).

.thumb
.thumb_func
ping:
    ldr r0,=pong
    bx r0
.code 32
pong:
    ldr r0,=ping
    bx r0

и теперь мы получаем то, что хотим

d6008148 <ping>:
d6008148:   4803        ldr r0, [pc, #12]   ; (d6008158 <pong+0xc>)
d600814a:   4700        bx  r0

d600814c <pong>:
d600814c:   e59f0008    ldr r0, [pc, #8]    ; d600815c <pong+0x10>
d6008150:   e12fff10    bx  r0

d6008158:   d600814c    strle   r8, [r0], -ip, asr #2
d600815c:   d6008149    strle   r8, [r0], -r9, asr #2

0xD600815C имеет тот набор lsbit, чтобы вам не приходилось выполнять какую-либо работу. Компилятор позаботится обо всем этом, когда вы выполняете вызовы функций C, например. Для ассемблера, хотя вы должны использовать этот .thumb_func (или какую-то другую директиву, если он есть), чтобы получить газ, чтобы знать, что это ярлык большого пальца и установите для него lsbit.

Итак, эксперимент, приведенный ниже, был сделан на mpcore, который является ARM11, но я также пробовал функции тестирования с 1 по 4 на ARM7TDMI и qemu с теми же результатами.

.globl testarm
testarm:
    mov r0,pc
    bx lr

armbounce:
    mov r0,lr
    bx lr

.thumb
.thumb_func
.globl testthumb1
testthumb1:
    mov r0,pc
    bx lr
    nop
    nop
    nop
bounce:
    bx lr
.thumb_func
.globl testthumb2
testthumb2:
    mov r2,lr
    mov r0,pc
    bl bounce
    bx r2
    nop
    nop
    nop
.thumb_func
.globl testthumb3
testthumb3:
    mov r2,lr
    mov lr,pc
    mov r0,lr
    bx r2
    nop
    nop
    nop
.thumb_func
.globl testthumb4
testthumb4:
    push {lr}
    ldr r2,=armbounce
    mov r1,pc  ;@ -4
    add r1,#5  ;@ -2
    mov lr,r1  ;@ +0
    bx r2      ;@ +2
    pop {r2}   ;@ +4
    bx r2
.thumb_func
.globl testthumb5
testthumb5:
    push {lr}
    ldr r2,=armbounce
    mov lr,pc
    bx r2
    pop {r2}
    bx r2
.thumb_func
.globl testthumb6
testthumb6:
    push {lr}
    bl testthumb6a
.thumb_func
testthumb6a:
    mov r0,lr
    pop {r2}
    bx r2

.thumb_func
.globl testthumb7
testthumb7:
    push {lr}
    bl armbounce_thumb
    pop {r2}
    bx r2

.thumb_func
.globl testthumb8
testthumb8:
    push {lr}
    bl armbounce_thumb_two
    pop {r2}
    bx r2

.align 4
armbounce_thumb:
    ldr r1,[pc]
    bx r1
.word armbounce

nop
.align 4
armbounce_thumb_two:
    bx pc
    nop
.code 32
    b armbounce

который становится

d60080b4 <testarm>:
d60080b4:   e1a0000f    mov r0, pc
d60080b8:   e12fff1e    bx  lr

d60080bc <armbounce>:
d60080bc:   e1a0000e    mov r0, lr
d60080c0:   e12fff1e    bx  lr

d60080c4 <testthumb1>:
d60080c4:   4678        mov r0, pc
d60080c6:   4770        bx  lr
d60080c8:   46c0        nop         ; (mov r8, r8)
d60080ca:   46c0        nop         ; (mov r8, r8)
d60080cc:   46c0        nop         ; (mov r8, r8)

d60080ce <bounce>:
d60080ce:   4770        bx  lr

d60080d0 <testthumb2>:
d60080d0:   4672        mov r2, lr
d60080d2:   4678        mov r0, pc
d60080d4:   f7ff fffb   bl  d60080ce <bounce>
d60080d8:   4710        bx  r2
d60080da:   46c0        nop         ; (mov r8, r8)
d60080dc:   46c0        nop         ; (mov r8, r8)
d60080de:   46c0        nop         ; (mov r8, r8)

d60080e0 <testthumb3>:
d60080e0:   4672        mov r2, lr
d60080e2:   46fe        mov lr, pc
d60080e4:   4670        mov r0, lr
d60080e6:   4710        bx  r2
d60080e8:   46c0        nop         ; (mov r8, r8)
d60080ea:   46c0        nop         ; (mov r8, r8)
d60080ec:   46c0        nop         ; (mov r8, r8)

d60080ee <testthumb4>:
d60080ee:   b500        push    {lr}
d60080f0:   4a15        ldr r2, [pc, #84]   ; (d6008148 <armbounce_thumb_two+0x8>)
d60080f2:   4679        mov r1, pc
d60080f4:   3105        adds    r1, #5
d60080f6:   468e        mov lr, r1
d60080f8:   4710        bx  r2
d60080fa:   bc04        pop {r2}
d60080fc:   4710        bx  r2

d60080fe <testthumb5>:
d60080fe:   b500        push    {lr}
d6008100:   4a11        ldr r2, [pc, #68]   ; (d6008148 <armbounce_thumb_two+0x8>)
d6008102:   46fe        mov lr, pc
d6008104:   4710        bx  r2
d6008106:   bc04        pop {r2}
d6008108:   4710        bx  r2

d600810a <testthumb6>:
d600810a:   b500        push    {lr}
d600810c:   f000 f800   bl  d6008110 <testthumb6a>

d6008110 <testthumb6a>:
d6008110:   4670        mov r0, lr
d6008112:   bc04        pop {r2}
d6008114:   4710        bx  r2

d6008116 <testthumb7>:
d6008116:   b500        push    {lr}
d6008118:   f000 f80a   bl  d6008130 <armbounce_thumb>
d600811c:   bc04        pop {r2}
d600811e:   4710        bx  r2

d6008120 <testthumb8>:
d6008120:   b500        push    {lr}
d6008122:   f000 f80d   bl  d6008140 <armbounce_thumb_two>
d6008126:   bc04        pop {r2}
d6008128:   4710        bx  r2
d600812a:   46c0        nop         ; (mov r8, r8)
d600812c:   46c0        nop         ; (mov r8, r8)
d600812e:   46c0        nop         ; (mov r8, r8)

d6008130 <armbounce_thumb>:
d6008130:   4900        ldr r1, [pc, #0]    ; (d6008134 <armbounce_thumb+0x4>)
d6008132:   4708        bx  r1
d6008134:   d60080bc            ; <UNDEFINED> instruction: 0xd60080bc
d6008138:   46c0        nop         ; (mov r8, r8)
d600813a:   46c0        nop         ; (mov r8, r8)
d600813c:   46c0        nop         ; (mov r8, r8)
d600813e:   46c0        nop         ; (mov r8, r8)

d6008140 <armbounce_thumb_two>:
d6008140:   4778        bx  pc
d6008142:   46c0        nop         ; (mov r8, r8)
d6008144:   eaffffdc    b   d60080bc <armbounce>
d6008148:   d60080bc            ; <UNDEFINED> instruction: 0xd60080bc
d600814c:   e1a00000    nop         ; (mov r0, r0)

И результаты вызова и печати всех этих функций

D60080BC testarm
D60080C8 testthumb1
D60080D6 testthumb2
D60080E6 testthumb3
D60080FB testthumb4
         testthumb5 crashes
D6008111 testthumb6
D600811D testthumb7
D6008127 testthumb8

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

Я программировал режим ARM и большого пальца на этом уровне в течение многих лет, и как-то все время это делал неправильно. Я думал, что программный счетчик всегда держал режим в этом lsbit, я знаю, как вы знаете, что вы хотите, чтобы он был установлен или не установлен, когда вы выполняете инструкцию bx.

Очень рано в описании процессора процессора ARM в Справочном руководстве по архитектуре ARM (если вы пишете ассемблер, у вас уже должно быть это, если, возможно, на большинство ваших вопросов будет дан ответ).

Program counter Register 15 is the Program Counter (PC). It can be used in most
      instructions as a pointer to the instruction which is two instructions after 
      the instruction being executed...

Итак, давайте проверим и посмотрим, что это на самом деле означает, означает ли это в ручном режиме две инструкции, 8 байтов впереди? и в режиме большого пальца, две инструкции впереди или 4 байта впереди?

Итак, testarm проверяет, что счетчик программы находится на 8 байт. который также является двумя инструкциями.

testthumb1 проверяет, что программа имеет 4 байта вперед, что в этом случае также является двумя инструкциями.

testthumb2

d60080d2:   4678        mov r0, pc
d60080d4:   f7ff fffb   bl  d60080ce <bounce>
d60080d8:   4710        bx  r2

если счетчик программ был двумя головками "инструкций", мы получили бы 0xD60080D8, но вместо этого мы получим 0xD60080D6, который на четыре байта впереди, и это имеет больший смысл. Ручной режим 8 байт вперед, режим большого пальца 4 байта вперед, не возиться с инструкциями декодирования (или данными), которые впереди исполняемого кода, просто добавьте 4 или 8.

testthumb3 был надеждой, что mov lr, pc был особенным, это не так.

если вы еще не видите шаблон, Lsbit счетчика программ НЕ установлен, и я предполагаю, что это имеет смысл для таблиц ветвей, например. Поэтому mov lr, pc in thumb mode НЕ настраивает регистр ссылок для возврата.

testthumb4 очень болезненным образом выполняет счетчик программ везде, где этот код происходит с в конце концов и на основе тщательно размещенных инструкций вычисляет обратный адрес, если вы измените эту последовательность команд между mov r1, pc и bx r2, вам нужно перенастроить добавление. Теперь почему мы не можем сделать что-то вроде этого:

add r1,pc,#1
bx r2

с инструкциями большого пальца, которые вы не можете, с большим пальцем2 вы, вероятно, могли бы. И, кажется, есть некоторые процессоры (armv7), которые поддерживают обе команды руки и thumb/thumb2, поэтому вы можете оказаться в ситуации, когда вы захотите это сделать, но вы не добавили бы # 1, потому что команда добавления большого пальца2, если есть одна который допускает верхние регистры и имеет три операнда, будет инструкцией 4 байт большого пальца. (вам нужно добавить # 3).

Итак, testthumb5 прямо из кода, который я показал вам, который приводит к части этого вопроса, и он падает. это не так, как это работает, извините, я вводил в заблуждение людей. Я попытаюсь вернуться и исправить вопросы SO, с которыми я использовал это.

testthumb6 - это эксперимент, чтобы убедиться, что мы все не сумасшедшие. Все хорошо, что реестр ссылок действительно получает набор lsbit, так что, когда вы bx lr позже, он знает режим с этого бита.

testthumb7, это происходит от батута стороны ARM, который вы видите, когда линкер делает, переходя из режима ручного режима в большой, в этом случае, хотя я перехожу из режима большого пальца в режим руки. почему can not компоновщик делает это таким образом? потому что в режиме большого пальца, по крайней мере, вам нужно использовать низкий регистр и в этот момент игры, после компиляции кода, линкер не имеет никакого способа узнать, какой регистр он может испортить. В ручном режиме, хотя ip-регистр, не уверенный, что это может быть r12, может быть поврежден, я думаю, он зарезервирован для использования компилятором. Я знаю в этом случае, что r1 может быть поврежден и использован, и это работает по желанию. вызывается код armbounce, который захватывает реестр ссылок, если туда, куда нужно вернуться, которая представляет собой инструкцию большого пальца (набор lsbit) после bl armbounce_thumb в функции testthumb7, именно там, где мы хотели, чтобы она была.

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

d6008140 <armbounce_thumb_two>:
d6008140:   4778        bx  pc
d6008142:   46c0        nop         ; (mov r8, r8)
d6008144:   eaffffdc    b   d60080bc <armbounce>

A bx pc. Из вышеприведенных экспериментов мы знаем, что pc на четыре байта впереди, мы также знаем, что lsbit НЕ SET. Так что это говорит о ветке к ARM CODE, которая составляет четыре байта после этого. Nop - двухбайтовый разделитель, тогда нам нужно сформировать инструкцию ARM с четырьмя байтами И ПОДКЛЮЧАЙТЕ НА ЧЕТЫРЕ БЫСТРОГО ГРАНИЦЫ, и мы делаем эту безусловную ветвь в любом месте, куда мы направляемся, это может быть что-то или что-то вроде ldr pc, = что-то в зависимости от того, как далеко вы должны идти. Очень сложно.

Оригинал bl arm_bounce_thumb_two устанавливает регистр ссылок, чтобы вернуться к инструкции после этого bl. Батут не изменяет реестр ссылок, он просто выполняет ветки.

если вы хотите перейти в режим большого пальца руки, сделайте то, что делает компоновщик:

...
bl myfun_from_arm
...


myfun_from_arm:
  ldr ip,[pc]
  bx ip
.word myfun

который выглядит так, когда он это делает (захватывается из другого двоичного файла не в 0xD6008xxx, а в 0x0001xxxx).

   101f8:   eb00003a    bl  102e8 <__testthumb1_from_arm>


000102e8 <__testthumb1_from_arm>:
   102e8:   e59fc000    ldr ip, [pc]    ; 102f0 <__testthumb1_from_arm+0x8>
   102ec:   e12fff1c    bx  ip
   102f0:   00010147    andeq   r0, r1, r7, asr #2

так что независимо от того, что это ip-регистр (r12?), они не возражают против его уничтожения, и я предполагаю, что вы можете сами его уничтожить.