Как переопределить стандартные функции libc?

2 theanine [2015-01-30 03:29:00]

Например, если я хочу переопределить malloc(), что это лучший способ сделать это?

В настоящее время самым простым способом я знаю:

malloc.h

#include <stdlib.h>
#define malloc my_malloc
void* my_malloc (size_t size);

foobar.c

#include "malloc.h"

void foobar(void)
{
    void* leak = malloc(1024);
}

Проблема с этим подходом заключается в том, что теперь нам нужно использовать "malloc.h" и никогда не использовать "stdlib.h". Есть ли способ обойти это? Мне особенно интересно импортировать сторонние библиотеки, не изменяя их вообще, но заставляя их вызывать мои пользовательские функции libc (например, malloc).

c override malloc libc


1 ответ


2 Moises Silva [2015-06-26 20:15:00]

Короткий ответ: вероятно, вы хотите использовать трюк LD_PRELOAD: что такое трюк LD_PRELOAD?

Этот подход в основном вставляет вашу собственную пользовательскую разделяемую библиотеку во время выполнения перед загрузкой любой другой разделяемой библиотеки, экспортируя функции, которые вы хотите переопределить, например, malloc(). К моменту загрузки других разделяемых библиотек ваш символ уже существует и получает предпочтение при разрешении вызовов этого имени из других библиотек. Из вашей оболочки/замены malloc() вы можете даже выбрать вызов следующего символа malloc, который обычно будет фактическим символом libc.

Этот пост в блоге содержит много полной информации об этом методе:

http://samanbarghi.com/blog/2014/09/05/how-to-wrap-a-system-call-libc-function-in-linux/

Обратите внимание, что пример переопределяет функции libc write() и puts(), но для malloc() применяется одна и та же логика:

LD_PRELOAD позволяет загружать общую библиотеку перед любыми другими библиотеками. Поэтому мне нужно всего лишь написать общую библиотеку, которая переопределяет функции write и puts. Если мы обертываем эти функции, нам нужен способ вызова реальных функций для выполнения системного вызова. dlsym просто делает это для нас [man 3 dlsym]:> Функция dlsym() берет "дескриптор" динамической библиотеки, возвращаемой dlopen() и символом с нулевым символом, возвращая адрес, в который этот символ загружается в память, Если символ не найден, в указанной библиотеке или в любой из библиотек, которые были автоматически загружены dlopen(), когда эта библиотека была загружена, dlsym() возвращает NULL...

Поэтому внутри функции-оболочки мы можем использовать dlsym, чтобы получить адрес связанного символа в памяти и вызвать функцию glibc. Другой подход может быть вызван syscall напрямую, оба подхода будут работать.

В этом сообщении в блоге также описывается метод компиляции, который я не знал об этом, включая передачу флага компоновщика в ld, "--wrap":

Другим способом обертывания функций является использование компоновщика в момент ссылки. GNU linker предоставляет возможность обертывать функцию для символа [man 1 ld]:> Использовать функцию обертки для символа. Любая неопределенная ссылка на символ будет разрешена "__wrap_symbol". Любая неопределенная ссылка на "__real_symbol" будет разрешена к символу.

Удобная вещь о LD_PRELOAD - это то, что может позволить вам изменить реализацию malloc() на производственных приложениях для быстрого тестирования или даже позволить пользователю выбирать (я делаю это в некоторых серверных приложениях), какую реализацию использовать. Например, библиотека " tcmalloc " может быть легко вставлена в приложение для оценки прироста производительности в сильно поточных приложениях (где tcmalloc имеет тенденцию выполнять намного лучше, чем реализация libc malloc).

Наконец, если вы работаете в Windows, попробуйте это: эквивалент LD_PRELOAD для Windows для предварительной загрузки разделяемых библиотек