Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does LD_PRELOAD not seem to work for write with wc

Tags:

c

linux

linker

ld

I was playing around with LD_PRELOAD to intercept libc calls, it appears that the write call doesn't get intercepted with wc, though it does seem to work with cat. A stripped down version of the problem appears below.

RedHat Linux 2.6.9-42.ELsmp

Makefile

writelib:
        gcc -Wall -rdynamic -fPIC -c write.c
        gcc -shared -Wl,-soname,libwrite.so -Wl,-export-dynamic -o libwrite.so write.o -ldl

write.c:

#include <stdio.h>
#include <string.h>
#ifndef __USE_GNU
 #define __USE_GNU
 #define __USE_GNU_DEFINED
#endif
#include <dlfcn.h>
#ifdef __USE_GNU_DEFINED
 #undef __USE_GNU
 #undef __USE_GNU_DEFINED
#endif
#include <unistd.h>
#include <stdlib.h>

static ssize_t (*libc_write)(int fd, const void *buf, size_t len);

ssize_t
write(int fd, const void *buf, size_t len)
{
    static int already;
    ssize_t ret;

    if (!already) {
            if ((libc_write = dlsym(RTLD_NEXT, "write")) == NULL) {
                    exit(1);
            }
            already = 1;
    }


    ret = (*libc_write)(fd,"LD_PRELOAD\n",11);
    return len; // not ret so cat doesn't take forever
}

Output:

prompt: make
gcc -Wall -rdynamic -fPIC -c write.c
gcc -shared -Wl,-soname,libwrite.so -Wl,-export-dynamic -o libwrite.so write.o -ldl
prompt: LD_PRELOAD=./libwrite.so /bin/cat write.c
LD_PRELOAD
prompt: LD_PRELOAD=./libwrite.so /usr/bin/wc write.c
 32  70 572 write.c

Any explanations ?

like image 534
Chaim Geretz Avatar asked Jul 01 '11 20:07

Chaim Geretz


2 Answers

That's because while cat uses write, wc uses printf, which is probably using either an inlined version of write, or its reference to write is bound to libc, so cannot be interposed.

This can easily be seen using ltrace:

$ echo foo | ltrace wc 2>&1 | grep 'write\|print'
printf("%*s", 7, "1")                            = 7
printf(" %*s", 7, "1")                           = 8
printf(" %*s", 7, "4")                           = 8


$ echo foo | ltrace cat 2>&1 | grep 'write\|print'
write(1, "foo\n", 4foo
like image 149
ninjalj Avatar answered Nov 15 '22 20:11

ninjalj


LD_PRELOAD is really a very poor method for intercepting and redirecting calls. It only works with shared libraries, and depending on how the library was linked and what levels of optimization and inlining were being used, the calls you want to intercept may not be reliably interceptable.

A great alternative that avoids all these problems, especially when it's syscalls you want to intercept and rewrite, is using the ptrace tracing/debugging interface. Unfortunately there doesn't seem to be any tool to automate this approach at this time.

like image 30
R.. GitHub STOP HELPING ICE Avatar answered Nov 15 '22 19:11

R.. GitHub STOP HELPING ICE