Сборка модуля для ядра linux

Содержание
  1. Сборка модулей ядра
  2. Содержание
  3. Модули ядра [ править ]
  4. О модулях и названиях [ править ]
  5. Как собрать модуль локально [ править ]
  6. Что нам нужно [ править ]
  7. Сборка [ править ]
  8. Как собрать модуль правильно [ править ]
  9. Шаблоны [ править ]
  10. Подготовка рабочей директории [ править ]
  11. Сборка модуля из шаблона (с помощью gear-specsubst) [ править ]
  12. Управление архитектурами, для которых собираются модули [ править ]
  13. Создание тегов для сборки на git.alt (схема с gear-specsubst) [ править ]
  14. Сборка модулей на git.alt [ править ]
  15. Некоторые моменты [ править ]
  16. Пересобрать отдельно один модуль [ править ]
  17. Сборка нового модуля [ править ]
  18. Сборка kernel-source-module [ править ]
  19. Создание нового шаблона [ править ]
  20. Как выложить свой модуль в репозиторий [ править ]
  21. Рекомендации по взаимодействию с мейнтейнерами ядер [ править ]
  22. Про symvers и модули зависящие от других модулей [ править ]
  23. Сборка модуля ядра Linux без точных заголовочных файлов
  24. Конфигурация ядра
  25. Написание простого модуля
  26. Сборка модуля
  27. Изучаем ядро

Сборка модулей ядра

Содержание

Модули ядра [ править ]

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

Модули могут подключаться к ядру по команде пользователя (modprobe, insmod) или автоматически при помощи udev, и быть выгружены либо самим ядром, либо командой rmmod.

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

О модулях и названиях [ править ]

Поскольку в репозитории может быть множество ядер, модули собираются особым способом: имеется пакет с исходными кодами модуля, и пакеты с модулями, собранным для конкретного ядра. При этом SRPM последних содержит только .spec и патчи, а исходные коды получает по сборочным зависимостям. Таким образом, для модуля module и варианта ядра flavour у нас имеются пакеты с именами

  • kernel-source-module — содержит только исходники
  • kernel-modules-module-flavour — модуль module, собранный для ядра flavour (например, kernel-modules-nvidia-std-def)

Поле release пакетов с модулями заполняется так: alt . . , где

  • — релиз собственно модуля, то есть, если мы обновили именно модуль, то это поле изменяется;
  • — версия ядра в формате (2^16) * major + (2^8) * mid + minor, то есть 2.6.25=132633. Не пугайтесь, это число рассчитывает скрипт, описанный позже;
  • — релиз пакета с ядром.

К примеру, модуль с nvidia для ядра kernel-image-std-def-2.6.25-alt8 будет называться kernel-modules-nvidia-std-def-173.14.12-alt1.132633.8.

Как собрать модуль локально [ править ]

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

Что нам нужно [ править ]

Кроме gcc, make и прочих стандартных сборочных вещей нам нужно kernel-headers-modules- (и всё что от него зависит). Этот пакет содержит ту часть исходных кодов, заголовочных файлов, make-файлов и скриптов, которые необходимы для сборки модулей для данного ядра.

Сборка [ править ]

Скачав и распаковав исходники модуля, мы обнаружим что просто make обычно не работает. Эта проблема специфична для Sisyphus/ALT Linux и состоит в том, что для сборки модуля необходимы заголовки ядра, которые ищутся в каталоге /lib/modules/ /build , но не могут быть найдены там, потому что в ALT Linux и Sisyphus доступ пользователям в /lib/modules/ запрещён.

Для того, чтобы обойти эту проблему, нужно переопределить переменную (обычно KERNELSOURCE или KSRC ) в Makefile . Далее запускаем сборку, например make KSRC=/usr/src/linux-2.6.25-std-def . Обычно модуль после этого собирается.

Собранный модуль можно попробовать загрузить с помощью insmod , или положить его к другим модулям ядра в /lib/modules/ и загрузить modprobe . Если модуль загрузился и работает, то можно переходить к следующей части.

Как собрать модуль правильно [ править ]

Почему предыдущий способ не является правильным? Потому что он не дистрибутивен. Для нормальной сборки нам нужны:

  • знание git (крайне желательно хотя бы начальное знание!)
  • умение пользоваться gear и hasher
  • настроенный hasher
  • доступ на git.alt (для публикации результатов)
  • достаточно терпения

Шаблоны [ править ]

