Микроконтролери и електроника
http://mcu-bg.com/mcu_site/

GCC - НЕинициализирани променливи?
http://mcu-bg.com/mcu_site/viewtopic.php?f=16&t=6853
Страница 1 от 3

Автор:  Цецо [ Сря Май 27, 2009 2:02 pm ]
Заглавие:  GCC - НЕинициализирани променливи?

Как се обявява в GCC статична променлива която не се инициализира от startup кода?

Автор:  Zdrav [ Сря Май 27, 2009 2:24 pm ]
Заглавие: 

Аз правя отделна секция в линкер скрипта и после в сорса използвам това:
Код:
uint32   g_ulResetID      __attribute__((section(".noinit_variables")));

Автор:  woody [ Сря Май 27, 2009 3:10 pm ]
Заглавие: 

Какво имаш предвид под неинициализирана?

Както казва Zdrav, правиш си твоя секция в линкер скрипта и си я слагаш в нея с атрибута за секция.

Автор:  Цецо [ Сря Май 27, 2009 3:49 pm ]
Заглавие: 

Мда. Ама тоя линкерски език ми е много тегав.

Как точно се обяснява на линкера, кои секции трябва да се инициализират и кои не?

Иначе става дума точно за променлива обслужваща кучето.....

Автор:  woody [ Сря Май 27, 2009 4:38 pm ]
Заглавие: 

Преди да се викне "main()" се изпълнява малко кодец обикновено с име близко до CRT0, идва от "C runtime".
Там се прави каквото се прави за съответната платформа/OS. Например се занулява ".bss" секцията и ".data" се копира от ROM ако е била такава постановката.

Така че каквато и твоя секция да сложиш, никой няма да ти я прави нищо. :)

Има и друг вариант. Понеже секциите на обектните файлове се събират после в секции на изходния файл (и това се указва от линкер скрипта как точно да става), можеш дори да не правиш специална изходна секция. Достатъчно е да укажеш някаква твоя обектна секция, примерно ".ceco" и после в скрипта да я набуташ в ".text" примерно ако адресното пространсто за код и данни е едно и също и позволява писане (зависи от постановката ти). Или в коя да е друга която отговаря на изискванията ти.

Пример:

Код:
int special_var __attribute__((section(".ceco")));


