Что такое объектный файл

Что такое объектный файл

101364 просмотра

5 ответа

8004 Репутация автора

Я читаю о библиотеках в C, но я еще не нашел объяснения, что такое объектный файл. В чем реальная разница между любым другим скомпилированным файлом и объектным файлом?
Я был бы рад, если бы кто-то мог объяснить на человеческом языке.

Ответы (5)

11 плюса

170332 Репутация автора

Объектный файл — это то, что вы получаете, когда компилируете один (или несколько) исходный файл (файлы).

Это может быть либо полностью укомплектованный исполняемый файл или библиотека, либо промежуточные файлы.

Объектные файлы обычно содержат собственный код, информацию о компоновщике, символы отладки и так далее.

Автор: Mat Размещён: 10.10.2011 08:34

129 плюса

70599 Репутация автора

Объектный файл — это реальный вывод на этапе компиляции. В основном это машинный код, но в нем есть информация, которая позволяет компоновщику увидеть, какие символы в нем есть, а также символы, необходимые для работы. (Для справки, «символы» — это в основном имена глобальных объектов, функций и т. Д.)

Компоновщик берет все эти объектные файлы и объединяет их в один исполняемый файл (при условии, что он может, т. Е. Что нет никаких дублирующих или неопределенных символов). Многие компиляторы сделают это за вас (читай: они запускают компоновщик самостоятельно), если вы не скажете им «просто скомпилировать», используя параметры командной строки. ( -c это обычная опция "просто скомпилировать; не ссылаться".)

Автор: cHao Размещён: 10.10.2011 08:36

67 плюса

3756 Репутация автора

Объектный файл — это сам скомпилированный файл. Там нет никакой разницы между ними.

Исполняемый файл формируется путем связывания файлов объектов.

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

Этот машинный код низкого уровня является двоичным представлением инструкций, которые вы также можете написать непосредственно, используя язык ассемблера, а затем обработать код языка ассемблера (представленный на английском языке) на машинном языке (представленный в шестнадцатеричном формате), используя ассемблер.

Вот типичный высокоуровневый поток для этого процесса для кода на языке высокого уровня, такого как C

-> проходит через препроцессор

-> дать оптимизированный код, все еще в C

-> проходит через компилятор

-> дать ассемблерный код

-> проходит через ассемблер

-> дать код на машинном языке, который хранится в ОБЪЕКТНЫХ ФАЙЛАХ

-> проходит через линкер

-> чтобы получить исполняемый файл.

Этот поток может иметь некоторые вариации, например, большинство компиляторов могут напрямую генерировать код машинного языка, не используя ассемблер. Точно так же они могут сделать предварительную обработку для вас. Тем не менее, приятно разбить составляющие для лучшего понимания.

Автор: RHT Размещён: 10.10.2011 09:01

2 плюса

21 Репутация автора

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

Точно так же, как процессор требует для запуска двоичного кода, объектные файлы похожи на двоичный код, но не связаны между собой. При связывании создаются дополнительные файлы, поэтому пользователю не нужно самим компилировать язык Си. Пользователи могут напрямую открывать exe-файл, когда объектный файл связан с каким-либо компилятором, таким как язык c, vb и т. Д.

Автор: Farhan Размещён: 13.07.2013 12:44

25 плюса

6889 Репутация автора

Есть 3 вида объектных файлов.

Перемещаемые объектные файлы

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

Если у вас есть a.c исходный файл, чтобы создать его объектный файл с помощью GCC, вы должны выполнить: gcc a.c -c

Полный процесс будет следующим: препроцессор (cpp) будет работать по переменному току. Его выходные данные (неподвижный источник) будут переданы в компилятор (cc1). Его вывод (сборка) будет подан в ассемблер (как), который будет производить relocatable object file . Этот файл содержит объектный код и ссылки (и может отлаживать, если -g использовался) метаданные и не может быть непосредственно выполнен.

Общие объектные файлы

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

Исполняемые объектные файлы

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

