#!/bin/sh

# program
#
# dlopen():
# libe.so
# liba.so
# <- libb.so
#    <- libd.so
#
# Expected: dlsym(RTLD_NEXT) finds symbol in order libe.so, liba.so, libb.so,
# libd.so


. ./test_setup


# create libd.so
cat > libd.c << EOI
int a() { return 1; }
EOI

# build
compile_lib -o libd.so libd.c


# create libb.so
cat > libb.c << EOI
#define __USE_GNU
#include <dlfcn.h>
int
a()
{
	int (*nextA)();
	*(void**)&nextA = dlsym(RTLD_NEXT, "a");
	return (nextA != 0 ? nextA() : 0) + 2;
}
EOI

# build
compile_lib_dl -o libb.so libb.c ./libd.so


# create liba.so
cat > liba.c << EOI
#include <dlfcn.h>
int
a()
{
	int (*nextA)();
	*(void**)&nextA = dlsym(RTLD_NEXT, "a");
	return (nextA != 0 ? nextA() : 0) + 4;
}
EOI

# build
compile_lib_dl -o liba.so liba.c ./libb.so


# create libe.so
cat > libe.c << EOI
#include <dlfcn.h>
int
a()
{
	int (*nextA)();
	*(void**)&nextA = dlsym(RTLD_NEXT, "a");
	return (nextA != 0 ? nextA() : 0) + 8;
}
EOI

# build
compile_lib_dl -o libe.so libe.c


# create program
cat > program.c << EOI
#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>

int
a()
{
	int (*nextA)();
	*(void**)&nextA = dlsym(RTLD_NEXT, "a");
	return (nextA != 0 ? nextA() : 0) + 16;
}

int
main()
{
	void* liba;
	void* libe;
	void* self;

	libe = dlopen("./libe.so", RTLD_NOW | RTLD_GLOBAL);
	if (libe == NULL) {
		fprintf(stderr, "Error opening libe.so: %s\n", dlerror());
		exit(117);
	}

	liba = dlopen("./liba.so", RTLD_NOW | RTLD_GLOBAL);
	if (liba == NULL) {
		fprintf(stderr, "Error opening liba.so: %s\n", dlerror());
		exit(117);
	}

	return a();
}
EOI

# build
compile_program_dl -o program program.c

# run
test_run_ok ./program 31
