programing

malloc에 대한 래퍼 함수를 만들고 C에서 무료로 사용할 수 있습니다.

goodsources 2023. 6. 15. 21:49
반응형

malloc에 대한 래퍼 함수를 만들고 C에서 무료로 사용할 수 있습니다.

다음에 대한 래퍼 함수를 만들려고 합니다.free그리고.mallocC에서 메모리 누수를 알려줍니다.이 기능들을 신고하는 방법을 아는 사람이 있습니까? 그래서 제가 전화할 때.malloc()그리고.free()표준 lib 함수가 아닌 내 사용자 정의 함수를 호출합니까?

몇 가지 옵션이 있습니다.

  1. GLIBC 관련 솔루션(대부분 Linux).컴파일 환경이glibc와 함께gcc선호되는 방법은 말록 훅을 사용하는 것입니다.사용자 정의를 지정할 수 있을 뿐만 아니라malloc그리고.free또한 스택의 반송 주소로 발신자를 식별합니다.

  2. POSIX 관련 솔루션.정의malloc그리고.free실행 파일의 원래 할당 루틴에 대한 래퍼로, libc에서 버전을 "삭제"합니다.포장지 안에서 원본을 호출할 수 있습니다.malloc사용하여 조회할 수 있는 구현RTLD_NEXT의 응용 프로그램 함수와 연결해야 .-ldl.

    #define _GNU_SOURCE
    #include <dlfcn.h>
    #include <stdio.h>
    
    void* malloc(size_t sz)
    {
        void *(*libc_malloc)(size_t) = dlsym(RTLD_NEXT, "malloc");
        printf("malloc\n");
        return libc_malloc(sz);
    }
    
    void free(void *p)
    {
        void (*libc_free)(void*) = dlsym(RTLD_NEXT, "free");
        printf("free\n");
        libc_free(p);
    }
    
    int main()
    {
        free(malloc(10));
        return 0;
    }
    
  3. Linux 전용.동적 라이브러리의 함수를 에서 지정하여 비침습적으로 재정의할 수 있습니다.LD_PRELOAD환경 변수입니다.

    LD_PRELOAD=mymalloc.so ./exe
    
  4. Mac OSX 전용입니다.

    Linux와하게 될 이라는 DYLD_INSERT_LIBRARIES환경 변수입니다.

LD_PRELOAD를 사용하여 래퍼 및 "덮어쓰기" 기능을 수행할 수 있습니다(앞의 예와 유사).

LD_PRELOAD=/path.../lib_fake_malloc.so ./app

하지만 저는 이것을 "약간" 더 똑똑하게 하는 것을 추천합니다. 즉, dlsym에 한 번 전화하는 입니다.

#define _GNU_SOURCE
#include <stdio.h>
#include <stdint.h>
#include <dlfcn.h>

void* malloc(size_t size)
{
    static void* (*real_malloc)(size_t) = NULL;
    if (!real_malloc)
        real_malloc = dlsym(RTLD_NEXT, "malloc");

    void *p = real_malloc(size);
    fprintf(stderr, "malloc(%d) = %p\n", size, p);
    return p;
}

제가 여기서 찾은 예는 제이 콘로드의 http://www.jayconrod.com/cgi/view_post.py?23 게시물입니다.

하지만 이 페이지에서 제가 정말 멋진 것은 GNU 링커가 유용한 옵션인 --wrap을 제공한다는 것입니다."mand"를 선택하면 다음과 같은 예가 있습니다.

void *
__wrap_malloc (size_t c)
{
    printf ("malloc called with %zu\n", c);
    return __real_malloc (c);
}

저는 "사소한 예"라는 그들의 말에 동의합니다.심지어 dlsym도 필요하지 않습니다.

제 "맨드" 페이지의 한 부분을 더 인용하겠습니다.

--wrap=symbol
       Use a wrapper function for symbol.
       Any undefined reference to symbol will be resolved to "__wrap_symbol".
       Any undefined reference to "__real_symbol" will be resolved to symbol.

저는 설명이 완벽하고 그런 것들을 어떻게 사용하는지 보여주기를 바랍니다.

저의 경우 memalign/align_malloc을 malloc로 포장해야 했습니다.다른 솔루션을 사용해 본 결과 아래와 같은 솔루션을 구현하게 되었습니다.작동이 잘 되는 것 같습니다.

mymalloc.c.

