Отслеживание простой программы в сборке - PullRequest
1 голос
/ 13 августа 2011

Я создал простую программу на c для добавления двух чисел:

void main(){
     int a = 4;
     int b = 5;
     int c = a+b;
}

и назвал ее test.c Я использовал "arm-linux-gcc -S test.c" для создания test.s (Код сборки) Теперь я хочу видеть значение каждого из 16 регистров после каждой инструкции по сборке.Что я должен делать?У меня нет никакого опыта в сборке, и я относительно новичок в Linux, поэтому я не очень осведомлен об используемых инструментах.Пожалуйста помоги!Заранее спасибо.

Ответы [ 2 ]

5 голосов
/ 14 августа 2011

Ну, вы говорите о двух разных вещах. Если вы хотите увидеть содержимое регистров, вам нужно выполнить программу, поэтому вам нужно создать двоичный файл. Ориентация на систему, а затем один шаг через нее. да GDB будет работать, если у вас есть правильный GDB, указывающий на правильную систему. Вы также можете использовать отладчик jtag, один шаг и затем сбросить регистры.

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

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

Вот кое-что попробовать. У меня есть симулятор набора команд thumb, thumb - это 16-битное подмножество ARM (все еще в семействе ARM, отношение один к одному с инструкциями ARM). Зайдите на github и скачайте его. В thumbulator.c измените этот цикл:

int run ( void )
{
    unsigned int ra;
    reset();
    while(1)
    {
        printf("-- 0x%08X --\n",reg_norm[15]-3);
        if(execute()) break;
        for(ra=0;ra< 8;ra++) printf("r%u 0x%08X ",ra,reg_norm[ra]); printf("\n");
        for(    ;ra<16;ra++) printf("r%u 0x%08X ",ra,reg_norm[ra]); printf("\n");

    }
    dump_counters();
    return(0);
}

Чтобы добавить printfs для отображения регистров.

Перейдите в каталог blinker, измените notmain (), чтобы он напоминал вашу программу:

int notmain ( void )
{
    int a = 4;
    int b = 5;
    int c;
    c = a + b;
    return(0);
}

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

Измените эту строку, чтобы использовать ваш компилятор (я добавляю -gcc во время сборки).

ARMGNU = arm-linux

удалить -O2 из этой строки:

COPS = -Wall -mthumb -nostdlib -nostartfiles -freedtanding

И измените это, чтобы просто построить двоичный файл gnu / gcc, а не двоичный файл llvm.

all: gnotmain.bin

Теперь постройте это.

Посмотрите на файл gnotmain.list, вы увидите нечто подобное, но не обязательно именно это, зависит от вашего gcc.

