Модераторы: xvr

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Экспорт ядровых функций в модуль 
:(
    Опции темы
cupper
Дата 20.3.2010, 18:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



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

и что такое modpost ?

Заранее спасибо.

а что произойдет если например под телом функции pintk убрать строчку 
EXPORT_SYMBOL(printk)

при компиляции в модуле уже не будет происходить связка с этой функций ? А если сделать отдельный хедер, в котором определить свою функцию printk , сделать там же 
EXPORT_SYMBOL(printk)

и подключить в модуле при компиляции этот хедер тогда в модуле будет происходить вызов не ядерной printk а "моей из моего хедера". Ядро собирается без этого хедера.

Или же получиться какая ошибка ? 

Экспортируемые функции разбросаны по всему ядру, экспорт их производится сразу под телом функции, а при написании модуля указываются только хередеры. Вопрос: все экспортируемые ядром функции собраны в неком одном хедеры или также размазаны по всем ?
PM MAIL   Вверх
nickless
Дата 21.3.2010, 18:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Гентозавр
****


Профиль
Группа: Участник Клуба
Сообщений: 2976
Регистрация: 29.8.2005
Где: Germany

Репутация: 4
Всего: 181



Модератор: Тема перенесена из Общих вопросов по никсам


--------------------
user posted image

Real men don't use backups, they post their stuff on a public ftp server and let the rest of the world make copies
- Linus Torvalds
PM MAIL   Вверх
MAKCim
Дата 21.3.2010, 19:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



Цитата(cupper @  20.3.2010,  18:09 Найти цитируемый пост)
я о самом механизме ядра/компилятора с помощью которого в скомпилином модулей оказываются толи ядреса функция в ядре толи они туда статиком в гоняются.

вам для начала необходимо ознакомиться с форматом elf для _объектных_ файлов
без этого ничего не поймете

если говорить простым языком, то модуль ядра - это объединение объектных файлов (aka объектный файл), получаемых при компиляции исходного кода модуля
каждый объектный файл содержит таблицу символов
каждый символ в ней либо связан с конкретной секцией данного объектного файла, либо является специальным
первый случай характеризуется тем, что поле индекса в дескрипторе символа в таблице содержит номер секции: 1,2,...
во втором случае значение поля не индекс, а одно из специальных значений: SHN_COMMON, SHN_ABS, SHN_UNDEF
(подробнее в спецификации elf)
наш случай - SHN_UNDEF
ядро при загрузке модуля читает таблицу символов и если индекс в дескрипторе равен SHN_UNDEF, то осуществляет поиск символа посредcтвом вызова функции ядра resolve_symbol
код

а вот уже resolve_symbol непосредственно связана с EXPORT_SYMBOL
расммотрим определение EXPORT_SYMBOL
Код

#define __EXPORT_SYMBOL(sym, sec)                               \
         extern typeof(sym) sym;                                 \
         __CRC_SYMBOL(sym, sec)                                  \
         static const char __kstrtab_##sym[]                     \
         __attribute__((section("__ksymtab_strings"), aligned(1))) \
         = MODULE_SYMBOL_PREFIX #sym;                            \
         static const struct kernel_symbol __ksymtab_##sym       \
         __used                                                  \
         __attribute__((section("__ksymtab" sec), unused))       \
         = { (unsigned long)&sym, __kstrtab_##sym }
 
#define EXPORT_SYMBOL(sym)                                      \
         __EXPORT_SYMBOL(sym, "")

мы видим, что экспортирование - это ни что иное как создание переменной типа struct kernel_symbol в спец. секции __ksymtab
при сборке образа ядра все объекты struct kernel_symbol для экспортируемых переменных линейно располагаются в этой секции
ее адрес определяется в скрипте компоновщика и участвует в разрешении символом им же
так вот, в resolve_symbol просто осуществляется поиск символа в данной секции
если же EXPORT_SYMBOL используется в модуле ядра, то при загрузке модуля в его дескриптор заносится адрес этой секции (она - часть модуля)
и опять таки, в resolve_symbol, если символ не найден в __ksymtab ядра, то по очереди просматриваются __ksymtab каждого загруженного модуля

modpost много всего делает
как пример, динамически формирует <module>.mod.c файл, в котором определяется и инициализируется дескриптор модуля (struct module) 


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
cupper
Дата 22.3.2010, 00:30 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



Суть улавливаю. В принцепи все так и представлялось, только без подробностей реализации. 
А теперь такой вопрос: а что если в ядре у экспортируемых функций убрать непосредственно их экспорт (EXPORT_SUMBOL(bla)), она тогда станет не экспортируемой, ее адрес не появится в списке. А что произойдет в модуле ? на месте вызова экспортируемой функции вероятно вылезет ошибка и неизвестной/недоступной функции. И если создать отдельный хедер, в котором реализовать функцию с тем же именем и параметрами (идентичную по определению, экспортируемой) и подключать его в модуле, тогда на месте вызова ранее экспортируемой функции будет вставляться моя функция ? Или же будет по прежнему адрес функции ядра но на этапе разрешения символов будет получен игнор ?

PS. нет, я не было хакер, не было кодер smile все чисто из научного интереса и нездоровой логики %)
PM MAIL   Вверх
MAKCim
Дата 22.3.2010, 18:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



Цитата(cupper @  22.3.2010,  00:30 Найти цитируемый пост)
А что произойдет в модуле ?

модуль не загрузится

Цитата(cupper @  22.3.2010,  00:30 Найти цитируемый пост)
тогда на месте вызова ранее экспортируемой функции будет вставляться моя функция ?

да


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
cupper
Дата 22.3.2010, 23:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



Цитата(MAKCim @ 22.3.2010,  18:39)
Цитата(cupper @  22.3.2010,  00:30 Найти цитируемый пост)
тогда на месте вызова ранее экспортируемой функции будет вставляться моя функция ?

да

Я еще немного по вытягиваю из вас инфу smile

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

Я так пологая что такое возможно если бы при компиляции модуля был бы некий файл с примерно следующей логикой
имя_функции -> "символ"
имя_функцииё -> "символ1"

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

Я конечно скорее всего ошибаюсь, но я тогда не понимаю как решается этот вопрос. 
И если он решается именно так, то даже если в ядре убрать EXPORT_SYMBOL (что на этапе загрузки в ядро помешало бы связыванию с функций ядра) это не позволит реализовать такую подмену на этапе компиляции. 
Но что то мне подсказывает что я несу редкостную ахинею smile поэтому поясните пожалуйста как оно в действительности smile
PM MAIL   Вверх
MAKCim
Дата 23.3.2010, 09:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



Цитата(cupper @  22.3.2010,  23:47 Найти цитируемый пост)
тогда каким путем при компиляции модуля устраняется ошибка о том что функция не реализованна ?

вернемся к формату elf
существует 4 типа файла: исполняемый (executable), динамически загружаемый (shared object), статический (static object), объектный (relocatable)
модуль ядра - объектный файл, точнее объединение объектных файлов, полученных при компиляции
скомпилируйте след. программу
Код

#include <stdio.h>

/* main.c */
int main()
{
    printf("Hello, world!\n");
    return 0;
}

Код

# gcc -Wall -c main.c

и посмотрите через readelf с ключом --all
Код

# readelf --all main.o

в таблице символов будет находится кроме всего прочего символ printf с индексом UND (тип SHN_UNDEF из пред. примера), а на месте вызова функции в main - релокация, ссылающаяся на этот символ
по сути main.o - это и есть модуль ядра (отличия в данном случае не столь существены)
ошибка же отсутствия определения возникает при создании статических, динамически загружаемых или исполняемых файлов


Цитата(cupper @  22.3.2010,  23:47 Найти цитируемый пост)
 понимаю то что по логики на месте вызова функции должен ставить какойто "символ"

это называется релокация
в объектном файле есть таблица релокаций, каждый элемент в ней - дескриптор
основные поля: offset (P), symbol (S), addend (A), type (T)
T определяет, как использовать P, S и A, чтобы получить значение релокации
в примере выше T = R_X86_PC32, а значение высчитывается V = S - P + A
S - адрес символа (printf), P - где будет расположено значение релокации (смещение в секции кода main.o), A = 4
т. е., если к примеру P = 0xA, а адрес printf 0x400100, то 32-х разрядное значение, записываемое по адресу .text + 0xA будет равно
0x400100 - 0xA + 4 = 0x4001FA
вызов printf - direct call
адрес перехода = операнд + IP след. за call инструкции
т. е. релокация в данном случае и есть
Цитата

какойто "символ"

Цитата(cupper @  22.3.2010,  23:47 Найти цитируемый пост)
и при компиляции модуля например известно о всех возможных экспортируемых функция, это обеспечивало бы замену имени функции на некий "символ" (идентификатор)

имя функции уже есть символ типа SHN_UNDEF


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
cupper
Дата 23.3.2010, 14:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



спасибо за наиподробнейшее объяснение, но из выше сказанного я понимаю что даже если реализовать в отдельном файле свою функцию то подмены не получиться ?
PM MAIL   Вверх
MAKCim
Дата 23.3.2010, 14:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



Цитата(cupper @  23.3.2010,  14:00 Найти цитируемый пост)
свою функцию то подмены не получиться ? 

почему? все получится


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
cupper
Дата 23.3.2010, 15:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



Цитата(MAKCim @ 23.3.2010,  14:16)
Цитата(cupper @  23.3.2010,  14:00 Найти цитируемый пост)
свою функцию то подмены не получиться ? 

почему? все получится

т.е. он вначале посмотрит что если эта функция реализована в хедерах (что не просто ее определение есть, а именно что есть тело функции) то он возьмет его, а если нету тела то он подумает что это экспортируемая функция ?)

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

