Создать бинарный файл линукс

Обновлено: 06.07.2024

Чтобы сразу начать программировать, создадим еще один клон известной программы "Hello World". Что делает эта программа, вы знаете. Откройте свой любимый текстовый редактор и наберите в нем следующий текст:

Я назвал свой файл hello.c. Вы можете назвать как угодно, сохранив суффикс .c. Содержимое файла hello.c - это исходный код программы ('program source', 'source code' или просто 'source'). А hello.c - это исходный файл программы ('source file'). Hello World - очень маленькая программа, исходный код которой помещается в одном файле. В "настоящих" программах, как правило, исходный код разносится по нескольким файлам. В больших программах исходных файлов может быть больше сотни.

Наш исходный код написан на языке программирования C. Языки программирования были придуманы для того, чтобы программист мог объяснить компьютеру, что делать. Но вот беда, компьютер не понимает ни одного языка программирования. У компьютера есть свой язык, который называют машинным кодом или исполняемым кодом ('executable code'). Написать Hello World в машинном коде можно, но серьезные программы на нем не пишутся. Исполняемый код не только сложный по своей сути, но и очень неудобный для человека. Программа, которую можно написать за один день на языке программирования будет писаться целый год в машинном коде. Потом программист сойдет с ума. Чтобы этого не случилось, был придуман компилятор ('compiler'), который переводит исходный код программы в исполняемый код. Процесс перевода исходного кода программы в исполняемый код называют компиляцией.

Вы наверняка догадались, что опция -o компилятора gcc указывает на то, каким должно быть имя выходного файла. Как вы позже узнаете, выходным файлом может быть не только бинарник. Если не указать опцию -o, то бинарнику, в нашем случае, будет присвоено имя a.out.

Осталось только запустить полученный бинарник. Для этого набираем в командной строке следующую команду:

Мы рассмотрели идеальный случай, когда программа написана без синтаксических ошибок. Попробуем намеренно испортить программу таким образом, чтобы она не отвечала канонам языка C. Для этого достаточно убрать точку с запятой в конце вызова функции printf(): Теперь, если попытаться откомпилировать программу, то компилятор выругается, указав нам на то, что он считает неправильным: В первой строке говорится, что в файле hello.c (у нас он единственный) в теле функции main() что-то произошло. Вторая строка сообщает, что именно произошло: седьмая строка файла hello.c вызвала ошибку (error). Далее идет расшифровка: синтаксическая ошибка перед закрывающейся фигурной скобкой.

Заглянув в файл hello.c мы с удивлением обнаружим, что нахулиганили мы не в седьмой, а в шестой строке. Дело в том, что компилятор обнаружил нелады только в седьмой строке, но написал 'before' (до), что означает "прокручивай назад".

Естественно, пока мы не исправим ошибку, ни о каком бинарнике не может идти и речи. Если мы удалим старый бинарник hello, доставшийся нам от прошлой компиляции, то увидим, что компиляция испорченного файла не даст никакого результата. Однако иногда компилятор может лишь "заподозрить" что-то неладное, потенциально опасное для нормального существования программы. Тогда вместо 'error' пишется 'warning' (предупреждение), и бинарник все-таки появляется на свет (если в другом месте нет явных ошибок). Не следует игнорировать предупреждения, за исключением тех случаев, когда вы на 100% знаете, что делаете.

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

2.2. Мультифайловое программирование

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

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

Давайте сначала разберемся, как из исходного файла получается бинарник. Подобно тому как гусеница не сразу превращается в бабочку, так и исходный файл не сразу превращается в бинарник. После компиляции создается объектный код. Это исполняемый код с некоторыми "вкраплениями", из-за которых объектный код еще не способен к выполнению. Сразу в голову приходит стиральная машина: вы ее только что купили и она стоит у вас дома в коробке. В таком состоянии она стирать не будет, но вы все равно рады, потому что осталось только вытащить из коробки и подключить.

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

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

В Linux в качестве линковщика используется программа ld, обладающая приличным арсеналом опций. К счастью gcc самостоятельно вызывает компоновщик с нужными опциями, избавляя нас от "ручной" линковки.

Попробуем теперь, вооружившись запасом знаний, написать мультифайловый Hello World. Создадим первый файл с именем main.c: Теперь создадим еще один файл hello.c со следующим содержимым:

Здесь функция main() вызывает функцию print_hello(), находящуюся в другом файле. Функция print_hello() выводит на экран заветное приветствие. Теперь нужно получить два объектных файла. Опция -c компилятора gcc заставляет его отказаться от линковки после компиляции. Если не указывать опцию -o, то в имени объектного файла расширение .c будет заменено на .o (обычные объектные файлы имеют расширение .o): Итак, мы получили два объектных файла. Теперь их надо объединить в один бинарник: Компилятор "увидел", что вместо исходных файлов (с расширением .c) ему подбросили объектные файлы (с расширением .o) и отреагировал согласно ситуации: вызвал линковщик с нужными опциями.

