Идентификация исходников как модуля ядра Linux
В этом посте я расскажу о своих поисках признаков того, как можно определить, что из некоторых файлов исходных текстов собирается модуль ядра Linux, а не обычный исполняемый файл.Допустим, что информации о назначении исходников нет или её пытаются преднамеренно скрыть.
#01 __KERNEL__ При сборке исходных текстов определён символ препроцессора __KERNEL__.Как пишут Alessandro Rubini и Jonathan Corbet в книге «Драйверы устройств в Linux»:
«Поскольку модуль не связывается ни с одной из стандартных библиотек, исходные тексты модуля не должны подключать обычные заголовочные файлы. В модулях ядра могут использоваться только те функции, которые экспортируются ядром. Все заголовочные файлы, которые относятся к ядру, расположены в каталогах include/linux и include/asm, внутри дерева каталогов с исходными текстами ядра (как правило это каталог/usr/src/linux).Ранние версии Linux (основанные на libc версии 5 и более ранних) устанавливали символические ссылки из /usr/include/linux и /usr/include/asm на фактические каталоги из исходных текстов ядра, таким образом дерево заголовочных файлов libc могло ссылаться на заголовочные файлы ядра. Это позволяло подключать заголовочные файлы ядра в пользовательские приложения тогда, когда в этом возникала необходимость.Но даже теперь, когда заголовочные файлы ядра отделены от заголовочных файлов, используемых прикладными программами, все равно иногда возникает необходимость включения их в программы, работающие в пространстве пользователя, чтобы воспользоваться определениями, отсутствующими в обычных заголовочных файлах. Однако большая часть определений из заголовочных файлов ядра относится исключительно к ядру и «невидима» для обычных приложений, поскольку доступ к этим определениям заключен в блоки #ifdef __KERNEL__. Это кстати одна из причин, почему необходимо определять символ __KERNEL__ при сборке модуля.»
Например, в makefile может присутствовать строчка «CFLAGS = -D__KERNEL__».Или »-D__KERNEL__» можно обнаружить в логах сборки .#02 MODULE Если модуль не линкуется к ядру статически, то в составе переменной CFLAGS обязательно будет присутствовать строка »-DMODULE». Этот символ препроцессора должен быть определён должен быть определен до подключения файла linux/module.h.#03 Все имена объявлены как static и имеют уникальный префикс Таким образом разработчик избегает «загрязнения» пространства имён ядра — иначе при отладке ему пришлось бы отлавливать имена своего модуля среди всех имён ядра. Использование префикса освобождает от обязанности придумывать уникальные имена, которые не будут совпадать с именами, уже присутствующими в пространстве имён ядра.#04 printk () В исходных текстах вместо функции printf () используется функция printk (). «Драйверы устройств в Linux» говорит: «Функция printk определена в ядре и по своему поведению напоминает функцию printf из стандартной библиотеки языка C. Зачем же тогда ядру своя собственная функция? Все просто — ядро, это самостоятельный код, который собирается без вспомогательных библиотек языка C.»
#05 init_module и cleanup_module «Драйверы устройств в Linux» говорит:
«Приложение выполняется как цельная задача, от начала и до конца. Модуль же просто регистрирует себя самого в ядре, подготавливая его для обслуживания возможных запросов и его функция «main» завершает свою работу сразу же после вызова. Другими словами, задача функции init_module (точка входа) состоит в подготовке функций модуля для последующих вызовов. Она как бы говорит ядру: «Эй! Я здесь! Вот то, что я могу делать!». Вторая точка входа в модуль — cleanup_module — вызывается непосредственно перед выгрузкой модуля. Она сообщает ядру: «Я ухожу! Больше не проси меня ни о чем!».»
#06 Использование current→ «Драйверы устройств в Linux» говорит:
»<...> Код ядра может определить текущий процесс, обратившийся к модулю, через глобальный элемент current — указатель на struct task_struct, который в ядре 2.4 объявляется в файле
Имя команды хранится в поле current→comm и представляет собой имя файла программы.
А какие вы ещё знаете отличия модуля ядра от исполняемого файла на уровне исходников?