Пиков / КАПО
Как читать авторский курс и нормативные материалы

Это учебные материалы, а не заключение о соответствии конкретного продукта. Организационные модели курса отделяйте от терминов стандарта: ППК в опубликованном проекте — машиночитаемый перечень, а живой реестр его версий является практикой сопровождения. Термин OSA в разных материалах имеет разный охват. Статус и обязательность нормативного документа проверяются отдельно; проект КАПО опубликован как проект первой редакции.

ELF и разделы dynamic

Почему это важно для SCA/OSA

Секция .dynamic ELF-файла — это машиночитаемая запись о том, что бинарь требует от загрузчика. Она хранит набор тегов DT_*: имена нужных библиотек (DT_NEEDED), собственное имя модуля (DT_SONAME), пути поиска (DT_RPATH/DT_RUNPATH) и прочее. Для SCA это самый прямой и дешёвый источник состава: чтобы перечислить динамические зависимости, достаточно прочитать структуру .dynamic, не запуская файл и не имея журнала сборки.

Понимать .dynamic именно как структуру данных важно, потому что от тегов зависит интерпретация. DT_NEEDED даёт имя, но не путь; DT_RPATH/DT_RUNPATH меняют, какой файл реально подставится под это имя. Читать список зависимостей в отрыве от тегов поиска — значит рисковать тем, что инвентаризация назовёт системный пакет, а в продукт уедет vendored-копия.

Как это проявляется

Полный дамп секции читается одной командой; каждая строка — отдельный тег DT_*:

$ readelf -d ./app

Dynamic section at offset 0x2da8 contains 27 entries:
  Tag                Type        Name/Value
 0x0000000000000001 (NEEDED)     Shared library: [libssl.so.3]
 0x0000000000000001 (NEEDED)     Shared library: [libcrypto.so.3]
 0x0000000000000001 (NEEDED)     Shared library: [libc.so.6]
 0x000000000000000e (SONAME)     Library soname: [libapp.so.1]
 0x000000000000001d (RUNPATH)    Library runpath: [$ORIGIN/../lib]
 0x000000000000000c (INIT)       0x3000

Здесь читается всё нужное для инвентаризации: какие библиотеки требуются (NEEDED), как называет себя сам модуль (SONAME — по нему его найдут другие), и — критично — RUNPATH: $ORIGIN/../lib, то есть загрузчик будет искать .so рядом с бинарём раньше системных путей.

Ключевые теги .dynamic для SCA | Тег | Что означает | Зачем SCA | |-----|--------------|-----------| | DT_NEEDED | имя требуемой .so (soname) | список динамических зависимостей | | DT_SONAME | как называется сам модуль | идентичность библиотеки-артефакта | | DT_RPATH | устаревший путь поиска (до LD_LIBRARY_PATH) | риск подмены/vendored | | DT_RUNPATH | современный путь поиска (после LD_LIBRARY_PATH) | то же, приоритетнее RPATH |

Слепая зона или приём

Слепая зона .dynamic — расхождение между именем и файлом. DT_NEEDED называет libssl.so.3, но DT_RUNPATH = $ORIGIN/../lib заставит загрузчик взять libssl.so.3 из каталога поставки, а не системный libssl3. Инвентаризация по одному DT_NEEDED запишет системную версию, тогда как в рантайме поедет vendored-копия с другой. Приём — всегда читать RPATH/RUNPATH вместе со списком NEEDED и сверять с фактическим путём резолвинга (ldd, см. ldd readelf objdump).

Вторая граница: .dynamic по определению не содержит статически вшитого кода — там нет ни DT_NEEDED, ни иного следа для .a-архивов. Это зона статической линковки, а не .dynamic.

Что делать на проекте

  1. Снимать состав из .dynamic через readelf -d — статически, не исполняя файл; это базовый источник динамических зависимостей.
  2. Читать не только DT_NEEDED, но и DT_RPATH/DT_RUNPATH: их наличие — сигнал, что фактический файл может отличаться от системного.
  3. При непустом RPATH/RUNPATH сверять имя из NEEDED с реальным путём резолвинга и помечать vendored-копии отдельно.
  4. Использовать DT_SONAME как идентичность модуля при маппинге и дедупликации компонентов.
  5. Отсутствие DT_NEEDED для ожидаемой библиотеки трактовать не как «зависимости нет», а как «возможно, вшита статически» — и уходить в build-scan.
  6. Для разбора .dynamic пачкой бинарей — программный парсер вместо ручного readelf (см. LIEF как программный анализ ELF).

Связь