Давайте разберемся, что же все-таки произошло. В этом нам поможет утилита nm. Я уже оговорился, что объектные файлы содержат таблицу символов. Утилита nm как раз позволяет посмотреть эту таблицу в читаемом виде. Те, кто пробовал программировать на ассемблере знают, что в исполняемом файле буквально все (функции, переменные) стоит на своей позиции: стоит только вставить или убрать из программы один байт, как программа тут же превратиться в груду мусора из-за смещенных позиций (адресов). У объектных файлов особая роль: они хранят в таблице символов имена некоторых позиций (глобально объявленных функций, например). В процессе линковки происходит стыковка имен и пересчет позиций, что позволяет нескольким объектным файлам объединиться в один бинарник. Если вызвать nm для файла hello.o, то увидим следующую картину: О смысловой нагрузке нулей и литер U,T мы будем говорить при изучении библиотек. Сейчас же важным является то, что в объектном файле сохранилась информация об использованных именах. Своя информация есть и в файле main.o: Таблицы символов объектных файлов содержат общее имя print_hello. В процессе линковки высчитываются и подставляются в нужные места адреса, соответствующие именам из таблицы. Вот и весь секрет.

2.3. Автоматическая сборка

В предыдущем разделе для создания бинарника из двух исходных файлов нам пришлось набрать три команды. Если бы программу пришлось отлаживать, то каждый раз надо было бы вводить одни и те же три команды. Казалось бы выход есть: написать сценарий оболочки. Но давайте подумаем, какие в этом случае могут быть недостатки. Во-первых, каждый раз сценарий будет компилировать все файлы проекта, даже если мы исправили только один из них. В нашем случае это не страшно. Но если речь идет о десятках файлов! Во-вторых, сценарий "намертво" привязан к конкретной оболочке. Программа тут же становится менее переносимой. И, наконец, простому скрипту не хватает функциональности (задание аргументов сборки и т. п.), а хороший скрипт (с многофункциональными прибамбасами) плохо модернизируется.

Выход из сложившейся ситуации есть. Это утилита make, которая работает со своими собственными сценариями. Сценарий записывается в файле с именем Makefile и помещается в репозиторий (рабочий каталог) проекта. Сценарии утилиты make просты и многофункциональны, а формат Makefile используется повсеместно (и не только на Unix-системах). Дошло до того, что стали создавать программы, генерирующие Makefile'ы. Самый яркий пример - набор утилит GNU Autotools. Самое главное преимущество make - это "интеллектуальный" способ рекомпиляции: в процессе отладки make компилирует только измененные файлы.

То, что выполняет утилита make, называется сборкой проекта, а сама утилита make относится к разряду сборщиков.

Любой Makefile состоит из трех элементов: комментарии, макроопределения и целевые связки (или просто связки). В свою очередь связки состоят тоже из трех элементов: цель, зависимости и правила.

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

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

Теперь рассмотрим пример. Попробуем составить сценарий сборки для рассмотренного в предыдущем разделе мультифайлового проекта Hello World. Создайте файл с именем Makefile: Обратите внимание, что в каждой строке перед вызовом gcc, а также в строке перед вызовом rm стоят табуляции. Как вы уже догадались, эти строки являются правилами. Формат Makefile требует, чтобы каждое правило начиналось с табуляции. Теперь рассмотрим все по порядку.

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

Первая связка имеет цель hello. Цель отделяется от списка зависимостей двоеточием. Список зависимостей отделяется от правил символом новой строки. А каждое правило начинается на новой строке с символа табуляции. В нашем случае каждая связка содержит по одному правилу. В списке зависимостей перечисляются через пробел вещи, необходимые для выполнения правила. В первом случае, чтобы скомпоновать бинарник, нужно иметь два объектных файла, поэтому они оказываются в списке зависимостей. Изначально объектные файлы отсутствуют, поэтому требуется создать целевые связки для их получения. Итак, чтобы получить main.o, нужно откомпилировать main.c. Таким образом файл main.c появляется в списке зависимостей (он там единственный). Аналогичная ситуация с hello.o. Файлы main.c и hello.c изначально существуют (мы их сами создали), поэтому никаких связок для их создания не требуется.

Особую роль играет целевая связка clean с пустым списком зависимостей. Эта связка очищает проект от всех автоматически созданных файлов. В нашем случае удаляются файлы main.o, hello.o и hello. Очистка проекта бывает нужна в нескольких случаях: 1) для очистки готового проекта от всего лишнего; 2) для пересборки проекта (когда в проект добавляются новые файлы или когда изменяется сам Makefile; 3) в любых других случаях, когда требуется полная пересборка (напрмиер, для измерения времени полной сборки).

Теперь осталось запустить сценарий. Формат запуска утилиты make следующий: Опции make нам пока не нужны. Если вызвать make без указания целей, то будет выполнена первая попавшаяся связка (со всеми зависимостями) и сборка завершится. Нам это и требуется: В процессе сборки утилита make пишет все выполняемые правила. Проект собран, все работает.

Теперь давайте немного модернизируем наш проект. Добавим одну строку в файл hello.c: Теперь повторим сборку: Утилита make "пронюхала", что был изменен только hello.c, то есть компилировать нужно только его. Файл main.o остался без изменений. Теперь давайте очистим проект, оставив одни исходники: В данном случае мы указали цель непосредственно в командной строке. Так как целевая связка clean содержит пустой список зависимостей, то выполняется только одно правило. Не забывайте "чистить" проект каждый раз, когда изменяется список исходных файлов или когда изменяется сам Makefile.

2.4. Модель КИС

Любая программа имеет свой репозиторий - рабочий каталог, в котором находятся исходники, сценарии сборки (Makefile) и прочие файлы, относящиеся к проекту. Репозиторий рассмотренного нами проекта мультифайлового Hello World изначально состоит из файлов main.c, hello.c и, собственно, Makefile. После сборки репозиторий дополняется файлами main.o, hello.o и hello. Практика показывает, что правильная организация исходного кода в репозитории не только упрощает модернизацию и отладку, но и предотвращает возможность появления многих ошибок.

Модель КИС (Клиент-Интерфейс-Сервер) - это элегантная концепция распределения исходного кода в репозитории, в рамках которой все исходники можно поделить на клиенты, интерфейсы и серверы.

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

Часто бывает, что клиент сам становится сервером, точнее начинает играть роль промежуточного сервера. Хороший пример - наш мультифайловый Hello World. Здесь функция print_hello() (клиент) пользуется услугами стандартной библиотеки языка C (сервер), вызывая функцию printf(). Однако в дальнейшем функция print_hello() сама становится сервером, предоставляя свои услуги функции main(). В языке C++ довольно часто клиент создает производный класс, который наследует некоторые механизмы базового класса сервера. Таким образом клиент сам становится сервером, предоставляя услуги своего производного класса.

Клиент с сервером должны "понимать" друг друга, иначе взаимодействие невозможно. Интерфейс (протокол) - это условный набор правил, согласно которым взаимодействуют клиент и сервер. В нашем случае (мультифайловый Hello World) интерфейсом (протоколом) является общее имя в таблице символов двух объектных файлов. Такой способ взаимодействия может привести к неприятным последствиям. Клиент (функция main()) не знает ничего, кроме имени функции print_hello() и, наугад вызывает ее без аргументов и без присваивания. Иначе говоря, клиент не знает до конца правил игры. В нашем случае прототип функции print_hello() неизвестен.

и если произойдет сбой, скажем, из-за разрешений, вы можете попробовать это перед выполнением

Надеюсь, это поможет

:-) Если вы не опечатка, почему вы используете ./commonRT вместо ./commonKT ??

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

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

Обратите внимание на несоответствия, такие как -msse4.2 [enabled] на хосте сборки, но отсутствует sse4_2 флаг в возможностях процессора.

Если это не помогает, предоставьте выходные данные ldd commonKT как на хосте сборки, так и на хосте выполнения.

Это ответ на @craq:

Я немного удивлен, что вам пришлось «установить его на исполняемый файл» - мой gcc всегда устанавливает сам флаг исполняемого файла , Это говорит о том, что gcc не ожидал, что это будет конечный исполняемый файл, или что он не ожидал, что он будет исполняемым в этой системе.

Теперь я попытался создать объектный файл, вот так:

С другой стороны, таким образом, выходные данные команды file идентичны вашей:

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

Я говорю следующее: я подозреваю, что это как-то связано с тем, как вы компилируете и связываете свой код. Может быть, вы можете пролить свет на то, как вы это делаете?

Единственный способ, который подходит мне ):

Затем запустите его, написав

Если вы получили ошибку разрешения, вам может потребоваться запустить приложение с привилегиями root:

опция компиляции -c делает компиляцию только компиляцией и сборкой, но без ссылки.

Если это не опечатка, как указывалось ранее, это может быть неправильными параметрами компилятора, такими как компиляция 64-битной под 32-битной. Это не должен быть набор инструментов.

полный путь для двоичного файла. Например: /home /vitaliy2034 /имя_бинального_файла. Или же используйте директиву "./+binary_file_name". './' в системе unix возвращает полный путь к каталогу, в котором вы открываете терминал (оболочку). Я надеюсь, что это помогает. Извините за мой английский язык)

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

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

Подготовка