Поскольку править спеки каждого пакета с модулями для каждой версии ядра несколько глупо, была разработана схема, при которой для каждого модуля создается шаблон, а gear (при помощи директивы specsubst, в которой указывается flavour ядра) подгоняет этот шаблон к конкретному ядру (в том числе вычисляет релиз модуля). Остальная работа ложится на макрос %setup_kernel_module, который вычисляет все остальные параметры, нужные для сборки модуля: версию ядра, релиз ядра и релиз модуля. Сами шаблоны хранятся в git-репозитории, в котором есть множество веток, ветки с шаблонами называются template/module/distro, где module — это собственно название модуля (например, nvidia) а distro — это то, подо что мы собираем. Обычно это sisyphus, но, поскольку для разных бранчей шаблоны приходиться менять, можно установить поле distro в соответствующее значение. Обычно distro сейчас совпадает с именем бранча.

Читайте также:  Kali linux словарь wpa2

Подготовка рабочей директории [ править ]

Нам потребуются утилита km-create-tag для сборки модулей из пакета kernel-build-tools:

Для использования бранчей с шаблонами придётся их завести локально, например:

Сборка модуля из шаблона (с помощью gear-specsubst) [ править ]

Когда вы собираете локально в тестовых целях, удобно установить параметр gear.specsubst.kflavour с вашим ядром:

После этого модуль можно собрать с помощью команды:

Управление архитектурами, для которых собираются модули [ править ]

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

Начиная с kernel-build-tools-0.112-alt1, в нём реализована следующая функциональность:

В файле .gear/km-karch может сохраняться информация о архитектурах, для которых должен собираться модуль. Вот пример такого файла для virtualbox:

Такой файл описывает, что для ядра std-pae модуль должен собираться только на архитектуре i586, а для других ядер — для i586 и x86_64

Эта информация учитывается скриптом km-create-tag, но может быть переопределена при помощи ключа -a этого скрипта (не рекомендуется, так как эта информация не сохранится для следующего запуска km-create-tag и модуль для каких-то архитектур может оказаться молча потерян).

Для того, чтоб это работало, в спек файле должны быть строки:

А в .gear/rules файле строка:

Создание тегов для сборки на git.alt (схема с gear-specsubst) [ править ]

Например, для создания тега для модуля nvidia, собираемого для ядра std-def

Или, для создания тегов для всех модулей, можно сделать:

Сборка модулей на git.alt [ править ]

После этого можно запушить полученные бранчи и тэги на git.alt.

и добавить список модулей к последнему task’у (где предположительно уже добавлен на сборку соответствующий kernel-image):

Следует, однако, учитывать, что updatemodules и km-create-tag не затирают файл out/taglist и вам нужно делать это вручную.

Если же вы обновляете ядро, но не собираетесь обновлять шаблоны модулей, можно сделать:

$ ssh git.alt task add [номер задания] kmodules std-def

это добавит в задание (где предположительно уже добавлен на сборку соответствующий kernel-image) все модули, собранные для ядра std-def.

Некоторые моменты [ править ]

  • Если в репозитории: kernel-image-std-def-3.0.4-alt0.M60P.1.i586.rpm
  • Тогда в имени пакета: kernel-modules-emlog-std-def-0.51-alt2.196612.0.M60P.1.i586.rpm

В релиз модуля запаковывается версия + релиз ядра. СТРАШНО?

Пересобрать отдельно один модуль [ править ]

Допустим нужно пересобрать в репозитарий некий модуль без пересборки ядра и всех модулей.

Ветка kernel-modules-rt3070-std-def/sisyphus — должна быть вытянута последняя из http://git.altlinux.org/gears/

  1. Изменяем ветку template/rt3070/sisyphus, коммитим наши изменения
  2. km-create-tag -k std-def rt3070
  3. проверяем сборку чем-нибудь вроде: gear -t $(git describe) —hasher — hsh $TMP/

Сборка нового модуля [ править ]

Сборка kernel-source-module [ править ]

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

Редактируем по образу и подобию kernel-source-module.spec — обычно там надо заменить имя модуля, версию, описание и changelog, а сам процесс сборки обычно можно не трогать.

Далее собираем пакет при помощи gear. В результате в пакете должен оказаться всего один файл, а именно /usr/src/kernel/sources/kernel-source-module.tar.bz2

