Предыстория

В 2011 году было написан некоторый код для микроконтроллера ARM Cortex-M3 производства NXP из серии LPC17xx. В ходе работы потребовался скрипт для компоновщика GNU ld. В качестве образца был взят первый попавшийся под руку (возможно, из тулчейна CodeSourcery). В процессе изменения найденного скрипта он был дополнен и к указанным в нём параметрам были, по возможности, добавлены комментарии. Секции и параметры, оказавшиеся излишними для ARM Cortex-M3, тем не менее не удалялись -- в целях пущей наглядности скрипта. Откомментированный скрипт sections.ld приведён ниже.

NXP LPC17xx позволяет запускать код из оперативной памяти. Для использования этой возможности в sections.ld секции для размещения кода и данных указаны псевдонимами. Реальные имена секций и соответствующая карта памяти определены в сопутствующих скриптах компоновщика text2flash_memory_map.ld и text2ram_memory_map.ld.

Для использования скрипта следует передать gcc следующие параметры:

Лицензия

Скрипт sections.ld, скорее всего, лицензирован под GPLv2 .

Скрипты text2flash_memory_map.ld и text2ram_memory_map.ld, а также русскоязычные комментарии в sections.ld лицензированы под CC0 .

Скрипты

text2flash_memory_map.ld


MEMORY
{
  RomFlash (rx) : ORIGIN = 0x00000000, LENGTH = 256K

  RamLocal (wx) : ORIGIN = 0x10000000, LENGTH = 32K
  RamAHB_0 (wx) : ORIGIN = 0x2007C000, LENGTH = 16K
  RamAHB_1 (wx) : ORIGIN = 0x20080000, LENGTH = 16K
}

REGION_ALIAS ("REGION_TEXT",     RomFlash);
REGION_ALIAS ("REGION_DATALOAD", RomFlash);
      

text2ram_memory_map.ld


MEMORY
{
  RomFlash (rx) : ORIGIN = 0x00000000, LENGTH = 256K

  RamLocal (wx) : ORIGIN = 0x10000000, LENGTH = 32K
  RamAHB_0 (wx) : ORIGIN = 0x2007C000, LENGTH = 16K
  RamAHB_1 (wx) : ORIGIN = 0x20080000, LENGTH = 16K
}

REGION_ALIAS ("REGION_TEXT",     RamLocal);
REGION_ALIAS ("REGION_DATALOAD", RamLocal);
      

sections.ld


/* Для кода (потока инструкций) указано выравнивание по слову 
 * (см. ARMv7-M ARM A3.1, A3.2.1, A4.1, A5.1; описание работы Prefetching Unit).
 *
 * Для данных указано выравнивание по слову (см. ARM A3.2.1, A3.5.7).
 *
 * Адрес начала стека должен быть кратен 8-ми (см. ARM B1.5.7, C.1.1).
 *
 * (В архитектуре ARM-v7M размер слова (word) равен 4-ём байтам [32-м битам]).
 *
 * Ссылки (здесь и далее):
 * [ARM]   ARMv7-M Architecture Reference Manual
 *         (по содержанию версии issue "Derrata 2010_Q3", November 2010)
 * [EHABI] Exception Handling ABI for the ARM Architecture
 *         (по содержанию версии 28th October 2009)
 *
 * Описание GCC vague linkage (использующего, при необходимости, секции с
 * именами gnu.linkonce.*) см. в GCC Manual (гл. Vague linkage).
 */

OUTPUT_FORMAT ("elf32-littlearm", "elf32-bigarm", "elf32-littlearm")
OUTPUT_ARCH   ("arm")

/* CS3 - CodeSourcery Common Startup Code Sequence */

EXTERN (__cs3_reset_cortex_m)
ENTRY  (__cs3_reset_cortex_m)

EXTERN  (__cs3_interrupt_vector_cortex_m)

