Вставка части общего объекта по soname

Я написал общий объект, который изменяет аргументы для FreeType FT_Load_Glyph а также FT_Render_Glyph функции, в настоящее время, вставив его с LD_PRELOAD а также dlsym,

Это прекрасно работает, но мне любопытно узнать, есть ли способ сделать эти изменения:

  • всем программам, которые используют FreeType на данном хосте (например, запущенный Debian);
  • без забивания любых программ, которые на самом деле не связаны с FreeType;
  • без простого применения LD_PRELOAD на все программы на хосте;
  • без необходимости технического обслуживания, если только не изменено имя пользователя FreeType; а также
  • без изменения файлов FreeType или программ на хосте.

Единственные два "решения", которые я смог придумать, это уродливые хаки:

  • в LD_PRELOAD все программы, все время, которые кажутся медленными и хрупкими; или же
  • например, скопировать libfreetype.so.6.12.3 в libxxxxtype.so.6.12.3; затем
    • залатать сонаму в libxxxxtype.so.6.12.3 в libxxxxtype.so.6;
    • связать вставляемый общий объект с libxxxxtype.so.6; а также
    • установить общий объект как, например, libfreetype.so.6.999,

По сути, я бы хотел прозрачно пропатчить пару функций в общем объекте, пропуская оставшиеся функции без необходимости иметь доступ к источнику общего объекта или программам, которые его используют, но если я сделаю поддельный общий объект с сонамой libfreetype.so.6Я не могу найти чистый способ связать его с (или dlopen) реальный libfreetype.so.6,

Это мой первый настоящий эксперимент с разделяемыми библиотеками, поэтому, пожалуйста, потерпите меня, если в этом вопросе есть какие-то неверные предположения или он просто не имеет смысла.

3 ответа

Решение

Можете ли вы попробовать использовать uprobes динамически украсть контроль над некоторыми функциями?

Проверьте http://www.brendangregg.com/blog/2015-06-28/linux-ftrace-uprobe.html

uprobes: динамическая трассировка на уровне пользователя, которая была добавлена ​​в Linux 3.5 и улучшена в Linux 3.14. Это позволяет вам отслеживать функции уровня пользователя; например, возвращение функции readline() из всех запущенных оболочек bash с возвращенной строкой:

# ./uprobe 'r:bash:readline +0($retval):string'
Tracing uprobe readline (r:readline /bin/bash:0x8db60 +0($retval):string). Ctrl-C to end.
 bash-11886 [003] d... 19601837.001935: readline: (0x41e876 <- 0x48db60) arg1="ls -l"
 bash-11886 [002] d... 19601851.008409: readline: (0x41e876 <- 0x48db60) arg1="echo "hello world""
 bash-11886 [002] d... 19601854.099730: readline: (0x41e876 <- 0x48db60) arg1="df -h"
 bash-11886 [002] d... 19601858.805740: readline: (0x41e876 <- 0x48db60) arg1="cd .."
 bash-11886 [003] d... 19601898.378753: readline: (0x41e876 <- 0x48db60) arg1="foo bar"
^C
Ending tracing...

И http://www.brendangregg.com/blog/2015-07-03/hacking-linux-usdt-ftrace.html

Были и другие решения для отслеживания функций пространства пользователя, такие как ftrace, systemtap, dtrace, lttng. Некоторые из них нуждаются в перекомпиляции и статическом определении точек трассировки в программе; и uprobes - " динамическое отслеживание на уровне пользователя".

Некоторые ссылки об uprobes:

Есть handler uprobes, который имеет pt_regs, Как сказано в последней ссылке: "Таким образом, Uprobes реализует механизм, с помощью которого функция ядра может быть вызвана всякий раз, когда процесс выполняет определенное расположение команды". Он предлагает, чтобы апробации могли заменить некоторые решения на основе ptrace / gdb; так что есть возможность изменить выполнение любой программы, попавшей в активный uprobe, путем изменения ее регистра eip/rip (PC).

Вы можете попробовать некоторые другие инструменты динамического инструментария, такие как pin или же dyninst; но они предназначены для использования в каждом процессе.

LD_PRELOAD все программы, все время, что кажется медленным и хрупким

Это хорошее решение (для того, что вы хотите). Я не вижу лучшего.

  • Это не хрупкое. Он предоставляет информацию для компоновщика времени выполнения документированным способом. Ты ни на что не надрываешься, притворяешься, что не то, что есть. Вы просто изменяете иерархию предпочтений для разрешения имени функции.

  • Это не медленно. Линкер должен что-то сделать когда-нибудь. Нужно проверить, LD_PRELOAD определяется, что в любом случае является операцией в пространстве пользователя. Так что он пойдет по этому пути и загрузит вашу библиотеку, прежде чем делать кучу других работ. Я был бы удивлен, если бы время было даже измеримым при нормальных обстоятельствах.

У меня есть две проблемы, но они ортогональны технике. Код на самом деле должен работать во всех случаях, и вам нужно немного покопаться в структуре создания процесса, чтобы убедиться, LD_PRELOAD на самом деле определяется везде. Помимо этого, ld.so определяет свои переменные окружения именно для вашего предполагаемого использования. С кем спорить?

Другим решением было бы сделать системное "наложение" для lib с настраиваемым libfreetype, а затем проксировать неизмененные методы в настоящую lib.

Вы должны сделать пользовательскую библиотеку совместимой с реальной. Вы можете к этому с помощью dlopen с абсолютным путем (например, dlopen("/usr/lib64/libfreetype.so.6")), копируя определения реальных, экспортируемых функций и передавая их dlsym, Он считает, что для простоты обслуживания вы можете заменить прокси-типы аргументов на простые void*, Вам нужно будет вносить изменения только при изменении функций freetype (количество аргументов, имена функций).

Чтобы создать lib "overlay", вы можете установить пользовательский lib, например, в. "/opt/myapp/lib64/libfreetype.so.6", затем добавьте этот путь к динамическим путям компоновщика. Возможно, вам придется создать символические ссылки для других версий или скомпилировать новую пользовательскую библиотеку, если оригинальная реализация изменится. Все, что нужно для слежки за реальной библиотекой и поддержания работы других приложений:)

Google говорит, что для изменения путей загрузки во время выполнения в Debian вы должны просто отредактировать /etc/ld.so.conf, добавлять /opt/myapp/lib64 путь в начале, поэтому он будет проверен первым. Теперь любое приложение, ищущее freetype, должно загрузить вашу библиотеку, вы можете проверить это с помощью ldd <path to app>,

Я могу вспомнить только один случай, когда это решение не будет работать: если приложение загружает пакетный libfreetype или загружает его по полному пути, а не по имени.

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