Saturday, March 19, 2016

Эмуляция vs Виртуализация

Эмуляция - ориентирована на использование аппаратуры полностью отличной от базовой системы с полной изоляцией от внешнего мира. Подразумевается полная эмуляция оборудования и временных характеристик, если требуется. Возможна в режиме бинарной трансляции, когда код преобразуется в код совпадающий с системой команд базовой системы(это может осуществляться как на уровне пользовательского приложения, так и на уровне операционной системы и самого процессора, как пример процессоры Transmeta), а так же в режиме интерпретации бинарного кода для случаев, когда код подразумевает само модификацию и компиляция кода в другую систему команд будет неэффективна.
Виртуализация - подразумевается что используется архитектура близкая или совместимая на бинарном уровне с базовой системой, как результат при выполнении не привилегированных команд можно выполнять их напрямую и используя аппаратную поддержку виртуализации вызывать наши обработчики на команды которые потенциально могут изменить состояние системы или обращения к прерываниям с внешними устройствами. Также возможно использование бинарной трансляции кода для замены потенциально опасных команд на заглушки вызывающие код обработчиков непосредственно без инициирования прерывания. В данном режиме эмулируется кое какое простое оборудование способное выполнить наиболее критические операции подобные доступа к сети и устройствам ввода вывода.
Паравиртуализация - базируется на тех же технологиях как и виртуализация, но позволяет улучшить производительность так как вместо эмуляции протокола работы с подобием реальной аппаратуры мы может обойтись одним псевдоустройством с областью памяти для обмена пакетами с каким-то методом синхронизации, когда запись в определенную область или вызов прерывания сигнализирует готовность данных для обмена. Также позволяет контролировать внутренние параметры системы и освобождать свободную память или понизить частоту опроса и выдачи процессорного времени виртуальной машине, как результат нагрузка систему снижается и более прогнозируема или контролируема со стороны планировщиков базовой системы.
Вложенная виртуализация - является технологией рассчитанной на предоставление возможности запуска вложенных виртуальных машин на уже виртуализированной системе без использования программной эмуляции вложенной системы. Основная задача проэмулировать аппаратные расширения виртуализации, например, команды смены режима процессора или распределения памяти. Основная проблема в данном режиме у нас уже есть какие-то обработчики, привязанные к прерываниям или выполнению команд критичных для состояния системы. И дать возможность совместного управления основной системой и виртуализированной мы не можем, так они имеют максимальный приоритет и если вложенная система будет иметь доступ к ним они могут устроить побег из изоляции. А значит нужно дать возможность виртуальной машине первого уровня привязать новые обработчики для системных функции и при срабатывании исключений для критичных команд, передать сообщение об этом на корректный уровень виртуализации с нулевого уровня в первый уровень виртуализации для корректной обработки событий 2 уровня виртуализации. Частично это решается через бинарную трансляцию кода исполняемого в виртуальной машине, что позволяет все критичные команды заменить на вызов обработчиков для этих команд без переходов между уровнями.
Контейнеры - организуется через стандартную функциональность операционной системы по изоляции процессов и подмены ресурсов. Позволяет получить очень легковесную изоляцию вложенной системы от базовой системы и других запущенных контейнеров. Организовано через создание пространств имен для всех ресурсов системы, когда существуют базовое пространство доступное лишь в базовым процессам, и выделенные подпространств доступных в контейнерах. Главный недостаток отсутствие возможности запуска процессов рассчитанных на запуск под другой семейство операционных систем, так как процесс будет видеть тоже самое ядро что и основная система, частично это решается расширением количества подсистем доступных в базовом ядре или запуском дополнительных прослоек эмуляции API не доступного на базовой системе, как пример может быть использовано UNIX подсистема в NT ядре или Wine для Unix ядер.

Thursday, February 11, 2016

Netconf клиент

Пример использования netconf протокола совместно с cloudify. 

Для использования этого протокола с использованием python есть несколько вариантов:
  • использовать уже существующие библиотеки заточенные под конкретные устройства (не обязательно аппаратные, можно и программные реализации) - ncclient. Хорошее решение если кто-то побеспокоился и создал код для нужного устройства, если нет нужно разбираться и писать код;
  • реализовать самому протокол посмотрев в стандарты: rfc6241 - общее описания какие команды, есть и как их использовать; rfc6242 - низкоуровневое описание как общаться. Ничего особо сложного в этом нет, но если использовать python или C больше похоже на изобретение велосипеда;
  • остановится на решении которое бы реализовывало протокол, но сами пакеты пользователь формулирует в удобном ему виде.
