На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Правила трёх "С"
Пожалуйста,
1. Соблюдайте правила Форума.
2. Слушайте советы Модераторов.
(например, http://forum.sources.ru/index.php?act=ST&f=7&t=80382 )
3. Сверяйтесь с учебником по Великому и Могучему
  
> GCC, LD, AR ... , Утилиты для программирования...
    ПРЕДУПРЕЖДЕНИЕ!!!

    До того, как начнете читать убедитесь, что:
    0. У Вас на машине установлен один из Linux'ов. То, что здесь написано, справедливо в общем случае для
    любой системы в которй используются GNU C & utils, не только для Linux, но не всегда, т.к. общий случай на то
    он и "общий". В Linux'е это работает. В других системах -- смотрите сами. Хотя, общие идеи и имена программ
    могут и совпадать. Дело в том, что в версиях UNIX, распространяемых на коммерческой основе, пакеты разработчика
    всегда предоставлялись разработчиками ОС. В той же SCO Open Server/Open DeskTop пакет разработчика предоставлялся
    на основе отдельного Лицензионного соглашения и устанавливался в системе отдельно. Но CC, LD, AR были и там.
    Я никоим образом не претендую на абсолютную полноту изложения. Этот документ не более
    чем "стартовая точка" входа ("въезда", "вкуривания", "всасывания", ...) во всю эту байду.

    1. /dev/head != /dev/ass

    2. Power is on in the /dev/brains, subsytem of /dev/head. 'Smoke test' on /dev/brain was passed. Tested and
    approved.

    Если не уверены что Вам это нужно, то не читайте! И, если Вы завалили систему, то не говорите, что Вас не
    предупреждали. Под 'системой' имеется в виду и Ваша /dev/head в том числе. Здесь описаны "развлечения надолго".
    Я прекрасно понял что я сподобился написать... Хэх! "Добро пожаловать на машинный уровень, Нео, мне нравится
    бродить здесь по ночам..." (с) какой-то пенек, по-моему, Советник, из релиза Матрицы то ли два, то ли три...


    Почему gcc? Есть же cc...
    Ну, gcc это целая веселая семейка компиляторов. Там... много всего. Кто и как -- не знаю, но ваш покорный
    слуга разбирался только с С и С++. Было бы интересно, если бы кто-то еще продолжил бы тему.

    В Linux gcc это то же самое, что и сс. Если Вы поменяете вызов gcc на, не спорю, более стандартный cc, то в
    Linux получите то же самое.

    Итак, что же делает компилятор-то? По идее, берет исходные тексты, расположенные в разных модулях и порождает
    объектный код, по одному файлу для каждого исходного модуля. Качество кодогенерации определяется
    применяемыми методиками оптимизации. См. тему по "методикам". Проблема в том, что для каждого ключа оптимизации
    используется "каскад" методик и разделить что происходит при использовании одного ключа, а что происходит при
    использовании другого, практически невозможно без анализа сырцов gcc. Мне, как-то, правда и без такового
    головной боли более чем...

    В Windows, как правило, любой поставщик компилятора считает делом чести навязать Вам свою среду разработки. Причем,
    зачем это делается, совсем не ясно, но это уже дело windows... Важно отметить, что в Linux не важно какой конкретно
    IDE Вы пользуетесь. Если компилятор gcc, то Вы можете пользоваться какой угодно. Главное в том, что Вы можете как
    "врукопашную" редактировать Makefile, так и препоручать это среде (IDE). Без разницы -- кто как привык.

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

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

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

    Для новичков -- man gcc, info gcc. Про методики оптимизации -- гляньте в Форуме -- уже было.

    Что еще будет добавлено. Несколько позже... Ну, GCC, сам по себе, "оптимистический" компилятор... Хэх! Мне бы его
    заботы... Конечно же, будет добавлена тема по семейству программ, именуемых по их родоначальнику -- lint'у,
    "линтами". Точнее, по одной проге -- SPLint, как по правопреемнице и наследнице lint'а в деле отлова багов и
    глюков. Это единственное, что дает вероятность 0.8, что все что Вы могли не заметить сами, будет Вам
    продемонстрировано.

    Что есть "линкер"?
    Он же -- "редактор связей". Может вызываться как сам по себе -- напрямую, так и работать из-под gcc.
    Задачи линкера поскромнее. Линкер берет объектные файлы (по сути дела, почти готовые к запуску), строит систему
    ссылок в пределах рабочего проекта (модуля или модулей) и, на основании Ваших указаний, добавляет внешние ссылки или
    внешний код. В любом случае, при генерации исполняемого файла, к тому, что получается из суммы объектных файлов,
    будет добавлен стартовый код приложения и ссылки на внешние модули. К примеру, Вы всегда можете посмотреть на список
    библиотек времени выполнения для какого-либо file, если дадите команду ldd file. Более подробная и интересная
    информация будет выдана по запросу readelf file, если Вы работаете с ELF-файлами. Или по objdump.

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

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

    Что происходит в случае линковки с использованием разделяемых библиотек. Линкер, создавая таблицу ссылок, которая есть
    в каждом исполняемом модуле, "вкатывает" туда ссылки на модули разделяемых библиотек для данной системы и на имена
    функций, присутствующие в этих библиотеках. В таком случае система должна "знать" где ей найти те или иные библиотеки
    времени исполнения. Таким "перстом указующим" в системе является файл /etc/ld.so.cache, работающий с библиотеками,
    указанными в /etc/ld.so.conf. Конфигурация производится через /sbin/ldconfig.

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

    Важно. Применяя те или иные ключи линкера, Вы можете заставить линкер сгенерировать нетепичные для Вашей версии ОС файлы.
    К примеру, Вы можете указать иной формат файла, адреса расположения тех или иных сегментов (.data, .text, .bss), вывести
    в файл или на терминал таблицу ссылок (имен) в генерируемом файле (map) и еще многое другое. См. man ld.

    OOPS! Чуть не забыл! Для ассемблера (as86) есть и свой линкер -- (ld86).

    Что есть "библиотека". Как ее создать?
    Вообще говоря, в Linux есть (хэх!) три типа библиотек. Это:
    1. Статические библиотеки. Это библиотеки, которые линкуются на этапе сборки Вашей программы к Вашему коду. По сути дела,
    это набор объектных файлов и заголовочный файл, в котором сказано к каким данным и коду Вы можете получить доступ и
    как использовать.
    Т.е. из файла src.c можно создать либу вот так:
    ExpandedWrap disabled
       
      $ gcc -Wall -g -c -o libtest.o src.c  -- здесь создаем объектник.
      $ ar rcs libtest.a libtest.o          -- здесь создаем либу. Обратите на расширение -- .a! Это она и есть.

    Либа создана. Теперь можно скопировать ее в стандартные пути и тогда либо Вы сможете просто указывать ее имя при линковке,
    либо можете через ключ -L. указать местный каталог и через ключ -ltest указать эту либу. Дело Ваше.
    Единственный минус -- при линковке Ваш рещультирующий файл, использующий эту либу будет "толстеть" ровно настолько, насколько
    "толста" Ваша либа.

    2. Динамические библиотеки. Это библиотеки, которые загружаются программой в момент начала ее исполнения. Т.е. общие
    для многих программ данные и функции (методы) вынесены в одну либу. Как только программа, использующая такую либу стартует,
    система подгружает эту либу в адресное пространство процесса и, в случае обращения к функции (данным) в этой либе,
    не надо ничего и нигде искать все уже готово -- только обращайся... В то же время, экономится дисковое пространство за счет
    того, что особоупотребительные функции, применяемые множеством программ, не хранятся в теле этих программ, а выделены в
    отдельные модули. Ну и что, что память "кушается"? В конце-то концов, одновременно не могут работать все бинарные
    файлы из дистрибутива! Т.е. количество активных процессов всегда конечно и счетно. Вот только запуск процессов -- величина
    абсолютно случайная, т.к. мы не можем предсказать когда конкретно и какой программой пользователь воспользуется...
    Случайные процессы в чистом их виде. Как и в первом случае:
    ExpandedWrap disabled
       
      $ gcc -fPIC -Wall -g -c src.c                                          -- делаем объектник, см. ниже.
      $ gcc -g -shared -Wl -soname,libtest.so.1 -o libtest.so.1.0 src.o -lc  -- получаем библиотеку, основанную на
                                            стандартной библиотеке С (libc). См. ниже.
                                            Номера 1 и 0 -- старшая и младшая цифири ревизии
                                            версии 1.0, к примеру. Чтоб не путаться.
                                            
      $ su
      # cp /usr/local/lib libtest.so.1.0 -- копируем полученные файл libtest.so.1.0 в каталог /usr/lib или /usr/local/lib.
                            Это проще, т.к. по идее, они должны быть уже включены в общесистемные пути.
      Далее. Здесь же:
      # cd /usr/local/lib
      # ln -sf libtest.so.1.0 libtest.so.1  
      # ln -sf libtest.so.1   libtest.so    -- получаем имя, под которым либа "известна" компилятору.
      # /sbin/ldconfig                      -- перечитываем библиотеки.
      # exit
      $ компилимся, линкуемся, радуемся новой либе.

    Собственно, получили три файла, стандартных для разделяемой библиотеки... Код только в одном. Теперь, в случае линковки,
    мы можем ею пользоваться наряду со стандартными либами. А вот в случае переноса проекта на другую машину/платформу, все-таки
    придется провериться насколько Ваша либа соотвествует целевой платформе. Проверяйте все и всегда!

    3. Загружаемые динамически библиотеки.
    Это довольно интересные библиотеки, к которым обращение идет через механизм функций dl* (dlopen(),... dlclose()).
    Самое главное в том, что эти либы грузятся в коде только тогда, когда Вам они непременно нужны, причем, Вы передаете
    управление только функции, которая Вам в данный момент нужна, а далее можете просто выгрузить весь этот модуль или
    использовать другую функцию из этого же модуля. Т.е. это -- готовый механизм плагинов, по сути дела, wrapper, сразу же
    предоставляемый к Вашим услугам!

    Общие замечания.
    Когда, IMHO, стоит делать библиотеки.
    Предположим, Вы пишете некий проект, в котором есть ряд независимых программ. Что делать, если Вы вдруг замечаете,
    что вот этот ломоть кода Вы используете более чем в одной программе? Ну, скажем, работа с COMM-портами...

    Да все просто -- создайте библиотеку. Т.е. создайте заголовочный файл, где Вы можете описать как вызовы функций, так и
    данные для работы. Создайте модуль (модули) исходного кода, где реализуйте все то, что вы хотите реализовать.
    Причем, Вам нужно будет компилиться с ключами -fPIC. Это дает код, не зависящий от конкретного
    адреса. Кроме того, Вам могут понадобиться ключ -rdynamic (-Wl, -export-dynamic -- в случае, если Вы
    планируете работать с кодом на не-Linux платформе). man gcc.

    Сразу. Здесь же замечание. Я довольно часто пользуюсь справочником Б. Строуструпа. И заметил интересный момент. Если
    где-то в третьем издании он говорил примерно так:
    ".... если Вам понравилась идея метода или Вы считаете, что кто-то этим методом класса может воспользоваться,
    то реализуйте этот метод....", то в пятом издании он уже начал сетовать на то, что код стал слишком "жирным",
    библиотеки, в том числе и стандартные, -- ну просто необъятными...

    Угу. При таком-то подходе... Прав был А. Голуб, когда говорил, что не нужно реализовывать общие методы, а нужно
    реализовывать конкретные методы. Ну, или как-то так, на абсолютную точность цитат и не претендую. Действительно,
    не стоит "раздувать" объем кода. Правило KISS в действии... Не доводите, Бога ради, до абсурда!

    С разделяемыми библиотеками связана потенциально опасная проблема. Не стоит ее ни приувеличивать, ни недооценивать.
    Дело в том, что на слабоконтролируемой системе есть возможность "прикрутить" свою либу и переопредитить
    стандартные пути для данного пользователя или для всей системы в целом от рута:
    ExpandedWrap disabled
       
      LD_LIBRARY_PATH='pwd':$LD_LIBRARY_PATH;export LD_LIBRARY_PATH

    Что запихать в либу? Да все, что Вам в голову придет. Можете хоть свой руткит изваять на базе вот такой "либы"...
    Не просто, но возможно, поверьте.

    Т.е., если Вы производите ревизию каталогов пользователей и вдруг видите вот такое... То можно начинать бить тревогу,
    хоть она и женского рода... В любом случае, это уже повод для начала задания вопросов, особенно, если что-то странное
    творится. К примеру, если этот каталог принадлежит "просто пользователю", который про программирование, скажем так,
    "слышал"... или вдруг ни стого, ни с сего нагрузка на сеть или систему возрасла, хотя ничего не меняли до того,
    как... Если честно, то все, что в технологической системе Вами не контролируется, то потенциально может контролироваться
    кем-то еще. У меня не паранойя, просто я так думаю и, поверьте, не безосновательно. Слушайте систему и сеть и
    слышьте их! Или меняйте специальность, пока не совсем поздно.

    Кстати, посмотрите на возможные пути (LD_). Там... много интересного.

    Обслуживанием библиотек занимается "архивариус" -- ar. Внимательно man ar, info ar, Бога ради
    до всех "разборок" с библиотеками, тем более, со стандартными! А то "запорете" либу, придется либо gcc переставлять,
    да и то, если "убъете" стандартую, то и не встанет у Вас ничего.... Хэх! Перечитал -- имеется ввиду конечно же gcc...
    ;D Если Вы "вкатаете" в стандратную либу свой код и, мало того, своим кодом поменяете какую-то стандартную функцию,
    то, если Вы ошиблись, Вы "грохнете" тем самым стандартную функцию -- "раз" и "два" -- либу в целом. Как тогда
    компилиться будем? Выходов несколько -- компилить именно эту либу на другой (сходной) машине и подменять файл
    либы (только вначале придется ее "выводить из оборота", т.к. если она будет загружена, то фиг Вы ее выгрузите и
    поменяете), либо просто и без особых затей переставлять все, что, IMHO, в данном случае может быть просто проще.

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

    Что Вам еще может понадобиться -- man nm. Nm -- это программа, показывающая символические имена в библиотеках.
    info libtool -- для более простой работы с библиотеками.

    PS Дальше будет формат ELF, старт программы... Оставайтесь с нами... :D
    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
    0 пользователей:


    Рейтинг@Mail.ru
    [ Script execution time: 0,0156 ]   [ 15 queries used ]   [ Generated: 25.04.24, 00:41 GMT ]