00000074 <notmain>:
  74:   b580        push    {r7, lr}
  76:   b084        sub sp, #16
  78:   af00        add r7, sp, #0
  7a:   2304        movs    r3, #4
  7c:   60fb        str r3, [r7, #12]
  7e:   2305        movs    r3, #5
  80:   60bb        str r3, [r7, #8]
  82:   68fa        ldr r2, [r7, #12]
  84:   68bb        ldr r3, [r7, #8]
  86:   18d3        adds    r3, r2, r3
  88:   607b        str r3, [r7, #4]
  8a:   2300        movs    r3, #0
  8c:   1c18        adds    r0, r3, #0
  8e:   46bd        mov sp, r7
  90:   b004        add sp, #16
  92:   bd80        pop {r7, pc}

Вы также увидите код, загружающий процессор:

00000000 <hang-0x50>:
   0:   40080000    andmi   r0, r8, r0
   4:   00000053    andeq   r0, r0, r3, asr r0
   8:   00000051    andeq   r0, r0, r1, asr r0
...

00000052 <_start>:
  52:   f000 f80f   bl  74 <notmain>
  56:   df01        svc 1
  58:   e7fe        b.n 58 <_start+0x6>

Что отличается от того, что вы увидите на ARM, флэш-диск загружается как ARM cortex-m3, а не как традиционный ARM, основанный на наборе команд ARM. Таким образом, число по адресу 4 в этом случае является адресом для сброса (lsbit установлен для указания режима большого пальца, поэтому адрес действительно равен 0x52). Тогда код _start вызывает notmain, и вы попадаете в свой код.

Причина, по которой я упоминаю об этом, заключается в том, что когда вы запускаете бегунок (./thumbulator blinker / gnotmain.bin) и те printfs, которые вы добавили, сбрасывают все регистры, вы увидите, что он делает несколько вещей до и после notmain.

- 0x00000052 - r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x40080000 r14 0xFFFFFFFF r15 0x00000057 - 0x00000054 - r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x40080000 r14 0x00000057 r15 0x00000077 - 0x00000074 - r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFF8 r14 0x00000057 r15 0x00000079 - 0x00000076 - r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x00000000 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFE8 r14 0x00000057 r15 0x0000007B - 0x00000078 - r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x4007FFE8 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFE8 r14 0x00000057 r15 0x0000007D - 0x0000007A - r0 0x00000000 r1 0x00000000 r2 0x00000000 r3 0x00000004 r4 0x00000000 r5 0x00000000 r6 0x00000000 r7 0x4007FFE8 r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFE8 r14 0x00000057 r15 0x0000007F - 0x0000007C -

- 0x00000052 - это первая выполненная инструкция, которая является первой инструкцией после _start, это инструкция с двумя командами, поэтому 0x52 и 0x54 переходят в 0x74, что является началом notmain.Который начинается с нажатия r7 в случае моей компиляции, поэтому r13 должен измениться, чтобы отразить что-то было передано.Следующая инструкция sub sp, # 16, снова изменится r13 (sp - это r13, указатель стека).

При 0x7A мы получим первый бит вашего кода C.mov r3, # 4, затем сохраняет это в стек в 0x7C (это не оптимизированный код).Затем строка b = 5 кода, материал c = a + b с большим количеством стеков (не оптимизировано).и он сворачивает вниз.

Какое отношение имеет к этому оптимизация?Что ж, ваша программа на самом деле ничего не делает, поэтому, если бы вы оптимизировали ее (поместили -O2 обратно в переменную окружения COPS), вы получили бы это:

00000074 <notmain>:
  74:   2000        movs    r0, #0
  76:   4770        bx  lr

В основном это так:

int notmain ( void )
{
    return(0);
}

Суть программы.

Если вы хотите увидеть оптимизированный код того, что вы пытаетесь сделать, В ОТДЕЛЬНОМ ФАЙЛЕ .C поместите этот код:

int xfun ( int a, int b )
{
    return(a+b);
}

добавьте это в проект (скомпилируйте его ОТДЕЛЬНО! в его собственный файл .o).

измените notmain на этот

int xfun ( int, int );
int notmain ( void )
{
    return(xfun(4,5));
}

И теперь вы видите то, что вас, вероятно, интересует.

00000080 <xfun>:
  80:   1808        adds    r0, r1, r0
  82:   4770        bx  lr

Имитируйте это с помощью миниатюры и посмотрите на инструкцию до и после 0x80

- 0x0000007C - r0 0x00000004 r1 0x00000005 r2 0x00000000 r3 0x00000000 r4 0x00000000 r5 0x000000 r5 0x0000000000 500x000000 r5 0x00000000r8 0x00000000 r9 0x00000000 r10 0x00000000 r11 0x00000000 r12 0x00000000 r13 0x4007FFF8 r14 0x0000007F r15 0x00000083 - 0x00000080 - r0 0x00000009 r1 0x0000005 r00 0000000 r00 000000000x00000000 r6 0x00000000 0x00000000 r7 r8 0x00000000 0x00000000 г9 r10 r11 0x00000000 0x00000000 0x00000000 r12 r13 r14 0x4007FFF8 0x0000007F r15 0x00000085

1069 * Перед Г0 4, r1 = 5 надстройку r0 = r0 + r1 происходит и r0 теперь 9.

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

00000074 <notmain>:
  74:   b508        push    {r3, lr}
  76:   2004        movs    r0, #4
  78:   2105        movs    r1, #5
  7a:   f000 f801   bl  80 <xfun>
  7e:   bd08        pop {r3, pc}

00000080 <xfun>:
  80:   1808        adds    r0, r1, r0
  82:   4770        bx  lr

movs - это ход с обновленными флагами, он получит код ARM за секунду.переместите число 4 в r0, затем переместите число 5 в r1, ветвь связи bl в основном это ветвь с возвращаемым значением, установленным в r14, чтобы вы могли вернуться (вызов функции вместо ветви).bl к xfun мы видим добавление r0, r1, r0, что означает r0 = r1 + r0, поэтому мы знаем, что r0 уничтожено, было 4, теперь это 9. И в основном мясо вашей программы закончено.

Итак, вернемся к инструкциям ARM, и это было интересно с компилятором, который я использую, используя ваш вариант компилятора для демонстрации:

arm-linux-gcc -S -O2 notmain.c

дает

mov r0, #4
mov r1, #5
b   xfun

, когда выЕсли разобраться в этом, то делает ли оптимизация хвоста возвращаемое значение из xfun такое же, как возвращаемое значение из notmain, поэтому оставляет r14 неизменным и позволяет xfun вернуться к тому, кто вызвал notmain.

И другой файл:

arm-linux-gcc -S -O2 xbox.c

Дает

add r0, r1, r0
bx  lr

Поскольку инструкции ARM могут выбирать, изменять флаги или нет, вы не видите mov и добавляете, вы видите mov и add, потому что код не выполняет никаких условий.связанные с этими инструкциями, чтобы они не генерировали версию s.

Я буквально нахожусь в середине нескольких проектов Gidhub, когда я набираю это.mbed_samples - это плата микроконтроллера на основе ARM cortex-m3 (только инструкция thumb / thumb2).Я так же долго писал о том, как загружать и использовать инструменты и тому подобное для создания и выполнения двоичных файлов (то, что вы захотите сделать в ближайшее время).Я также только что опубликовал lsasim на днях, не ARM, не связанный с ARM, но там есть LEARNASM.txt, который может быть полезным для изучения ASM в первый раз.Бэкенд компилятора C очень сырой, и я бы не стал сильно с ним связываться, пройдясь по учебнику LEARNASM.txt.Затем перейдите на веб-сайт ARM http://infocenter.arm.com слева под содержимым, нажмите, чтобы развернуть архитектуру ARM, затем нажмите Справочное руководство, чтобы просмотреть доступные справочные руководства.С правой стороны он покажет вам ARMv5 ARM (Архитектурное справочное руководство), который раньше назывался ARM ARM до того, как у него было так много разных ядер.Это будет список традиционных 32-битных инструкций ARM, который вы создали.у него также есть набор команд thumb, который работает на большинстве современных ARM-ядер.Thumb2 только на некоторых ядрах, и вы хотели бы что-то вроде ARMv7-M ARM для cortex-m3, который заменяет ARM7 (ARM7 - это ARMv4T, я знаю, цифры могут сбивать с толку) для встроенных микроконтроллеров.QEMU может выполнять инструкции ARM, thumb и thumb2, но запуск некоторых программ, в которых вы будете иметь представление о происходящем, займет у вас довольно много времени.В GDB есть ARMulator, который ARM использует в течение длительного времени по разным причинам, я не использую GDB, так что не знаю ничего, кроме того, что там есть симулятор ARM (он также использует thumb и, возможно, thumb2, если этоимеет значение вообще).это может быть ваш самый быстрый путь к запуску чего-либо и сбросу регистров, основанного на руке gdb.Возможно, CodeSource поставляется с одним, если не искать emdebian или просто создать свой собственный.Также доступен вариант thumbulator, но я строго ограничил его инструкциями большого пальца, и он загружается как cortex-m3, а не как рычаг (его легко изменить, если хотите).у мамы есть оружие, но получить его, чтобы скомпилировать где-нибудь за пределами мамы и кормить его программами, вероятно, больше, чем исходный код в gdb.

Совершенно другой путь - получить что-то вроде, скажем, olimex sam7-h64 доска от sparkfun.com, они продают их по цене около 16 долларов каждая, раньше это было в два или три раза больше.это ARM7 и будет запускать инструкции ARM.получить olimex wiggler или я предпочитаю amontek jtag-tiny.Затем вы можете использовать jtag для загрузки программ в оперативную память, и пошагово проходить через них, сбрасывая любые или все регистры или память, когда захотите.Я не могу придумать ни одной из плат, которые я знаю, если бы они выполняли инструкции ARM, но иметь встроенный в плату jtag, сегодня было бы тривиально делать с чипами ftdi, вместо этого появляются микроконтроллеры на базе cortex-m с этимЧто-то вроде либо ftdi-чипа, который обеспечивает последовательный и битовый jtag, либо, что более распространено, на плате фактически есть два микроконтроллера от этого производителя, один - интерфейс usb, который вы не можете перепрограммировать, другой - чип, который вы купиличтобы играть с, передний микроконтроллер обрабатывает JTAG вещи.Из них Stellaris поможет вам с jtag

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

Удачи.

1 голос
/ 13 августа 2011

objdump должен иметь возможность разобрать код сборки ARM. Другим вариантом может быть gdb, но я не уверен, поддерживает ли он ARM или нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...