Как пример последнего может быть cloudify-netconf-plugin :-) Код в репозитории можно разбить на 3 вида:
  • реализация самого протокола общения netconf_connection.py, поддерживает NetConf 1.0 и 1.1 поверх ssh.
  • реализация преобразования python словарей в xml нужного формата и обратно utils.py
  • интеграция с cloudify xml_rpc.py
Как результат мы имеем дополнение к cloudify которое позволяет автоматизировать действия с настройками устройств поддерживающих этот протокол используя tosca yaml blueprints формат.

Cloudify позволяет сделать гораздо более широкие возможности чем поменять настройку какого-то устройства и при желании можно дописать плагинчик под что угодно, если плагинов по умолчанию или скриптования на bash c python вам не достаточно, но сейчас не об этом речь. Более подробно можно почитать на getcloudify.org.
Вернёмся к тому что представляет из себя плагин - он позволяет описать последовательность команд которые нужно отправить на устройство в yaml как последовательность действий внутри ноды указав действие которое нужно выполнить с указанием параметров для этого действия. Система изначально ориентирована на то что нужно получить описание интерфейса устройства в виде yang файла потом прогнать их через yttc для получения шаблона который потом подключить в нужный blueprint. Но никто не мешает напрямую писать нужные описания данных сразу в blueprint.

Запускать для turing машины это все достаточно просто(только нужно пометь в этой последовательности пароли и хост на реальные):
  • mkdir turing
  • virtualenv turing
  • cd turing
  • . bin/activate
  • git clone git@github.com:cloudify-cosmo/cloudify-netconf-plugin.git
  • pip install -e cloudify-netconf-plugin
  • pip install cloudify
  • touch netconf_inputs.yaml
  • echo "netconf_ip: localhost" >> netconf_inputs.yaml
  • echo "netconf_user: netconf" >> netconf_inputs.yaml
  • echo "netconf_password: netconf" >> netconf_inputs.yaml
  • cfy local init -p cloudify-netconf-plugin/blueprint_examples/turing.yaml -i netconf_inputs.yaml
  • cfy local execute -w install

Код можно использовать как совместно с cloudify, так и без него только нужно написать обертку над модулями для запуска функциональности.

Monday, January 4, 2016

Netconf protocol

Инструкция по установке тестового сервера для проверки netconf протокола. Для этой цели вполне подойдет netopeer сервер, желательно его ставить на centos, так как установка на убунту сложнее так как нет части библиотек и нужно ставить их вручную.
  • Для начала нужно скачать образ CentOS-7-x86_64-Minimal-1511.iso и запустить в любимой виртуалке Затем установить пакеты, которые в дальнейшем будут нужны для сборки сервера:
    • yum install libssh-devel libcurl-devel epel-release gcc libxml2-devel libxslt-devel libtool git wget libcurl-devel libssh2-devel libxml2 libxml2-python dbus-devel readline-devel
  • И доставить еще немножко
    • yum install python-pip
    • pip install pyang
    Это нужно так как pip идет отдельном репозитории epel-release и как следствие поставить сразу пакет и репозиторий не получится.
  • Поставить libnetconf библиотеку
    • git clone https://github.com/CESNET/libnetconf.git
    • cd libnetconf/
    • git checkout 0.9.x (commit 1f117e5a8017c3ec630f890faaee8ff28d382798)
    • ./configure --prefix=/usr
    • make
    • make install
    • ln -s /usr/lib/pkgconfig/libnetconf.pc /usr/lib64/pkgconfig/
    • cd ../
  • Установить сервер
    • git clone https://github.com/CESNET/netopeer.git
    • cd netopeer/
    • git checkout libnetconf-0.9.x (commit 092e2ea95e77e1b6a2f93a53d43b1917532a1137)
    • cd server
    • ./configure --sysconfdir=/etc --prefix=/usr
    • make
    • make install
  • Установить клиентские утилиты
    • cd ../cli/
    • ./configure --sysconfdir=/etc --prefix=/usr
    • make
    • make install
    • netopeer-configurator
  • Добавить модуль для эмуляции расширения протокола
    • cd ../netopeer/transAPI/turing/
    • ./configure --prefix=/usr
    • make
    • make clean
    • make
    • make install
  • И проверить, что у нас получилось:
    • netopeer-server -v 3 -d
    • ssh -p 830 root@localhost -s netconf
Последняя команда должна выдать полный список поддерживаемых расширений протокола. Все конечно ставится варварским способом в основную систему без пакетов и отката изменений, но это все же виртуальная машина.

Wednesday, August 5, 2015

Step tracker

Достоинства открытого протокола для мелкой электроники и в частности трекеров шагов, можно не заморачиваться с приложениями пользователи с конспиративными наклонностями(из фонда fsf.org) сами напишут приложении и построят, если нужно графики и тому подобное. Пользователи более или менее довольны, что могут сами проверить: что отправляется и принимается и если что: сами осуществить поддержку устаревших в каком-то плане устройств. Можно если что добавить поддержку этих устройств на новых платформах. Но возникает проблема в цене, если протокол доступен пользователь может уйти от идеи материальной поддержки производителя или его сервисов, он же сам может все сделать. Также конкуренты могут сделать дешевле или добавить в свое устройство поддержку вашего сервиса и не вкладывать деньги в поддержку сервиса, и вы будете осуществлять его своим и чужим. А он будет только клепать устройства. Поэтому возникает достаточно сложная задача в каком объёме будет окупаться ваш продукт, в цену будет вкладываться разработка устройства, производство и сервисные услуги, такие как сообщества пользователей устройства и страница рейтингов пользователей, если это игра или подразумевается какой-то вид вовлечения в сообщество под средством игры(кто быстрее ходит, сбрасывает вес, бывает в новых местах). Причем последний пункт может быть сразу вложен в цену продукта так и быть оплачен отдельно через платную поддержку.

И так история разбора протокола одного трекера....

При этом не использовались методы обратного инжинеринга, подобного декомпиляции программного или аппаратного продуктов. Все данные получены через наблюдение данных отображаемых устройством и передаваемых через usb шину. Замечалось что на экране показывается и какие пакеты идут с устройства к программе и уточнялись примерно по показаниям программы особенности данных, такие как примерное распределение значений данных и предположения как можно было реализовать с нуля и как можно хранить эти данные. Как результат единственно используемое средство это слежение за трафиком и просмотр общедоступных данных отображенных на устройстве и статистика на сервере.
Общая структура системы была такова: устройство в виде ремешка с экраном на котором отображались количество шагов, расстояние пройденное за день и потраченные килокалории. В полночь карета превращалась в тыкву и все значения на экране обнулялись. Связь с внешним миром у устройства была только через usb, есть другие версии устройств с Bluetooth, но у меня было только подобное.
Существовали различные приложения для этого устройства:
  1. Приложение под мобильную платформы, через otg на android не заработало, так как не видело устройства. Содержало не очень приятную особенность, получало почти все возможные разрешения, зачем оно ему нужно, не знаю. 
  2. Под iphone не проверял так как такого в наличии у меня не было, сказать ничего не могу. 
  3. Поэтому осталось только приложение под windows. К сожалению именно так под линукс не было, но на результат это впрочем не влияло. Поэтому пришлось скачать альфа версию десятки и поставить на виртуалку. Приложение выполняло роль посредника вычитывало данные с трекера и синхронизировало их с сайтом. И содержало ссылку на сайт, где можно было просмотреть эти данные.
Для того чтобы дамп стал возможен нужно добавить своего пользователя в группу (vboxusers для debian), которая дает virtualbox захватывать на себя usb устройства по фильтру. Поэтому нужно уточнить тип устройства с которого будем снимать трафик и внести его в список устройств которые нужно пробросить в виртуальную машину и добавить себя в группу. После этого устанавливаем windows и программу обслуживания трекера. После этого нужно начать вычитывать данные с файла в который сохраняется весь трафик с шины.
Для сохранение трафика устройства ищем свое устройство в /sys/kernel/debug/usb/devices или lsusb, в моем случае это был преобразователь последовательного порта в usb именно так оно отображалось в системе, обращаем внимание только на номер шины, так как мы будем сохранять все пакеты на шине (должно быть что-то подобное Bus=04) и запускаем сохранение этих данных вычитывая /sys/kernel/debug/usb/usbmon/4u, перед этим вызывать modprobe usbmon. Или через tcpdump -i usbmon4 -w usblog.pcap как уж будет удобнее. Я выбрал первое.
После этого я начал анализировать пакеты, которые идут устройству и выявил, что общение всегда начинается похожего на статусный пакет, в ответ на который всегда возвращается номер трекера и еще пара байт, которая до сих пор непонятно для чего предназначена.
Следующим шагом нужно было понять, с какими параметрами происходит соединение, по сохраненному трафику это было не понятно, так как средство, которое я использовал для визуализации, не содержало фильтров для подобного трафика. Поэтому проверил набор стандартных скоростей и характеристик потоков и одна из скоростей подошла. Если бы не подошла, то пришлось бы все же посмотреть на код драйверов для этого преобразователя чтобы расшифровать технический трафик.
Попробовав отправить наш статусный пакет трекеру, и получив тот же самый ответ, что и в нашем трафике - я понял, что на верном пути, параметры связи совпали и можно пробовать следующие пакеты.
Следующим шагом было несколько раз сбросить устройство с интерфейса и установить часы, это были единственные пункты подразумевающее действия, на которые может повлиять пользователь. Запомнив время в которое выполнялись эти команды, стало возможным отфильтровать эти 2 команды. И можно было начинать разбираться с форматом пакета. По дампам на устройство всегда уходили 17 байтные пакеты, первый байт был постоянен в одно значение для всех пакетов. Второй байт зависел от команды, трое из которых мы отфильтровали (были еще, но на данном этапе они были не важны). Затем 14 байт предположительно данных завершающихся нулем и один байт, похожий на хеш сумму так как изменялся в зависимости от данных, и для одного значения устанавливаемых часов всегда совпадал, как и для других команд, на содержимое которых мы не могли влиять.
Поэтому я предположил что это crc8, по сути, единственный хеш который приходил в голову сразу, и начал искать его реализацию или сайт, которые могут посчитать хеш по уже известным данным. Проверив данные, что у меня есть на соответствие по результату crc, оказалось, что не угадал. На одном сайте был пример простейшего хеша заключающегося в сумме всех байт по модулю 256 и он подошел к уже известным пакетам. И проверив это предположение попытавшись отослать немного измененный пакет для установки времени и время установилось в том виде что указывал в пакете. И теперь можно разбираться с форматом этого времени, по внешнему виду значения всех чисел в дате было в классическом человеко-читаемом виде(не секунды с начала эпохи) в bcd формате когда в полубайте храниться десятичное число и в hex виде совпадает посимвольно обычному десятичному числу.
После этого осталось три не опознанных пакета, первый всегда предшествовал остальным двум и содержал 4 не нулевых байта, и после выполнения остальных команд это число уменьшалось, поэтому я предположил, что это количество дней содержащихся в памяти. Остальные две команды возвращали одна большой дамп каких-то пакетов и одна только один пакет. Посмотрев статистику за предыдущие дни - я увидел, что интервал для данных меньше часа и предположил что первая это как раз дамп по времени, а вторая какой-то итого. Так первая выдала больше одного пакета я начал высчитывать эти данные несколько раз, и заметил, что всегда возвращается 96 пакетов и в каждом пакете был увеличивающийся байт. И тут можно предположить, что оно возвращает значения по 15 минутный интервалам за день. Так данные показываются на индикаторе только за сегодня я провел эксперимент, делая шаги и замечая когда и сколько их я сделал. В результате я узнал что среднее значение в этом пакете это шаги.
Далее я обратил внимание, что в этих запросах на дамп байт после кода команды увеличивают. И сделав запрос с циклом от минимального 0 до максимально значения, которое было в дампах полученных вначале, можно было сделать вывод, что это день начиная от сегодня. Но оставалось понять, что делает вторая команда, если она всегда возвращает нули. И на следующий день я обнаружил, что мой трекер забыл предыдущее дни, и тут меня осенило - это удаление дня из памяти. И оставалось понять, как 4 байта описывают заполненность памяти и почему на 3 возвращается только 2 дня, на 7 только 3, а на следующий по счету день вместо 96 пакетов приходит один и байт после кода команды другой. Методом не сложных умозаключений, смотря на hex код, я понял, что это не количество, а маска заполненности памяти.
Теперь осталось понять самую малость, что за остальные два числа в режиме бодрствования и что за данные в режиме сна. (Трекер имеет два таких режима, и в режиме сна шаги не считает, но проверяет качество сна, если верить графикам). Первый случай решился достаточно просто, я решил просуммировать оставшиеся числа в столбик, и оказалось, что это расстояние и затраченные энергия. Перед этим я думал, что это какие-то слагаемые шагов, так как иногда сумма этих чисел была равна шагам, но эта идея отпала когда я выяснил, что это не корректно - сумма крайних столбцов не равна среднему. Для определения, где старший байт, а где младший - я сделал более 300 шагов за интервал и там, где появилась эта единица то и старший разряд.
А теперь сон, при включенном сне один байт после кода операции меняется это выявить оказалось достаточно просто, но что делают остальные байты, которые не всегда нули и наверное что-то значат? Попытка включить режим сна и походить привела к тому, что этот интервал засчитывался как бодрствование. Данные по графикам на сайте были не точны, но каждый вид активности глубокий сон или легкий отображался другим цветом и сравнив данные на графике и распределение чисел в дампе по интервалу построив график в libreoffice calc получилось, что это значения по какому-то интервалу. Но остался вопрос размерности этих чисел, может быть один байт если это не шаги и большого уровня данных быть не должно осталось только это каким-то образом проверить.
Как понять тип сна я смоделировать не мог, так не мог сравнивать, вид сна на экране не отображается, только то что мы в режиме сна.Чтобы понять безопасность сервиса, я решил посмотреть, что и как отправляется на сервер, оказалось, что ничего шифровалось. И данные в режиме сна идут побайтно, получается это значение в интервале около 2 минут. Остались личные данные вида роста, пола и веса, но изменение их на сайте не создавало новых пакетов, поэтому я решил, что они обратно не передаются.
Вот и вся история, если нашли ошибки в рассуждениях пишите... Буду благодарен за уточнения и пожелания.

