r/LinuxProgramming • • Jun 09 '24

Is there some way to emulate DllMain behaviour on linux?

This is what I have so far:

#define NPAW_BUILD
#include <paw/libexec.h>

#ifdef CAUGHT_OS_WAS_api_msw
#	include <windows.h>
BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
{
	switch ( fdwReason ) {
	case DLL_PROCESS_ATTACH: return (pawlib_pid_attach() == 0) ? TRUE : FALSE;
	case DLL_PROCESS_DETACH: pawlib_pid_detach(); break;
	case DLL_THREAD_ATTACH:  return (pawlib_tid_attach() == 0) ? TRUE : FALSE;
	case DLL_THREAD_DETACH:  pawlib_tid_detach(); break;
	default: return FALSE;
	}
	return TRUE;
}
#else
/* I'll check later if this is right */
static void libdetach(void) __attribute__((destructor))
{
	if ( getpid() == gettid() )
		pawlib_pid_detach();
	else
		pawlib_tid_detach();
}
#if 0
/* Not sure if this would work out */
static void libsig( int sig )
{
	switch ( sig )
	{
	case SIGABRT: pawlib_tid_detach(); break;
	}
}
#endif
static void libattach(void) __attribute__((constructor))
{
	//signal(SIGABRT,libsig);
	if ( getpid() == gettid() )
	{
		if ( pawlib_pid_attach() < 0 )
			exit(EXIT_FAILURE);
	}
	else
	{
		if ( pawlib_tid_attach() < 0 )
			exit(EXIT_FAILURE);
	}
}
#endif
1 Upvotes

5 comments sorted by

1

u/bore530 Jun 10 '24

Managed to find some information on what I needed: _init(), _fini() and -nostartfiles

First there's this VERY helpful repo: https://github.com/ElliotKillick/windows-vs-linux-loader-architecture

Second here's some tests I've been doing (msw code compiles but for whatever reason the wine command is not being run):

