Линковка это

Что произойдет, если в разных исходных файлах будут объявлены переменные с одним и тем же именем х? Решать этот вопрос предстоит линковщику, опираясь на правила по умолчанию и «подсказки», которые даст разработчик. Возможно, тот просто не знал, что имя уже используется, и нужно трактовать эти переменные как различные. Или, напротив, разработчик имел в виду одну и ту же переменную, и нужно позаботиться, чтобы все ссылки на х указывали на одну область памяти. Эти два варианта решения соотвествуют двум типам линковки.

Внешняя линковка (external linkage) означает, что одинаковые имена, используемые в разных файлах, трактуются линковщиком как один объект. Для имен функций и переменных, объявленных вне функций, внешняя линковка применяется по умолчанию.

Внутренняя линковка (internal linkage) для переменной x или функции f() из файла a.c означает, что указанные объекты является «собственностью» этого файла, а переменные или функции с такими же именами, но объявленные в других файлах, являются другими объектами.

Интересно, что для обозначения внешней линковки используется ключевое слово extern
, тогда как для внутренней особого ключевого слова (например, intern) нет. Вместо этого используется ключевое слово static. Это может вызвать путаницу.

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

  1. Для переменных, областью действия которых является файл, static указывает только на тип линковки:
    • любая переменная x, объявленная вне функции, размещается в памяти по статической модели, независимо от того, объявлена она как static int x, extern int x или просто как int x;
    • в данном случае по умолчанию используется внешняя линковка, поэтому использование static изменяет тип линковки на внутреннюю.
  2. Для переменных, областью действия которых является блок, static влияет только на модель памяти:
    • указание static на линковку не влияет, поскольку по умолчанию для таких переменных итак используется внутренняя линковка;
    • переменные, определенные в блоке, по умолчанию являются автоматическими, поэтому указание ключевого слова static изменит эту модель памяти на статическую.

  3. Для функций static влияет только на тип линковки:
    • областью действия функций всегда является файл, поэтому, как и для всех объектов с такой областью действия, для функций по умолчанию используется внешняя линковка. static изменяет это умолчание;
    • здесь нет пересечений с моделью памяти; в этом смысле функции всегда являются статическими, поскольку их областью действия является файл.

dkhramov.dp.ua

Базовые понятия, кратко

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

int imported(int);    static int internal(int x) {  return x * 2;  }    int exported(int x) {  return imported(x) * internal(x);  }  

Теперь скомпилируем этот файл:

gcc -c test.c  nm test.o    000000000000000e T exported   U imported  0000000000000000 t internal  

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

 

Процесс линковки


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

  • Линковщик поддерживает таблицу символов. Эта таблица помимо всего содержит два списка
    • Список символов экспортируемых всеми объектными файлами и библиотеками, обработанными на данный момент.
    • Список не определенных символов, которые обработанные на данный момент объектные файлы и библиотеки запросили. Имеются ввиду те символы что еще не были найдены.
  • Когда линковщик обрабатывает новый объектный файл, он смотрит на:
    • Экспортируемые символы — они добавляются в список экспортируемых символов. Если какой-то из них находился в списке не определенных символов, то он оттуда убирается. Если какой-то из символов уже находится в списке экспортируемых символов, то мы получаем ошибку: множественное объявление(multiple definition).
    • Импортируемые символы — эти добавляются в список не определенных символов, если их не удалось найти в списке экспортируемых.

  • Когда линковщик обрабатывает новую библиотеку, то все становиться несколько интересней. Линковщик проходится по всем объектным файлам внутри библиотеки и для каждой он смотрит в первую очередь экспортируемые символы:
    • Если какой-то из экспортируемых символов находится в списке не определенных, тогда объектный файл добавляется к линкуемым. В ином случае следующий шаг пропускается.
    • Если объектный файл был добавлен, то он обрабатывается как обычный — экспортируемые и импортируемые символы добавляются в соотвествующие таблицы.
    • Наконец,  если какой-либо  из объектных файлов библиотеки был добавлен, то библиотека сканируется заново — возможно что какие-то из импортируемых символов могут быть найдены в других объектных файлах в этой библиотеки.

Когда линковщик заканчивает, он смотрит таблицы символов. Если таблица не определенных символов не пуста линковщик выкинет исключение «undefined reference«.

Стоит заметить что после того как библиотека будет просканирована, линковщик к ней уже возвращаться не будет. Даже если она экспортирует символы которые понадобятся библиотекам которые будут просканированы  позже нее. Единственный случай при котором линковщик пересканирует библиотеку был описан выше — когда какой-либо из объектных файлов этой библиотеки был слинкован, тогда библиотека сканируется заново. Поведение линковщика можно изменить флагами линковки(см. —start-group —end-group в руководстве ld).


Так же стоит заметить что линковщик добавляет не все объектные файлы библиотеки, а только те что содержат необходимые экспортируемые символы(те что есть в списке неопределенных символов). Это очень важная особенность линковки статических библиотек, которая часто применяется, допустим библиотека C(libc) сформирована так что объектный файл содержит только одну функцию. Например, если ваша программа использует только strlen, то только этот объектный файл будет слинкован с вашим приложением/библиотекой(а так же те объектные файлы что потребуются strlen), что позволит уменьшить конечный размер приложения/библиотеки.