Результатом работы компоновщика над несколькими relocatable object files является executable object file . Компоновщик объединяет все файлы входных объектов из командной строки слева направо, объединяя все входные разделы одного типа (например .data ) и выходной раздел одного типа. Он использует symbol resolution и relocation .

Бонус:

При связывании с a static library функции, на которые имеются ссылки во входных объектах, копируются в конечный исполняемый файл. С dynamic libraries , таблица символов создаются вместо этого даст возможность динамического связывания с библиотечными функциями / глобал. Таким образом, результатом является частично исполняемый объектный файл, так как он зависит от библиотеки. (простыми словами, если библиотека исчезла, файл больше не может выполняться).

Процесс связывания может быть выполнен следующим образом: ld a.o -o myexecutable

Команда: gcc a.c -o myexecutable вызовет все команды, упомянутые в точке 1 и в точке 3 (cpp -> cc1 -> as -> ld 1 )

1: фактически это collect2, который является оберткой над ld.

Гид по линкерам для начинающих. Часть 1

Следующие понятия используются как синонимы: линкер и компоновщик, определение и дефиниция, объявление и декларирование. Серым выделены вставки с примерами.

Именование составных частей: что внутри Си файла

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

  • Определение переменной приводит к тому, что компилятор выделяет под неё память и возможно заполняет каким-то начальным значением
  • Определение функции приводит к тому, что компилятор генерирует код для этой функции

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

Для переменных, определение бывает двух типов

  • Глобальные переменные, которые существуют всё время существования программы (статическое размещение) и к которым обычно обращаются из многих функций
  • Локальные переменные, которые существуют только во время выполнения функции, в которой они объявлены (локальное размещение) и доступные только внутри неё

Для ясности, «доступный» значит, что на переменную можно ссылаться по имени, которое связано с её определением.

Есть пара случаев, когда всё не так очевидно

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

Стоит сразу напомнить о том, что объявление функции статической уменьшает её область видимости до того файла, в котором она определена (именно, к ней могут обратиться функции из этого файла).

Локальных и глобальных переменные также можно разделить на неинициализированные и инициализированные (которые предварительно заполнены каким-то значением).

В конце концов, мы можем работать с переменными, созданными динамически с помощью функции malloc (или оператора new в С++). К области памяти по имени обратиться нельзя, поэтому мы используем указатели – именованные переменные, которые хранят адрес неименованного участка памяти. Этот участок может быть также освобождён с помощью free (или delete), поэтому считают, что память имеет динамическое размещение.

Читайте также:  Ошибка распаковки архива при установке игры

Соберём теперь всё вместе

Code Data
Global Local Dynamic
Initialized Uninitialized Initialized Uninitialized
Declaration int fn(int x); extern int x; extern int x; N/A N/A N/A
Definition int fn(int x) int x = 1;
(at file scope)
int x;
(at file scope)
int x = 1;
(at function scope)
int x;
(at function scope)
( int* p = malloc(sizeof(int)); )

Проще посмотреть на эту программу

Пусть этот файл называется file.c. Собираем так

Получим объектный файл file.o

Что делает компилятор Си

Работа компилятора си в том, чтобы превратить файл с кодом из понимаемого (иногда) человеком в нечто, понимаемое компьютером. На выходе компилятор даёт объектный файл, который на платформе UNIX имеет обычно расширение .o, а на windows .obj. Содержимое объектного файла это, по сути, два типа объектов

  • Код, соответствующий определениям функций
  • Данные, соответствующие глобальным переменным, определённым в файле (если они предварительно инициализированы, то там же хранится и их значение)

Экземпляры этих объектов будут иметь имена, связанные с ними – имена переменных или функций, определения которых привели к их генерации.

Объектный код – это последовательность (подходяще закодированных) машинных инструкций, соответствующих инструкциям на языке Си – всем этим if, while и даже goto. Все эти команды оперируют разного рода информацией, и эта информация должны быть где-то сохранена (для этого нужны переменные). Кроме того, они могут обращаться к другим кускам кода, который определён в файле.

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

Работа компоновщика (линкера) в исполнении этих обещаний, но что делать компилятору, когда он сталкивается с неопределёнными сущностями?

По сути, компилятор просто оставляет заглушку. Заглушка (ссылка) имеет имя, но значения, связанного с ним, ещё не известно.

Теперь мы можем примерно описать, как будет выглядеть наша программа

Рис. 1. Структура объектного файла

Анализ объектного файла

Пока мы работали с абстрактной программой; теперь важно посмотреть ,как она выглядит на практике. На платформе UNIX можно воспользоваться утилитой nm. На Windows примерным аналогом служит dumpbin с флагом /symbols, хотя есть и порт GNU binutils, который включает nm.exe.

Посмотрим, что нам выдаст для написанной выше программы nm:

Для ранее скомпилированного файла file.o

От системы к системе вывод может отличаться, но ключевая информация – это класс каждого символа и его размер (если доступен). Класс может иметь следующие значения

  • Класс U означает неизвестный (unknown), или заглушку, как было сказано выше. Всего два таких объекта: fn_a и z_global (некоторые версии nm могут также вывести section, которая в данном случае будет *UND* или UNDEF)
  • Класс t или T обозначает, что код определён – t локально или T – это статическая функция. Также может быть выведена секция .text
  • Класс d и D обозначают инициализированную глобальную переменную, d – локальную, D – не локальную. Сегмент для данных переменных обычно .data
  • Для неинициализированных глобальных переменных используется класс b, если статическая/локальная или B и C, если нет. Обычно это сегмент .bss или *COM*

Есть и другие гнусные классы, представляющие какие-то внутренние механизмы компилятора.

Что делает компоновщик. Часть 1

Как мы уже определились ранее , объявление переменной или функции – это обещание компилятору, что где-то есть определение этой переменной или функции, и что работа линкера заключается в том, чтобы исполнять эти обещания. На нашей диаграммой объектного файла это также может быть названо «заполнением пустот».

Для иллюстрации этого вот вам ещё один си файл в дополнение к первому

Пусть этот файл называется main.c. Компилируем его как и ранее

Рис. 2. Структура объектного файла

С этими двумя диаграммами теперь мы видим, что все точки могут быть соединены (а если нет, то компоновщик выдаст ошибку). У каждой вещи своё место, и у каждого места есть вещь, и компоновщик может заменить все заглушки, как показано на рисунке.

Рис. 3. Результирующая работа компоновщика

Для ранее скомпилированных main.o и file.o сборка исполняемого файла

Вывод nm для исполняемого файла (в нашем случае out.exe):

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

Для очистки вывода в UNIX можно убрать всё, что начинается с подчерка.

Дублирование символов

В предыдущем разделе мы уяснили, что если компоновщик не может найти определения для объявленного символа, то он выбросит ошибку. Что случится в том случае, если будет найдено два определения для символа во время линковки?

В Си++ вся просто – по стандарту у символа должно быть всегда одно определение (т.н. правило одного определения) секции 3.2 стандарта языка.

Для си всё менее ясно. У функции или инициализированной глобальной переменной всегда должно быть только одно определение. Но определение неинициализированной глобальной переменной может быть рассмотрено как предварительное. Си в таком случае позволяет (по крайней мере, не запрещает) разным файлам кода иметь свои предварительные определения для того же объекта.

Тем не менее, линкерам также приходится иметь дело и с другими языками программирования, для которых правило одного определения не подходит. Например, для Фортрана вполне нормально иметь копию каждой глобальной переменной в каждом файле, где к ней обращаются. Компоновщик вынужден избавляться от всех копий, выбирая одну (обычно, самую старшую версию, если у них разный размер) и выбрасывая остальные. Эта модель часто называется общей (COMMON) моделью сборки, из-за служебного слова COMMON языка FORTRAN.

В результате, UNIX компоновщик обычно не жалуется на дублирование дефиниций символа, по крайней мере, пока дублированный символ неинициализированная глобальная переменная (такая модель известна как ослабленная модель – relaxed ref/def model линковки). Если это вас беспокоит (а должно!) найдите в документации к своему компилятору ключ, который делает поведение более строгим. Например –fno-common для GNU компилятора заставляет помещать неинициализированные переменные в BSS сегмент, вместо генерации общих блоков.

Что делает операционная система

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

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

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

Для неинициализированных переменных ОС не будет копировать значений из памяти (т.к. их нет) и заполнит всё нулями. Кусок памяти, инициализированный 0 называют bss сегментом.

Начальное значение инициализированных переменных хранится на диске, в исполняемом файле; для неинициализированных переменных хранится их размер.

Рис. 4. Хранение исполняемого файла

Заметьте, что мы всё это время говорим только о глобальных переменных и ни разу не упомянули локальные или динамически созданные объекты.

Читайте также:  Не подключается телефон к айтюнсу через компьютер

Эти данные не нуждаются в работе линкера, потому что время их жизни начинается с того момента, как запустится программа – задолго после того, как компоновщик закончит свою работу. Тем не менее, для полноты картины всё-таки укажем ещё раз

  • Локальные переменные располагаются на участке памяти, известном как стек, который растёт и уменьшается, когда начинает исполняться или заканчивает работу функция
  • Динамическая память выделяется на участке, известно как куча; выделением заведут функция malloc

Мы можем добавить теперь эти недостающие участки памяти на нашу диаграмму. Так как и куча и стек могут менять свой размер во время работы программы, для устранения проблем, они растут на встречу друг другу. Таким образом, нехватка памяти случится только тогда, когда они встретятся (а для этого необходимо использовать много памяти).

Рис. 5. Загрузка исполняемого файла и его размещение в памяти

Что делает компоновщик. Часть 2

После изучения основ работы компоновщика, начнём пробираться дальше и изучать его дополнительные возможности, в том порядке, в котором они исторически возникали и добавлялись в линкер.

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

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

Статические библиотеки

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

На UNIX системах статическая библиотека обычно генерируется командой ar, а сам библиотечный файл имеет расширение .a. Также обычно эти файлы начинаются с префикса lib и передаются линкеру с флагом –l, за которым следует имя библиотеки без префикса lib и без расширения (например, для файла libfred.a надо добавить -lfred).

Более сложный пример, пусть у нас имеются три файла

Соберём a.c, b.c и c.c в библиотеку libabc.a. Сначала скомпилируем все файл (можно и по-отдельности, вместе быстрее)

Получим четыре объектных файла. После этого соберём a, b и c в один файл

и теперь можем скомпилировать программу

Заметьте, что попытка собрать так

приведёт к ошибке – компоновщик начнёт жаловаться на неразрешённые символы.

На windows статические библиотеки обычно имеют расширение .lib и генерируются утилитой LIB, но путаницу вносит то, что такое же расширение и у библиотек импорта, которые просто содержат список доступных в динамической библиотеке (dll) вещей.

Пока линкер проходит по коллекции объектных файлов, чтобы собрать их вместе, он составляет список ещё не разрешённых символов. После обработки списка всех явно объявленных объектов у компоновщика теперь есть ещё одно место, где можно поискать необъявленные символы – в библиотеке. Если неразрешённый объект находится в библиотеке, то он добавляется, точно как если бы пользователь указал его в командной строке.

Обратите внимание на уровень детализации объектов: если нужен какой-то конкретный символ, то добавляется весь объект, содержащий этот символ. Таким образом, линкер может попасть в ситуацию, когда он делает шаг вперёд и два назад, потому что новый объект, в свою очередь может содержать свои, неразрешённые символы.

Другая важная деталь – порядок событий. Библиотеки опрашиваются только после того, как была проведена нормальная компоновка, и они обрабатываются по порядку, слева направо. Это значит, что если библиотека требует символ, который был ранее в предыдущей подключённой библиотеке, то линкер не сможет его автоматически найти.

Пример должен помочь разобраться в этом более подробно. Пусть у нас имеются объектные файлы a.o, b.o и библиотеки libx.a, liby.b.

