Могу ли я написать библиотеку для предварительной загрузки на C++? Есть ли что-нибудь, что мне нужно сделать, кроме добавления `extern "C"` к функциям для перехвата?

Я работаю над личным проектом, в котором мне нужно перехватывать API-интерфейсы Linux, такие как open(), read() и т. д., и я хотел бы провести по ним некоторый анализ данных. Мне нужно будет сохранить структуру данных C++ std::map в библиотеке, которая будет обновляться потокобезопасным способом.

Поскольку я хочу использовать std::map C++, я подумывал написать библиотеку (.so) для предварительной загрузки с использованием C++, а перед перехватываемыми функциями будет стоять extern "C", чтобы предотвратить искажение имен компилятором C++.

Но правильный ли это подход? Должен ли я написать общую библиотеку исключительно на C и реализовать структуру данных map с нуля?

Я думаю, что тебя минусуют, потому что ты спрашиваешь мнения. Измените его, чтобы спросить, можете ли вы написать предварительную загрузку на C++, и есть ли что-нибудь помимо extern "C", что вам нужно сделать особенно.

Streve Ford 27.07.2024 02:43

@StreveFord Большое спасибо :) Я отредактировал заголовок вопроса :)

Aravind A 27.07.2024 02:55

Вам нужна ваша программа для предварительной загрузки общей библиотеки (в Linux нет DLL) или вам нужна библиотека, которая предварительно загружает некоторый код?

Thomas Matthews 27.07.2024 03:09

Напоминание: глобальные и статические переменные будут инициализированы раньше main().

Thomas Matthews 27.07.2024 03:10

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

Thomas Matthews 27.07.2024 03:11

@ThomasMatthews Да, я мог бы написать двоичное дерево с нуля или использовать библиотеку :) Но я хотел знать, могу ли я использовать расширенные возможности STL (C++) в моей библиотеке LD_PRELOAD, которые могут подключать API

Aravind A 27.07.2024 04:24
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
6
63
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

это правильный подход?

В этом подходе нет ничего плохого, но он может усложниться (см. ниже).

Должен ли я написать общую библиотеку исключительно на C и реализовать структуру данных карты с нуля?

Это позволит избавиться от многих потенциальных осложнений.


Так в чем же сложности?

Чтобы использовать std::map, вам придется связать предварительную загрузку .so с libstdc++.so.6, которая попытается инициализировать себя раньше, чем это сделает ваша библиотека.

А libstdc++ большой и сложный. Я не удивлюсь, если его инициализация вызовет open(), read() и т. д.

Это означает, что ваша библиотека должна быть готова обрабатывать эти вызовы, прежде чем libstdc++.so сможет работать.

Следующая последовательность действий может привести к сбою вашей библиотеки:

  • libstdc++.so вызывает open, который вставлен вашей библиотекой
  • ваш интерпозер пытается поместить что-то в экземпляр std::map, который снова вызывает libstdc++.so,
  • который дает сбой, потому что он не ожидает, что его собственный вызов open вернется в libstdc++.

Возможны многие варианты вышеизложенного, и какой вариант вы получите, может зависеть от точного набора функций, которые вы вставляете, набора вызовов, которые вы совершаете в libstdc++, и версии libstdc++.

То есть он может работать нормально, пока вы не попробуете его в другой системе, или пока вы не обновите свой g++, или пока вы не добавите новый интерпозер. Или может вообще не работать.

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

P.S. В моей системе следующая программа:

int main() { return 0; }

построенный с помощью g++ main.cc -Wl,--no-as-needed и запущенный с помощью LD_DEBUG=bindings ./a.out, показывает, что мой libstdc++.so вызывает следующие libc.so.6 функции:

   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `__libc_single_threaded' [GLIBC_2.32]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `__cxa_finalize' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `stdin' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `stderr' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `stdout' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `pthread_once' [GLIBC_2.34]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `__newlocale' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `__uselocale' [GLIBC_2.3]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `wctob' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `btowc' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `__wctype_l' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `__cxa_atexit' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `secure_getenv' [GLIBC_2.17]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `malloc' [GLIBC_2.2.5]
   1926652:     binding file /lib/x86_64-linux-gnu/libstdc++.so.6 [0] to /lib/x86_64-linux-gnu/libc.so.6 [0]: normal symbol `fflush' [GLIBC_2.2.5]

Как видите, open и read не вызываются, а malloc и fflush вызываются, поэтому их вставка может вызвать проблемы.

Большое спасибо за этот замечательный ответ! :) Спасибо, что нашли время проверить все, прежде чем дать ответ :)

Aravind A 27.07.2024 06:03

Другие вопросы по теме