Давайте разберем на простом примере, есть файл simplefunc.c:

int func(int i) {   return i + 21;  }  

и файл simplemain.c:

int func(int);    int main(int argc, const char* argv[])  {   return func(argc);  }  

Скомпилируем эти 2 файла в приложение:

$ gcc -c simplefunc.c  $ gcc -c simplemain.c  $ gcc simplefunc.o simplemain.o  $ ./a.out ; echo $?  

Все работает как и задумывалось.

Но что если simplefunc мы преобразуем в статическую библиотеку?! И попробуем собрать:

$ ar r libsimplefunc.a simplefunc.o  $ ranlib libsimplefunc.a  $ gcc simplemain.o -L. -lsimplefunc  $ ./a.out ; echo $?  22  

Все работает, но что будет если изменить порядок линковки:


$ gcc -L. -lsimplefunc simplemain.o  simplemain.o: In function 'main':  simplemain.c:(.text+0x15): undefined reference to 'func'  collect2: ld returned 1 exit status  

Вот все и сломалось. Понимание алгоритмов линковки помогает понять почему этот пример не линкуется. На момент обработки libsimplefunc.a линковщик еще не «видел» simplemain.o и потому func еще не в списке неопределенных символов. Когда линковщик обрабатывает библиотеку, он видит экспортируемый символ func, но он не «нужен» и потому объектный файл не линкуется. Когда же он доходит до simplemain.o, то он находит импортируемый символ func, который добавляется в список неопределенных символов, правда на вход линковщика больше не поступают файлы и потому func остается не определенным.

А в предыдущем случае, где библиотека и объектный файл поступали в другом порядке этого не произошло. Отсюда следует что если объектный файл или библиотека AAA нуждается в символе из библиотеки BBB, то AAA должна появится перед BBB на входе линковщика. Все это относиться к линковки статических библиотек.
Статья является частичным переводом статьи Eli Bendersky.

codedream.me

Правильная перелинковка страниц

Правильная перелинковка страниц сайта отличается от неправильной перелинковки, также как наушники, которые лежат у вас в кармане, и наушники, которые вы, матерясь, все-таки размотали.

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

Для того, чтобы создать структуру страниц, необходимо:


  • собрать подробное семантическое ядро;
  • создать общую структуру страниц сайта;
  • создать логическую структуру страниц сайта.

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

На схеме 1 показана общая структура, где главная страница (выделено зеленым) на вершине схемы собирает на себя ссылки со всех страниц сайта, другие же страницы связаны следующим образом:

  • Главная страница ссылается на категории (желтые), подкатегории (оранжевые) и конечные страницы (красные).
  • Категории ссылаются на другие категории (желтые), подкатегории (оранжевые) и конечные страницы (красные).
  • Подкатегории ссылаются на категории (желтые), другие подкатегории (оранжевые) и конечные страницы (красные).
  • Конечные страницы ссылаются на категории (желтые), подкатегории (оранжевые) и другие конечные страницы (красные).

На схеме показано 4 уровня вложенности, однако каждая страница сайта должна быть доступна не более чем за 2-3 клика от главной страницы. Чем глубже страница, от главной — тем ниже ее приоритет, и тем реже она будет индексироваться.

Взаимная перелинковка внутренних страниц

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

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

Схемы перелинковки страниц сайта

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

Примеры схем перелинковки страниц сайта:

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


Логичнее всего переносить вес на страницы, которые отвечают самым высококонкурентным запросам: высокочастотным и среднечастотным; низкочастотные запросы чаще всего занимают высокие позиции самостоятельно при хорошей оптимизации страниц.

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

Плагины для перелинковки страниц

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

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


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

Вывод

Не обманывайте себя тем, что вы нашли ПРОСТОЙ И БЫСТРЫЙ способ эффективной перелинковки, его не существует. Работайте с семантическим ядром и статистикой, стройте четкое логическое ядро сайта и создавайте перелинковку в его рамках своими руками. Вы лучше поймете реальные потребности целевой аудитории, повысите качество предоставления информации на сайте и вам воздастся за труды.

Это действительно работает.

Остались вопросы? Пиши в комментариях.
Жми репост, сохраняй себе, делись с статьей с друзьями.

convertmonster.ru

Линкануть – происходит от английского слова link, что переводится как ссылка или указатель. Что такое линкануть? Это отправить куда-нибудь определенную ссылку.

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

1. Ссылка на сайт или какой-то интернет-ресурс. Например, сидя в RaidCall, вы можете попросить скинуть вам ссылку на гайд или форум. Тогда человек скопируют ее с адресной строки браузера и вставит в чат рк. При нажатии вы сразу выйдете на нужный сайт. Также для вставки можно воспользоваться сочетанием клавиш Ctrl+V.

2. Линк предмета, части игровой экипировки и т.д. Когда вы собираетесь продать какой-то предмет в игре, то прежде вам необходимо показать его покупателю. Когда торгуете через аукцион, то все свойства и характеристики видны, но за выставление на аук берется комиссия, потому продавать напрямую выгоднее. Передавать для демонстрации вещь потенциальному покупателю не только долго, но и опасно – вам могут не заплатить. Вот тут на помощь и приходит линкование предмета в чат.

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

Кидать ссылки можно не только на части экипировки, но и на многие другие предметы:

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

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

Линкануть в игре можно почти все, даже профессии и навыки. Но мобов, боссов или персонажей обычно нельзя. Во-первых, параметров очень много, а во-вторых, внутреннее содержание (распределение статов, их значения и т.д.) – личное дело каждого и почти тайна.

Примеры

«Кинь линк на учителя ювелиров»

«Сделай линк своих наручей»

«Линкани мне свой хилящий скил»

gamebizclub.com

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

Перевод статьи Beginner’s guide to linkers с примерами и дополнениями.

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

  • Именование составных частей: что внутри Си файла
  • Что делает компилятор Си
  • Анализ объектного файла
  • Что делает компоновщик. Часть 1
  • Дублирование символов
  • Что делает операционная система
  • Что делает компоновщик. Часть 2
  • Статические библиотеки
  • Разделяемые библиотеки

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

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

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

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

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

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

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

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

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

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

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

В конце концов, мы можем работать с переменными, созданными динамически с помощью функции 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));)

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

 /* Это определение неинициализированной глобальной переменной */ int x_global_uninit;  /* Это определение инициализированной глобальной переменной */ int x_global_init = 1;  /* Это определение неинициализированной глобальной переменной,  но к ней можно обратиться по имени только из этого же си файла */ static int y_global_uninit;  /* Это определение инициализированной глобальной переменной,  но к ней можно обратиться по имени только из этого же си файла */ static int y_global_init = 2;  /* Это объявление глобальной переменной, которая существует  где-то в другом месте программы */ extern int z_global;  /* Это объявление функции, которая определена где-то в другом  месте программы. Можно добавть служебное слово extern. Но это не имеет значения */ int fn_a(int x, int y);  /* Это определение функции, но так как она определена со  словом static, то доступна только в этом же си файле */ static int fn_b(int x) {  return x+1; }  /* Это определение функции. Её параметтры рассматриваются  как локальные переменные */ int fn_c(int x_local) {  /* Это определение неинициализированной локальной переменной */  int y_local_uninit;  /* Это определение инициализированной локальной переменной */  int y_local_init = 3;   /* Этот код ссылает на локальные и глобальные переменные и функции по имени */  x_global_uninit = fn_a(x_local, x_global_init);  y_local_uninit = fn_a(x_local, y_local_init);  y_local_uninit += fn_b(z_global);  return (y_global_uninit + y_local_uninit); } 

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

 	cc -g -O -c file.c

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 00000000 b .bss 00000000 d .data 00000000 N .debug_abbrev 00000000 N .debug_aranges 00000000 N .debug_info 00000000 N .debug_line 00000000 N .debug_loc 00000000 i .drectve 00000000 r .eh_frame 00000000 r .rdata$zzz 00000000 t .text  U _fn_a 00000000 T _fn_c 00000000 D _x_global_init 00000004 C _x_global_uninit  U _z_global 

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

  • Класс 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

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

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

 /* Инициализированная глобальная переменная */ int z_global = 11; /* Вторая глобальная переменная с именем y_global_init,  но обе они статические */ static int y_global_init = 2; /* Объявление ещё одной глобальной переменной */ extern int x_global_init;  int fn_a(int x, int y) {  return(x+y); }  int main(int argc, char *argv[]) {  const char *message = "Hello, world";   return fn_a(11,12); } 

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

 cc –g –O –c main.c 
Рис. 2. Структура объектного файла
Рис. 2. Структура объектного файла

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

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

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

cc -o out.exe main.o file.o

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

 Symbols from sample1.exe:  Name Value Class Type Size Line Section  _Jv_RegisterClasses | | w | NOTYPE| | |*UND* __gmon_start__ | | w | NOTYPE| | |*UND* __libc_start_main@@GLIBC_2.0| | U | FUNC|000001ad| |*UND* _init |08048254| T | FUNC| | |.init _start |080482c0| T | FUNC| | |.text __do_global_dtors_aux|080482f0| t | FUNC| | |.text frame_dummy |08048320| t | FUNC| | |.text fn_b |08048348| t | FUNC|00000009| |.text fn_c |08048351| T | FUNC|00000055| |.text fn_a |080483a8| T | FUNC|0000000b| |.text main |080483b3| T | FUNC|0000002c| |.text __libc_csu_fini |080483e0| T | FUNC|00000005| |.text __libc_csu_init |080483f0| T | FUNC|00000055| |.text __do_global_ctors_aux|08048450| t | FUNC| | |.text _fini |08048478| T | FUNC| | |.fini _fp_hw |08048494| R | OBJECT|00000004| |.rodata _IO_stdin_used |08048498| R | OBJECT|00000004| |.rodata __FRAME_END__ |080484ac| r | OBJECT| | |.eh_frame __CTOR_LIST__ |080494b0| d | OBJECT| | |.ctors __init_array_end |080494b0| d | NOTYPE| | |.ctors __init_array_start |080494b0| d | NOTYPE| | |.ctors __CTOR_END__ |080494b4| d | OBJECT| | |.ctors __DTOR_LIST__ |080494b8| d | OBJECT| | |.dtors __DTOR_END__ |080494bc| d | OBJECT| | |.dtors __JCR_END__ |080494c0| d | OBJECT| | |.jcr __JCR_LIST__ |080494c0| d | OBJECT| | |.jcr _DYNAMIC |080494c4| d | OBJECT| | |.dynamic _GLOBAL_OFFSET_TABLE_|08049598| d | OBJECT| | |.got.plt __data_start |080495ac| D | NOTYPE| | |.data data_start |080495ac| W | NOTYPE| | |.data __dso_handle |080495b0| D | OBJECT| | |.data p.5826 |080495b4| d | OBJECT| | |.data x_global_init |080495b8| D | OBJECT|00000004| |.data y_global_init |080495bc| d | OBJECT|00000004| |.data z_global |080495c0| D | OBJECT|00000004| |.data y_global_init |080495c4| d | OBJECT|00000004| |.data __bss_start |080495c8| A | NOTYPE| | |*ABS* _edata |080495c8| A | NOTYPE| | |*ABS* completed.5828 |080495c8| b | OBJECT|00000001| |.bss y_global_uninit |080495cc| b | OBJECT|00000004| |.bss x_global_uninit |080495d0| B | OBJECT|00000004| |.bss _end |080495d4| A | NOTYPE| | |*ABS* 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

 	ar rcs libfile.a file.o 	
 	gcc main.o libfile.a -o out.exe 	

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

a.c

 	int a_f(int a) { 		return a + 1; 	}

b.c

 	int b_f(int a) { 		return a + 1; 	}

c.c

 	int c_f(int a) { 		return a + 1; 	}

И главный файл

abc.c

 	#include <stdio.h>  	int main() { 		 		int a = a_f(0); 		int b = a_f(1); 		int c = a_f(2); 		 		printf("%d %d %d", a, b, c); 		 		return 0; 	} 	

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

 	gcc –g –O –c a.c b.c c.c abc.c 	

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

 	ar rcs libabc.a a.o b.o c.o 	

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

 	gcc -o abc.exe libabc.a abc.o 	

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

 	gcc -o abc.exe abc.o libabc.a  	

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

На 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), всё содержимое этой библиотеки отображается в адресное пространство. Это сильно отличается от статической библиотеки, из которой вытаскивается только тот объектный файл, который содержит определение объявленного символа.

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

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

 /usr/bin:ldd xeyes  linux-gate.so.1 => (0xb7efa000)  libXext.so.6 => /usr/lib/libXext.so.6 (0xb7edb000)  libXmu.so.6 => /usr/lib/libXmu.so.6 (0xb7ec6000)  libXt.so.6 => /usr/lib/libXt.so.6 (0xb7e77000)  libX11.so.6 => /usr/lib/libX11.so.6 (0xb7d93000)  libSM.so.6 => /usr/lib/libSM.so.6 (0xb7d8b000)  libICE.so.6 => /usr/lib/libICE.so.6 (0xb7d74000)  libm.so.6 => /lib/libm.so.6 (0xb7d4e000)  libc.so.6 => /lib/libc.so.6 (0xb7c05000)  libXau.so.6 => /usr/lib/libXau.so.6 (0xb7c01000)  libxcb-xlib.so.0 => /usr/lib/libxcb-xlib.so.0 (0xb7bff000)  libxcb.so.1 => /usr/lib/libxcb.so.1 (0xb7be8000)  libdl.so.2 => /lib/libdl.so.2 (0xb7be4000)  /lib/ld-linux.so.2 (0xb7efb000)  libXdmcp.so.6 => /usr/lib/libXdmcp.so.6 (0xb7bdf000) 

На Windows, например

 ldd C:WindowsSystem32rundll32.exe  ntdll.dll => /c/WINDOWS/SYSTEM32/ntdll.dll (0x77100000)  KERNEL32.DLL => /c/WINDOWS/System32/KERNEL32.DLL (0x763a0000)  KERNELBASE.dll => /c/WINDOWS/System32/KERNELBASE.dll (0x73e10000)  apphelp.dll => /c/WINDOWS/system32/apphelp.dll (0x71ec0000)  AcLayers.DLL => /c/WINDOWS/AppPatch/AcLayers.DLL (0x78830000)  msvcrt.dll => /c/WINDOWS/System32/msvcrt.dll (0x74ef0000)  USER32.dll => /c/WINDOWS/System32/USER32.dll (0x76fb0000)  win32u.dll => /c/WINDOWS/System32/win32u.dll (0x74060000)  GDI32.dll => /c/WINDOWS/System32/GDI32.dll (0x74b00000)  gdi32full.dll => /c/WINDOWS/System32/gdi32full.dll (0x741e0000)  SHELL32.dll => /c/WINDOWS/System32/SHELL32.dll (0x74fc0000)  cfgmgr32.dll => /c/WINDOWS/System32/cfgmgr32.dll (0x74900000)  windows.storage.dll => /c/WINDOWS/System32/windows.storage.dll (0x74390000)  combase.dll => /c/WINDOWS/System32/combase.dll (0x76490000)  ucrtbase.dll => /c/WINDOWS/System32/ucrtbase.dll (0x74100000)  RPCRT4.dll => /c/WINDOWS/System32/RPCRT4.dll (0x76b50000)  bcryptPrimitives.dll => /c/WINDOWS/System32/bcryptPrimitives.dll (0x74940000)  powrprof.dll => /c/WINDOWS/System32/powrprof.dll (0x73c20000)  advapi32.dll => /c/WINDOWS/System32/advapi32.dll (0x76ad0000)  sechost.dll => /c/WINDOWS/System32/sechost.dll (0x76440000)  shlwapi.dll => /c/WINDOWS/System32/shlwapi.dll (0x76d30000)  kernel.appcore.dll => /c/WINDOWS/System32/kernel.appcore.dll (0x73c10000)  shcore.dll => /c/WINDOWS/System32/shcore.dll (0x76c20000)  profapi.dll => /c/WINDOWS/System32/profapi.dll (0x73c70000)  OLEAUT32.dll => /c/WINDOWS/System32/OLEAUT32.dll (0x76e20000)  msvcp_win.dll => /c/WINDOWS/System32/msvcp_win.dll (0x74080000)  SETUPAPI.dll => /c/WINDOWS/System32/SETUPAPI.dll (0x766c0000)  MPR.dll => /c/WINDOWS/SYSTEM32/MPR.dll (0x6cac0000)  sfc.dll => /c/WINDOWS/SYSTEM32/sfc.dll (0x2380000)  WINSPOOL.DRV => /c/WINDOWS/SYSTEM32/WINSPOOL.DRV (0x6f2f0000)  bcrypt.dll => /c/WINDOWS/SYSTEM32/bcrypt.dll (0x73b70000)  sfc_os.DLL => /c/WINDOWS/SYSTEM32/sfc_os.DLL (0x68e00000)  IMM32.DLL => /c/WINDOWS/System32/IMM32.DLL (0x76d90000)  imagehlp.dll => /c/WINDOWS/System32/imagehlp.dll (0x749a0000) 

Причиной этого большего дробления связана с тем, что операционная система достаточно умная и вы можете дублировать дисковое пространство не только статическими библиотеками. Разные исполняемые процессы могут расшаривать также один сегмент кода (но не data/bss сегменты). Чтобы это сделать, вся библиотека должна быть отображена в один проход, чтобы все внутренние ссылки выстроились в один ряд: если один процесс вытащил a.o и c.o, а второй b.o и c.o, для операционной системы никакого совпадения не будет.

Применение ключевого слова Volatile

learnc.info

Схемы перелинковки сайта

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

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

Перелинковка под высокочастотные запросы

Для продвижения самых высокочастотных (ВЧ) запросов обычно выбирается главная страница, т.к. она, при прочих равных условиях, обладает максимальным статическим весом. Также не стоит забывать, что максимальное количество естественных ссылок пользователи ставит в виде http://www.site.ru/.

Для передачи максимального веса на главную страницу сайта лучшего всего использовать следующий вид перелинковки:

Перелинковка под среднечастотные запросы

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

Схема перелинковки, позволяющая передать максимум статического веса, имеет вид:

Перелинковка под низкочастотные запросы

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

Наиболее оптимальная схема перелинковки статей для продвижения НЧ запросов имеет вид:

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

Основные правила перелинковки

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

  • ставьте уникальные анкоры в каждой ссылки перелинковки (используйте морфологию, разбавляйте дополнительными словами);
  • закрывайте при помощи атрибута rel=»nofollow» внешние ссылки и ссылки, мешающие правильной передачи веса;
  • пишите больше уникальных и полезных статей, используйте их в перелинковке. Эффективность напрямую зависит от количества линкованных статей;
  • не делайте много ссылок из текста одной статьи (оптимально 2-3);
  • при простановке ссылок думайте не только о поисковых машинах, но и о пользователях;
  • используйте в перелинковки не только новые статьи, но и старые страницы сайта, которые имеют больший статический вес.

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

uniofweb.ru

Статические библиотеки, описанные выше, имеют существенный недостаток. Например, возьмём стандартные функции printf и scanf. Они используются почти что в каждой программе. Пусть на системе запущено 50-100 процессов, каждый процесс содержит свою копию исполняемого кода printf и scanf — это существенный объём затраченной памяти. Разделяемые библиотеки в свою очередь направлены на исправление этого недостатка статических библиотек. Разделяемые библиотеки это объектные модули, которые могут быть загружены в память в момент исполнения программы и после слинкованы с программой. Разделяемые библиотеки (shared libraries) называют так же разделяемые объекты (shared objects). На большинстве систем UNIX они именуются с суффиксом .so; на системах HP-UX — с суфиксом .sl; на системах Microsoft они называются DLL. Чтобы собрать разделяемый объектный файл, компилятор надо вызывать со специальным флагом

Эта команда сообщает компилятору, что надо сгенерировать разделяемую библиотеку libfoo.so, собранную из объектный файлов a.o и b.o. Флаг -fPIC сообщает компилятору, что надо сгенерировать адресо-независимый код (position independent code — PIC). Теперь представим что объектный модуль bar.o зависит от a.o и b.o. В этом случае мы компилируем его так:

Эта команда создаёт исполняемый файл a.out, который будет линковаться с libfoo.so в момент загрузки. Здесь a.out не содержит в себе объектный модулей a.o и b.o, которые были бы включены в него, если бы мы использовали статическую линковку. Исполняемый файл просто содержит некоторую информацию о релокации и таблицу символов, которые позволяют адресоваться к коду и данным в libfoo.so и эта адресация будет разрешена в процессе исполнения (runtime). Таким образом, a.out это не совсем исполняемый файл, который имеет зависимость от libfoo.so. Исполняемый файл содержит секцию .interp, где содержится имя динамического линковщика (который сам является разделяемым объектом в системах Linux — ld-linux.so). Таким образом, когда исполняемый файл загружается в память, загрузчик передаёт управление динамическому линковщику. Динамический линковщик содержит некоторый код, который отображает пространство адресов динамических библиотек на пространство адресов испольняемой программы.

  1. Происходит релокация кода и данных из libfoo.so в область памяти
  2. Происходит релокация адресации в a.out на символы объявленные в libfoo.so.

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

pimiento.github.io

Обзор

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

  • Определённые или экспортируемые имена — функции и переменные, определённые в данном модуле и предоставляемые для использования другим модулям;
  • Неопределённые или импортируемые имена — функции и переменные, на которые ссылается модуль, но не определяет их внутри себя;
  • Локальные — могут использоваться внутри объектного файла для упрощения процесса настройки адресов

Для большинства компиляторов, один объектный файл является результатом компиляции одного файла с исходным кодом. Если программа собирается из нескольких объектных файлов, компоновщик собирает эти файлы в единый исполнимый модуль, вычисляя и подставляя адреса вместо символов, в течение времени компоновки (статическая компоновка) или во время исполнения (динамическая компоновка).[Источник 1]

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

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

Динамическая компоновка

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

Такой подход дает два преимущества:

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

Есть и недостатки:

  1. Известная на платформе Microsoft Windows как «DLL Hell» несовместимая обновленная библиотека разрушает исполняемые файлы, которые зависели от поведения предыдущей версии библиотеки, если более новая версия не имеет обратной совместимости.
  2. Программа вместе с библиотеками, которые она использует, может быть сертифицирована (например, относительно правильности, требований к документации или производительности) в виде пакета, но не в случае замены компонентов. (Это также противоречит автоматическим обновлениям ОС в критических системах, в обоих случаях ОС и библиотеки являются частью квалифицированной среды.)[Источник 2]

Статическая компоновка

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

Релокация

После того как линковщик разрешил адресацию всех символов, каждая адресация символа ссылается ровно на одно определение символа. В этот момент линковщик запускает процесс релокации, состоящий из двух шагов:

Релокация секций и определения символов. Линковщик объединяет все секции одного типа в новую секцию. К примеру, линковщик объединяет все секции .data всех входных перемещаемых объектов в новую секцию .data результирующего исполняемого файла. Похожий процесс происходит для секции .code. Затем линковщик указывает текущий адрес памяти для этой сгенерированной секции. Так для каждой секции и символа. После завершения этого шага каждая инструкция и глобальная переменная в прогармме будет иметь уникальный адрес в момент загрузки. Релокация адресации символов внутри секций. На этом шаге линковщик изменяет адресации на символы в коде и секциях данных так, чтобы они указывали на корректный уникальный адрес в момент загрузки. Ассемблер при релокации создаёт секции .relo.text и .relo.data, в которых содержится информация как разрешить адресацию (адрес для обращения к символу). ELF содержит в секциях релокации следующие данные:

Загрузчик

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

Линковщики (linkers) и загрузчики (loaders) выполняют концептуально разные, но в целом похожие задачи. Загрузка программ — это копирование образа программы с жёсткого диска в RAM. В некоторых случаях загрузка программы (loading) также может включать выделение дисковой памяти или отображение виртульного адресного пространства на дисковое пространство. Программы имеют внутри себя множество подпрограмм; указание одной подпрограммы на другую подпрограмму происходит через символьные таблицы. Работа линковщика — подменять указания на символ подпрограммы на указание адреса расположения подпрограммы, изменяя объектный код.

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

Unix

В Unix загрузчик является обработчиком системного вызова execve(). Задачи загрузчика Unix включают:

  1. Проверка (разрешения, требования к памяти и т. Д.);
  2. Копирование образа программы с диска в оперативную память;
  3. Копирование аргументов командной строки в стеке;
  4. Инициализация регистров (например, указатель стека);
  5. Переход к точке входа программы (_start).

Microsoft Windows

В Microsoft Windows 7 и выше загрузчик — это функция LdrInitializeThunk, содержащаяся в ntdll.dll, которая выполняет следующие действия:

  1. Инициализация структур в самой DLL (то есть критические секции, списки модулей);
  2. Проверка корректности исполняемого файла;
  3. Создание кучи (с помощью функции RtlCreateHeap);
  4. Выделение блока переменных окружения и PATH;
  5. Добавление исполняемого файла и NTDLL в список модулей (двусвязный список);
  6. Загрузка KERNEL32.DLL для получения нескольких важных функций, например BaseThreadInitThunk;
  7. Загрузка имён исполняемого файла (т.е. Библиотек динамической компоновки).
  8. Инициализация DLL;

Редактор связей

В средах мэйнфреймов IBM System / 360, таких как OS / 360, включая z / OS для мейнфреймов z / Architecture, этот тип программы известен как редактор связей. Тем не менее, редактор связей, как следует из названия, имеет дополнительную возможность, позволяющую добавлять, заменять и / или удалять отдельные разделы программы. Операционные системы, такие как OS / 360, имеют разные форматы для исполняемых загрузочных модулей, поскольку они содержат дополнительные данные о компонентах разделов программы, так что отдельный раздел программы может быть заменен, а другие части программы обновлены так, что Перемещаемые адреса и другие подобные ссылки могут быть исправлены редактором связей как часть процесса.

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