Это сообщение отредактировал(а) cupper - 23.3.2010, 15:36
PM MAIL   Вверх
MAKCim
Дата 23.3.2010, 18:39 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



Цитата(cupper @  23.3.2010,  15:22 Найти цитируемый пост)
т.е. он вначале посмотрит что если эта функция реализована в хедерах (что не просто ее определение есть, а именно что есть тело функции) то он возьмет его, а если нету тела то он подумает что это экспортируемая функция ?)

никто ничего смотреть не будет
если функция, объявленная в хидере, определена в одном из *.c файлов модуля, то при сборке модуля компоновщик в одном из *.o файлов найдет определение соответствующего символа и в конечном *.ko файле он не будет иметь индекс SHN_UNDEF и ядро при загрузке просто вычислит его адрес с учетом смещения и адреса секции


Цитата(cupper @  23.3.2010,  15:22 Найти цитируемый пост)
вы это было не правильно, если сделать еще один .h файл с определение аналогичной экспорируемой функции, не отключаю хедер ядра с таким же определение функции, то должен возникнуть конфликт имен.

нет
точнее да, но только для static inline функций, которые не подставляются компилятором




--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
cupper
Дата 23.3.2010, 19:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



Цитата(MAKCim @ 23.3.2010,  18:39)
Цитата(cupper @  23.3.2010,  15:22 Найти цитируемый пост)
т.е. он вначале посмотрит что если эта функция реализована в хедерах (что не просто ее определение есть, а именно что есть тело функции) то он возьмет его, а если нету тела то он подумает что это экспортируемая функция ?)

никто ничего смотреть не будет
если функция, объявленная в хидере, определена в одном из *.c файлов модуля, то при сборке модуля компоновщик в одном из *.o файлов найдет определение соответствующего символа и в конечном *.ko файле он не будет иметь индекс SHN_UNDEF и ядро при загрузке просто вычислит его адрес с учетом смещения и адреса секции


Цитата(cupper @  23.3.2010,  15:22 Найти цитируемый пост)
вы это было не правильно, если сделать еще один .h файл с определение аналогичной экспорируемой функции, не отключаю хедер ядра с таким же определение функции, то должен возникнуть конфликт имен.

нет
точнее да, но только для static inline функций, которые не подставляются компилятором

бальшое спасибо за терпение и разъяснения smile
PM MAIL   Вверх
MAKCim
Дата 23.3.2010, 19:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



cupper
не за что  smile 


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
cupper
Дата 31.3.2010, 23:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 525
Регистрация: 29.11.2006

Репутация: нет
Всего: 1



У меня появился еще вопрос, разрешите задать ?)

Все выше описанное очень походит для решения данной подзадачи, если бы не одно НО. Оно заключается в следующем.
Рассмотрим функцию kazlloc реализованную в include/linux/slab.h
Код

static inline void *kzalloc(size_t size, gfp_t flags)
{
    return kmalloc(size, flags | __GFP_ZERO);
}

Как видно это простой врапер на функцию kmalloc которая, незнаю как правильно сказать, модели памяти зависимая, тобишь имееет свою реализацию для slab slob slub
Вопрос 1:  Метка inline тут делает эту функцию "вкомпилируемой" в обжект модуля? Поэтому данную функцию даже не требуется делать экспортируемой. 

Продолжаем, функция kmalloc это также можно сказать врапер, но уже побольше, реализованная в 
include/linux/slab_def.h
include/linux/slob_def.h
include/linux/slub_def.h
Рассмотрим реализацию для slab
Код

static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
    struct kmem_cache *cachep;
    void *ret;

    if (__builtin_constant_p(size)) {
        int i = 0;

        if (!size)
            return ZERO_SIZE_PTR;

#define CACHE(x) \
        if (size <= x) \
            goto found; \
        else \
            i++;
#include <linux/kmalloc_sizes.h>
#undef CACHE
        return NULL;
found:
#ifdef CONFIG_ZONE_DMA
        if (flags & GFP_DMA)
            cachep = malloc_sizes[i].cs_dmacachep;
        else
#endif
            cachep = malloc_sizes[i].cs_cachep;

        ret = kmem_cache_alloc_notrace(cachep, flags);

        trace_kmalloc(_THIS_IP_, ret,
                  size, slab_buffer_size(cachep), flags);

        return ret;
    }
    return __kmalloc(size, flags);
}

Уже мясо. На сколько мне известно метка __always_inline это чтото типо inline "беспесды". Тобишь если просто inline может быть в каком то случае отменен компилятором то функция с такой меткой 100% вкомпилиться в модуль. 
Из это й же функции уже начинаются вызываться экспортируемый функции, например __kmalloc
Код

void *__kmalloc(size_t size, gfp_t flags)
{
    return __do_kmalloc(size, flags, __builtin_return_address(0));
}
EXPORT_SYMBOL(__kmalloc);

Уже без меток inline и с EXPORT_SYMBOL. Т.е. вплоть до таких вызова непосредственно экспортируемых, функции фраперы типа kzalloc будут вкомпиливаться в модуль. Зачем это было сделано и что мешало сделать kzalloc тоже укспортируемой, я думаю мало кому известно. Но вот именно такое построение интерфейсов портит все до чего я докапывался в верхних постах. Т.е. нельзя реализовать два тела функции kzalloc, одно по идее находящееся в slab.c являющееся экспортирумой функция и использумой ядром при сборке. А другое реализованное в файле my_slab.c как inline которое не использовалось бы при сборке ядра но использовалось бы при сборке модуля для того чтобы вкомпилится в него и таким образом сделать подмену. Нельзя именно потому что тело функции уже находится в хедере (нельзя подключить хедер к .с файлу и повторно реализовать тело функции).

Был придуман выход из этой ситуации. Он заключается в следующем. В файле slab.h в котором реализованная функция kzalloc, сделать вот так
Код

#ifdef MY_HACK_CONFIG // Эта функция будет использоваться при сборке модулей
static inline void *kzalloc(size_t size, gfp_t flags)
{
    //...
}
#else // Эта будет использоваться при сборке ядра
static inline void *kzalloc(size_t size, gfp_t flags)
{
    return kmalloc(size, flags | __GFP_ZERO);
}
#endif


Вопрос 2: Можно ли один и тот же конфиг, сделать при компиляции файлов ядра выключенным а при сборке модулей включенным ? Если да то как ?) (я плохо владею макфайлами, а попытавшись что то понять в ядровом, я потерялся в нем).
По хорошему нужно дать возможность при конфигурировании конфига включать и выключать эту функциональность. Это можно сделать следующим образом:
1. В основном конфиге не будет вообще MY_HACK_CONFIG, будет некая другая например CONFIG_FUCK_HUCK
2. На этапе собрки модулей в макфайле будет проверятся, включенали опция CONFIG_FUCK_HUCK, если да то компилировать модули с включеной  MY_HACK_CONFIG.
Таким образом при включенной или выключенной CONFIG_FUCK_HUCK ядро всегда будет собираться только со своей функцией, а модули будут собиратся с подмененой функций только при включеной  CONFIG_FUCK_HUCK.
Можно ли это сделать и как я тоже не знаю smile
PM MAIL   Вверх
MAKCim
Дата 1.4.2010, 08:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Воін дZэна
****


Профиль
Группа: Экс. модератор
Сообщений: 5644
Регистрация: 10.12.2005
Где: Менск, РБ

Репутация: 84
Всего: 207



может, натолкнет на мысль, есть стандартный макрос MODULE, который определен при компиляции исходного кода модуля
т. е. ядерную функцию оберни в #ifndef MODULE...#endif а свою реализацию в #if defined MODULE && defined CONFIG_FUCK_FUCK...#endif


--------------------
Ах, у елі, ах, у ёлкі, ах, у елі злыя волкі ©

PM MAIL   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С/С++: Программирование под Unix/Linux"
xvr
  • Проставьте несколько ключевых слов темы, чтобы её можно было легче найти.
  • Не забывайте пользоваться кнопкой "Код".
  • Вопросы мобильной разработки тут
  • Телепатов на форуме нет! Задавайте чёткий, конкретный и полный вопрос. Указывайте полностью ошибки компилятора и компоновщика.
  • Новое сообщение должно иметь прямое отношение к разделу форума. Флуд, флейм, оффтопик запрещены.
  • Категорически запрещается обсуждение вареза, "кряков", взлома программ и т.д.

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, xvr.

 
 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Программирование под Unix/Linux | Следующая тема »


 




[ Время генерации скрипта: 0.1268 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.