Обратите внимание на то, что некоторые пакеты с исходниками в своём имени содержат версию, например, kernel-source-kdbus-5.0.0.31-5.0.0.31-alt1. Это сделано для того, чтобы можно было иметь возможность собирать разные версии ядер с разными версиями модулей (например, новые модули не собираются с 2.6.18).

Создание нового шаблона [ править ]

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

где module — название вашего модуля.

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

Внимание, верхняя запись changelog должна оставаться неизменной и состоять их тех жутких макросов, из которых она состоит в любом модуле ядра в любом актуальном репозитории ALT

Как выложить свой модуль в репозиторий [ править ]

Выкладывание модулей ничем не отличается от выкладывания обычных пакетов.

Выкладываем собственно модуль:

Осталось собрать пакеты через git.alt.

Важное замечание: для того чтобы сборка прошла правильно, kernel-source-module должен быть собран до сборки kernel-modules-module.

Рекомендации по взаимодействию с мейнтейнерами ядер [ править ]

Для нормальной совместной работы рекомендуется:

  • При обновлении модуля обновлять сборки под максимальное количество ядер
  • Своевременно обновлять шаблоны в репозитории на git.alt
  • Оповестить мейнтейнеров ядер (в списке рассылки devel-kernel), о том что есть ваш модуль,
  • Настроить git remote на kernel-modules других мейнтейнеров,
  • В спеках kernel-modules- поле Packager установить в Kernel Maintainers team.
Читайте также:  How to increase virtual memory in windows

Про symvers и модули зависящие от других модулей [ править ]

Иногда бывает, что пакет с модулями, должен собираться под другой пакет с модулями. Так например просиходит с gspca и v4l. Для нормальной сборки нам нужно 2 вещи: во-первых, проставить правильно зависимости на headers (у v4l есть свои хедеры), во-вторых, нужно импортировать файл с symvers. В gscpca проблема решилась добавлением следующей строчки в %prep фазу пакета с модулем gspca:

Источник

Сборка модуля ядра Linux без точных заголовочных файлов

Представьте, что у вас имеется образ ядра Linux для телефона на базе Android, но вы не располагаете ни соответствующими исходниками, ни заголовочными файлами ядра. Представьте, что ядро имеет поддержку подгрузки модулей (к счастью), и вы хотите собрать модуль для данного ядра. Существует несколько хороших причин, почему нельзя просто собрать новое ядро из исходников и просто закончить на том (например, в собранном ядре отсутствует поддержка какого-нибудь важного устройства, вроде LCD или тачскрина). С постоянно меняющимся ABI ядра Linux и отсутствием исходников и заголовочных файлов, вы можете подумать, что окончательно зашли в тупик.

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

Но больше об этом далее.

Конфигурация ядра

Первый шаг — найти исходники ядра наиболее близкие к тому образу ядра, насколько это возможно. Наверное, получение правильной конфигурации — наиболее сложная составляющая всего процесса сборки модуля. Начните с того номера версии ядра, который может быть прочитан из /proc/version . Если, как я, вы собираете модуль для устройства Android, попробуйте ядра Android от Code Aurora, Cyanogen или Android, те, что наиболее ближе к вашему устройству. В моем случае, это было ядро msm-3.0. Заметьте, вам не обязательно необходимо искать в точности ту же версию исходников, что и версия вашего образа ядра. Небольшие отличия версии, наиболее вероятно, не станут помехой. Я использовал исходники ядра 3.0.21, в то время как версия имеющегося образа ядра была 3.0.8. Не пытайтесь, однако, использовать исходники ядра 3.1, если у вас образ ядра 3.0.x.

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

Предполагая, что arm-eabi-gcc у вас доступен по одному из путей в переменной окружения PATH, и что терминал открыт в папке с исходными файлами ядра, вы можете начать конфигурацию ядра и установку заголовочных файлов и скриптов:

Сборка silentoldconfig , наиболее вероятно, спросит, хотите ли вы включить те или иные опции. Вы можете выбрать умолчания, но это вполне может и не сработать.

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

Написание простого модуля

Чтобы создать пустой модуль, необходимо создать два файла: исходник и Makefile . Расположите следующий код в файле hello.c , в некоторой отдельной директории:

Поместите следующий текст в файл Makefile в той же директории:

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

Сборка модуля

При обычной сборки ядра система сборки ядра создает файл hello.mod.c , содержимое которого может создать различные проблемы:

