Моя проблема следующая:

Я пытаюсь написать встроенное приложение, которое должно иметь собственный сценарий компоновщика (с использованием компилятора / компоновщика arm-none-eabi-gcc).

Встроенный загрузчик загружает двоичный файл и запускается с адреса 0x8000, поэтому мне нужен специальный скрипт компоновщика, который позволяет мне поместить желаемую функцию запуска в этот адрес. Код скрипта следующий:

MEMORY
{
    ram : ORIGIN = 0x8000, LENGTH = 0x1000
}

SECTIONS
{
    .start : { *(.start) } > ram
    .text : { *(.text*) } > ram
    .bss : { *(.bss*) } > ram
}

Имея это то, что я хочу сделать сейчас, это иметь функцию, которая будет вставлена ​​в раздел .start так, чтобы она находилась в начале 0x8000. Для этого в моей библиотеке я использую следующую функцию:

__attribute__((section(".start"))) void notmain() {
    main();
}

Кажется, это работает нормально, но позже я свяжу эту библиотеку с функцией notmain с проектом, который определяет функцию main(). В процессе связывания я вижу, что раздел .start больше не существует, и символ notmain полностью отсутствует. Когда я перемещаю функцию notmain из библиотеки (в проект), все в порядке.

Насколько я понимаю, этот компоновщик видит, что раздел .start вообще не используется в моем приложении, что заставляет его пропускать все разделы. Я уже пробовал добавить несколько атрибутов в функцию notmain, например (__attribute__((used)) __attribute__((externally_visible))), но это тоже не сработало (notmain все еще отсутствует в окончательном двоичном файле).

Исходный код CMake следующий:

** Проект **

project(AutomaticsControlExample)

enable_language(ASM)

set(CMAKE_CXX_STANDARD 14)

set(SOURCES main.cpp PID.hpp)
set(DEPENDENCIES RPIRuntime PiOS)

add_executable(${PROJECT_NAME} ${SOURCES})
target_link_libraries(${PROJECT_NAME} ${DEPENDENCIES})
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
        COMMAND ${CMAKE_OBJDUMP} -D ${PROJECT_NAME}
        COMMAND ${CMAKE_OBJDUMP} -D ${PROJECT_NAME} > ${PROJECT_NAME}.list
        COMMAND ${CMAKE_OBJCOPY} ${PROJECT_NAME} -O binary ${PROJECT_NAME}.bin
        COMMAND ${CMAKE_OBJCOPY} ${PROJECT_NAME} -O ihex ${PROJECT_NAME}.hex)

** Библиотека **

project(RPIRuntime)

enable_language(ASM)

set(CMAKE_CXX_STANDARD 14)

set(LINKER_SCRIPT memmap)
set(LINKER_FLAGS "-T ${CMAKE_CURRENT_SOURCE_DIR}/${LINKER_SCRIPT}")


set(SOURCES
        notmain.cpp
        assert.cpp)

add_library(${PROJECT_NAME} STATIC ${SOURCES})
target_link_libraries(${PROJECT_NAME} ${LINKER_FLAGS})

Мой вопрос: есть ли способ запретить компоновщику пропускать ссылку на раздел .start?

2
DawidPi 21 Июн 2017 в 15:02

1 ответ

Лучший ответ

Как вы знаете, статическая библиотека - это ar архив объектных файлов.

Предположим, что libfoobar.a содержит только foo.o и bar.o. Связь:

g++ -o prog a.o foo.o bar.o     # A

не то же самое, что ссылка:

g++ -o prog a.o -lfoobar.   # B

Компоновщик безоговорочно использует каждый объектный файл в последовательности связывания, поэтому в случае A он связывает a.o, foo.o, bar.o в prog.

Компоновщик не безоговорочно потребляет каждый объектный файл, который является членом статическая библиотека в последовательности связывания. Статическая библиотека - это способ предложить компоновщик - набор объектных файлов, из которых можно выбрать нужные.

Предположим, что a.o вызывает функцию foo, которая определена в foo.o, и что a.o не ссылается ни на что, определенное в bar.o.

В этом случае компоновщик безоговорочно связывает a.o с prog, после чего prog содержит неопределенную ссылку на foo, для которой компоновщику требуется определение. Затем он достигает libfoobar.a и проверяет архив (по его индексу, обычно), чтобы узнать, определяет ли какой-либо член архива foo. Он обнаруживает, что foo.o делает так. Таким образом, он извлекает foo.o из архива и связывает его. Не нуждается в определениях для любых символов, определенных в bar.o, поэтому bar.o не добавляется к связи. В linkage B точно такой же, как:

g++ -o prog a.o foo.o

Предположим, с другой стороны, что a.o вызывает bar, который определен в bar.o, и не ссылается ни на что, определенное в foo.o. В этом случае связь B точно так же, как:

g++ -o prog a.o bar.o

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

Ваша функция notmain не упоминается в единственном объектном файле main.o, который вы явно связываете свою программу. Следовательно, когда main.o связан с вашей программой, программа не содержит неопределенной ссылки на notmain: компоновщик не требует определения of notmain - он никогда не слышал о notmain - и не будет связывать какой-либо объектный файл из статической библиотеки, чтобы получить определение notmain. В этом нет ничего делать с разделами ссылок.

Связывая обычную программу со статическими библиотеками, вы, естественно, делаете это так:

g++ -o prog main.o x.o ... -ly -lz ....

Где один из файлов *.o, скажем main.o - это объектный файл, который определяет функцию main. Ты никогда поместите main.o в одну из статических библиотек. Это потому, что в обычной программе main не вызывается ни в одном из других объектных файлов, на которые вы явно ссылаетесь, поэтому, если main.o был в одной из ваших библиотек, связь:

g++ -o prog x.o ... -ly -lz ...

Не нужно было бы находить определение main в любом из -ly -lz ..., и никакого определения из main будут связаны.

То же самое и с вашим notmain. Если вы хотите, чтобы он был связан, вы можете сделать одно из: -

  1. Добавьте -Wl,--undefined=notmain в параметры связи (заменив notmain на искаженное имя notmain для C ++). Это заставит компоновщик предположить, что у него есть неопределенная ссылка на notmain, даже если она не видела.

  2. Добавьте команду EXTERN(notmain) в свой сценарий компоновщика (снова с искажением для C ++). Это эквивалентно 1 .

  3. Явно свяжите объектный файл, который определяет notmain. Не помещайте его в статическую библиотеку.

3 - это именно то, что вы сделали, когда обнаружили, что:

Когда я перемещаю функцию notmain из библиотеки (в проект), все в порядке.

Однако для 3 вам не нужно компилировать notmain.cpp в своем проекте и любом другом проект, которому требуется notmain.o. Можно собрать самостоятельно, установить в /usr/local/lib и явно добавить /usr/local/lib/notmain.o в увязка вашего проекта. Это будет следование примеру самого GCC, который явно связывает файлы запуска crt*.o обычной программы, просто добавляя их абсолютные имена для связи, например

/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crti.o
...
/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/crtn.o
7
Mike Kinghan 21 Июн 2017 в 22:00
Спасибо, это очень понятный ответ. Я наконец понимаю, почему это работает именно так. Большое спасибо !
 – 
DawidPi
21 Июн 2017 в 22:31