Код:
SECTIONS {
        .text : {
                ...
                * (.ceco)
                ...
        }


Линкер-скрипт парчето указва обектната секция ".ceco" да се разположи в изходната ".text".

Автор:  woody [ Сря Май 27, 2009 4:44 pm ]
Заглавие: 

А ето и един още по-лесен вариант. Изключи някаква област от RAM-та от линкер скрипта, т.е. да не се ползва.
Направи си структура и указател към нея. Инициализирай указателя към парчето директно с абсолютен адрес щом не ти се занимава с линкер скриптове. Воала! Имаш NV променливи (каквато разбирам ти е целта).

Код:
struct nv {
    int wd;
    int prm1, prm2, prm3;
    // ...
} *nv;

// ...

nv = (struct nv *)0xFFFF0000;    // at upper 64K

nv->wd = 0;
nv->prm1 = ...

Автор:  Цецо [ Сря Май 27, 2009 4:45 pm ]
Заглавие: 

Бе това ми е горе долу ясно. Това което не ми е ясно е как се обяснява коя секция какво да я прави компилатора?

Т.е. къде се указва че точно bss трябва да се занули, а точно data да се копира от ROM-a? Гледам линкерския скрипт не виждам подобни "разяснения" в него.

.text ми е в FLASH-а, т.е там променливи няма какво да търсят.

Автор:  miro_atc [ Сря Май 27, 2009 6:34 pm ]
Заглавие: 

1. Компилаторът генерира обекти - променливи, константи и всякакви други глупости, като за всеки обект се указва секция в обектния файл.
По принцип ти с __attribute__ може да кажеш в коя секция искаш дадения обект да бъде разположен, но ако не го направиш ще ти я наблсъка в съответната секция по подразбиране. Поне засега не ми попадала документация относно всички секции по подразбиране. Така че за bss, text и т.н. се ориентирам, но има и разни други дето пък не ги разбирам...

2. Линкерският скрипт описва как от многото обектни файлове и техните секции да се сглоби изходен файл. Така ти на практика определяш разпожението на секциите от обектните файлове в изходния файл (това което казва woody).

3. Линкерът изпълнявайки скрипта разполага всяко нещо на мястото му (евентуално премахва ненужните неща) но в крайна сметка всеки обект си получава конкретни адреси. Включително се изчисляват и константите (етикетите).

4. Обикновено имаш два етикета между които се разполагат променливите в RAM-a. Примерно при мен е:

Код:
   .zero (NOLOAD) :
   {
        _szero = .;
       *(.bss)
      *(.bss.*)
      *(.gnu.linkonce.b.*)
       *(COMMON)
       . = ALIGN(4);
        _ezero = .;
     } >ram


В случая това са _szero и _ezero. Между тези два адреса линкера ще разположи BSS-те на всички (*) обектни файлове...

5. В стартъп кода имам едно цикълче дето нулира паметта между _szero и _ezero



Накратко казано компилатора нищо не указва (освен кое в коя секция на обектния файл се намира). Линкерът (и скрипта) реално разполагат нещата. А стартъп кода е отговорен за нулирането.

С други думи по подразбиране нищо в GCC не се инициализира и даже няма изобщо да се линкне. Забрави за С-стандандартите дето ти казват, че static глобална променлива трябва да се нулира - ще се нулира само ако ТИ вкараш такъв линкерски скрипт и стартъп код и го извикаш този код и при това го извикаш навреме (преди да си разрешил прекъсванията).

Автор:  woody [ Сря Май 27, 2009 6:51 pm ]
Заглавие: 

Цецо, ела с лапнитоп на бира и ще дигнем мъглата. :drinkers:

Автор:  Zdrav [ Сря Май 27, 2009 9:02 pm ]
Заглавие: 

Цецо написа:
Бе това ми е горе долу ясно. Това което не ми е ясно е как се обяснява коя секция какво да я прави компилатора?

Т.е. къде се указва че точно bss трябва да се занули, а точно data да се копира от ROM-a? Гледам линкерския скрипт не виждам подобни "разяснения" в него.

.text ми е в FLASH-а, т.е там променливи няма какво да търсят.


Не че знам повече от woody и miro_atc ама гледам, че има леко разминаване между въпросите и обясненията. По подразбиране всяка глобална променлива която е инициализирана с нещо различно от нула отива в .data секцията. Всички останали глобални променливи отиват в .bss. Стартъп кода е този който решава коя секция да се занули и коя да се копира и коя да не се пипа. miro_atc го е обяснил по-горе механизма. В стартъп кода са видими адресите за начало и край на секциите. И с едно просто цикълче се извършва нулиране или копиране.
Като си направиш твоя секция гледай да не съвпадне името и с някоя от стандартните все пак. Щом като са променливи естествено че ще е в RAM. Може да си направиш и отделна изходна секция съдържаща само твоята входна секция.
Код:
.noinit_out_section(NOLOAD) :
   {
       *(.noinit)
     } >ram

В стартъп кода просто тази секция няма да се пипа. За да се прави там нещо с нея трябва да си драснеш собственоръчно код в стартъп файла.

Автор:  Цецо [ Сря Май 27, 2009 9:39 pm ]
Заглавие: 

Ясно. Утре ще пробвам.

А гледам че в моя линкерски скрипт (който съм присвоил), за .data и .bss са дефинирани като:

Цитат:
.data : ALIGN ( 8 )
{
__cs3_region_start_ram = .;
*(.cs3.region-head.ram)
KEEP(*(.jcr))
*(.got.plt) *(.got)
*(.shdata)
*(.data .data.* .gnu.linkonce.d.*)
. = ALIGN ( 8 );
*(.ram)
_edata = .;
} >ram AT>rom
.bss :
{
*(.shbss)
*(.bss .bss.* .gnu.linkonce.b.*)
*(COMMON)
. = ALIGN ( 8 );
*(.ram.b)
_end = .;
__end = .;
} >ram AT>rom


1. Що за животно е обект от тип COMMON?

2. >ram АТ>rom - що чини тоя "ром" тука?

@woody - Абе ти за какъв човек ме имаш? Ще дойда да пием бира и ще седна да с лаптопа да чопля подобни дивотии? Как пък не. Ай сиктир :) Бирата трябва да се уважава.

Автор:  woody [ Сря Май 27, 2009 10:03 pm ]
Заглавие: 

Тези готовите линкер скриптове са пълни с какви ли не неща за да угаждат на всички конфигурации и варианти.

1) COMMON са едни стари UNIX разбирания за неинициализираните променливи, че може да ги декларираш във всеки обектен файл и линкерът ги припокрива после. Отсвири го, това е мухъл от някога-бешило, дори ако можеш слагай "-fno-common" че да врещи при дефиниция на повече от едно място.

2) Това най-вероятно са run и load адресите. Run е виртуалния, адресът с който ще се резолвира дадено име. Load е къде физически ще бъде в имиджа.


Аз си пиша мои кратки линкер скриптове и CRT0 и си контролирам всичко. Не е трудно и помага за цялостния контрол. Така знам кои променливи са инициализирани, как, кога и т.н. ;)

Цената е малко RTFM първия път, после всичко си идва на мястото. Тези концепции са си същите от десетилетки и за щастие всичко е просто и логично.

Автор:  Цецо [ Вто Юни 16, 2009 12:35 pm ]
Заглавие: 

Следващ въпрос:

Как да напъхам константен стринг на специфичен адрес? Трябва ли да му правя отделна секция???? Не мога ли да го направя директно от кода с някакъв атрибут? Нещо от сорта на:

const char str[] = "bla-bla" __атрибут тури го ей тука.

Автор:  Zdrav [ Вто Юни 16, 2009 12:42 pm ]
Заглавие: 

Не, по същия начин със отделна секция се прави.

Автор:  woody [ Вто Юни 16, 2009 12:52 pm ]
Заглавие: 

С атрибут не може. Нямам под ръка за проба в момента, но пробвай да сложиш в линкер скрипта в правилната секция:

str = 0x1234;


Другите начини са с отделна секция, като аз лично бих сложил всички такива "фиксирани" данни в една структура.

Страница 1 от 3 Часовете са според зоната UTC + 2 часа [ DST ]
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/