|
|
  |
Что делает Си-код до main? |
|
|
|
Sep 28 2010, 06:57
|
Участник

Группа: Участник
Сообщений: 39
Регистрация: 22-02-09
Из: Минск
Пользователь №: 45 206

|
Немного почитал про это здесь: http://infocenter.arm.com/help/index.jsp?t...i/Bce3gfea.html(использую RVDS4) Не совсем понятно с настройкой стека. Там пишут, что настройку стека выполняет пользовательский стартап код. Я так и делаю, но еще до main библиотечная функция (sys_stackheap_outer) меняет указатель стека. Что это означает? Может ли библиотечный код пытаться настроить стеки сам? Я вроде даже не давал ему никакой информации о размещении памяти на МК. Если из стартапа вызываю сразу main, то программа работает, но не инициализируются глобальные переменные. Но если я вызываю __main, то указатель стека изменяется и программа не доходит до main. Также библиотечный код генерирует программное прерывание (SWI) с кодом 0x123456. Зачем это нужно?
|
|
|
|
|
Sep 28 2010, 11:27
|
Участник

Группа: Участник
Сообщений: 39
Регистрация: 22-02-09
Из: Минск
Пользователь №: 45 206

|
Цитата(gotty @ Sep 28 2010, 13:23)  Для корректного перехода в Main необходим текущий стек правильно настроить. Стартап помимо инициализации указателя на вершину стека инициализирует глобальные и статические переменные. Если вы пишете свой стартап - то должны реализовать необходимый минимум. Ну, на страничке, ссылку на которую я кидал, написано так: Сначала вызывается начальный код пользователя, который должен: - инициализировать указатели стеков - настроить MMU/MPU - настроить кэш/включить TCM После должна вызываться __main из стандартной библиотеки Си . __main выполняет следующие действия: -копирует код (?) -копирует/распаковывает RW-данные (поэтому нет ничего удивительного, что когда я вызывал сразу main не инициализировались глобальные переменные) -обнуляет неинициализированные данные Далее вызывается __rt_entry, которая - инициализирует библиотечные функции - вызывает __user_initial_stackheap() - вызывает $Sub$main() В основном коде функции __user_initial_stackheap() и $Sub$main() я не переопределял, значит будут вызваны библиотечные. Там же написано, что если используется scatter-файл для линкера, то линкер создаст __user_initial_stackheap(). Scatter-файл я не использую. Что происходит в этом случае? После этого вызывается main(). Проблема в том, что библиотечный код изменяет указатель стека и прога рушится. Изменение SP происходит в функции sys_stackheap_outer.
|
|
|
|
|
Sep 29 2010, 17:38
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-04-05
Пользователь №: 3 977

|
Цитата(imiron13 @ Sep 28 2010, 15:27)  Проблема в том, что библиотечный код изменяет указатель стека и прога рушится. Изменение SP происходит в функции sys_stackheap_outer. У Вас до исполнения библиотечного кода SP имеет нужное Вам значение? Чему оно равно и в каком месте присваивается SP? В Keil для nxp176x сделано так - в стартапе под стек выделяется место в ОЗУ, адрес вершины этого блока ОЗУ и присваивается SP при исполнении кода, который до main. А сам код запускается так - делается переход из бутлоадера по адресу, равному значению первого слова во флеше. В нулевое слово флеша обычно записывается это самое значение для SP, но значение нулевого слова нигде не используется.
|
|
|
|
|
Sep 30 2010, 07:43
|
Участник

Группа: Участник
Сообщений: 39
Регистрация: 22-02-09
Из: Минск
Пользователь №: 45 206

|
Цитата(vallav @ Sep 29 2010, 20:38)  У Вас до исполнения библиотечного кода SP имеет нужное Вам значение? Чему оно равно и в каком месте присваивается SP? Да до исполнения библиотечного кода все ок, вот этот кусочек стартапа(стартап самодельный: МК нестандартный) настраивает указатели стека (0x007fff0 - конец ОЗУ) Код isrReset ldr r1, =0x007fff0 ; SP для FIQ msr cpsr_c, #(17 | 0xC0) ; переход в режи FIQ mov sp, r1 sub r1, r1, #1024 ;FIQ_STACK_SIZE msr cpsr_c, #(18 | 0xC0) ; переход в режим IRQ mov sp, r1 sub r1, r1, #1024 ;IRQ_STACK_SIZE msr cpsr_c, #(19 | 0xC0) ; переход в режим Supervisor mov sp, r1 ; Supervisor SP Теперь указатели стека имеют верные значения (0007FFF0-FIQ,0007FBF0-IRQ,0007F7F0-SV). Далее работа идет в режиме Supervisor. В конце стартапа: Код ldr R0, = __main BX R0 Далее выполняется библиотечный код, который изменяет SP. Изменение происходит в функции sys_stackheap_outer (сверял адрес выполняемого кода в отладчике с map-файлом). Ситуация усложняется тем, что не знаю армовский асм, и не могу разобраться, что пытается сделать код, изменяющий SP. Кусочек, изменяющий SP на скрине (до выполнения mov sp,r0 в SP все еще верное значение; вообще-то SP сохраняется в R1, затем в SP возвращается значение из R1, но оно уже затерто). У меня ступор, буду оч благодарен за помощь и советы.
Эскизы прикрепленных изображений
|
|
|
|
|
Sep 30 2010, 13:20
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-04-05
Пользователь №: 3 977