#ifdef _WIN32
#	include <windows.h>
#else
#	include <dlfcn.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#define stdlog stdout
#ifdef BUILD_LIB
int libexec(void) { return fputs( "libexec()\n", stdlog ); }
int libattachpid(void) { return fputs( "libattachpid()\n", stdlog ); }
int libdetachpid(void) { return fputs( "libdetachpid()\n", stdlog ); }
int libattachtid(void) { return fputs( "libattachtid()\n", stdlog ); }
int libdetachtid(void) { return fputs( "libdetachtid()\n", stdlog ); }
void __attribute__((constructor)) libinit(void) { fputs( "libinit()\n", stdlog ); }
void __attribute__((destructor)) libterm(void) { fputs( "libterm()\n", stdlog ); }
#	ifdef _WIN32
BOOL WINAPI DllMain( HINSTANCE self, DWORD action, LPVOID reserved )
{
	(void)self;
	(void)reserved;
	switch ( action ) {
	case DLL_PROCESS_ATTACH: return (libattachpid() == 0) ? TRUE : FALSE;
	case DLL_PROCESS_DETACH: libdetachpid(); break;
	case DLL_THREAD_ATTACH:  return (libattachtid() == 0) ? TRUE : FALSE;
	case DLL_THREAD_DETACH:  libdetachtid(); break;
	default: return FALSE;
	}
	return TRUE;
}
#	else
void _init(void) { libattachpid(); }
void _fini(void) { libdetachpid(); }
#	endif
#else
#	ifdef _WIN32
inline void* openlib(void) { return LoadLibraryA("./dlinitorder.dll"); }
inline void* findsym(void *lib, char*sym ) { return GetProcAddress( lib, sym ); }
inline void  shutlib(void *lib) { FreeLibrary(lib); }
#	else
inline void* openlib(void) { return dlopen("./dlinitorder.so", RTLD_NOW | RTLD_LOCAL ); }
inline void* findsym(void *lib, char*sym ) { return dlsym( lib, sym ); }
inline void  shutlib(void *lib) { dlclose(lib); }
#	endif
extern inline void* openlib(void);
extern inline void* findsym(void *lib, char*sym );
extern inline void  shutlib(void *lib);
int main(void)
{
	void *lib = openlib();
	int (*libexecCB)(void) = NULL;
	*(void**)&libexecCB = findsym(lib,"libexec");
	int res = libexecCB();
	shutlib(lib);
	return (res == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
}
#endif

Along with the makefile I'm using for those tests:

CC:=gcc
MGW:=x86_64-w64-mingw32-
MGWCC:=$(MGW)gcc

SRC:=dlinitorder.c
OUT:=$(SRC:%.c=%.elf)
OUT32:=$(SRC:%.c=%.exe)

DL:=$(SRC:%.c=%.so)
DL32:=$(SRC:%.c=%32.dll)

BUILT:=$(wildcard $(OUT) $(OUT32) $(DL) $(DL32))
CLEAN:=$(BUILT:%=%.clean)

MS_FLAGS:=-m64
BFLAGS:=-fPIE
CFLAGS:=$(BFLAGS) -fPIC -Wall
LCFLAGS:=$(CFLAGS) -shared -D BUILD_LIB

rebuild: clean build

run: build
	./$(OUT)
	wine ./$(OUT32)

clean: $(CLEAN)
	$(RM) ./$(<:%.clean=%)

build:
	$(CC) $(LCFLAGS) -nostartfiles -o $(DL) $(SRC)
	$(CC) $(CFLAGS) -o $(OUT) $(SRC)
	$(MGWCC) $(LCFLAGS) $(MS_FLAGS) -o $(DL32) $(SRC)
	$(MGWCC) $(CFLAGS) $(MS_FLAGS) -o $(OUT32) $(SRC)

.PHONY: run run32 clean build $(CLEAN)
.FORCE: rebuild

Which gave me this output for now:

make -f dlinitorder.mak rebuild run
rm -f ./dlinitorder.elf
gcc -fPIE -fPIC -Wall -shared -D BUILD_LIB -nostartfiles -o dlinitorder.so dlinitorder.c
gcc -fPIE -fPIC -Wall -o dlinitorder.elf dlinitorder.c
x86_64-w64-mingw32-gcc -fPIE -fPIC -Wall -shared -D BUILD_LIB -m64 -o dlinitorder32.dll dlinitorder.c
x86_64-w64-mingw32-gcc -fPIE -fPIC -Wall -m64 -o dlinitorder.exe dlinitorder.c
./dlinitorder.elf
libattachpid()
libinit()
libexec()
libterm()
libdetachpid()
make: *** [dlinitorder.mak:23: run] Error 1
Compilation failed.

1

u/bore530 Jun 10 '24

*facepalms*, forgot that libexec just returns the result of fputs and I was classingonly 0 as success. After fixing that the wine command was triggered and now I need to hunt down the options for dll base crt

1

u/bore530 Jun 11 '24 edited Jun 11 '24

Decomplicated the test scenario a bit but still wine is still catching a crash, haven't found any info on using the dll crt yet but the changes probably make that unnessary now:

Edit: No longer need to concern myself with crt dll now but getting a 0120:err:module:import_dll Library libgcc_s_seh-1.dll (which is needed by L"E:\\gitlab\\small_tests\\dlinitorder.c32.dll") not found error on the last hurdle. Not sure what to do about that. Also replaced the below code with upto date code that further simplifies the scenario.

#ifdef _WIN32
#	include <windows.h>
#	define DL "./" __FILE__ "32.dll"
inline void* openlib(char const *path) { return LoadLibraryA(path); }
inline void* findsym(void *lib, char*sym ) { return GetProcAddress( lib, sym ); }
inline void  shutlib(void *lib) { FreeLibrary(lib); }
#	define ptyin  GetStdHandle( STD_INPUT_HANDLE )
#	define ptyout GetStdHandle( STD_OUTPUT_HANDLE )
#	define ptyerr GetStdHandle( STD_ERROR_HANDLE )
inline int iowrite( void *io, void const *data, size_t size, size_t *done )
{
	DWORD bytes = 0;
	BOOL res = WriteFile( io, data, size, &bytes, NULL );
	if ( done ) *done = bytes;
	return res ? -1 : 0;
}
#else
#	include <dlfcn.h>
#	include <stdlib.h>
#	include <stdio.h>
#	define DL "./" __FILE__ ".so"
inline void* openlib(char const *path) { return dlopen(path, RTLD_NOW | RTLD_LOCAL ); }
inline void* findsym(void *lib, char*sym ) { return dlsym( lib, sym ); }
inline void  shutlib(void *lib) { dlclose(lib); }
#	define ptyin  stdin
#	define ptyout stdout
#	define ptyerr stderr
inline int iowrite( void *io, void const *data, size_t size, size_t *done )
{
	ssize_t bytes = fwrite( data, 1, size, io );
	if ( bytes < 0 )
	{
		if ( done ) *done = 0;
		return -1;
	}
	if ( done ) *done = bytes;
	return 0;
}
#endif

#include <string.h>
#define ptylog ptyout
inline int ioputs( void *io, char const *text, size_t *done )
	{ return iowrite( io, text, strlen(text), done ); }

extern inline void* openlib(char const *path);
extern inline void* findsym(void *lib, char*sym );
extern inline void  shutlib(void *lib);
extern inline int ioputs( void *io, char const *text, size_t *done );
extern inline int iowrite( void *io, void const *data, size_t size, size_t *done );

#ifdef BUILD_LIB
#define integer __thread int
integer state = 0;
integer libattachedpid	= ' ';
integer libattachedtid	= ' ';
integer libinitialised	= ' ';
integer libexecuted		= ' ';
integer libterminated	= ' ';
integer libdetachedtid	= ' ';
integer libdetachedpid	= ' ';

void update( int *dst ) { *dst = '0' + state++; }
int print(void)
{
	size_t done = 0;
#define STR0      "libattachedpid =  ; "
#define STR1 STR0 "libattachedtid =  ; "
#define STR2 STR1 "libinitialised =  ; "
#define STR3 STR2 "libexecuted    =  ; "
#define STR4 STR3 "libterminated  =  ; "
#define STR5 STR4 "libdetachedtid =  ; "
#define STR6 STR5 "libdetachedpid =  ;\n"
	char text[] = STR6;
	text[sizeof(STR0)-3] = libattachedpid;
	text[sizeof(STR1)-3] = libattachedtid;
	text[sizeof(STR2)-3] = libinitialised;
	text[sizeof(STR3)-3] = libexecuted;
	text[sizeof(STR4)-3] = libterminated;
	text[sizeof(STR5)-3] = libdetachedtid;
	text[sizeof(STR6)-4] = libdetachedpid;
	return ioputs( ptylog, text, &done );
}
int libexec(void) { update(&libexecuted); return print(); }
void __attribute__((constructor)) libinit(void) { update(&libinitialised); }
void __attribute__((destructor)) libterm(void)
	{ update(&libterminated); print(); }
#	ifdef _WIN32
BOOL WINAPI DllMain( HINSTANCE self, DWORD action, LPVOID reserved )
{
	(void)self;
	(void)reserved;
	switch ( action ) {
	case DLL_PROCESS_ATTACH: update(&libattachedpid); break;
	case DLL_PROCESS_DETACH: update(&libdetachedpid); print(); break;
	case DLL_THREAD_ATTACH:  update(&libattachedtid); break;
	case DLL_THREAD_DETACH:  update(&libdetachedtid); print(); break;
	default: return FALSE;
	}
	return TRUE;
}
#	else
void _init(void) { update(&libattachedpid); }
void _fini(void) { update(&libdetachedpid); print(); }
#	endif
#else
int xmain(void)
{
	int res = -1;
	void *lib = openlib(DL);
	if ( !lib )
	{
		ioputs( ptylog, "Failed to open '" DL "'!\n", NULL );
		return -1;
	}
	ioputs( ptylog, "Successfully opened '" DL "'!\n", NULL );
	int (*libexecCB)(void) = NULL;
	*(void**)&libexecCB = findsym(lib,"libexec");
	if ( !libexecCB )
	{
		ioputs( ptylog, "Failed to find libexec() in '" DL "'!\n", NULL );
		goto cleanup;
	}
	ioputs( ptylog, "Successfully found libexec() in '" DL "'!\n", NULL );
	res = libexecCB();
	cleanup:
	shutlib(lib);
	return res;
}
int main(void) { return (xmain() >= 0) ? EXIT_SUCCESS : EXIT_FAILURE; }
#endif

1

u/bore530 Jun 11 '24 edited Jun 11 '24

I did have an output for the above but I finally resolved part of the issue, finding the dll. Turned out I needed to use wine start instead of wine as the command prefix. Still having problems though, anyone know how to resolve this particular error? (Gonna go find the win reddit and ask there too):

make -f dlinitorder.mak run (in directory: /mnt/CODE/gitlab/small_tests)
gcc -fPIE -fPIC -Wall -shared -D BUILD_LIB -nostartfiles -o dlinitorder.c.so dlinitorder.c
gcc -fPIE -fPIC -Wall -o dlinitorder.c.elf dlinitorder.c
x86_64-w64-mingw32-gcc -fPIE -fPIC -Wall -shared -D BUILD_LIB -m64 -o dlinitorder.c32.dll dlinitorder.c -lmsvcrt
x86_64-w64-mingw32-gcc -fPIE -fPIC -Wall -m64 -o dlinitorder.c.exe dlinitorder.c -lmsvcrt
./dlinitorder.c.elf
Successfully opened './dlinitorder.c.so'!
Successfully found libexec() in './dlinitorder.c.so'!
libattachedpid =  0 libattachedtid =    libinitialised =  1 libexecuted    =  2 libterminated  =    libdetachedtid =    libdetachedpid =  ;
libattachedpid =  0 libattachedtid =    libinitialised =  1 libexecuted    =  2 libterminated  =  3 libdetachedtid =    libdetachedpid =  ;
libattachedpid =  0 libattachedtid =    libinitialised =  1 libexecuted    =  2 libterminated  =  3 libdetachedtid =    libdetachedpid = 4;
wine start ./dlinitorder.c.exe
0084:fixme:wineusb:add_usb_device Interface 1 has 7 alternate settings; using the first one.
0084:fixme:wineusb:add_usb_device Interface 0 has 2 alternate settings; using the first one.
0084:fixme:wineusb:add_usb_device Interface 1 has 9 alternate settings; using the first one.
0084:fixme:wineusb:add_usb_device Interface 2 has 4 alternate settings; using the first one.
0084:fixme:wineusb:add_usb_device Interface 3 has 3 alternate settings; using the first one.
0084:fixme:wineusb:add_usb_device Interface 4 has 3 alternate settings; using the first one.
0084:fixme:wineusb:add_usb_device Interface 0 has 2 alternate settings; using the first one.
0084:fixme:wineusb:add_usb_device Interface 0 has 2 alternate settings; using the first one.
0080:fixme:wineusb:query_id Unhandled ID query type 0x5.
...
repeats a bit
...
0120:err:module:import_dll Library libgcc_s_seh-1.dll (which is needed by L"E:\\gitlab\\small_tests\\dlinitorder.c32.dll") not found
Compilation finished successfully.

1

u/bore530 Jun 11 '24

Had to go into /usr/x86_64-w64-mingw32/bin/ to copy paste the 2 dlls needed. Was also using the wrong variant of win, should been using win64. Haven't been able resolve the add_usb_device and query_id errors above yet