File a.o b.o libx.a liby.a
Object a.o b.o x1.o x2.o x3.o y1.o y2.o y3.o
Definitions a1, a2, a3 b1, b2 x11, x12, x13 x21, x22, x23 x31, x32 y11, y12 y21, y22 y31, y32
Undefined references b2, x12 a3, y22 x23, y12 y11 y21 x31

После обработки файлов a.o и b.o линкер разрешит ссылки b2 и a3, оставив неопределёнными x12 и y22. В этом месте линкер начинает проверять первую библиотеку libx.a и узнаёт, что может вытащить x1.o, в котором определён символ x12; сделав это, линкер получает в нагрузку неопределённые символы x23 и y12, объявленные в x1.o (т.о. в списке неопределённых значатся y22, x23 и y23).

Линкер всё ещё проверяет libx.a, поэтому без труда разрешает символ x23, вытащив его из x2.o библиотеки libx.a. Но этот x2.o добавляет y11 (который теперь состоит из y11, y22 и y12). Ни один из них далее не может быть разрешён с помощью библиотеки libx.a, поэтому линкер переходи к файлу liby.a.

Здесь происходит примерно то же самое, и компоновщик вытаскивает y1.o и y2.o. Первый добавляет y21, но он легко разрешается, так как уже вытащен на свет y2.o. Результатом всей работы становится то, что компоновщик смог разрешить все символы и достал почти все объектные файлы, которые будут помещены в конечный исполняемый файл.

Заметьте, что если бы b.o, например, содержало ссылку на y32, то всё пошло по другому сценарию. Обработка libx.a была бы такой же, но вот обработка liby.a вытащила y3.o, содержащий ссылку x31, которая определена в libx.a. Так как обработка libx.a уже закончена, то компоновщик бы выдал ошибку.

Это пример циклической зависимости двух библиотек libx и liby.

Разделяемые библиотеки

У популярных стандартных библиотек Си (обычно libc) есть очевидный недостаток – каждый исполняемый файл будет иметь свою копию одного и того же когда. Если каждая программа имеет копию printf, fopen и тому подобного, то много дискового пространства будет потрачено впустую.

Другой, менее очевидный недостаток заключается в том, что после статической компоновки код программы неизменен. Если кто-то найдёт и исправит баг в printf, то все программы, использующие эту библиотеку, нужно будет пересобрать.

Для того чтобы обойти эти проблемы, были внедрены разделяемы библиотеки (обычно они имеют расширение .so или .dll по Windows, или .dylib под Mac OS X). При работе с такими библиотеками линкер не обязан объединять все элементы в одну картинку. Вместо этого он оставляет что-то вроде долговой расписки и переносит выплату на тот момент, когда программа будет запущена.

Говоря короче: если линкер узнаёт, что неопределённый символ в разделяемой библиотеке, то он не добавляет определения в исполняемый файл. Вместо этого компоновщик записывает в программу имя символа и библиотеки, в которой он предполагаемо определён.

Во время исполнения программы операционная система определяет, что эти пропущенные биты линкуются “just in time” – во время выполнения. Перед запуском функции main уменьшенная версия линкера (часто это ld.so) проходит по спискам должников и доделывает финальную часть работы – вытаскивает код из библиотеки и собирает пазл.

Это значит, что ни у одной из исполняемых программ нет копии printf. Новая исправленная версия libc может просто подменить старую, и она будет подхвачена каждой из программ при новом запуске.

Есть и другое важное отличие динамической библиотеки от статической, и это отражается в степени детализации ссылок. Если определённый символ достаётся из разделяемой библиотеки (например, printf из libc), всё содержимое этой библиотеки отображается в адресное пространство. Это сильно отличается от статической библиотеки, из которой вытаскивается только тот объектный файл, который содержит определение объявленного символа.

Читайте также:  Кубик рубика мировой рекорд 2018

