Intercept dynamic library methods with LD PRELOAD: Difference between revisions

From RidgeRun Developer Wiki
(Created page with "=Introduction= Let's say you have a binary called 'main' which was compiled against a shared memory 'libprint1.so', which has a method called print_msg() which prints 'hello f...")
 
mNo edit summary
Line 3: Line 3:


main.c
main.c
<source lang="c" >
<syntaxhighlight lang="C" >
#include "print.h"
#include "print.h"


Line 11: Line 11:
     return 0;
     return 0;
}
}
</source>
</syntaxhighlight>


print.h
print.h
<source lang="c" >
<syntaxhighlight lang="C" >
#ifndef RR_PRINT_H
#ifndef RR_PRINT_H
#define RR_PRINT_H
#define RR_PRINT_H
Line 33: Line 33:
     printf("hello from print1\n");
     printf("hello from print1\n");
}
}
</source>
</syntaxhighlight>


This library can be compiled as dynamic and linked to the main program with
This library can be compiled as dynamic and linked to the main program with


<source>
<syntaxhighlight lang="bash">
gcc -shared -o libprint1.so print1.c
gcc -shared -o libprint1.so print1.c
gcc -o main main.c -L. -lprint1
gcc -o main main.c -L. -lprint1
</source>
</syntaxhighlight>


As expected when executed the program will output
As expected when executed the program will output
<pre>
<syntaxhighlight lang="bash">
hello from print1
hello from print1
</pre>
</syntaxhighlight>


Lets say you don't want to recompile main, or you simply do not have that source code, but you want to replace print_msg with another functionality. Here is where the concept of LD_PRELOAD comes in handy. This is an environment variable that will instruct the dynamic linker the priority of the dynamic libraries when executing a binary.
Lets say you don't want to recompile main, or you simply do not have that source code, but you want to replace print_msg with another functionality. Here is where the concept of LD_PRELOAD comes in handy. This is an environment variable that will instruct the dynamic linker the priority of the dynamic libraries when executing a binary.
Line 52: Line 52:


print2.c
print2.c
<source lang="c" >
<syntaxhighlight lang="C" >
#include "print.h"
#include "print.h"


Line 58: Line 58:
     printf("hello from print2\n");
     printf("hello from print2\n");
}
}
</source>
</syntaxhighlight>


Again, compile with
Again, compile with
<source>
<syntaxhighlight lang="bash">
gcc -shared -o libprint2.so print2.c
gcc -shared -o libprint2.so print2.c
</source>
</syntaxhighlight>


But now, execute main like this
But now, execute main like this


<source>
<syntaxhighlight lang="bash">
LD_PRELOAD=libprint2.so ./main
LD_PRELOAD=libprint2.so ./main
</source>
</syntaxhighlight>


The output will be
The output will be
<source>
<syntaxhighlight lang="bash">
hello from print2
hello from print2
</source>
</syntaxhighlight>


You can even replace calls to stdlib like malloc and free, to help you keep track of memory allocation.
You can even replace calls to stdlib like malloc and free, to help you keep track of memory allocation.

Revision as of 12:28, 16 May 2024

Introduction

Let's say you have a binary called 'main' which was compiled against a shared memory 'libprint1.so', which has a method called print_msg() which prints 'hello from print1'. This looks like this

main.c

#include "print.h"

int main(int argc, char *argv[])
{
    print_msg();
    return 0;
}

print.h

#ifndef RR_PRINT_H
#define RR_PRINT_H

#include <stdio.h>

void print_msg();

#endif

</source>

print1.c
<source lang="c" >
#include "print.h"

void print_msg(){
    printf("hello from print1\n");
}

This library can be compiled as dynamic and linked to the main program with

gcc -shared -o libprint1.so print1.c
gcc -o main main.c -L. -lprint1

As expected when executed the program will output

hello from print1

Lets say you don't want to recompile main, or you simply do not have that source code, but you want to replace print_msg with another functionality. Here is where the concept of LD_PRELOAD comes in handy. This is an environment variable that will instruct the dynamic linker the priority of the dynamic libraries when executing a binary.

Let's change the message of print_msg()

print2.c

#include "print.h"

void print_msg(){
    printf("hello from print2\n");
}

Again, compile with

gcc -shared -o libprint2.so print2.c

But now, execute main like this

LD_PRELOAD=libprint2.so ./main

The output will be

hello from print2

You can even replace calls to stdlib like malloc and free, to help you keep track of memory allocation.