Saturday, August 1, 2015

SSD firmware update

Если судить по последним сообщениям проблема с последней прошивкой связанна с реализацией raid в ядре linux, поэтому обновление должно быть безопасно для обычных пользователей.
Samsung более не распространяет архив с кодом для обновления. Вместо него предлагая образ для cdrom, но если его записать на flash он не распознаётся как загрузочная. Поэтому для обновления нужно поступить немного по другому:
  1. bunzip2 FreeDOS-1.1-memstick-2-256M.img.bz2
  2. dd if=FreeDOS-1.1-memstick-2-256M.img of=/dev/sdb bs=512k 
  3. mount -t auto -o loop Samsung_SSD_840_EVO_EXT0DB6Q.iso /media/usb1/ 
  4. mount -t auto -o loop /media/usb1/isolinux/btdsk.img /media/usb2/ 
  5. mount -t auto /dev/sdb1 /media/usb0/ 
  6. cp /media/usb2/samsung/ /media/usb0/ -vr 
  7. umount /media/usb2/ 
  8. umount /media/usb1/ 
  9. umount /media/usb0/ 
  10. sync

Sunday, June 21, 2015

Сила эмуляции

Если посмотреть на современные компьютеры с несколько необычного угла,то можно увидеть: что ничего по сути не поменялось. Да все стало миниатюрнее и содержит большее количество транзисторов, но количество реально работающих транзисторов и объёмы памяти доступные для процессора не поменялись. Конечно это не совсем так и я сильно утрирую, но всё же посмотрим к чему это нас приведет.