Другими словами, разделяемая библиотека это результат работы компоновщика (а не просто собранные в кучу с помощью ar объектные файлы) с разрешёнными ссылками внутри объектов этого файла. Ещё раз: nm удачно это иллюстрирует. Для статической библиотеки nm покажет набор отдельных объектных файлов. Для разделяемой библиотеки liby.so укажет только неопределённый символ x31. Также, для нашего примера с порядком компоновки и циклической ссылкой, проблем не будет, потому что весь контент y3.o и x3.o так и так уже вытащен.

Есть также другой полезный инструмент ldd. Он показывает все разделяесые библиотеки, от которых зависит исполняемый файл или библиотека с информацией о том, где её можно найти. Чтобы программа могла удачно запуститься, нужно, чтобы все эти библиотеки были найдены, вместе со всеми их зависимостями (обычно, на UNIX системах загрузчик ищет библиотеки в списке папок, который хранится в переменной окружения LD_LIBRARY_PATH).

Ассемблер и компоновщик создают объектные файлы, которые могут выполняться устройством. Формат для этих объектных файлов назван общим объектным файловым форматом (COFF – ООФФ).

ООФФ упрощает модульное программирование при написании программы на языке ассемблера, поскольку он позволяет представлять программу и данные в виде блоков. Эти блоки известны как разделы. Как ассемблер, так и компоновщик обеспечены директивами, которые позволяют создавать и манипулировать разделами.

Разделы. Наименьший модуль объектного файла называется разделом. Раздел — блок программы или данных, который занимает непрерывное пространство на карте памяти с другими разделами. Каждый раздел объектного файла является самостоятельным и отличным от других. По умолчанию ООФФ объектных файлов всегда содержит три раздела:

Раздел текста — .text обычно содержит выполняемый код.

Раздел данных — .data обычно содержит инициализированные данные.

Раздел .bss обычно резервируется для неинициализированных переменных.

Кроме того, ассемблер и компоновщик позволяют Вам создавать, называть и связывать поименованные разделы, которые используются подобно разделам .data, .text и .bss. Есть два основных типа разделов:

Инициализированные разделы содержат данные или код. Разделы .text и .data проинициализированы. Именуемые разделы, создаваемые с помощью директивы ассемблера .sect, также проинициализированы;

Неинициализированные разделы резервируют пространство на карте памяти для неинициализированных данных. Раздел .bss неинициализированный. Именуемые разделы, создаваемые с помощью директивы ассемблера .usect, также неинициализированные.

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

В системе имеется целевая платформа (Устройство), это плата с конкретным процессором. Там имеется целевая память, содержащая разделы: ПЗУ (ROM), перепрограммируемое ПЗУ (EEPROM), ОЗУ (RAM). Этим разделам назначается область адресов в карте распределения целевой памяти. Компоновщик должен перемещать в целевую память разделы объектного кода. Поскольку большинство систем содержат различные типы памяти, то применение разделов может помочь использовать целевую память более эффективно. Все разделы независимо переместимые, любой раздел можно разместить в любом блоке целевой памяти. Если у вас нет целевой платформы, то отладчик имитирует ее.

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

Директивы .bssи .usectсоздают неинициализированные разделы; директивы .text, .data, и .sectсоздают инициализированные разделы.

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

Неинициализированные разделырезервируют пространство в памяти C28x; они обычно распределены в ОЗУ. Эти разделы не имеют никакого фактического содержания в объектном файле, они просто резервируют память. Программа может использовать это пространство во время выполнения для создания и сохранения переменных. Неинициализированные области данных формируются, используя директивы ассемблера .bssи .usect:

Директива .bss резервирует пространство в .bss разделе. Директива .bss резервирует данное число байтов в .bss разделе. Вы должны определить размер, нет никакого значения по умолчанию.

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

Каждый раз, когда Вы вызываете .bssили .usectдирективу, ассемблер резервирует дополнительное пространство в .bssили названном разделе. Синтаксис директивы .bss:

.bss символ, размер в словах [, флаг блокировки[, флаг выравнивания [, тип]]

Синтаксис директивы .usect:

символ .usect ”имя раздела”, размер в словах [, флаг блокировки[, флаг выравнивания]

Символ указывает на первый байт, зарезервированный этим обращением директивы .bss или .usect. Символ соответствует имени переменной, для которой Вы резервируете пространство. На него может ссылаться любой другой раздел, а также он может быть объявлен, как глобальный символ (директивой ассемблера .global).

Размер в словах – абсолютное выражение.

Флаг выравнивание – необязательный параметр. Он определяет минимальное выравнивание в байтах, требуемое распределяемым пространством. Значение по умолчанию – 1 байт. Значение должно быть степенью числа 2.

Директивы инициализированных разделов (.text, .data, и .sect) указывают ассемблеру прекратить трансляцию в текущий раздел и начать транслировать в обозначенный раздел. Директивы .bssи .usect, однако, не заканчивают данный раздел и не начинают новый, они просто выходят от текущего раздела временно. Директивы .bssи .usectмогут появляться где-нибудь в инициализированном разделе, не воздействуя на его содержание.

Ассемблер обрабатывает неинициализированные подразделы (созданные .usectдирективой) тем же самым способом, как неинициализированные разделы.

Инициализированные разделысодержат выполняемый код или инициализированные данные. Содержание этих разделов записывается в объектный файл и помещается в память C28x, когда программа загружается. Каждый инициализированный раздел – независимо перемещаемый и может ссылаться на символы, которые определены в других разделах. Компоновщик автоматически решает эти ссылки к другим разделам. Три директивы предписывают ассемблеру размещать код или данные в раздел. Синтаксис этих директив:

.sect «имя раздела»

Когда ассемблер сталкивается с одной из этих директив, он останавливает трансляцию в текущий раздел (действует как команда конца текущего раздела). Затем он транслирует последующий код в обозначенный раздел, пока не обнаружит другую .text, .data, или .sectдирективу.

Разделы формируются через итеративный процесс. Например, когда ассемблер первый раз сталкивается с директивой .data, раздел .data- пуст. Инструкции, следующие за первой директивой .data, собираются в .dataраздел (пока ассемблер не сталкивается с .textили .sectдирективой). Если ассемблер сталкивается с последующими .dataдирективами, то он прибавляет инструкции после этих директив .dataк инструкциям, уже находящимся в разделе .data. Это создает единый раздел .data, который может быть распределен непрерывно в памяти.

Инициализированные подразделы создаются .sectдирективой. Ассемблер обрабатывает инициализированные подразделы тем же самым способом, как инициализированные разделы.

Названные разделы— разделы, которые Вы создаете. Вы можете использовать их подобно заданным по умолчанию .text, .data, и .bssразделам, но они транслируются отдельно. Например, повторное использование .textдирективы создает единый .textраздел в объектном файле. Во время компоновки этот .textраздел распределяется в памяти как одиночный модуль. Предположим, что имеется часть выполняемого кода (возможно подпрограмма инициализации), которую Вы не хотите размещать вместе с .text. Если Вы размещаете этот сегмент кода в названном разделе, он транслируется отдельно от .text, и Вы можете распределять его в памяти отдельно. Вы можете также транслировать инициализированные данные, который отличны от .dataраздела, и Вы можете резервировать пространство для неинициализированных переменных, которые отличны от .bssраздела.

Ссылка на основную публикацию
Что мне задали завтра на русский
Проверка орфографии на 5-ege.ru (введите текст в форму ниже): Если нужно проверить пунктуацию, воспользуйтесь сервисом Проверка пунктуации онлайн. Наш сервис...
Чистка матрицы зеркального фотоаппарата
Нам доверяют сотрудники: Вопросы и предложения: info@fixit24.ru Адрес: г. Москва, м. Тверская, ул. Тверская, д. 20, 2 этаж, офис 204....
Чистка кэша на ноутбуке
Все, что находит отображение в браузере (музыка, картинки, видео) перед воспроизведением сохраняются на ваш ПК как временные файлы.Если их количество...
Что лучше газель некст или фиат дукато
На прошлой неделе Газель-Некст была признана лучшим автомобилем года в России. Эксперты коммерческого транспорта оценили ее в 2–3 раза выше,...
Adblock detector