PROVIDE (__cs3_stack = ORIGIN(RamLocal) + LENGTH(RamLocal));
EXTERN  (__cs3_stack_size)

PROVIDE (__cs3_heap_start = _end);
EXTERN  ( __cs3_heap_end)
PROVIDE (__cs3_heap_end = __cs3_stack - __cs3_stack_size);

SECTIONS
{
  .text ORIGIN(REGION_TEXT):
  {
    __cs3_interrupt_vector = __cs3_interrupt_vector_cortex_m;
    KEEP(*(.cs3.interrupt_vector))
    ASSERT (. != __cs3_interrupt_vector, "No interrupt vector");

    __cs3_reset = __cs3_reset_cortex_m;
    KEEP(*(.cs3.reset))
    ASSERT (. != __cs3_reset, "No reset code");

    /* Микроконтроллеры NXP LPC17xx предоставляют механизм Code Read
     * Protection (CRP), включающий различные уровни защиты программного кода
     * путём ограничения или полного отключения доступа к FLASH-памяти чипа
     * и/или отключения ISP (перепрограммирования через встроенный
     * bootloader).
     *
     * Механизм CRP конфигурируется путём записи одной из "магических"
     * констант (размером в слово) во FLASH-память, по адресу 0x000002FC.
     *
     * Константа, помещённая в секцию '.lpc17xx.crp', сконфигурирует CRP. По
     * умолчанию, по адресу 0x000002FC будет записан 0 (что не включит никакой
     * из уровней защиты).
     */
    . = 0x2FC;
    KEEP (*(.lpc17xx.crp))
    FILL (0x00)

    /* 0x2FC + 4 = 0x300 (4 байта -- размер константы, конфигурирующей CRP) */
    . = 0x300;

    /* .text   - основная секция программного кода.
     * .text.* - отдельные секции для функций, создаваемые GCC при включении
     *           опции -ffunction-sections
     * .gnu.linkonce.t.* - секции программного кода, используемые GCC для
     *                     vague linkage, если либо формат целевого объектного
     *                     файла отличен от ELF, либо GCC был собран с
     *                     использованием binutils, не поддерживающих секции
     *                     ELF типа "COMDAT group".
     */ 
    *(.text .text.* .gnu.linkonce.t.*)

    /* Procedure Linkage Table (PLT) -- используется при динамической
     * компоновке (dynamic linkage) для обращения к функциям разделяемого
     * объекта (shared object).
     */
    *(.plt)

    /* Предупреждения при компоновке (секции, специфические для GNU ld).
     *
     * При включении в компоновку объектного файла с секцией '.gnu.warning',
     * GNU ld безусловно выведет содержимое этой секции в виде текста
     * предупреждения (warning).
     *
     * При наличии секции '.gnu.warning.SYMBOL' GNU ld выведет содержимое этой
     * секции в виде предупреждения (warning), если и когда будет обнаружена
     * неопределённая ссылка (undefined reference) на 'SYMBOL'. Предупреждение
     * будет выведено при встрече неопределённого символа с указанным именем в
     * объектном файле. В отличии от обычного предупреждения о неопределённом
     * символе, это не будет показано при встрече записи таблицы
     * переразмещений (relocation entry).
     *
     * Содержимое секций не включается в выходной файл.
     *
     * См. http://www.airs.com/blog/archives/54
     *     (Ian Lance Tailor - Linkers part 17, Warning symbols)
     */
    *(.gnu.warning .gnu.warning.*)

    /* Секции, создаваемые GNU ld при компоновке для семейства ARM (независимо
     * от конкретной целевой архитектуры).
     * См. руководство по GNU linker, глава 4.4 ("ld and ARM family")
     *
     * .glue_7  - секция для программного "клея" между ARM- и Thumb-кодом,
     *            позволяющего вызывать функций в Thumb-коде из ARM-кода
     * .glue_7t - секция для программного "клея" между ARM- и Thumb-кодом,
     *            позволяющего вызывать функций в ARM-коде из Thumb-кода
     * .vfp11_veneer - секция для программного кода, осуществляющего обход
     *                 ошибки, присутствующей в некоторых сопроцессорах VFP11
     *                 (код генерируется компоновщиком; генерация кода
     *                 конфигурируется отдельной опцией компоновщика)
     */
    *(.glue_7) *(.glue_7t) *(.vfp11_veneer)

    /* .ARM.extab - таблица обработчиков исключительных ситуаций
     *              (exception-handling table)
     *
     * Присутствие секции .ARM.extab диктуется стандартом EHABI.
     *
     * Секция таблицы обработчиков именуется '.ARM.extab' с опциональным
     * суффиксом, состоящим из любых символов (EHABI 4.4.1).
     *
     * .gnu.linkonce.armextab.* - секции, используемые GCC для vague linkage,
     *                            если либо формат целевого объектного файла
     *                            отличен от ELF, либо GCC был собран с
     *                            использованием binutils, не поддерживающих
     *                            секции ELF типа "COMDAT group".
     */
    *(.ARM.extab* .gnu.linkonce.armextab.*)

    /* Область языко-зависимых данных (language-specific data area) для
     * каркасного кода обработки исключений, генерируемого компилятором GCC
     * (сами данные также генерируются компилятором GCC).
     *
     * Секция '.gcc_except_table' тесно связана с секцией
     * '.eh_frame'/'.ARM.extab'.
     */
    *(.gcc_except_table)
  } >REGION_TEXT

  /* .init - секция кода, выполняющегося в ходе инициализации программы (перед
   *         вызовом входной точки программы, т.е. перед вызовом функции
   *         main).
   *
   * NB! Вместо помещения инструкций кода в секцию '.init' пользователю
   * рекомендуется использовать современный способ инициализации и добавлять
   * указатели на функции инициализации в секцию '.init_array' (при помощи
   * соответствующих средств компилятора).
   *
   * Специальная секция с именем '.init' и вышеназванным назначением описана в
   * System V gABI. Вызов пользовательского кода, помещённого в секцию
   * '.init', возлагается на библиотеку времени выполнения (runtime library)
   * -- например, на C runtime. Если в библиотеке не предусмотрен такой вызов,
   * то пользовательский код в секции '.init' не выполнится.
   *
   * В настоящее время секция '.init' используется, в основном, исключительно
   * библиотекой C времени выполнения (newlib, glibc) для вызова служебных
   * функций инициализации. Это достигается компоновкой со следующими
   * объектными файлами из состава C runtime:
   * - crti.o -- содержит пролог функции _init, помещаемый в начало секции
   *             '.init'
   * - crtn.o -- содержит эпилог функции _init, помещаемый в конец секции
   *             '.init'
   * - crtbegin.o, crtend.o -- содержат служебные функции, вызов которых
   *                           размещается в секции '.init'
   *
   * Т.о. служебные функции инициализации (наряду с пользовательским кодом,
   * размещённым в секции '.init') выполняются при вызове функции _init.
   *
   * См. руководство GCC Internals, гл. How initialization functions are
   * handled
   */
  .init : ALIGN(4)
  {
    KEEP(*(.init))
  } >REGION_TEXT

  /* .fini - секция кода, выполняющегося при завершении программы (после
   *         возврата из входной точки программы, т.е. после возврата из
   *         функции main).
   *
   * NB! Вместо помещения инструкций кода в секцию '.fini' пользователю
   * рекомендуется использовать современный способ терминации и добавлять
   * указатели на функции терминации в секцию '.fini_array' (при помощи
   * соответствующих средств компилятора).
   *
   * Специальная секция с именем '.fini' и вышеназванным назначением описана в
   * System V gABI. Вызов пользовательского кода, помещённого в секцию
   * '.fini', возлагается на библиотеку времени выполнения (runtime library)
   * -- например, на C runtime. Если в библиотеке не предусмотрен такой вызов,
   * то пользовательский код в секции '.fini' не выполнится.
   *
   * В настоящее время секция '.fini' используется, в основном, исключительно
   * библиотекой C времени выполнения (newlib, glibc) для вызова служебных
   * функций терминации. Это достигается компоновкой со следующими объектными
   * файлами из состава C runtime:
   * - crti.o -- содержит пролог функции _fini, помещаемый в начало секции
   *             '.fini'
   * - crtn.o -- содержит эпилог функции _fini, помещаемый в конец секции
   *             '.fini'
   * - crtbegin.o, crtend.o -- содержат служебные функции, вызов которых
   *                           размещается в секции '.fini'
   *
   * Т.о. служебные функции терминации (наряду с пользовательским кодом,
   * размещённым в секции '.fini') выполняются при вызове функции _fini.
   *
   * См. руководство GCC Internals, гл. How initialization functions are
   * handled
   */
  .fini : ALIGN(4)
  {
    KEEP(*(.fini))
  } >REGION_TEXT

  /* .eh_frame_hdr - заголовок секции '.eh_frame', содержащий, в том числе,
   *                 адрес начала секции '.eh_frame' и (опционально) индекс
   *                 записей в таблице данных, расположенной в '.eh_frame'.
   *
   * NB! Современные версии GCC (>= 4.1) для целевой платформы ARM EABI вместо
   * '.eh_frame_hdr' и '.eh_frame' используют секции '.ARM.exidx' и
   * '.ARM.extab', определённые стандартом ARM EHABI. Но GCC некоторых ранних
   * подверсий 4.4.x/4.5.x создаёт [ненужную] секцию '.eh_frame' при
   * определённых обстоятельствах (GCC bug #40521).
   *
   * Информация в секции '.eh_frame_hdr' (равно как и сама секция)
   * генерируется компоновщиком GNU ld при включении опции '--eh-frame-hdr'.
   * Сгенерированная секция размещается в отдельном сегменте ELF-файла с типом
   * PT_GNU_EH_FRAME.
   *
   * Для использования '.eh_frame_hdr' исходный код компилятора из состава GCC
   * должен быть соответствующим образом сконфигурирован перед постройкой GCC.
   * Для актуальных (на момент написания комментария) версий GCC (<= 4.6.0)
   * конфигурация производится автоматически с условием, что:
   * 1. используемый компоновщик поддерживает создание секции '.eh_frame_hdr',
   * 2. стандартная библиотека C для целевой платформы предоставляет функцию
   *    dl_iterate_phdr,
   * 3. конфигуратор GCC (gcc/configure.ac) сумел определить первое и второе
   *    обстоятельства.
   *
   * Будучи сконфигурирован с выполнением этих условий, gcc автоматически
   * компонует с включением опции '--eh-frame-hdr' и использует информацию
   * из '.eh_frame_hdr' во время выполнения программы.
   *
   * В ином случае, служебная процедура поиска в таблице '.eh_frame',
   * генерируемая компилятором GCC, будет выполнять поиск необходимой
   * информации без заранее созданного индекса, путём автоматической
   * регистрации записей из '.eh_frame' во время запуска программы и
   * последующего обхода зарегистрированных записей.
   *
   * См. http://www.airs.com/blog/archives/166
   *     (Ian Lance Taylor - GCC exception frames)
   */ 
  .eh_frame_hdr : ALIGN(4)
  {
    /* Информация в секции '.eh_frame_hdr' (равно как и сама секция)
     * генерируется компоновщиком GNU ld при включении опции '--eh-frame-hdr'.
     */
    KEEP(*(.eh_frame_hdr))
  } >REGION_TEXT

  /* .eh_frame - таблицы с данными, необходимыми для раскручивания стека при
   *             обработке исключительных ситуаций (tables that describe how
   *             to unwind the stack on exception handling).
   *
   * NB! Современные версии GCC (>= 4.1) для целевой платформы ARM EABI вместо
   * '.eh_frame' используют секцию '.ARM.extab', определённую стандартом ARM
   * EHABI. Но GCC некоторых ранних подверсий 4.4.x/4.5.x создаёт [ненужную]
   * секцию '.eh_frame' при определённых обстоятельствах (GCC bug #40521).
   */
  .eh_frame : ALIGN(4)
  {
    /* Таблицы с данными, необходимыми для раскручивания стека при обработке
     * исключительных ситуаций, генерируются компилятором GCC, размещающим их
     * в секции с предопределённым именем '.eh_frame'.
     */
    KEEP(*(.eh_frame))
  } >REGION_TEXT

  /* .ARM.exidx - индекс записей в таблице обработчиков исключительных
   *              ситуаций (index table for exception-handling table entries).
   *
   * Присутствие секции .ARM.exidx диктуется стандартом EHABI.
   *
   * Согласно ARM EHABI 4.4.1 у входных секций .ARM.exidx установлен флаг
   * атрибута SHF_LINK_ORDER. Если включить входные секции .ARM.exidx,
   * например, в выходную секцию .text, то компоновщик (GNU ld) аварийно
   * завершится с ошибкой: ".text has both ordered [`.ARM.exidx.<...>' in
   * <...>] and unordered [`.text' in <...>] sections". Поэтому входные секции
   * .ARM.exidx выделены в отдельную выходную секцию.
   */
  .ARM.exidx : ALIGN(4)
  {
    /* Символ __exidx_start используется служебной библиотекой libgcc */
    PROVIDE_HIDDEN(__exidx_start = .);

    /* Секция индекса записей именуется '.ARM.exidx' с опциональным суффиксом,
     * состоящим из любых символов (EHABI 4.4.1).
     *
     * .gnu.linkonce.armexidx.* - секции, используемые GCC для vague linkage,
     *                            если либо формат целевого объектного файла
     *                            отличен от ELF, либо GCC был собран с
     *                            использованием binutils, не поддерживающих
     *                            секции ELF типа "COMDAT group".
     */
    *(.ARM.exidx* .gnu.linkonce.armexidx.*)

    /* Символ __exidx_end используется служебной библиотекой libgcc. */
    PROVIDE_HIDDEN(__exidx_end = .);
  } >REGION_TEXT

  .rodata : ALIGN(4)
  {
    /* .rodata   - секция константных данных (данных только для чтения, не
     *             изменяющихся в ходе работы программы)
     * .rodata.* - отдельные секции для константных данных, создаваемые GCC
     *             при включении опции -fdata-sections
     * .gnu.linkonce.r.* - секции константных данных, используемые GCC для
     *                     vague linkage, если либо формат целевого объектного
     *                     файла отличен от ELF, либо GCC был собран с
     *                     использованием binutils, не поддерживающих секции
     *                     ELF типа "COMDAT group".
     */ 
    *(.rodata .rodata.* .gnu.linkonce.r.*)

    /* .preinit_array - секция для указателей на функции пре-инициализации,
     *                  вызываемых перед любыми другими функциями
     *                  инициализации (перед выполнением кода из секции
     *                  '.init' и вызовами функций из секции '.init_array').
     *
     * Специальная секция с именем '.preinit_array' и вышеназванным
     * назначением описана в System V gABI. Вызов функций, указатели на
     * которые содержатся в секции '.preinit_array', возлагается на библиотеку
     * времени выполнения (runtime library) -- например, на C runtime. Если в
     * библиотеке не предусмотрен такой вызов, то функции из секции
     * '.preinit_array' не будут вызваны.
     *
     * Секция '.preinit_array' предназначена для пре-инициализации
     * исполняемого файла (executable), выполняемой перед инициализацией
     * динамически скомпонованных с ним разделяемых объектов (shared objects).
     *
     * Символы __preinit_array_start и __preinit_array_end используются
     * библиотекой C времени выполнения (newlib, glibc).
     */
    . = ALIGN(4);
    PROVIDE_HIDDEN(__preinit_array_start = .);
    KEEP (*(.preinit_array))
    PROVIDE_HIDDEN(__preinit_array_end = .);

    /* .init_array - секции с указателями на функции инициализации,
     *               выполняющиеся перед вызовом входной точки программы, т.е.
     *               перед вызовом функции main.
     *
     * Специальная секция с именем '.init_array' и вышеназванным назначением
     * описана в System V gABI. Вызов функций, указатели на которые содержатся в
     * секции '.init_array', возлагается на библиотеку времени выполнения
     * (runtime library) -- например, на C runtime. Если в библиотеке не
     * предусмотрен такой вызов, то функции из секции '.init_array' не будут
     * вызваны.
     *
     * GCC использует секцию '.init_array' для обеспечения вызова статических
     * конструкторов: функций, объявленных с __attribute__((constructor)).
     *
     * Для статических конструкторов с объявленным приоритетом PRIORITY,
     * используются секции с именем '.init_array.PRIORITY'
     *
     * Также GCC использует секцию '.init_array' для вызова конструкторов
     * статических объектов C++.
     *
     * Символы __init_array_start и __init_array_end используются библиотекой
     * C времени выполнения (newlib, glibc).
     */
    . = ALIGN(4);
    PROVIDE_HIDDEN(__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array))
    PROVIDE_HIDDEN(__init_array_end = .);

    /* .fini_array - секции с указателями на функции терминации,
     *               выполняющиеся по завершению программы (после возврата из
     *               входной точки программы, т.е. из функции main).
     *
     * Специальная секция с именем '.fini_array' и вышеназванным назначением
     * описана в System V gABI. Вызов функций, указатели на которые содержатся в
     * секции '.fini_array', возлагается на библиотеку времени выполнения
     * (runtime library) -- например, на C runtime. Если в библиотеке не
     * предусмотрен такой вызов, то функции из секции '.fini_array' не будут
     * вызваны.
     *
     * GCC использует секцию '.fini_array' для обеспечения вызова статических
     * деструкторов: функций, объявленных с __attribute__((destructor)).
     *
     * Для статических деструкторов с объявленным приоритетом PRIORITY,
     * используются секции с именем '.fini_array.PRIORITY'
     *
     * Символы __fini_array_start и __fini_array_end используются библиотекой
     * C времени выполнения (newlib, glibc).
     */
    . = ALIGN(4);
    PROVIDE_HIDDEN(__fini_array_start = .);
    KEEP (*(.fini_array))
    KEEP (*(SORT(.fini_array.*)))
    PROVIDE_HIDDEN(__fini_array_end = .);

    /* .ctors - секции с указателями на функции инициализации, выполняющиеся
     *          перед вызовом входной точки программы, т.е. перед вызовом
     *          функции main.
     *
     * NB! Секции '.ctors' выполняют ту же функцию, что и секции
     * '.init_array', но являются расширением GCC, созданным до введения в
     * стандарт System V gABI секции '.init_array'. Начиная с версии 4.7 GCC
     * вместо секций '.ctors' использует (по возможности) секции
     * '.init_array'.
     *
     * См. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46770
     */
    . = ALIGN(4);
    PROVIDE_HIDDEN(__ctors_start__ = .);
    KEEP (*crtbegin.o(.ctors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
    KEEP (*(SORT(.ctors.*)))
    KEEP (*crtend.o(.ctors))
    PROVIDE_HIDDEN(__ctors_end__ = .);

    /* .dtors - секции с указателями на функции терминации, выполняющиеся по
     *          завершению программы (после возврата из входной точки
     *          программы, т.е. из функции main).
     *
     * NB! Секции '.dtors' выполняют ту же функцию, что и секции
     * '.fini_array', но являются расширением GCC, созданным до введения в
     * стандарт System V gABI секции '.fini_array'. Начиная с версии 4.7 GCC
     * вместо секций '.dtors' использует (по возможности) секции
     * '.fini_array'.
     *
     * См. http://gcc.gnu.org/bugzilla/show_bug.cgi?id=46770
     */
    . = ALIGN(4);
    PROVIDE_HIDDEN(__dtors_start__ = .);
    KEEP (*crtbegin.o(.dtors))
    KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
    KEEP (*(SORT(.dtors.*)))
    KEEP (*crtend.o(.dtors))
    PROVIDE_HIDDEN(__dtors_end__ = .);

    . = ALIGN(4);
    _etext = .;
    __etext = .;
  } >REGION_TEXT

  .data : ALIGN(4)
  {
    __sdata = .;

    KEEP(*(.jcr))
    *(.got.plt) *(.got)
    *(.shdata)
    *(.data .data.* .gnu.linkonce.d.*)

    . = ALIGN (4);
    __edata = .;
  } >RamLocal AT>REGION_DATALOAD

  .bss : ALIGN(4)
  {
    __sbss = .;
    __bss_start__ = __sbss;

    *(.shbss)
    *(.bss .bss.* gnu.linkonce.b.*)

    /* Common symbols are used for uninitialized global variables in C */
    *(COMMON)

    . = ALIGN (4);
    __ebss = .;
    __bss_end__ = __ebss;
  } >RamLocal

  _end = .;
  __end = .;

  .heap (NOLOAD) :
  {
    *(.heap)
  } >RamLocal

  .stack (__cs3_stack - __cs3_stack_size) (NOLOAD):
  {
    *(.stack)
  } >RamLocal

  /* Stabs debugging sections.  */
  .stab          0 : { *(.stab) }
  .stabstr       0 : { *(.stabstr) }
  .stab.excl     0 : { *(.stab.excl) }
  .stab.exclstr  0 : { *(.stab.exclstr) }
  .stab.index    0 : { *(.stab.index) }
  .stab.indexstr 0 : { *(.stab.indexstr) }
  .comment       0 : { *(.comment) }
  /* DWARF debug sections.
     Symbols in the DWARF debugging sections are relative to the beginning
     of the section so we begin them at 0.  */
  /* DWARF 1 */
  .debug          0 : { *(.debug) }
  .line           0 : { *(.line) }
  /* GNU DWARF 1 extensions */
  .debug_srcinfo  0 : { *(.debug_srcinfo) }
  .debug_sfnames  0 : { *(.debug_sfnames) }
  /* DWARF 1.1 and DWARF 2 */
  .debug_aranges  0 : { *(.debug_aranges) }
  .debug_pubnames 0 : { *(.debug_pubnames) }
  /* DWARF 2 */
  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
  .debug_abbrev   0 : { *(.debug_abbrev) }
  .debug_line     0 : { *(.debug_line) }
  .debug_frame    0 : { *(.debug_frame) }
  .debug_str      0 : { *(.debug_str) }
  .debug_loc      0 : { *(.debug_loc) }
  .debug_macinfo  0 : { *(.debug_macinfo) }
  /* SGI/MIPS DWARF 2 extensions */
  .debug_weaknames 0 : { *(.debug_weaknames) }
  .debug_funcnames 0 : { *(.debug_funcnames) }
  .debug_typenames 0 : { *(.debug_typenames) }
  .debug_varnames  0 : { *(.debug_varnames) }
  /* DWARF 3 */
  .debug_pubtypes 0 : { *(.debug_pubtypes) }
  .debug_ranges   0 : { *(.debug_ranges) }
  /* DWARF 4 */
  .debug_types    0 : { KEEP(*(.debug_types*)) *(.gnu.linkonce.wt.*) }
  /* DWARF 5 */
  .debug_macro    0 : { *(.debug_macro) }

  .ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) }
  .note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
  /DISCARD/ : { *(.note.GNU-stack) *(.gnu_debuglink) *(.gnu.lto_*) }
}