Мне известны следующие варианты, но все они имеют свою цену и множество ограничений:
- Использование
libdl
(#include <dfcln.h>
)
- Вызовите инструмент, например
objdump
или nm
- Разбор объектных файлов самостоятельно (с использованием соответствующей библиотеки)
- Включите анализатор и сгенерируйте необходимую информацию во время компиляции.
- «Использовать компоновщик» для генерации символьных массивов.
Я буду использовать немного платформ модульных тестов в качестве примеров ниже, потому что автоматическое обнаружение тестов для платформ модульных тестов является типичным примером, когда рефлексия очень удобна, и это то, что большинству платформ модульных тестов для C не хватает .
Использование libdl
(#include <dfcln.h>
) (POSIX)
Если вы работаете в среде POSIX, с помощью libdl
можно немного подумать. Плагины разрабатываются таким образом.
Используйте
#include <dfcln.h>
в вашем исходном коде и ссылка с -ldl
.
Тогда у вас есть доступ к функциям dlopen()
, dlerror()
, dlsym()
и dlclose()
, с помощью которых вы можете загружать и получать доступ / запускать общие объекты во время выполнения. Тем не менее, это не дает вам легкий доступ к таблице символов.
Другим недостатком этого подхода является то, что вы в основном ограничивает отражение объектами, загружаемыми в качестве динамической библиотеки (общий объект, загружаемый во время выполнения через dlopen()
).
Бег nm
или objdump
Вы можете запустить nm
или objdump
, чтобы показать таблицу символов и проанализировать вывод.
Для меня nm -P --defined-only -g xyz.o
дает хорошие результаты, и анализ выходных данных тривиален.
Вас заинтересует только первое слово в каждой строке, которое является именем символа, и, возможно, второе, которое является типом раздела.
Если вы не знаете имя объекта каким-либо статическим способом, то есть объект на самом деле является общим объектом, по крайней мере, в Linux вы можете пропустить имена символов, начинающиеся с '_'.
objdump
, nm
или аналогичные инструменты также часто доступны вне среды POSIX.
Сам анализируем объектные файлы
Вы можете разобрать объектные файлы самостоятельно. Вы, вероятно, не хотите реализовать это с нуля, но используйте для этого существующую библиотеку. Вот как реализованы nm
, objdump
и даже libdl
. Вы можете заглянуть в исходный код nm
, objdump
и libdl
и библиотек, которые они используют, чтобы узнать, как они делают то, что делают.
Вовлечение парсера
Вы могли бы написать анализатор и генератор кода, который генерирует необходимую отражающую информацию во время компиляции и сохраняет ее в объектном файле. Тогда у вас будет много свободы и вы сможете даже внедрять примитивные формы аннотаций. Это то, что делают некоторые модульные тестовые среды, такие как AceUnit .
Я обнаружил, что написание синтаксического анализатора, охватывающего простой синтаксис языка Си, довольно тривиально. Написание парсера, который действительно понимает C и может работать со всеми случаями, НЕ тривиален.
Таким образом, это имеет ограничения, которые зависят от того, насколько экзотичен синтаксис Си, о котором вы хотите подумать.
«Злоупотребление» компоновщиком для генерации символьных массивов
Вы можете помещать ссылки на символы, которые вы хотите отразить в специальном разделе, и использовать конфигурацию компоновщика для создания границ раздела, чтобы вы могли получить к ним доступ в C.
Я описал здесь Внедрение N-зависимостей в C - лучший способ, чем массивы, определенные компоновщиком? как это работает.
Но будьте осторожны, это зависит от многих вещей и не очень переносимо. Я пробовал это только с GCC
/ ld
, и я знаю, что это работает не со всеми компиляторами / компоновщиками. Кроме того, почти гарантировано, что удаление мертвого кода не будет определять, как вы называете этот материал, поэтому, если вы используете удаление мертвого кода, вам придется добавить все отраженные символы в качестве точек входа.
Ловушки
Для некоторых механизмов устранение мертвого кода может быть проблемой, в частности, когда вы «злоупотребляете» компоновщиком для создания массивов символов. Эту проблему можно обойти, указав отраженные символы как точки входа для компоновщика, и в зависимости от количества символов это может быть ни приятно, ни удобно.
Заключение
Сочетание nm
и libdl
может действительно дать довольно хорошие результаты. Комбинация может быть почти такой же мощной, как уровень отражения, используемый JUnit 3.x в Java. Указанный уровень отражения достаточен для реализации среды модульного тестирования в стиле JUnit 3.x для C, включая обнаружение тестовых примеров в соответствии с соглашением об именах.
Привлечение синтаксического анализатора - это больше работы и ограничено объектами, которые вы сами компилируете, но дает вам больше силы и свободы. Указанный уровень отражения может быть достаточным для реализации среды модульного тестирования в стиле JUnit 4.x для C, включая обнаружение тестовых примеров по аннотациям. AceUnit является структурой модульного тестирования для C, которая делает именно это.
Объединение синтаксического анализа и компоновщика для генерации массивов символов может дать очень хорошие результаты - если ваша среда настолько под вашим контролем, что вы можете быть уверены, что работа с компоновщиком таким образом работает для вас.
И, конечно, вы можете комбинировать все подходы, чтобы соединять кусочки и кусочки, пока они не будут соответствовать вашим потребностям.