Подтвердить что ты не робот

Что означает KEEP в компоновщике script?

Руководство LD не объясняет, что делает команда KEEP. Ниже приведен фрагмент от стороннего компоновщика script, который имеет KEEP. Что делает команда KEEP в ld?

SECTIONS
{  
    .text :
    {
        . = ALIGN(4);
        _text = .;
        PROVIDE(stext = .);
        KEEP(*(.isr_vector))
        KEEP(*(.init))
        *(.text .text.*)        
        *(.rodata .rodata.*)        
        *(.gnu.linkonce.t.*)
        *(.glue_7)
        *(.glue_7t)
        *(.gcc_except_table)
        *(.gnu.linkonce.r.*)
        . = ALIGN(4);
        _etext = .;
        _sidata = _etext;
        PROVIDE(etext = .);   
            _fini = . ;
                *(.fini)

    } >flash
4b9b3361

Ответ 1

Afaik LD хранит символы в разделе, даже если на символы не ссылаются. (--gc сечения).

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


(Для Сабунку ниже)

Дерево зависимостей:

Если вы удаляете неиспользуемый код, вы анализируете код и отмечаете все доступные разделы (код + глобальные переменные + константы).

Итак, вы выбираете раздел, отмечаете его как "использованный" и видите, какой другой раздел он ссылается, затем вы отмечаете этот раздел как "использованный" и проверяете, что они ссылаются и т.д.

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

Поскольку раздел может ссылаться на несколько других разделов (например, одна процедура вызывает три разных других), если вы нарисуете результат, вы получите дерево.

Корни:

Приведенный выше принцип оставляет нам проблему: что такое "первый" раздел, который всегда используется? Первый node (корень) дерева, так сказать? Это то, что делает "keep()", он сообщает компоновщику, какие разделы (если доступны) являются первыми, на которые нужно смотреть. Как следствие, они всегда связаны между собой.

Обычно это разделы, вызываемые из загрузчика программ для выполнения задач, связанных с динамической компоновкой (могут быть необязательными и зависимыми от OS/fileformat) и точкой входа в программу.

Ответ 2

Минимальный пример Linux IA-32, иллюстрирующий его использование

main.S

.section .text
.global _start
_start:
    /* Dummy access so that after will be referenced and kept. */
    mov after, %eax
    /*mov keep, %eax*/

    /* Exit system call. */
    mov $1, %eax

    /* Take the exit status 4 bytes after before. */
    mov $4, %ebx
    mov before(%ebx), %ebx

    int $0x80

.section .before
    before: .long 0
/* TODO why is the '"a"' required? */
.section .keep, "a"
    keep: .long 1
.section .after
    after: .long 2

link.ld

ENTRY(_start)
SECTIONS
{
    . = 0x400000;
    .text :
    {
        *(.text)
        *(.before)
        KEEP(*(.keep));
        *(.keep)
        *(.after)
    }
}

Скомпилируйте и запустите:

as --32 -o main.o main.S
ld --gc-sections -m elf_i386 -o main.out -T link.ld main.o
./main.out
echo $?

Выход:

1

Если мы закомментируем строку KEEP, получится:

2

Если мы тоже:

  • добавить манекен mov keep, %eax
  • удалить --gc-sections

Выход возвращается к 1.

Протестировано на Ubuntu 14.04, Binutils 2.25.

Объяснение

Нет ссылки на символ keep и, следовательно, на содержащий его раздел .keep.

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

Так как мы добавляем 4 к адресу before, если раздел keep отсутствует, то статус выхода будет 2, который присутствует в следующем разделе .after.

TODO: ничего не произойдет, если мы удалим "a" из .keep, что делает его размещаемым. Я не понимаю, почему это так: этот раздел будет помещен в сегмент .text, который из-за этого волшебного имени будет выделяться.

Ответ 3

Заставить компоновщика сохранить некоторые определенные разделы

SECTIONS 
{
....
....

*(.rodata .rodata.*)

KEEP(*(SORT(.scattered_array*)));
}