|
Цитата(imiron13 @ Sep 30 2010, 11:43)  Да до исполнения библиотечного кода все ок, вот этот кусочек стартапа(стартап самодельный: МК нестандартный) настраивает указатели стека (0x007fff0 - конец ОЗУ)
Далее выполняется библиотечный код, который изменяет SP. Изменение происходит в функции sys_stackheap_outer (сверял адрес выполняемого кода в отладчике с map-файлом). Ситуация усложняется тем, что не знаю армовский асм, и не могу разобраться, что пытается сделать код, изменяющий SP. Кусочек, изменяющий SP на скрине (до выполнения mov sp,r0 в SP все еще верное значение; вообще-то SP сохраняется в R1, затем в SP возвращается значение из R1, но оно уже затерто). У меня ступор, буду оч благодарен за помощь и советы. То, что библиотечная функция при входе сохраняет старое значение стека а при выходе неправильно его восстанавливает - такое врядли. Я не знаю, как инициализация стека устроена в Вашем компиляторе. В разных сделано по разному. В некоторых - это нужно это делать самому. В Keil - в вызове __main ( кода инициализации ) перед вызовом main ( самой программы ) стек инициализируется значением _initial_sp, которое задается в стартапе при выделении блоков памяти. Выглядит это так: Stack_Size EQU 0x00000200 AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp Может у Вас то же самое, но, так как в стартапе Вы этой метке значения не задали, что то идет не так. У Вас стартап - с нуля самодельный? А работающего стартапа из примеров ( который можно под свою прогу переделать ) нет?
Сообщение отредактировал vallav - Sep 30 2010, 13:22
|
|
|
|
|
Sep 30 2010, 15:58
|
Местный
  
Группа: Участник
Сообщений: 328
Регистрация: 23-05-08
Пользователь №: 37 760

|
Цитата(imiron13 @ Sep 30 2010, 11:43)  Теперь указатели стека имеют верные значения (0007FFF0-FIQ,0007FBF0-IRQ,0007F7F0-SV). Далее работа идет в режиме Supervisor. В конце стартапа: Код ldr R0, = __main BX R0 А для System/User? Там кто SP настраивает? Можете Вы, а может и билиотечная функция (это как пожелаете): Код ; Enter User Mode and set its Stack Pointer MSR CPSR_c, #Mode_SYS IF :DEF:__MICROLIB
EXPORT __initial_sp
ELSE
MOV SP, R0 SUB SL, SP, #USR_Stack_Size Кстати и вызов __main надо делать из System или User (в зависимости от того, в каком режиме Вы желаете быть при входе в main()  ... хотя в сказанном не уверен  ). Цитата(vallav @ Sep 30 2010, 17:20)  В Keil - в вызове __main ( кода инициализации ) перед вызовом main ( самой программы ) стек инициализируется значением _initial_sp, которое задается в стартапе при выделении блоков памяти. Выглядит это так:
Stack_Size EQU 0x00000200
AREA STACK, NOINIT, READWRITE, ALIGN=3 Stack_Mem SPACE Stack_Size __initial_sp Сюдя по всему у топикстартера ARM7/ARM9, а не Cortex (как в LPC17xx)...
Сообщение отредактировал Student Pupkin - Sep 30 2010, 16:05
|
|
|
|
|
Sep 30 2010, 16:12
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-04-05
Пользователь №: 3 977

|
Цитата(Student Pupkin @ Sep 30 2010, 19:58)  Сюдя по всему у топикстартера ARM7/ARM9, а не Cortex (как в LPC17xx)... И что? Все компиляторы Си для ARM7/ARM9 предполагают явную установку стеков в стартапе? У автора сделана явная и она портится... Вот и появилось предположение, что это срабатывает неявная. В Keil для кортекса - что бы Вы не делали с SP в стартапе, в main прога вывалится с SP, равным _initial_sp.
|
|
|
|
|
Oct 1 2010, 07:00
|
Участник

Группа: Участник
Сообщений: 39
Регистрация: 22-02-09
Из: Минск
Пользователь №: 45 206

|
Кажется разобрался. Надо было написать функцию __user_initial_stackheap, которая возвращает вершину стека для основного режима, его границу, адрес кучи, ее границу. Заработало. Детали понимаю смутновато. Зачем библиотеке нужно перестраивать стек, и что она пыталась делать, когда я не писал эту функцию.
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|