LD_PRELOAD 메커니즘을 사용한 'malloc' 재정의
malloc 호출을 stderr에 기록할 수 있는 간단한 공유 라이브러리를 작성하려고 합니다(원하신다면 'mtrace'의 일종입니다.
그러나 이것은 동작하지 않습니다.제가 하는 일은 다음과 같습니다.
/* mtrace.c */
#include <dlfcn.h>
#include <stdio.h>
static void* (*real_malloc)(size_t);
void *malloc(size_t size)
{
void *p = NULL;
fprintf(stderr, "malloc(%d) = ", size);
p = real_malloc(size);
fprintf(stderr, "%p\n", p);
return p;
}
static void __mtrace_init(void) __attribute__((constructor));
static void __mtrace_init(void)
{
void *handle = NULL;
handle = dlopen("libc.so.6", RTLD_LAZY);
if (NULL == handle) {
fprintf(stderr, "Error in `dlopen`: %s\n", dlerror());
return;
}
real_malloc = dlsym(handle, "malloc");
if (NULL == real_malloc) {
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
return;
}
}
다음을 사용하여 컴파일합니다.
gcc -shared -fPIC -o mtrace.so mtrace.c
그리고 나서 제가 이 모든 걸 실행하려고 하면ls
:
$ LD_PRELOAD=./mtrace.so ls
malloc(352) = Segmentation fault
dlopen은 malloc이 필요할 것으로 생각됩니다.공유 라이브러리 내에서 dlopen을 재정의할 때 아직 할당되지 않은 버전을 사용합니다.real_malloc
.
문제는...어떻게 하면 될까요?
P.S. 태그가 부족해서 죄송합니다.적절한 태그를 찾을 수 없었고, 새로운 태그를 만들기에 충분한 평판을 얻지 못했습니다.
저는 항상 이런 식으로 합니다.
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
static void* (*real_malloc)(size_t)=NULL;
static void mtrace_init(void)
{
real_malloc = dlsym(RTLD_NEXT, "malloc");
if (NULL == real_malloc) {
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
}
}
void *malloc(size_t size)
{
if(real_malloc==NULL) {
mtrace_init();
}
void *p = NULL;
fprintf(stderr, "malloc(%d) = ", size);
p = real_malloc(size);
fprintf(stderr, "%p\n", p);
return p;
}
컨스트럭터를 사용하지 말고 첫 번째 호출로 초기화하면 됩니다.malloc
.사용하다RTLD_NEXT
피하다dlopen
malloc 훅도 시험해 볼 수 있습니다.이 모든 것은 GNU 확장이므로 다른 곳에서는 작동하지 않을 수 있습니다.
malloc에서 LD_PRELOAD를 사용하고 싶다고 해도, 받아들여진 회답의 코드가 segfault로 되어 있는 것을 알게 되면, 효과가 있다고 생각되는 솔루션이 있습니다.
segfault는 dlsym이 32바이트 동안 calloc을 호출하여 스택 끝에 재귀가 발생했기 때문에 발생했습니다.
저의 해결책은 dlsym이 malloc 함수 포인터를 반환하기 전에 할당을 처리하는 초단순 정적 할당기를 만드는 것이었습니다.
#define _GNU_SOURCE
#include <dlfcn.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
char tmpbuff[1024];
unsigned long tmppos = 0;
unsigned long tmpallocs = 0;
void *memset(void*,int,size_t);
void *memmove(void *to, const void *from, size_t size);
/*=========================================================
* interception points
*/
static void * (*myfn_calloc)(size_t nmemb, size_t size);
static void * (*myfn_malloc)(size_t size);
static void (*myfn_free)(void *ptr);
static void * (*myfn_realloc)(void *ptr, size_t size);
static void * (*myfn_memalign)(size_t blocksize, size_t bytes);
static void init()
{
myfn_malloc = dlsym(RTLD_NEXT, "malloc");
myfn_free = dlsym(RTLD_NEXT, "free");
myfn_calloc = dlsym(RTLD_NEXT, "calloc");
myfn_realloc = dlsym(RTLD_NEXT, "realloc");
myfn_memalign = dlsym(RTLD_NEXT, "memalign");
if (!myfn_malloc || !myfn_free || !myfn_calloc || !myfn_realloc || !myfn_memalign)
{
fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
exit(1);
}
}
void *malloc(size_t size)
{
static int initializing = 0;
if (myfn_malloc == NULL)
{
if (!initializing)
{
initializing = 1;
init();
initializing = 0;
fprintf(stdout, "jcheck: allocated %lu bytes of temp memory in %lu chunks during initialization\n", tmppos, tmpallocs);
}
else
{
if (tmppos + size < sizeof(tmpbuff))
{
void *retptr = tmpbuff + tmppos;
tmppos += size;
++tmpallocs;
return retptr;
}
else
{
fprintf(stdout, "jcheck: too much memory requested during initialisation - increase tmpbuff size\n");
exit(1);
}
}
}
void *ptr = myfn_malloc(size);
return ptr;
}
void free(void *ptr)
{
// something wrong if we call free before one of the allocators!
// if (myfn_malloc == NULL)
// init();
if (ptr >= (void*) tmpbuff && ptr <= (void*)(tmpbuff + tmppos))
fprintf(stdout, "freeing temp memory\n");
else
myfn_free(ptr);
}
void *realloc(void *ptr, size_t size)
{
if (myfn_malloc == NULL)
{
void *nptr = malloc(size);
if (nptr && ptr)
{
memmove(nptr, ptr, size);
free(ptr);
}
return nptr;
}
void *nptr = myfn_realloc(ptr, size);
return nptr;
}
void *calloc(size_t nmemb, size_t size)
{
if (myfn_malloc == NULL)
{
void *ptr = malloc(nmemb*size);
if (ptr)
memset(ptr, 0, nmemb*size);
return ptr;
}
void *ptr = myfn_calloc(nmemb, size);
return ptr;
}
void *memalign(size_t blocksize, size_t bytes)
{
void *ptr = myfn_memalign(blocksize, bytes);
return ptr;
}
이게 도움이 됐으면 좋겠네요.
다음으로 malloc 및 free hooking의 가장 간단한 예를 제시하겠습니다.
#define _GNU_SOURCE
#include <stdio.h>
#include <dlfcn.h>
static void* (*real_malloc)(size_t size);
static void (*real_free)(void *ptr);
__attribute__((constructor))
static void init()
{
real_malloc = dlsym(RTLD_NEXT, "malloc");
real_free = dlsym(RTLD_NEXT, "free");
fprintf(stderr, "init\n");
}
void *malloc(size_t size)
{
void *ptr = real_malloc(size);
fprintf(stderr, "malloc(%zd) = %p\n", size, ptr);
return ptr;
}
void free(void *ptr)
{
real_free(ptr);
fprintf(stderr, "free(%p)\n", ptr);
}
위의 예시로 확장하면 dlsym의 seg fault를 회피할 수 있습니다.mmap
초기화가 완료될 때까지:
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <dlfcn.h>
#include <sys/mman.h>
static void* (*real_malloc)(size_t) = NULL;
static void* (*real_realloc)(void*, size_t) = NULL;
static void* (*real_calloc)(size_t, size_t) = NULL;
static void (*real_free)(void*) = NULL;
static int alloc_init_pending = 0;
/* Load original allocation routines at first use */
static void alloc_init(void)
{
alloc_init_pending = 1;
real_malloc = dlsym(RTLD_NEXT, "malloc");
real_realloc = dlsym(RTLD_NEXT, "realloc");
real_calloc = dlsym(RTLD_NEXT, "calloc");
real_free = dlsym(RTLD_NEXT, "free");
if (!real_malloc || !real_realloc || !real_calloc || !real_free) {
fputs("alloc.so: Unable to hook allocation!\n", stderr);
fputs(dlerror(), stderr);
exit(1);
} else {
fputs("alloc.so: Successfully hooked\n", stderr);
}
alloc_init_pending = 0;
}
#define ZALLOC_MAX 1024
static void* zalloc_list[ZALLOC_MAX];
static size_t zalloc_cnt = 0;
/* dlsym needs dynamic memory before we can resolve the real memory
* allocator routines. To support this, we offer simple mmap-based
* allocation during alloc_init_pending.
* We support a max. of ZALLOC_MAX allocations.
*
* On the tested Ubuntu 16.04 with glibc-2.23, this happens only once.
*/
void* zalloc_internal(size_t size)
{
fputs("alloc.so: zalloc_internal called", stderr);
if (zalloc_cnt >= ZALLOC_MAX-1) {
fputs("alloc.so: Out of internal memory\n", stderr);
return NULL;
}
/* Anonymous mapping ensures that pages are zero'd */
void* ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, 0, 0);
if (MAP_FAILED == ptr) {
perror("alloc.so: zalloc_internal mmap failed");
return NULL;
}
zalloc_list[zalloc_cnt++] = ptr; /* keep track for later calls to free */
return ptr;
}
void free(void* ptr)
{
if (alloc_init_pending) {
fputs("alloc.so: free internal\n", stderr);
/* Ignore 'free' during initialization and ignore potential mem leaks
* On the tested system, this did not happen
*/
return;
}
if(!real_malloc) {
alloc_init();
}
for (size_t i = 0; i < zalloc_cnt; i++) {
if (zalloc_list[i] == ptr) {
/* If dlsym cleans up its dynamic memory allocated with zalloc_internal,
* we intercept and ignore it, as well as the resulting mem leaks.
* On the tested system, this did not happen
*/
return;
}
}
real_free(ptr);
}
void *malloc(size_t size)
{
if (alloc_init_pending) {
fputs("alloc.so: malloc internal\n", stderr);
return zalloc_internal(size);
}
if(!real_malloc) {
alloc_init();
}
void* result = real_malloc(size);
//fprintf(stderr, "alloc.so: malloc(0x%zx) = %p\n", size, result);
return result;
}
void *realloc(void* ptr, size_t size)
{
if (alloc_init_pending) {
fputs("alloc.so: realloc internal\n", stderr);
if (ptr) {
fputs("alloc.so: realloc resizing not supported\n", stderr);
exit(1);
}
return zalloc_internal(size);
}
if(!real_malloc) {
alloc_init();
}
return real_realloc(ptr, size);
}
void *calloc(size_t nmemb, size_t size)
{
if (alloc_init_pending) {
fputs("alloc.so: calloc internal\n", stderr);
/* Be aware of integer overflow in nmemb*size.
* Can only be triggered by dlsym */
return zalloc_internal(nmemb * size);
}
if(!real_malloc) {
alloc_init();
}
return real_calloc(nmemb, size);
}
glibc를 사용하는 경우 내장된 malloc 후크 메커니즘을 사용해야 합니다.이 페이지의 예에서는 원래 malloc을 검색하는 방법을 보여 줍니다.이는 특히 malloc'd 버퍼를 반환하는 라이브러리 함수가 자신의 버퍼와 일치하도록 하기 위해 추가 추적 정보를 할당에 추가하는 경우 중요합니다.free()
실행.
언급URL : https://stackoverflow.com/questions/6083337/overriding-malloc-using-the-ld-preload-mechanism
'sourcecode' 카테고리의 다른 글
jwt 인증을 사용하여 vue 응용 프로그램에 자동 로그인 (0) | 2022.08.07 |
---|---|
Nuxt JS vuex에서 Vue 라우터를 사용하여 리디렉션 (0) | 2022.08.07 |
VueJs 및 Larabel을 사용하여 여러 파일 업로드 (0) | 2022.08.07 |
webpack,vue.dll,vue 파일을 컴파일할 수 없습니다. (0) | 2022.08.07 |
변환에서 푸시할 때 Vuex 저장소에서 구성 요소 컬렉션을 업데이트하지 않음 (0) | 2022.08.07 |