/* 
 * Link-time interposition of malloc and free using the static
 * linker's (ld) "--wrap symbol" flag.
 * 
 * Compile the executable using "-Wl,--wrap,malloc -Wl,--wrap,free".
 * This tells the linker to resolve references to malloc as
 * __wrap_malloc, free as __wrap_free, __real_malloc as malloc, and
 * __real_free as free.
 */
#include <stdio.h>

void *__real_malloc(size_t size);
void __real_free(void *ptr);


/* 
 * __wrap_malloc - malloc wrapper function 
 */
void *__wrap_malloc(size_t size)
{
    void *ptr = __real_malloc(size);
    printf("malloc(%d) = %p\n", size, ptr);
    return ptr;
}

/* 
 * __wrap_free - free wrapper function 
 */
void __wrap_free(void *ptr)
{
    __real_free(ptr);
    printf("free(%p)\n", ptr);
}
 

C에서 제가 사용한 방법은 다음과 유사했습니다.

#define malloc(x) _my_malloc(x, __FILE__, __LINE__)
#define free(x) _my_free(x)

이를 통해 메모리가 할당된 라인과 파일을 큰 어려움 없이 감지할 수 있었습니다.교차 플랫폼이어야 하지만 매크로가 이미 정의된 경우 문제가 발생합니다(다른 메모리 누수 디텍터를 사용하는 경우에만 해당).

만약 당신이 C++에서 같은 것을 구현하고 싶다면, 그 절차는 조금 더 복잡하지만 같은 속임수를 사용합니다.

다음은 몇 년 동안 사용한 래퍼 함수의 집합입니다. (그리고 C를 디핑할 때도 여전히 사용합니다). 메모리 해방, 메모리 해방, 메모리 해방에 대한 참조, 버퍼 오버플로/언더플로 및 할당되지 않은 메모리 해방을 감지합니다.

ftp://ftp.digitalmars.com/ctools.zip

그들은 25년 동안 살아왔고 그들 자신을 증명했습니다.

매크로 전처리기를 사용하여 malloc을 재정의하고 mem 패키지를 무료로 사용할 수 있지만 strdup처럼 라이브러리 호출을 malloc으로 리디렉션하지 않기 때문에 반대하는 것이 좋습니다.

메모리 누수를 제거하는 것이 목표라면 Valgrind(무료) 또는 Purify(비용이 많이 드는)와 같은 도구를 사용하는 것이 더 쉽고 덜 거슬리는 방법입니다.

만약 당신이 malloc()와 free()에 대해 당신 자신의 함수를 정의하고 그것을 당신의 응용 프로그램과 명시적으로 연결한다면, 당신의 함수는 라이브러리에 있는 함수보다 우선적으로 사용되어야 합니다.

그러나 'malloc'이라는 함수는 라이브러리 malloc 함수를 호출할 수 없습니다. 왜냐하면 'c'에는 별도의 네임스페이스 개념이 없기 때문입니다.다시 말해, 당신은 malloc의 내부를 구현하고 스스로를 해방시켜야 합니다.

또 다른 접근법은 표준 라이브러리를 호출하는 함수 my_malloc()와 my_free()를 작성하는 것입니다.이는 malloc을 호출하는 코드가 my_xxx 함수를 호출하도록 변경되어야 한다는 것을 의미합니다.

사용자가 사용자 정의의 유일한 클라이언트인 경우malloc그리고.free(즉, 다른 라이브러리의 코드에 대해 이러한 메소드를 원숭이 패치하려는 것이 아닙니다.) 그런 다음 종속성 주입을 사용할 수 있습니다.

#ifndef ALLOCATOR_H
#define ALLOCATOR_H

#include <stddef.h>

struct Allocator;

typedef struct {
    void *(*allocate)(struct Allocator *allocator, size_t size);

    void (*free)(struct Allocator *allocator, void *object);
} AllocatorVTable;

typedef struct Allocator {
    const AllocatorVTable *vptr;
} Allocator;

typedef struct {
    Allocator super;
    char *buffer;
    size_t offset;
    size_t capacity;
} BufferedAllocator;

void BufferedAllocator_init(BufferedAllocator *allocator, char *buffer, size_t capacity);

typedef Allocator MallocAllocator;

void MallocAllocator_init(MallocAllocator *allocator);

void *Allocator_allocate(Allocator *allocator, size_t size);

void Allocator_free(Allocator *allocator, void *object);

#endif
#include "allocator.h"
#include "malloc.h"

void *Allocator_allocate(Allocator *allocator, size_t size) {
    return allocator->vptr->allocate(allocator, size);
}

void Allocator_free(Allocator *allocator, void *object) {
    allocator->vptr->free(allocator, object);
}

void *BufferedAllocator_allocate(Allocator *allocator, size_t size) {
    BufferedAllocator *bufferedAllocator = (BufferedAllocator *) allocator;
    if (bufferedAllocator->offset + size > bufferedAllocator->capacity) {
        fprintf(stderr, "buffer overflow: %ld + %ld > %ld\n",
                bufferedAllocator->offset, size, bufferedAllocator->capacity);
        return NULL;
    }
    bufferedAllocator->offset += size;
    return bufferedAllocator->buffer + bufferedAllocator->offset - size;
}

void BufferedAllocator_free(Allocator *allocator, void *object) {

}

const AllocatorVTable bufferedAllocatorVTable = {
        .allocate = BufferedAllocator_allocate,
        .free = BufferedAllocator_free,
};

void BufferedAllocator_init(BufferedAllocator *allocator, char *buffer,
                            size_t capacity) {
    allocator->super.vptr = &bufferedAllocatorVTable;
    allocator->buffer = buffer;
    allocator->offset = 0;
    allocator->capacity = capacity;
}

void *MallocAllocator_allocate(Allocator *allocator, size_t size) {
    return malloc(size);
}

void MallocAllocator_free(Allocator *allocator, void *object) {
    free(object);
}

const AllocatorVTable mallocAllocatorVTable = {
        .allocate = MallocAllocator_allocate,
        .free = MallocAllocator_free,
};

void MallocAllocator_init(MallocAllocator *allocator) {
    allocator->vptr = &mallocAllocatorVTable;
}
#include <assert.h>
#include "allocator_test.h"
#include "allocator.h"

void testAllocator() {
    {
        BufferedAllocator bufferedAllocator;
        char buffer[4];
        size_t capacity = sizeof(buffer);
        BufferedAllocator_init(&bufferedAllocator, buffer, capacity);
        Allocator *allocator = &bufferedAllocator.super;

        void *chill = Allocator_allocate(allocator, capacity);
        assert(chill == buffer);
        void *oops = Allocator_allocate(allocator, 1);
        assert(oops == NULL);
    }

    {
        MallocAllocator allocator;
        MallocAllocator_init(&allocator);

        void *chill = Allocator_allocate(&allocator, 100);
        assert(chill != NULL);
        void *alsoChill = Allocator_allocate(&allocator, 100);
        assert(alsoChill != NULL);
    }
}

그래서 당신은 주위를 지나갈 것입니다.Allocator *당신이 무언가를 할당하고 싶어하는 코드의 어떤 조각이든.char buf[n]스택에 있음).를 사용할 수 있습니다.MallocAllocator그냥 시스템을 사용하기 위해malloc/free아니면 당신은 사용할 수 있습니다.BufferedAllocator당신의 프로그램의 맨 위에.aBufferedAllocator정말 단순한 malloc/free의 예에 불과합니다.내 프로그램이 얼마나 많은 메모리를 사용할지를 미리 알고 있고 전체 프로그램이 끝날 때까지 어떤 개체도 삭제하지 않기 때문에 사용 사례에서 잘 작동합니다.이 인터페이스를 사용하면 이 강의에서 설명한 알고리즘 중 하나처럼 더 복잡한 알고리즘을 작성할 수 있습니다.파편화를 방지하기 위한 다양한 전략과 많은 절충안이 있으므로 자신만의 malloc/free를 굴리는 것이 정말 유용할 수 있습니다.

Linux를 사용하는 경우 malloc_hook()(GNU glibc 사용)을 사용할 수 있습니다.이 기능은 실제 malloc을 호출하기 전에 malloc이 자신의 기능을 호출하도록 할 수 있습니다.man 페이지에는 사용 방법에 대한 예제가 있습니다.

만약 당신이 통제하고 있는 기억, 즉 당신이 malloc을 하고 스스로 자유롭게 한다는 것에 대해서만 이야기한다면, 당신은 rmdebug를 볼 수 있습니다.아마 당신이 쓸 것이기 때문에 시간을 절약할 수 있습니다.만약 그것이 당신에게 중요하다면, 그것은 매우 자유로운 자격증을 가지고 있습니다.

저는 개인적으로 프로젝트에서 메모리 누수를 찾기 위해 그것을 사용합니다. 좋은 점은 그것이 발그랜드보다 훨씬 빠르다는 것입니다. 하지만 그것은 그렇게 강력하지 않기 때문에 당신은 전체 호출 스택을 얻을 수 없습니다.

언급URL : https://stackoverflow.com/questions/262439/create-a-wrapper-function-for-malloc-and-free-in-c

반응형