Как сгенерировать машинный код инструкций Thumb?
Я искал в Google, чтобы сгенерировать машинный код инструкций ARM, таких как этот. Преобразование очень простых инструкций ARM в двоичный / шестнадцатеричный формат.
Ответ ссылается на таблицу данных ARM7TDMI-S (ARM DDI 0084D). Схема инструкций по обработке данных достаточно хороша. К сожалению, это для инструкций ARM, а не для инструкций Thumb/Thumb-2.
Возьмите инструкцию B в качестве примера. Справочное руководство по архитектуре ARM - раздел A8.8.18 редакции ARMv7-A и ARMv7-R, кодировка T4:
Для кода сборки:
B 0x50
Как я могу закодировать непосредственное значение 0x50 в 4-байтовый машинный код? Или, если я хочу написать функцию C, которая принимает инструкцию B и входные данные, и возвращает закодированный машинный код. Как я могу реализовать такую функцию?
unsigned int gen_mach_code(int instruction, int relative_addr)
{
/* the int instruction parameter is assumed to be B */
/* encoding method is assumed to be T4 */
unsigned int mach_code;
/* construc the machine code of B<c>.W <label> */
return mach_code;
}
Я знаю непосредственные значения кодирования на ARM. Здесь http://alisdair.mcdiarmid.org/arm-immediate-value-encoding/ хороший учебник.
Я просто хочу знать, откуда взялись imm10 и imm11 и как с их помощью создать полный машинный код.
2 ответа
Во-первых, ARM7TDMI не поддерживает расширения thumb2, вместо этого он в основном определяет исходный набор команд thumb.
так почему бы просто не попробовать?
.thumb
@.syntax unified
b 0x50
запустить эти команды
arm-whatever-whatever-as b.s -o b.o
arm-whatever-whatever-objdump -D b.o
получить этот вывод
0: e7fe b.n 50 <*ABS*0x50>
так что это кодировка T2, и, как показывают новые документы для этой инструкции, поддерживаемой ARMv4T, ARMv5T*, ARMv6*, ARMv7, ARM7TDMI является ARMv4t
Итак, мы видим, что E7 соответствует началу 11100 определения этой инструкции, поэтому imm11 равен 0x7FE. который в основном является кодировкой перехода по адресу 0x000, так как он не связан ни с чем. откуда мне это знать?
.thumb
b skip
nop
nop
nop
nop
nop
skip:
00000000 <skip-0xc>:
0: e004 b.n c <skip>
2: 46c0 nop ; (mov r8, r8)
4: 46c0 nop ; (mov r8, r8)
6: 46c0 nop ; (mov r8, r8)
8: 46c0 nop ; (mov r8, r8)
a: 46c0 nop ; (mov r8, r8)
0xe004 начинается с 11100, так что это ветвь, кодирующая T2. imm11 это 4
нам нужно достичь от 0 до 0xC. ПК смещен на две ИНСТРУКЦИИ, когда применяется смещение. Документы говорят
Encoding T2 Even numbers in the range –2048 to 2046
а также
PC, the program counter
- When executing an ARM instruction, PC reads as the address of the current instruction plus 8. • When executing a
- Thumb instruction, PC reads as the address of the current instruction
plus 4.
так что все имеет смысл. 0xC-0x4 = 8. мы можем делать только четные операции, и в любом случае нет смысла переходить в середину инструкции, поэтому делим на 2, потому что инструкции большого пальца - это два байта (смещение в инструкциях, а не в байтах). так что дает 4
0xE004
вот один из способов генерации кодировки t4
.thumb
.syntax unified
b skip
nop
nop
nop
nop
nop
skip:
00000000 <skip-0xe>:
0: f000 b805 b.w e <skip>
4: 46c0 nop ; (mov r8, r8)
6: 46c0 nop ; (mov r8, r8)
8: 46c0 nop ; (mov r8, r8)
a: 46c0 nop ; (mov r8, r8)
c: 46c0 nop ; (mov r8, r8)
T4-кодировка ответвления - 11110 поверх первого полуслова, указывающего, что это либо неопределенная инструкция (что-либо, кроме ARMv6T2, ARMv7), либо расширение thumb2 для ARMv6T2, ARMv7.
второе половинное слово 10x1, и мы видим, что B выглядит хорошо, это расширенная ветвь thumb2.
S равно 0 imm10 равно 0 j1 равно 1 j2 равно 1 и imm11 равно 5
I1 = NOT(J1 EOR S); I2 = NOT(J2 EOR S); imm32 = SignExtend(S:I1:I2:imm10:imm11:’0’, 32);
1 EOR 0 это 1 верно? не то, что вы получаете 0. Таким образом, I1 и I2 оба равны нулю, s - это ноль. imm10 - это ноль. так что мы в основном на этом только смотрим на imm11 как положительное число
ПК на четыре впереди при выполнении, поэтому 0xE - 0x4 = 0xA.
0xA / 2 = 0x5 и это наше смещение ответвления pc + (5 * 2)
.syntax unified
.thumb
b.w skip
nop
here:
nop
nop
nop
nop
skip:
b.w here
00000000 <here-0x6>:
0: f000 b805 b.w e <skip>
4: 46c0 nop ; (mov r8, r8)
00000006 <here>:
6: 46c0 nop ; (mov r8, r8)
8: 46c0 nop ; (mov r8, r8)
a: 46c0 nop ; (mov r8, r8)
c: 46c0 nop ; (mov r8, r8)
0000000e <skip>:
e: f7ff bffa b.w 6 <here>
s равно 1, imm10 равно 0x3FF j1 равно 1 j2, равно 1 imm1 равно 0x7FA
1 или 1 равно 0, вы не получаете 1 для i1 и то же для i2
imm32 = SignExtend(S:I1:I2:imm10:imm11:’0’, 32);
s равен 1, поэтому это будет знак расширения 1, но последние несколько битов равны единице, поэтому imm32 - это 0xFFFFFFFA или инструкции -6 назад или -12 байт назад
поэтому наше смещение также ((0xE + 4) - 6)/2 = 6. или посмотрите на это по-другому от кодировки команд ПК - (6*2) = (0xE + 4) - 12 = 6 ответвлений до 0x6.
Таким образом, если вы хотите перейти к 0x70, а адрес инструкции - 0x12, тогда ваше смещение будет 0x70-(0x12+4) = 0x62 или 0x31 инструкции, мы знаем из пропуска, трюк состоит в том, чтобы сделать s 0, j1 и j2 1
0x12: 0xF000 0xB831 branch to 0x70
так что теперь зная, что мы можем вернуться к этому:
0: e7fe b.n 50 <*ABS*0x50>
смещение является расширенным знаком 0x7FE или 0xFFFFFFFE. 0xFFFFFFFE*2 + 4 = 0xFFFFFFFC + 4 = 0x00000000. Отделение до 0
добавить nop
.thumb
nop
b 0x50
00000000 <.text>:
0: 46c0 nop ; (mov r8, r8)
2: e7fe b.n 50 <*ABS*0x50>
та же кодировка
поэтому разборка подразумевает абсолютное значение 0x50, но не кодирует его, связывание не помогает, он просто жалуется
(.text+0x0): relocation truncated to fit: R_ARM_THM_JUMP11 against `*ABS*0x50'
этот
.thumb
nop
b 0x51
дает ту же кодировку.
Так что в принципе с этим синтаксисом что-то не так и / или он ищет метку с именем 0x50, возможно?
Я надеюсь, что вашим примером было то, что вы хотите знать кодировку ветви по какому-то адресу, а не этот точный синтаксис.
arm не похож на некоторые другие наборы инструкций, ветви всегда относительны. так что если вы можете добраться до места назначения на основе кодировки, тогда вы получите ответвление, в противном случае вам придется использовать bx или pop или один из других способов изменить компьютер (с абсолютным значением).
зная, что кодировка T2 из документов может достигать только 2048, затем поместите более 2048 nops между ветвью и местом назначения
b.s: Assembler messages:
b.s:5: Error: branch out of range
Может быть, это то, что вы хотите сделать?
.thumb
mov r0,#0x51
bx r0
00000000 <.text>:
0: 2051 movs r0, #81 ; 0x51
2: 4700 bx r0
филиал по абсолютному адресу 0x50. для этого конкретного адреса не нужны расширения thumb2.
.thumb
ldr r0,=0x12345679
bx r0
00000000 <.text>:
0: 4800 ldr r0, [pc, #0] ; (4 <.text+0x4>)
2: 4700 bx r0
4: 12345679 eorsne r5, r4, #126877696 ; 0x7900000
филиал по адресу 0x12345678 или любой другой возможный адрес.
Спасибо old_timer, но я не совсем тебя понимаю. Я прошу прощения за мое невежество...
Я попытался закодировать / декодировать инструкцию B, используя побитовые операции, хотя это было довольно просто и глупо:) Следующий код, похоже, сейчас работает. Jester
#define MAX_CODE_LEN 4
typedef unsigned char uchar;
typedef unsigned int uint;
static int decode_B_T4(const int code)
{
const int S = (code & (1 << 26)) ? 1 : 0; /* test bit [26] */
const int J1 = (code & (1 << 13)) ? 1 : 0; /* test bit [13] */
const int J2 = (code & (1 << 11)) ? 1 : 0; /* test bit [11] */
const int imm10 = (code >> 16) & 0b1111111111; /* extract imm10 */
const int imm11 = code & 0b11111111111; /* extract imm11 */
const int I1 = (~(J1 ^ S)) & 1;
const int I2 = (~(J2 ^ S)) & 1;
int offset = 0;
offset |= I1 << 23;
offset |= I2 << 22;
offset |= imm10 << 12;
offset |= imm11 << 1;
if (S) {
offset |= 0b11111111 << 24; /* sign extend */
}
return offset;
}
static int encode_B_T4(const int src_addr, const int dst_addr, uchar* buf)
{
assert(buf != NULL);
uint code;
const int code_len = 4; /* 4 bytes */
const int offset = (dst_addr & (~1)) - (src_addr & (~1)) - 4;
const int S = offset < 0; /* sign */
const int I1 = offset & (1 << 23) ? 1 : 0; /* test bit [23] */
const int I2 = offset & (1 << 22) ? 1 : 0; /* test bit [22] */
const int imm10 = (offset >> 12) & 0b1111111111; /* extract imm10 */
const int imm11 = (offset >> 1) & 0b11111111111; /* extract imm11 */
const int J1 = ((~I1 & 1) ^ S) & 1;
const int J2 = ((~I2 & 1) ^ S) & 1;
code = 0b11110 << 27; /* set the 5 MSB */
code |= S << 26;
code |= imm10 << 16;
code |= 1 << 15;
code |= J1 << 13;
code |= 1 << 12;
code |= J2 << 11;
code |= imm11;
assert(code_len <= MAX_CODE_LEN);
memcpy(buf, &code, code_len);
return code_len;
}