Форум программистов, компьютерный форум, киберфорум
Evg
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск  

Прототип механизма инициализации в glibc и gcc

Запись от Evg размещена 14.06.2016 в 15:23
Показов 3781 Комментарии 0
Метки c, gcc, glibc, си

Изначально предполагался ответ на вопрос из темы Книга по startup магии. Но текст получился объёмный, в итоге закинул в блог. Тут его по крайней мере можно подредактировать (если увижу ошибки), а так же добавить полезную информацию или даже довести до законченного состояния




Прототип, описывающий работу механизма инициализации glibc

Прототип реализован для системы команд x86 (т.е. 32-битный режим) для linux'а. Для других режимов или архитектур надо будет менять ассемблерную часть. Возможно, что в таком виде взлетит под windows, но не уверен. В некоторых прототипах присутствует конструкция asm ("строка"), она говорит о том, что в код функция должна попадать под именем "строка", т.е. без какого бы то ни было манглирования (под windows, например, компиляторы ко всем именам добавляют знак подчерка). Это нужно для гарантированного склеивания кода на Си и кода внутри ассемблерной вставки

Основой является функция my_init, расположенная в секции .my_init. Тело этой функции как бы размазано по нескольким объектным файлам. В каждом файле есть кусочек этой функции. Далее линкер при своей работе берёт в каждом объектном файле секцию .my_init и приклеивает к нему другие секции .my_init. Линкер понятия не имеет, что он собирает тело функции из отдельный частей. Он просто склеивает все секции .my_init из разных объектных файлов в одну общую секцию .my_init исполняемого файла и делает это по общим правилам склеивания секций. Чтобы функция my_init получилась "правильная", надо обеспечить, чтобы начало функции было в начале строки запуска линкера, а конец функции - в конце. Потому как линкер обрабатывает и склеивает файлы слева направо в соответствии с их порядком в командной строке. В обычном режиме работы, когда в gcc подаются пользовательские файлы, этот порядок обеспечивает сам gcc. Это легко можно увидеть, добавив в строку компиляции опцию -v (типа "gcc t.c -v"). Механизм финализации (т.е. вызов функции после завершения main, но до завершения работы программы) построен симметричным образом, я не стал его описывать

Предупреждаю сразу, что при реализации всяких конструкторов-деструкторов компилятор gcc напрямую НЕ использует данный механизм glibc. Поверх glibc'шного механизма компилятор gcc выстраивает второй уровень - компиляторный механизм. И в итоге только в одной точке подключает компиляторный механизм к glibc'шному механизму. Сделано это для того, чтобы можно было по простому прикручивать gcc не только к glibc'ям, но и к другим библиотекам, у которых какие-то свои собственные механизмы инициализации. Если не поленюсь, попозже опишу и его. Этот механизм сосредоточен в файлах crtbegin.o, crtend.o

ВНИМАНИЕ! Из-за ошибки форумного движка в тех ассемблерных вставках, где используется директива .section, написано "ax", хотя в тексте программы кавычки должны быть экранированы символами обратного слэша

C
/* Файл my_crt1.c */
 
/* Прототип glibc'шного модуля crt1.o */
 
#include <stdio.h>
 
extern void my_init (void) asm ("my_init");
extern void my_main (void);
 
/* Прототип настоящей точки входа (функции _start)
 * Считаем, что исполнение начинается как бы отсюда */
void my_start (void)
{
  printf ("my_start\n");
  my_init ();
  my_main ();
 
  /* Тут должен быть аналогичный вызов my_fini, но не стал его расписывать */
 
  /* После завершения работы main и my_fini должен быть вызван exit */
}
 
int main (void)
{
  my_start ();
  return 0;
}
C
/* Файл my_crti.c */
 
/* Прототип glibc'шного модуля crti.o */
 
/* Прототип glibc'шной функции _init, расположенной в секции .init
 * Начало тела функции my_init */
asm ("    .section .my_init, "ax", @progbits\n"
     "    .global my_init\n"
     "my_init:\n"
     "    push %ebp\n"
     "    mov  %esp,%ebp\n"
     "    sub  $0x8,%esp\n");
C
/* Файл my_crtn.c */
 
/* Прототип glibc'шного модуля crtn.o */
 
/* Конец тела функции my_init */
asm ("    .section .my_init, "ax", @progbits\n"
     "    leave\n"
     "    ret\n");