Если вы хотите отобразить содержимое каталога, используйте команду ls .

Создание файла с помощью сенсорной команды

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

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

Если файл file1.txt не существует, приведенная выше команда создаст его, в противном случае она изменит свои временные метки.

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

Создание файла с помощью оператора перенаправления

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

Это самая короткая команда для создания нового файла в Linux.

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

Создание файла с помощью команды cat

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

Чтобы создать новый файл, выполните команду cat, за которой следует оператор перенаправления > и имя файла, который вы хотите создать. Нажмите Enter введите текст и, когда вы закончите, нажмите CRTL+D чтобы сохранить файлы.

Создание файла с помощью команды echo

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

Если вы хотите создать пустой, просто используйте:

Создание файла с использованием Heredoc

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

Например, чтобы создать новый файл file1.txt вы должны использовать следующий код:

Тело heredoc может содержать переменные, специальные символы и команды.

Создание большого файла

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

Использование команды dd

Команда dd в основном используется для преобразования и копирования файлов.

Чтобы создать файл с именем 1G.test размером 1 ГБ, выполните:

Использование команды fallocate

fallocate командной строки для выделения реального дискового пространства для файлов.

Следующая команда создаст новый файл с именем 1G.test размером 1 ГБ:

Выводы

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

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

После выполнения генерируйте файл hello.i, откройте файл, мы найдем, что пакет stdio.h запущен.

-S

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

Проверьте файл Hello.s

gcc_s

-c

Только предварительная обработка, компиляция и сборка активирована, то есть он делает программу в файл OBJ. Окончательный скомпилирован в файл .o, это первый тип ** эльфа (** исполняемый исполняемый и связываемый формат, исполняемый и связанный файл),Доступный файл

ссылка на сайт

Генерировать файлы RINABLE HELLO

Просмотреть HELLO.O Использование NM

Было обнаружено, что PrintF не имеет адреса, который отражает роль ссылки, рядом с использованием nm hello

link

Было обнаружено, что было много @@@ glibc_2.2.5 за printf. Когда называется функция printf, она ищет .o ----> Эльф, этоДинамическая ссылкаметод.

2 Формы библиотеки ссылок

Библиотека Static Link (.a, .lib)

На этапе ссылки целевой файл сгенерированный сборки .o упакован к исполняемому с ссылочной библиотекой. Это можно понимать, что архивируйте серию файлов объектов (.O) в файл.

Add.cilet.C

Subtruction.cile.C

Скомпилируйте эти два файла, генерируйте файлы.

Генерировать архив libmath.a. Использоватьar команда

Использование использования команды Ar, общих параметров

Создать новый файл dmath.h

Создать новый файл Main.C

Когда программа является использование этой статической библиотеки ссылок, извлечь файл .o в libmath.a, ссылку на программу

В этой команде --L представлен в текущем каталоге, и -lmath автоматически сделает полное имя файла, например, добавление предложений Lib, Suffix .a, становится libmath.a, после нахождения этого файла. .O и подстрока. Вы выберете и составьте ссылку на main.o, образуют файл двоичного выполнения main.o

Это второй тип эльфа,запускаемый файл

Динамическая библиотека ссылок (.so, .dll)

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

Все еще используйте вышеуказанную библиотеку Static Linkadd.c файл,substruction.cс участиемmain.c Документ в качестве примера

PIC - это необративление кода Infient Postion, указывая на то, что код, который не связан с местоположением, который является функциями, требуемыми динамической библиотекой.

Создайте исполняемое имя под названием Main, но выполнение ./main Найду ошибку

Это связано с тем, что система находит динамическую библиотеку ссылок в папку / lib и / usr / lib, и программа работает, только знают имя библиотеки, мы можем установить переменную среды LD_LIBRARY_PATH, чтобы сделать программу выполнить поиск динамической ссылки Библиотеки под папкой, указанной этой переменной среды

Это будет успешно реализовано в это время.

Это третий тип эльфа,Общий объектный файл (общий объект)

Как работает так файл?

Используйте команду objdump -tT libdynamicmath.so Просмотр, поэтому библиотека включена функции

Я обнаружил, что в этом файле есть .plt.Procedure Linkage Table , Обработка связана таблица) и один .got.plt (Global Offset Table, Глобальный офсетный стол)

Поскольку это динамическая связь, функциональная позиция не может быть определена при компиляции, поэтому в PLT устанавливается PLT [X]. Не вызывайте функции Add и Subs в основном файле двоичного файла, но вызовите код агента в PLT [X], этот код агента найдет функции Add и Sub во время выполнения.

Вам необходимо использовать попадание, когда вы найдете функции Add и Sub, создайте GOT [Y] для функций Add Add и Sub при компиляции. Это реальный адрес функции во время выполнения.

Читайте также: