Начальное состояние программных регистров и стека в Linux ARM

StackOverflow https://stackoverflow.com/questions/1802783

  •  05-07-2019
  •  | 
  •  

Вопрос

В настоящее время я играю с ARM assembly в Linux в качестве учебного упражнения.Я использую "голую" сборку, т. е.нет libcrt или libgcc.Кто-нибудь может указать мне на информацию о том, в каком состоянии находятся указатель стека и другие регистры при запуске программы перед вызовом первой инструкции?Очевидно, что pc / r15 указывает на _start , а остальные, похоже, инициализированы в 0, за двумя исключениями;sp / r13 указывает на адрес, находящийся далеко за пределами моей программы, а r1 указывает на немного более высокий адрес.

Итак, к некоторым серьезным вопросам:

  • Каково значение в r1?
  • Является ли значение в sp законным стеком, выделяемым ядром?
  • Если нет, то каков предпочтительный метод выделения стека;использовать brk или выделить статический раздел .bss?

Любые указания были бы оценены.

Это было полезно?

Решение

Вот что я использую, чтобы запустить программу Linux / ARM с помощью моего компилятора:

/** The initial entry point.
 */
asm(
"       .text\n"
"       .globl  _start\n"
"       .align  2\n"
"_start:\n"
"       sub     lr, lr, lr\n"           // Clear the link register.
"       ldr     r0, [sp]\n"             // Get argc...
"       add     r1, sp, #4\n"           // ... and argv ...
"       add     r2, r1, r0, LSL #2\n"   // ... and compute environ.
"       bl      _estart\n"              // Let's go!
"       b       .\n"                    // Never gets here.
"       .size   _start, .-_start\n"
);

Как вы можете видеть, я просто получаю данные argc, argv и environ из стека в [sp].

Небольшое уточнение:Указатель стека указывает на допустимую область в памяти процесса.r0, r1, r2 и r3 - это первые три параметра вызываемой функции.Я заполняю их с помощью argc, argv и environ соответственно.

Другие советы

Поскольку это Linux, вы можете посмотреть, как это реализовано ядром.

Регистры, по-видимому, устанавливаются вызовом на start_thread в конце load_elf_binary (если вы используете современную систему Linux, она почти всегда будет использовать формат ELF).Для ARM регистры, по-видимому, заданы следующим образом:

r0 = first word in the stack
r1 = second word in the stack
r2 = third word in the stack
sp = address of the stack
pc = binary entry point
cpsr = endianess, thumb mode, and address limit set as needed

Очевидно, что у вас есть действительный стек.Я думаю, что ценности r0-r2 являются ненужными, и вы должны вместо этого прочитать все из стека (вы поймете, почему я так думаю позже).Теперь давайте посмотрим на то, что находится в стеке.То, что вы будете читать из стека, заполняется create_elf_tables.

Здесь следует отметить одну интересную вещь: эта функция не зависит от архитектуры, поэтому одни и те же вещи (в основном) будут помещены в стек в любой архитектуре Linux на базе ELF.Следующее находится в стеке в том порядке, в каком вы бы его прочитали:

  • Количество параметров (это argc в main()).
  • Один указатель на строку C для каждого параметра, за которым следует ноль (это содержимое argv в main(); argv указывал бы на первый из этих указателей).
  • Один указатель на строку C для каждой переменной среды, за которой следует ноль (это содержимое редко встречающегося envp третий параметр main(); envp указывал бы на первый из этих указателей).
  • "Вспомогательный вектор", представляющий собой последовательность пар (тип, за которым следует значение), завершающийся парой с нулем (AT_NULL) в первом элементе.Этот вспомогательный вектор содержит некоторую интересную и полезную информацию, которую вы можете увидеть (если используете glibc), запустив любую динамически связанную программу с LD_SHOW_AUXV переменная среды, установленная в 1 (например, LD_SHOW_AUXV=1 /bin/true).Здесь также все может немного отличаться в зависимости от архитектуры.

Поскольку эта структура одинакова для каждой архитектуры, вы можете посмотреть, например, на чертеж на странице 54 SYSV 386 ABI чтобы получить лучшее представление о том, как все сочетается (обратите внимание, однако, что константы вспомогательного векторного типа в этом документе отличаются от того, что использует Linux, поэтому вам следует посмотреть на их заголовки Linux).

Теперь вы можете понять, почему содержимое r0-r2 являются мусором.Первое слово в стеке - это argc, второй - это указатель на название программы (argv[0]), а третий, вероятно, был равен нулю для вас, потому что вы вызвали программу без аргументов (это было бы argv[1]).Я предполагаю, что они настроены таким образом для старших a.out двоичный формат, который, как вы можете видеть на create_aout_tables ставит argc, argv, и envp в стеке (так что они окажутся в r0-r2 в порядке, ожидаемом для вызова в main()).

Наконец, почему было r0 ноль для вас вместо единицы (argc должен быть один, если вы вызвали программу без аргументов)?Я предполагаю, что что-то глубоко в механизме системного вызова переписало его возвращаемым значением системного вызова (которое было бы равно нулю с момента успешного выполнения).Вы можете видеть в kernel_execve (который не использует механизм системного вызова, поскольку это то, что вызывает ядро, когда оно хочет выполнить из режима ядра), который оно намеренно перезаписывает r0 с возвращаемым значением do_execve.

Вот этот ЭЛТ uClibc.Это, по-видимому, предполагает, что все регистры не определены, за исключением r0 (который содержит указатель на функцию, который должен быть зарегистрирован с atexit()) и sp который содержит действительный адрес стека.

Итак, значение, которое вы видите в r1 вероятно, это не то, на что вы можете положиться.

Некоторые данные помещаются в стек для вас.

Я никогда не использовал ARM Linux, но я предлагаю вам либо взглянуть на исходный код libcrt и посмотреть, что они делают, либо использовать gdb для перехода к существующему исполняемому файлу.Вам не должен понадобиться исходный код, просто перейдите к ассемблерному коду.

Все, что вам нужно выяснить, должно произойти в самом первом коде, выполняемом любым двоичным исполняемым файлом.

Надеюсь, это поможет.

Тони

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top