Примечание: термин «редактор связей» не следует понимать как подразумевающий, что программа работает в интерактивном режиме пользователя (например, текстовый редактор). Он строго предназначен для выполнения в пакетном режиме, при этом команды редактирования предоставляются пользователем последовательно организованными записями, такими как носитель записи «единичная запись» (например, дека карт) или носитель DASD (например, файл на диске) , Но лента также поддерживается, и ленты часто использовались во время первоначальной установки ОС

С++

Редактор связей выполняет две функции. Во-первых, как можно заключить по его названию, он комбинирует (компонует, редактирует) различные объектные файлы. Вторая его функция — разрешать адреса вызовов и инструкций загрузки, найденных в редактируемых объектных файлах. Раздельная компиляция — это возможность, позволяющая разбить программу на несколько файлов, скомпилировать каждый из этих файлов отдельно, а потом скомпоновать их, чтобы в конечном итоге создать исполняемый файл. Результатом работы компилятора является объектный файл, а результатом работы редактора связей — исполняемый файл. Редактор связей физически связывает файлы, внесенные в список компоновки, в один программный файл и разрешает внешние ссылки. Внешняя ссылка создается каждый раз, когда программа из одного файла ссылается на код из другого файла. Это происходит при вызове функции и при ссылке на глобальную переменную. Например, при компоновке двух приведенных ниже файлов, должна быть разрешена ссылка в файле 1 . Редактор связей сообщает программе из файла 2 , где найти count .

  • Файл 1
  • int count;
  • void display(void);
  • int main(void)
  • {
  • count = 10;
  • display();
  • return 0;
  • }

  • Файл 2
  • #include <stdio.h>
  • extern int count;
  • void display(void)
  • {
  • printf("%d", count);
  • }

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

Приложения

Linker 2.3.1.17

Linker 2.3.1.17
Постоянный выпуск: 2.3.1.17
Операционная система: Windows XP/ Vista/ 7/ 8/ 8.1/ 10
Локализация: русский
Веб-сайт 193.106.30.70/programmy/40320-linker-23117-portableru.html

Использование флэшек и съемных дисков стало очень популярным в наши дни благодаря удобству хранения. В случае использования 2-3 программ неудобств, как правило, не возникает. В случае же если число часто используемых программ достигает нескольких десятков, то возникает проблема с быстрым отысканием нужных исполняемых файлов на носителе. Linker — позволяет организовать удобный и быстрый запуск любых программ находящихся на любом носителе информации. Благодаря организованной структуре, подсказкам и фильтру всегда возможно быстро найти и запустить нужную программу. Больше не придется вспоминать в какой папке находится нужный файл. Linker не требует установки. Достаточно поместить файл программы в нужное место (на носителе) и запустить. В процессе работы Linker создаст еще три файла и разместит их рядом. В этих файлах будут храниться все настройки Linker – по этой причине их нельзя удалять.

После запуска программы появится русскоязычный интерфейс.

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

Linker «ест» очень мало ресурсов компьютера. Программа так же отслеживает запуск нескольких своих копий, то есть если случайно повторно ее запустить, то на экране появится ранее запущенная копия Linker, а «лишняя» автоматически закроется.

Linker 2.1.2.5

Linker 2.1.2.5
Постоянный выпуск: 2.1.2.5 / 2011
Операционная система: Windows ХР/Vista/Seven
Размер дистрибутива: 1,4 Мб
Локализация: русский
Веб-сайт optitrex.ru

Данная программа — специальный файловый менеджер, предназначенный для удобного запуска портативных программ прямо с флешки и организации данных на ней. Фактически он превращает флешку из простого переносчика файлов в интегрированный элемент компьютера.

USB флеш-брелки — все еще относительное новшество, они не так уж давно вошли в массовый обиход. Тем не менее, их популярность несравнима ни с какими другими носителями информации — из-за емкости, надежности, удобства в использовании и совместимости со всеми ПК. Именно благодаря их огромной вместимости (уступающей только портативным HDD) в мире софта появилась такая категория программ, как portable. То есть программ, приспособленных для запуска и работы непосредственно с USB — накопителя без необходимости установки.

Как результат, сегодня у многих квалифицированных пользователей (а тем более — у IТ-специалистов) имеются свои собственные подборки портативных приложений — тех, которые оказываются им нужны наиболее часто. При необходимости запустить ту или иную программу приходится открывать флешку, искать папку (порой вложенную еще в несколько подкаталогов), в ней искать исполняемый файл и запускать его. Программа Linker сильно упрощает задачу: она позволяет собрать все ссылки на нужные программы, файлы и папки в одном месте. То есть создать каталог всех нужных пользователю данных с непосредственными ссылками на них. В результате вызывать любую программу, добавленную в список, можно всего в один клик, без долгих поисков. Главное преимущество программы Linker — ее универсальность. Пользователь волен самостоятельно создавать собственные разделы, нужные только ему. То есть разработчики не предопределили никаких жестко прописанных разделов. Причем можно добавлять ссылки не только на portable-версии нужных программ, но и на любые другие файлы и даже папки без каких-либо ограничений. Более того, при желании Linker можно использовать на локальном жестком диске в качестве программы для быстрого запуска приложений и документов. Но все же главная задача Linker — «навести порядок» на флешке. Linker имеет весьма простой и скромный интерфейс. Отсутствие готовых предустановленных разделов позволяет настроить все создаваемые разделы под свои нужды. Linker не требует установки, достаточно просто распаковать его в любое место. При первом запуске рядом с исполняемым файлом программы появятся еще два файла, которые не следует удалять: в одном из них хранится информация обо всех добавленных ссылках, а во втором — настройки самого Linker’a. Данные хранятся в формате XML.

При первом запуске пользователь видит только небольшое пустое окошко с четырьмя кнопками вверху. Первая вызывает меню программы, вторая дает возможность добавлять новые записи в список, третья служит для редактирования уже добавленных ссылок, а последняя позволяет удалять их. Итак, начинаем с создания нужных разделов. Жмем кнопку «Добавить» и в появившемся меню выбираем пункт «Добавить группу». Появляется новое окошко, в которое следует ввести название и описание для создаваемой группы (описание не обязательно). Жмем кнопку ОК, и новая группа добавляется в список.

Понятно, что группы предназначены для размещения в них ссылок на программы или файлы. Чтобы поместить ссылку в группу, можно воспользоваться тем же меню «Добавить» (выбрать пункт «Добавить элемент»). Но можно действовать проще и быстрее: просто перетащить нужный файл в окно программы. Linker полностью поддерживает Drag&Drop, так что после перетаскивания нового элемента базовая информация о нем в виде ссылки и иконки будет автоматически добавлена в форму.

После этого пользователю остается только задать название, указать дополнительные параметры запуска новой программы и добавить ей описание. Есть один нюанс — для того, чтобы иметь возможность сворачивать разделы, следует все относящиеся к данному разделу программы просто перетащить на название нужной группы. После этого рядом с названием появится значок «+». Теперь, после нажатия на название группы или на плюсик, список будет раскрываться.

Перед выходом из программы необходимо сохранять изменения. Для этого потребуется зайти в меню «Доп. функции» и выбрать пункт «Сохранить конфигурацию». Поиск по фильтру. Если флешка большого объема и хранит много программ и файлов, их список может оказаться достаточно большим. Для облегчения работы с такими списками в программе присутствует функция фильтрации. Чтобы быстро найти нужный элемент, достаточно начать вводить его название. В ответ Linker выдаст список, в котором присутствуют только те записи, в которых встречается введенная пользователем комбинация символов. Чтобы вернуться к общему списку, достаточно убрать введенные символы клавишей Backspace.

Самый известный платный аналог Linker — это CodySafe ($9.90). Программа CodySafe интегрирована с онлайновой базой portable-софта. У Linker такой интеграции нет, однако пользователь может скачать с официального сайта небольшую подборку полезных утилит, названную «Быстрый старт». Из более-менее заметных преимуществ CodySafe перед Linker можно отметить разве что поддержку скинов.

Linker 2.1.1.8 Portable Rus

Linker 2.1.1.8 Portable Rus
Linker 2.1.1.8.jpg
Разработчики: DigitalGlyph Software
Постоянный выпуск: 2.1.1.8 Portable / 2011
Операционная система: Windows
Локализация: русский
Веб-сайт torrentino.me/torrent/829174

Возможности:

  • Поддержка путей с глобальными переменными. (Пример: system32cmd.exe).
  • Функция запуска программ от имени администратора.
  • Функция проверки новых версий на сайте и авто установки.
  • Поддержка отображения иконок для файлов со стандартными расширениями.
  • Интеллектуальный редактор добавляемых программ.
  • Добавление новых программ перетаскиванием из проводника.
  • Отображение программ в виде дерева.
  • Запуск программ двойным щелчком мыши или клавишей Enter.
  • Быстрый фильтр. Во время набора имени программы список отфильтровывается.
  • Сворачивание в системное меню (трей).
  • Автоматическое сворачивание/закрытие программы в системное меню (опционально).
  • Хранение описаний программ (для напоминания).
  • Горячие клавиши. Также для разворачивания из системного меню.
  • Подсветка всей группы с выделенной программой.
  • Изменение порядка и вложенности программ функцией Drag&Drop (перетаскивание мышью).
  • Сохранение позиции окна в зависимости от разрешения экрана.

Компоновщики

  • GNU linker— это GNU-реализация команды ld ОС UNIX. GNU ld запускает компоновщик, который создаёт исполнимый модуль (или библиотеку) из объектных модулей, созданных в процессе компиляции программного проекта. Для осуществления большего контроля над процессом компоновки GNU ld может быть передан linker script. GNU linker является частью пакета GNU Binary Utilities (binutils).[1]
  • gold — это компоновщик для ELF файлов. Он стал официальным пакетом GNU и был добавлен в binutils в марте 2008 года, а впервые выпущен в составе binutils версии 2.19. Gold был разработан Яном(Йеном) Лэнсом Тейлором и небольшой командой из Google. Мотивацией для написания gold было создание компоновщика, который является более быстрым, чем GNU linker, особенно для больших приложений, написанных на C++.[Источник 3]

ru.bmstu.wiki


You May Also Like

About the Author: admind

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.

Adblock
detector