Проблемы с LD_PRELOAD и calloc () для некоторых исполняемых файлов - PullRequest
5 голосов
/ 27 октября 2011

Относительно предыдущего моего вопроса

Я успешно вставил malloc, но calloc кажется более проблематичным.

То есть с некоторыми хостами calloc застревает в бесконечном цикле с возможным внутренним calloc вызовом внутри dlsym. Однако базовый тестовый хост не демонстрирует такого поведения, а команда "ls" моей системы -.

Вот мой код:

// build with: g++ -O2 -Wall -fPIC -ldl -o libnano.so -shared Main.cc
#include <stdio.h>
#include <dlfcn.h>

bool gNanoUp = false;// global

// Function types
typedef void* (*MallocFn)(size_t size);
typedef void* (*CallocFn)(size_t elements, size_t size);

struct MemoryFunctions {
    MallocFn   mMalloc;
    CallocFn   mCalloc;
};

MemoryFunctions orgMemFuncs;

// Save original methods.
void __attribute__((constructor)) __nano_init(void) {
    fprintf(stderr, "NANO: init()\n");

    // Get address of original functions
    orgMemFuncs.mMalloc = (MallocFn)dlsym(RTLD_NEXT, "malloc");
    orgMemFuncs.mCalloc = (CallocFn)dlsym(RTLD_NEXT, "calloc");

    fprintf(stderr, "NANO: malloc() found @%p\n", orgMemFuncs.mMalloc);
    fprintf(stderr, "NANO: calloc() found @%p\n", orgMemFuncs.mCalloc);

    gNanoUp = true;
}

// replacement functions
extern "C" {
    void *malloc(size_t size) {
        if (!gNanoUp) __nano_init();
        return orgMemFuncs.mMalloc(size);
    }

    void* calloc(size_t elements, size_t size) {
        if (!gNanoUp) __nano_init();
        return orgMemFuncs.mCalloc(elements, size);
    }
}

Теперь, когда я делаю следующее, я получаю бесконечный цикл, за которым следует ошибка сегмента, например:

% setenv LD_PRELOAD "./libnano.so"
% ls
...
NANO: init()
NANO: init()
NANO: init()
Segmentation fault (core dumped)

Однако, если я закомментирую интерпозер calloc, он почти будет работать :

% setenv LD_PRELOAD "./libnano.so"
% ls
NANO: init()
NANO: malloc() found @0x3b36274dc0
NANO: calloc() found @0x3b362749e0
NANO: init()
NANO: malloc() found @0x3b36274dc0
NANO: calloc() found @0x3b362749e0
<directory contents>
...

Итак, что-то с "ls" означает, что init() вызывается дважды.

EDIT Обратите внимание, что следующая хост-программа работает правильно - init() вызывается только один раз, а calloc успешно вставляется, как видно из вывода.

// build with: g++ test.cc -o test
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char* argv[]) {

    void* p = malloc(123);
    printf("HOST p=%p\n", p);
    free(p);

    char* c = new char;
    printf("HOST c=%p\n", c);
    delete c;

    void* ca = calloc(10,10);
    printf("HOST ca=%p\n", ca);
    free(ca);
}

% setenv LD_PRELOAD "./libnano.so"
% ./test 
NANO: init()
NANO: malloc() found @0x3b36274dc0
NANO: calloc() found @0x3b362749e0
HOST p=0x601010
HOST c=0x601010
HOST ca=0x601030

Ответы [ 4 ]

3 голосов
/ 27 октября 2011

Что касается вызова __nano_init() дважды: вы объявили функцию как конструктор, поэтому она вызывается при загрузке библиотеки и вызывается второй раз явно, когда ваши реализации malloc() и calloc() первым позвонил. Выберите один.

В отношении calloc() interposer, вызывающего сбой вашего приложения: некоторые из используемых вами функций, включая dlsym() и fprintf(), могут сами пытаться выделить память, вызывая ваши функции interposer. Рассмотрите последствия и действуйте соответственно.

2 голосов
/ 12 сентября 2017

Я знаю, что немного опоздал (6 лет). Но я хотел переопределить calloc() сегодня и столкнулся с проблемой, потому что dlsym() внутренне использует calloc(). Я решил это, используя простую технику, и подумал поделиться этим здесь:

static unsigned char buffer[8192];

void *calloc(size_t nmemb, size_t size)
{
    if (calloc_ptr == NULL) // obtained from dlsym
            return buffer;

    init(); // uses dlsym() to find address of the real calloc()

    return calloc_ptr(len);
}

void free(void *in)
{
    if (in == buffer)
        return;

    free_ptr(in);
}

buffer удовлетворяет потребности dlsym() до тех пор, пока не будет найдено действительное calloc() и не будет инициализирован мой указатель функции calloc_ptr.

2 голосов
/ 28 октября 2011

Использование перехвата на основе dlsym может привести к сбоям, так как dlsym перезванивает в распределитель памяти.Вместо этого используйте malloc hooks , как я предложил в вашем предыдущем вопросе ;их можно установить, вообще не вызывая dlsym.

0 голосов
/ 20 марта 2017

Вы можете уйти с предварительным плохим calloc, который просто возвращает NULL. Это на самом деле работает на Linux, YMMV.

static void* poor_calloc(size_t nmemb, size_t size)
{
    // So dlsym uses calloc internally, which will lead to infinite recursion, since our calloc calls dlsym.
    // Apparently dlsym can cope with slightly wrong calloc, see for further explanation:
    // http://blog.bigpixel.ro/2010/09/interposing-calloc-on-linux
    return NULL; // This is a poor implementation of calloc!
}
...