Up ../

MacOSX Interpose Shared Library Functions

When I tried to get Fabio Busatto's libkeepalive working on a MacOSX 10.4.x machine I discovered that the runtime linker (dyld) doesn't support LD_PRELOAD but the dyld manual page hinted that DYLD_INSERT_LIBRARIES did the same thing. Alas! not that simple.

Not having much sucess I consulted my copy of Mac OS X internals: a systems approach (Amit Singh) in the hope of better understanding the runtime loader. And on page 73 (sect. 2.6.3.4 dyld Interposing) was exactly what I was looking for!

Basically dyld looks in any shared library specified in DYLD_INSERT_LIBRARIES for sections named

             __DATA __interpose

In those sections there are pairs of function pointers (addresses.) The first pointer is the address of your function to be interposed while the second pointer is the address of the original function to be replaced.

The code looks pretty much like this (from my macosx hack of libkeepalive)

[An updated version can be found under http://toves.freeshell.org/interpose/files/]


/*
// MacOSX defines the socket level to be the protocol number
// Or we could just 
// #define SOL_TCP	(6)
*/
static  int     sol_tcp = 0;
static  int     get_sol_tcp(void) {
        int     result  = 0;
        struct  protoent*       p = getprotobyname ("tcp");
        if (p) {
                result  = p->p_proto;
        }
        endprotoent();
        return  result;
}
# define        SOL_TCP (sol_tcp>0?sol_tcp:(sol_tcp = get_sol_tcp()))

/*
// Note that you can use the old function by name without further ado.
*/
int	my_socket (int dom, int type, int proto) {
	int	result	= socket (dom, type, proto); 
	if (result >= 0) {
		if (dom == PF_INET && type == SOCK_STREAM) {
			int	on	= 1;
			int	idle	= 240;	/* seconds ?*/
			setsockopt (result, SOL_SOCKET, SO_KEEPALIVE, &on, sizeof(on));
			setsockopt (result, SOL_TCP, TCP_KEEPALIVE, &idle, sizeof(idle));
		}
	}
	return	result;
}

struct	interpose { /* the struct/typenames are arbitrary */
	void*	new;
	void*	old;
};
typedef	struct	interpose	interpose_t;

/* The __attribute__/section definition is the critical part.
// If you have only one function then myreplacements doesn't have
// to be an array. Obviously the name myreplacements is also arbitrary.
// You can define multiple sections with the name __DATA, __interpose
// and the static linker will combine them just like any other section.
*/
static const interpose_t myreplacements[] \
	__attribute__((section("__DATA, __interpose"))) =
{
	{ (void*) my_socket, (void*) socket}
};

The compilation is simple compared with ELF/ECOFF systems.

gcc -Wall -dynamiclib -o libkeepalive.dylib libkeepalive.c

cp libkeepalive.dylib ${HOME}/lib

EXAMPLE

env DYLD_INSERT_LIBRARIES=${HOME}/lib/libkeepalive.dylib ssh sdf.lonestar.org

LICENSE
Creative Commons CC0 http://creativecommons.org/publicdomain/zero/1.0/legalcode

AUTHOR
James Sainsbury