C
/* Файл t1.c */
 
/* Прототип пользовательского исходника */
 
#include <stdio.h>
 
/* Прототип функции main */
void my_main (void)
{
  printf ("my_main\n");
}
C
/* Файл t2.c */
 
/* Прототип пользовательского исходника */
 
#include <stdio.h>
 
extern void foo (void) asm ("foo");
 
void foo (void)
{
  printf ("foo\n");
}
 
/* Здесь мы встраиваемся в glibc'шный механизм инициализации,
 * мы описываем серединный фрагмент функции my_init */
asm ("    .section .my_init, "ax", @progbits\n"
     "    call foo\n");
C
/* Файл t3.c */
 
/* Прототип пользовательского исходника */
 
#include <stdio.h>
 
extern void bar (void) asm ("bar");
 
void bar (void)
{
  printf ("bar\n");
}
 
/* Здесь мы встраиваемся в glibc'шный механизм инициализации,
 * мы описываем серединный фрагмент функции my_init */
asm ("    .section .my_init, "ax", @progbits\n"
     "    call bar\n");
Запуск этого хозяйства. Не забываем, что порядок файлов в командной строке важен (для файлов crt)

Code
$ gcc my_crt1.c my_crti.c t1.c t2.c t3.c my_crtn.c
 
$ ./a.out 
my_start
foo
bar
my_main
Ну и посмотрим глазками нашу чудо-функцию my_init. Как мы видим, она успешно склеилась из кусочков

Code
$ objdump -d -j .my_init a.out
 
Disassembly of section .my_init:
 
080484b8 <my_init>:
 80484b8:       55                      push   %ebp           <--- my_crti.c
 80484b9:       89 e5                   mov    %esp,%ebp
 80484bb:       83 ec 08                sub    $0x8,%esp
 80484be:       e8 45 ff ff ff          call   8048408 <foo>  <--- t2.c
 80484c3:       e8 54 ff ff ff          call   804841c <bar>  <--- t3.c
 80484c8:       c9                      leave                 <--- my_crtn.c
 80484c9:       c3                      ret
Метки c, gcc, glibc, си
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Часы электронные
Uhbif79 12.08.2026
Выкладываю программу часов. Программа позволяет: 1. Использовать системное время и дату, 2. Есть возможность вводить время и дату вручную. 3. Реализованы 2 будильника: начало и конец рабочего дня. . . .
Часы с будильником на основе класса QLCDNumber
Uhbif79 12.08.2026
Всем добрый день, выкладываю программу часов с будильником на основе класса QLCDNumber. Здесь я пробовал самостоятельно создавал классы, впервые столкнулся с видимостью переменной одного класса из. . .
Установка MinGW GCC 16.2 и CMake
8Observer8 10.08.2026
VK Видео: https:/ / vkvideo. ru/ video-240781534_456239017 YouTube: eY5-5PyI9NM Текстовая версия
Неделя из жизни имитационной модели склада: мои кривые руки растут, откуда надо
anaschu 10.08.2026
Неделя из жизни имитационной модели склада: как я почти написал неправильную логику и что с этим делать Работаю сейчас над учебно-рабочим проектом: строю в AnyLogic имитационную модель процессов. . .
Калькулятор для расчета родства
russiannick 07.08.2026
1. Задача: Создать калькулятор для расчета родства. Родственных связей существует 8 ступеней, такие как: p - отец P - мать q - муж Q - жена b - брат B - сестра s - сын S - дочь
Мир по моей воле
kumehtar 07.08.2026
Когда-то кажется, что всё просто. Ты весь такой светлый. Причиняешь добро. Борешься за справедливость в этом тёмном мире. Потом начинаешь замечать одну неприятную вещь. Почти каждый хороший. . .
Кредитный калькулятор
Maks 05.08.2026
Решение задачи по прикладной информатике средствами 1С. Задача: Напишите приложение-калькулятор, которое помогает рассчитывать параметры кредита для аннуитетного и дифференцированного видов. . .
У нас сейчас поговорку "Опять 25" нужно переделать на "Опять +35".
kumehtar 04.08.2026
С ностальгией вспоминаю времена моего детства, когда у нас и правда +25 - была максимальная температура летом. Раньше +25 °C реально казались вершиной жары, когда можно было весь день пропадать на. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru