почему примеры стартового программного обеспечения TI неправильно очищают сегмент BSS при компиляции с использованием CodeSourcery gcc

Я столкнулся с серьезными проблемами с бигльбоуном под управлением TI AM3359arm. Я использую исходный код для компиляции кода. Я попытался скомпилировать один из примеров, названный enet_lwip, который использует легковесный IP (lwip) для предоставления http-сервера.

Приложение вылетает в определенный момент. Путем отладки я обнаружил, что за это отвечает вот этот кусок кода:

unsigned int lwIPInit(LWIP_IF *lwipIf)
{

    struct ip_addr ip_addr;

    struct ip_addr net_mask;

    struct ip_addr gw_addr;

    unsigned int *ipAddrPtr;

    static unsigned int lwipInitFlag = 0;

    unsigned int ifNum;

    unsigned int temp;

    /* do lwip library init only once */
    if(0 == lwipInitFlag)
    {
        lwip_init();
    }

При этом происходит очень забавная вещь: можно было бы ожидать, что lwipInitFlag инициализируется значением 0 и, следовательно, функция вызывает lwip_init();

Что ж, этого не происходит даже при первом вызове функции lwIPInit. Причина этого в том, что для переменной lwipInitFlag не установлено значение 0.

Я хотел бы знать, почему это так. Если такая инициализация появляется в коде, компилятор должен сгенерировать последовательность для ее обнуления. Но, вероятно, поскольку ему предшествует модификатор static, он оставляет его «как есть». Почему?

lwipInitFlag находится в разделе компоновщика .bss, который указывает на память DDR. Как я могу гарантировать, что такие статические присваивания будут инициализированы?

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

Любая подсказка, как решить эту проблему?


Добавлю к этому дополнительную информацию: после ваших плодотворных советов я думаю, что у меня еще больше беспорядка в том, как это должно работать. Итак: Это правда, что я не называю/линкую crt*.o. С другой стороны, стартовая платформа TI содержит исходный код asm для инициализации, который ВЫПОЛНЯЕТ очистку BSS. Он делает это между адресами _bss_start и _bss_end.

При просмотре скрипта компоновщика все выглядит довольно обыденно:

SECTIONS
{

        . = 0x80000000;
        . = ALIGN(4);
        .startcode     :
        {
               *init.o      (.text)
        }

        . = ALIGN(4);
        .text      :
        {
                *(.text)
        }

        . = ALIGN(4);

        .data :
        {
                *(.data)
        }

        . = ALIGN(4);

        _bss_start = .;
        .bss :
        {
                *(.bss)
        }
        . = ALIGN(4);

        _bss_end = .;

        _stack = 0x87FFFFF8;
}

Таким образом, _bss_start — это адрес перед блоком BSS, а _bss_end — в конце блока. Проблема в том, какую карту генерирует Codesourcery.

Глядя на конец BSS в сгенерированном файле карты, я вижу это:

 COMMON         0x80088f0c      0x200 ../binary/armv7a/gcc/am335x/system_config/Debug/libsystem_config.a(interrupt.o)
                0x80088f0c                fnRAMVectors
                0x8008910c                . = ALIGN (0x4)
                0x8008910c                _bss_end = .
                0x87fffff8                _stack = 0x87fffff8
LOAD ../binary/armv7a/gcc/am335x/drivers/Debug/libdrivers.a
LOAD ../binary/armv7a/gcc/utils/Debug/libutils.a
LOAD ../binary/armv7a/gcc/am335x/beaglebone/platform/Debug/libplatform.a
LOAD ../binary/armv7a/gcc/am335x/system_config/Debug/libsystem_config.a
LOAD /opt/CodeSourcery/arm-none-eabi/lib//libc.a
LOAD /opt/CodeSourcery/lib/gcc/arm-none-eabi/4.5.2//libgcc.a
LOAD ../binary/armv7a/gcc/am335x/drivers/Debug/libdrivers.a
LOAD ../binary/armv7a/gcc/utils/Debug/libutils.a
LOAD ../binary/armv7a/gcc/am335x/beaglebone/platform/Debug/libplatform.a
LOAD ../binary/armv7a/gcc/am335x/system_config/Debug/libsystem_config.a
LOAD /opt/CodeSourcery/arm-none-eabi/lib//libc.a
LOAD /opt/CodeSourcery/lib/gcc/arm-none-eabi/4.5.2//libgcc.a
OUTPUT(Debug/bbdidt.out elf32-littlearm)

.bss.pageTable  0x8008c000     0x4000
 .bss.pageTable
                0x8008c000     0x4000 Debug/enetLwip.o

.bss.ram        0x80090000        0x4
 .bss.ram       0x80090000        0x4 Debug/lwiplib.o

Явно есть «что-то». Есть еще один раздел BSS после _bss_end, который содержит много вещей, которые должны быть обнулены, но не обнуляются, потому что обнуление заканчивается по адресу, заданному _bss_end.

Вероятная причина, по которой это делается именно так, заключается в том, что pageTable объявлена ​​статически и должна иметь граничный адрес 16 КБ:

static volatile unsigned int pageTable[4*1024] __attribute__((aligned(16*1024)));

Так как существует разрыв между последним объявленным компоновщиком сегментом BSS и pageTable, он помещает _bss_end в середину сегмента bss.

Теперь вопрос в том, как сообщить компоновщику (я использую для этого arm-none-eabi-ld), что _bss_end действительно должен быть в конце BSS, а не где-то посередине?

Большое спасибо


person David Belohrad    schedule 26.02.2013    source источник
comment
Кажется, что ВСЕ статические переменные не инициализированы. netif_add содержит аналогичное объявление статической переменной, и код также не выполняется. Итак, это сводится к общей проблеме: почему CodeSourcery v.4.5.2 не инициализирует статические переменные.   -  person David Belohrad    schedule 26.02.2013
comment
Что такое init.o? У вас есть источник? init.o традиционно отвечает за очистку BSS и настройку стека. Тот факт, что вы можете отлаживать что угодно, указывает на то, что у вас есть код запуска. Кроме того, как это запускается? Это записывается во флэш-память, поэтому вектор сброса переходит к первой части двоичного файла, или вы загружаетесь через загрузчик? Загрузчик может настроить стек и т. д.   -  person artless noise    schedule 27.02.2013
comment
Именно в этом суть. Init.o (отсюда исходный код init.S) содержит инициализацию стека и также очищает BSS. Однако он очищает BSS только между адресами _bss_start и _bss_end. Однако мой связанный код указывает _bss_end не в самом конце раздела BSS, а где-то посередине. Следовательно, код init.S инициализирует только часть раздела BSS. Он не инициализирует сегмент .data. И я заметил, что есть и некоторые переменные. Что касается данных, то это понятно. Потому что (где-то) ясно сказано, что этот раздел не настроен, а BSS должен быть настроен правильно   -  person David Belohrad    schedule 27.02.2013


Ответы (2)


Тот факт, что никакая статика не инициализируется, заставляет меня задаться вопросом: как вы получили код запуска? Этот код необходим для выполнения инициализации.

См. http://doc.ironwoodlabs.com/arm-arm-none-eabi/html/getting-started/sec-cs3-startup.html — раздел 5.2.3, в котором говорится:


Функция запуска C объявлена ​​следующим образом:

void __cs3_start_c (void) __attribute__ ((noreturn));

Эта функция выполняет следующие шаги:

  • Инициализируйте все .data-подобные разделы, скопировав их содержимое. Например, скрипты компоновщика профиля ПЗУ используют этот механизм для инициализации доступных для записи данных в ОЗУ из образа программы данных, доступного только для чтения.
  • ... и т.д

Похоже, вам может не хватать этого кода.

person Martin Thompson    schedule 26.02.2013
comment
Да, инициализация статических данных не входит в обязанности компилятора. Обычно об этом позаботятся файлы crt*.o. Если вы используете -nostdlib, вы должны указать свой собственный. Раздел .bss нужно только инициализировать нулями. - person ams; 26.02.2013

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

SECTIONS
{

        . = 0x80000000;
        . = ALIGN(4);
        .startcode     :
        {
               *init.o      (.text)
        }

        . = ALIGN(4);
        .text      :
        {
                *(.text)
        }

        . = ALIGN(4);

        .data :
        {
                *(.data)
        }

        . = ALIGN(4);

        _bss_start = .;
        .bss :
        {
                *(.bss)
                *(COMMON)
                *(.bss.*)
        }
        . = ALIGN(4);

        _bss_end = .;

        _stack = 0x87FFFFF8;
}

Поэтому я фактически заставил компоновщика включить в сегмент BSS все подсегменты, начинающиеся с COMMON и .bss.

Это, по-видимому, решает проблему, поскольку компоновщик теперь генерирует правильную карту, так что он помещает указатель _bss_end действительно на последний адрес раздела BSS.

Итак, мой софт теперь работает правильно, запускает PHY. Я все еще не могу получить DHCP, но я предполагаю, что это проблема неинициализированного сегмента .data. LwIP использует в некоторых местах статические назначения как

static u32_t xid = 0xABCD0000;

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

Спасибо всем

person David Belohrad    schedule 27.02.2013
comment
+1 Теперь, когда вы объяснили ситуацию в комментарии к вопросу, становится ясно, что это исправляет *(.bss.*) в разделе .bss. Можете ли вы отредактировать свой вопрос, чтобы сделать его более кратким. У вас есть другие проблемы с файлом компоновщика, такие как .rodata, которые вы можете в конечном итоге столкнуться. Кроме того, вы, вероятно, можете сэкономить место, используя псевдоним pageTable с кодом init.o. - person artless noise; 27.02.2013