Значение VERMAGIC_STRING определяется макросом UTS_RELEASE , который располагается в файле include/generated/utsrelease.h , генерируемом системой сборки ядра. По умолчанию, это значение определяется версией ядра и статуса git-репозитория. Это то, что устанавливает KERNELRELEASE при конфигурации ядра. Если VERMAGIC_STRING не совпадает с версией ядра, загрузка модуля приведет к сообщению подобного рода в dmesg :

Далее, также имеем здесь определение структуры модуля:

Само по себе, это определение выглядит безобидно, но структура struct module , определенная в include/linux/module.h , несет в себе неприятный сюрприз:

Это означает, что для того, чтобы указатель init оказался в правильном месте, CONFIG_UNUSED_SYMBOLS должен быть определен в соответствии с тем, что использует наш образ ядра. Что же насчет указателя exit, — это CONFIG_GENERIC_BUG , CONFIG_KALLSYMS , CONFIG_SMP , CONFIG_TRACEPOINTS , CONFIG_JUMP_LABEL , CONFIG_TRACING , CONFIG_EVENT_TRACING , CONFIG_FTRACE_MCOUNT_RECORD и CONFIG_MODULE_UNLOAD .

Читайте также:  Windows 10 стрелка вверх

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

Далее, определения версий символов:

Эти определения берутся из файла Module.symvers , который генеруется в соответствии с заголовочными файлами.

Каждая такая запись представляет символ, требуемый модулю, и то, какую сигнатуру должен иметь символ. Первый символ, module_layout , зависит от того, как выглядит struct module , то есть, зависит от того, какие опции конфигурации, упомянутые ранее, включены. Второй, __aeabi_unwind_cpp_pr0 , — функция, специфичная ABI ARM, и последний — для наших вызовов функции printk .

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

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

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

Изучаем ядро

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

Ядро использует следующую функцию для поиска в своей таблицы символов (в kernel/module.c):

Структура, используемая в данной функции, определена в include/linux/module.h:

Примечание: данный код ядра не изменился значительно за последние четыре года (видимо, с момента рассматриваемого релиза ядра 3.0, — прим. пер.).

То, что мы имеем выше в функции each_symbol_section — три (или пять, когда конфиг CONFIG_UNUSED_SYMBOLS включен) поля, каждое из которых содержит начало таблицы символов, ее конец и два флага.

Данные эти статичны и постоянны, что означает, что они появятся в бинарнике ядра как есть. Сканируя ядро на предмет трех идущих друг за другом последовательностей состоящих из трех указателей в адресном пространстве ядра и следом идущих значений типа integer из определений в each_symbol_section , мы можем определить расположение таблиц символов и сигнатур, и воссоздать файл Module.symvers из бинарника ядра.

К несчастью, большинство ядер сегодня сжатые ( zImage ), так что простой поиск по сжатому образу невозможен. Сжатое ядро на самом деле представляет небольшой бинарник, следом за которым идет сжатый поток. Можно просканировать файл zImage с тем, чтобы найти сжатый поток и получить из него распакованный образ.

Я написал скрипт для декомпрессии и извлечения информации о символах ядра в автоматическом режиме. Это должно работать с любой свежей версией ядра, при условии, что ядро не перемещаемое (relocatable) и вы знаете базовый адрес в памяти, куда оно грузится. Скрипт принимает опции для количества и порядка следования битов (endianness) архитектуры, и по умолчанию использует значения, подходящие для ARM. Базовый адрес, однако, должен быть указан. Он может быть найден, на ядрах ARM, в dmesg :

(прим. пер. — однако, не все ядра выводят эти данные в лог, мне довелось встретить один такой практически уникальный случай, когда, видимо, ввиду урезанных опций конфигурации эта информация не выводилась, в таком случае можно обратиться к конфигу PAGE_OFFSET в файле arch/arm/Kconfig и просто надеяться, что вендор использовал одно из дефолтных значений).

Базовый адрес в примере выше — 0xc0008000 .

Если как я, вы интересуетесь загрузкой модуля на девайсе Android, тогда бинарник ядра, что вы имеете — полный образ boot. Образ boot содержит другие вещи помимо ядра, так что вы не можете напрямую использовать его со скриптом выше. Исключение составляет только тот случай, если ядро в образе boot сжато, при этом часть скрипта, которая ожидает на входе сжатый образ, все равно найдет ядро.

Если ядро не сжато, вы можете использовать программу unbootimg как изложено в данном посте, для того, чтобы получить образ ядра из вашего образа boot. Как только у вас есть образ ядра, скрипт может быть запущен следующим образом:

Источник

Оцените статью