Использование BX в коде Thumb для вызова функции Thumb или для перехода к инструкции Thumb в другой функции - PullRequest
14 голосов
/ 21 февраля 2012

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

  1. Как использовать BX для перехода на существующий код THUMB встроенного ПО из моего кода THUMB.
  2. Как использовать BX для вызова существующей функции THUMB (сначала необходимо установить LR) из кода THUMB.

Насколько я понимаю, процессор смотрит на бит lsb (бит 0), и я должен убедиться, что он установлен на 1, чтобы сохранить состояние процессора в «состоянии большого пальца».Так что я думаю, мне нужно ДОБАВИТЬ 1, чтобы установить бит lsb в 1.

Итак ... скажем, я хочу просто ПЕРЕХОД на 0x24000 (в середине некоторого существующего кода THUMB)

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

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

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

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

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

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

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

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

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

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

1 Ответ

20 голосов
/ 21 февраля 2012

Ух ты, спасибо, что позвонил мне.Я знаю, что пробовал код 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-ассемблер, кстати, для других инструментов вам, возможно, придется сделать что-то еще.Для газа нужно поставить .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, так что вам не нужно выполнять какую-либо работу.Компилятор позаботится обо всем этом, например, когда вы делаете вызовы функций на языке Си.Для ассемблера вы должны использовать этот .thumb_func (или какую-либо другую директиву, если она есть), чтобы получить газ, чтобы узнать, что это метка большого пальца, и установить для вас lsbit.

Итак, эксперимент, приведенный ниже, был сделан наmpcore, который является ARM11, но я также попробовал функции testthumb с 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 Architectural Reference Manual (если вы пишете на ассемблере, у вас уже должно быть это, если нет, возможно, на большинство ваших вопросов будет дан ответ).

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,ПК был особенным, это не так.

если вы еще не видите шаблон, lsbit счетчика программы НЕ установлен, и я думаю, что это имеет смысл, например, для таблиц ветвления.Таким образом, mov lr, pc в режиме большого пальца НЕ настраивает регистр ссылок для возврата.

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

add r1,pc,#1
bx r2

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

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

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

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

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

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

A bx шт.Мы знаем из экспериментов выше, что pc на четыре байта впереди, мы также знаем, что lsbit НЕ УСТАНОВЛЕН.Так что это говорит о переходе к КОДУ ARM, который составляет четыре байта после этого.Nop - это двухбайтовый разделитель, тогда мы должны сгенерировать инструкцию ARM на четыре байта вперед и выровнять по четырем байтовым границам, и мы сделаем эту безусловную ветвь в любое место, куда мы идем, это может быть что-то ab или 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?), они не возражают уничтожить его, и я предполагаю, что вы можете его удалитьсами.

...