Рассмотрим, как же современная аппаратура работает.
  • При включении питания процессор включается и инициализирует свои внутренние структуры можно даже считать, что он вычитывает из своего ROM внутри себя начальную программу, так как современные процессоры кроме совсем простых RISC процессоров, содержат собственный микрокод который при старте конфигурирует внутренние структуры и загружает интерпретатор внешнего бинарного кода, так как сам процессор не знает ничего о своих CISC командах и все эти команды интерпретируются, скорее даже прекомпилируются в начале конвейера в VLIW(very long instruction word) и уже эти команды он выполняет. При этом благодаря идее темного кремния - части процессора которые в данный момент не работают отключают и уменьшают количество включенных транзисторов на каждом этапе. И по сути пытаются не делать универсальные блоки - так как экономить на транзисторном бюджете не нужно, можно создать отдельные блоки для сложения и вычитания и просто выключать лишнее и получать более оптимальное соотношение потребляемой мощности к производительности так как специализированная схема, заточенная под одну операцию всегда эффективнее.
  • После этого начинается загрузка BIOSа, которая содержит совсем маленький загрузчик цель которого включить уровне кеша выше первого, так как только первый уровень кеша доступен на этапе старта и это очень ограниченное количество килобайт. Или почти intel 80. Эти инструкции настраивают следующие уровни кеша и остальную встроенную в процессор периферию, и уж затем копируют туда следующие участки кода BIOSа перекрывая участки оригинального BIOSа и распаковывая его.
  • Инициализируем самые критичные блоки и обновляем микрокод процессора и мы имеем несколько мегабайт доступной памяти и можем регулировать питание внешних устройств и т.п.. Или почти intel 86 процессор.
  • И вот теперь можно протестировать внешнюю память и настроить ее - и мы получаем intel 386 и выше процессор и у нас несколько гигабайт адресное пространство. И начинаем инициализировать остальную периферию. И если это старые BIOS мы выходим в состояние 86 процессора в 16 режиме или если это UEFI - то в родном 32/64 режиме это уже зависит, как повезет.
  • И так мы имеем первый уровень интерпретации. Процессор интерпретирует CISС инструкции - память отображена один в один по адресам, но уже работает система когда все обращении к участку памяти он скрытно копируется в кеш и уже там над ним что-то делают.
Ползем далее - операционная система создает виртуальное адресное пространство или TLB структуры и блокирует все обращение к привилегированным инструкциям. Далее код уже не видит память и не может общаться с периферией. Но можно работать с виртуальными файловыми системами и получать данные не зная, как же это все работает внутри.
Далее мы идем в следующий уровень виртуализации - контейнеры браузеры, виртуальные машины. Нам создают отдельную нить исполнения и перехватывают все наши действия и обращения к памяти и эмулируют новую аппаратуру если нужно - и от сюда нам уже не выбраться - мы не видим ровным образом ничего - нам только сказали - захочешь чего либо - вызови это прерывание.

Аппаратура и драйвера тоже ничего по сути не видят, им их запросы к памяти через IOMMU блокируют с аппаратной стороны и через вынесение их кода на менее привилегированные уровни блокируют обращении с периферией - разрешая через прерывания вызывать код ядра из уровня ниже. Или используя отображение памяти - когда пробрасывается в область памяти процесса память с видеокарты или dma - для скрытого копирования памяти между устройствами удаётся скрыть все особенности аппаратуры и обращения к памяти.

А что этот код будет делать? и кто там? - мы не узнаем, может там совсем другая архитектура и мы в виртуалке?

А есть же еще TrustZone и SMM(system managment mode) код - который может стартовать до BIOSа или быть отдельным процессором, который видит наш процессор и его память с периферией, а мы его нет. На этих механизмах построен менеджмент серверов когда через отдельную сеть управляется состоянием сервера - без его участия, например вход в BIOS и проброс кадра видеокарты через сеть или DRM(digital rights management) логика.

HTTP2 vs Cache Proxy

Http2 убивает идею кешировантие на прокси, этот стандарт приносит передачу сразу в несколько потоков с использование приоритетов на каждый поток, но в нем не заложена идея кеширования на промежуточных узлах. Это становиться не возможно, после того как любое соединение становиться шифрованным и требует валидных сертификатов. В следствии этого нельзя несколько раз передать туже информации разным пользователям, так как для каждого соединения используются разные ключи для потока. Значит можно работать только по схеме CDN, когда на промежуточных узлах есть оригинальный сертификат и все данные перепаковываются им. Было бы замечательно, если бы существовала технология, когда данные с определённого не зашифрованного потока можно было закешировать и передавать отдельным зашифрованным каналом подписи для таких данных, а сами данные по усмотрению промежуточных узлов брались из отдельного кеша. Даже если в кеше были бы данные зашифрованные и хозяин данного кеша не мог получить эти данные в открытом виде, но оставались бы хеши от содержимого и подпись этого содержимого. Это вернет кеширование в общее потребление - и сразу несколько клиентов могли бы получать общие библиотеки по общему каналу, а подписи для этих библиотек и специфичную для пользователя информацию по другому.