This is the mail archive of the cygwin@cygwin.com mailing list for the Cygwin project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Run time linking a .dll into a .exe


I want to create some plugins for an executable.
The problem is that when I create a plugin
(.dll, shared object, or whatever you want to call it),
it sucks in code that is already in the executable.
In other words, I link the executable with some static
library, libfoo.a, and the executable gets routines from
libfoo.a. I then link the dll, and it gets some of the same
routines.

Normally this would be annoying since you have code
duplication, but would work. However, if the code
contains static variables, then the executable and the
dll have their own versions of the static, which get out
of sync.

Is there a way to link the dll so that it uses the code
inside the executable?

Here's a simple example of my problem. I have a Foo class
which contains a static to the class. I create a static library
(libfoo.a) out of the class code. I create a dll which calls
the routines in libfoo.a. I also create an application which
also calls routines in libfoo.a, and calls routines in the dll
via dlopen() and dlsym() which in turn call routines from libfoo.a.
The result is that different versions of the Foo static get
set.

============== my_app.cpp ==================

#include <iostream.h>
#include <dlfcn.h>
#include "foo_cls.hpp"

int
main()
{
  cout << "Foo::get_val() = " << Foo::get_val() << endl;
  cout << "Calling Foo::set_val(7)" << endl;
  Foo::set_val(7);
  cout << "Foo::get_val() = " << Foo::get_val() << "\n\n";

  void *handle = ::dlopen("bar.dll", RTLD_NOW);
  if (!handle)
  {
      cout << "Unable to open bar.dll.\n";
      return(1);
  }
  int (*get_val)(void) = (int (*)())::dlsym(handle, "get_val");
  void (*set_val)(int) = (void (*)(int))::dlsym(handle, "set_val");

  cout << "::get_val() = " << get_val() << endl;
  cout << "Calling ::set_val(13)" << endl;
  set_val(13);
  cout << "::get_val() = " << get_val() << "\n\n";

  ::dlclose(handle);

  cout << "Foo::get_val() = " << Foo::get_val() << endl;

  return(0);
}

============== foo_cls.hpp ==================

#ifndef _FOO_CLS_HPP_
#define _FOO_CLS_HPP_

class Foo
{
public:
  static void Foo::set_val(int new_val);
  static int Foo::get_val();

private:
  static int Foo::_val;
};

#endif // _FOO_CLS_HPP_

============== foo_cls.cpp ==================


#include "foo_cls.hpp"

int Foo::_val = 0;

void
Foo::set_val(int new_val)
{
  Foo::_val = new_val;
}

int
Foo::get_val()
{
  return(Foo::_val);
}

============== bar.hpp ==================

#ifndef _BAR_HPP_
#define _BAR_HPP_

extern "C" {
  void set_val(int new_val);
  int get_val();
};

#endif // _BAR_HPP_

============== bar.cpp ==================

#include "bar.hpp"
#include "foo_cls.hpp"

void
set_val(int new_val)
{
  Foo::set_val(new_val);
}

int
get_val()
{
  return(Foo::get_val());
}

/************************************************************************/
/* Under Cygwin, we can use the dl family of calls, but we need to jump */
/* through some hoops first. Specifically, we need to include           */
/* <cygwin/cygwin_dll.h> and we need to use the DECLARE_CYGWIN_DLL()    */
/* macro. During the link phase, we must use __cygwin_dll_entry@12 as   */
/* the entry point. See http://sources.redhat.com/cygwin/dl-docs.html. */
/************************************************************************/

#if defined(__CYGWIN__)

#include <cygwin/cygwin_dll.h>

DECLARE_CYGWIN_DLL(dll_entry);

int WINAPI
dll_entry(
  HANDLE ,
  DWORD reason,
  void *)
{
  switch (reason)
  {
      case DLL_PROCESS_ATTACH: break;
      case DLL_PROCESS_DETACH: break;
      case DLL_THREAD_ATTACH:  break;
      case DLL_THREAD_DETACH:  break;
  }
  return 1;
}

#endif /* defined(__CYGWIN__) */

============== Makefile ==================

CYGWIN_DIR = D:/Apps/Cygwin
DLL_DIR = .

TARGET = my_app.exe
LIB = libfoo.a
DLL = bar.dll

EXE_OBJS = my_app.o
DLL_OBJS = bar.o
LIB_OBJS = foo_cls.o

DLL_DEF_FILE = dll.def
DLL_EXP_FILE = dll.exp
DLL_BASE_FILE = dll.base
CYGWIN_ENTRY_PT = __cygwin_dll_entry@12

LIBS = \
  -L"$(CYGWIN_DIR)/lib" \
  -L"$(CYGWIN_DIR)/lib/gcc-lib/i686-pc-cygwin/2.95.3-4" \
  -L"$(CYGWIN_DIR)/lib/w32api" \
  -lstdc++ -lgcc -lc -lkernel32

all: $(TARGET) $(DLL)

$(TARGET) : $(EXE_OBJS) $(LIB)
  g++ $(EXE_OBJS) $(LIB) -o $@ --export-dynamic -W -Wall

$(LIB) : $(LIB_OBJS)
  ar ruv $@ $(LIB_OBJS)
  ranlib $@

$(DLL_DIR)/$(DLL): $(DLL_OBJS)
  echo EXPORTS > $(DLL_DEF_FILE)
  nm $(DLL_OBJS) | grep '^........ [BCDT] _' | sed 's/[^_]*_//' \
                                                  >> $(DLL_DEF_FILE)
  ld --base-file $(DLL_BASE_FILE) --dll -o dummy $(DLL_OBJS) $(LIB) \
     $(LIBS) -e $(CYGWIN_ENTRY_PT)
  dlltool --as=as --dllname $(DLL_DIR)/$(DLL) --def $(DLL_DEF_FILE) \
     --base-file $(DLL_BASE_FILE) --output-exp $(DLL_EXP_FILE)
  ld --base-file $(DLL_BASE_FILE) $(DLL_EXP_FILE) --dll \
     -o $(DLL_DIR)/$(DLL) $(DLL_OBJS) $(LIB) $(LIBS) -e $(CYGWIN_ENTRY_PT)
  dlltool --as=as --dllname $(DLL_DIR)/$(DLL) --def $(DLL_DEF_FILE) \
     --base-file $(DLL_BASE_FILE) --output-exp $(DLL_EXP_FILE)
  ld $(DLL_EXP_FILE) --dll -o $(DLL_DIR)/$(DLL) $(DLL_OBJS) $(LIB) \
     $(LIBS) -e $(CYGWIN_ENTRY_PT)
  rm dummy

clean:
  -rm $(EXE_OBJS) $(LIB_OBJS) $(LIB) $(DLL_OBJS) $(DLL_DEF_FILE) \
      $(DLL_BASE_FILE) $(DLL_EXP_FILE)

clean_all: clean
  -rm $(DLL_DIR)/$(DLL) $(TARGET)

%.o : %.cpp
  g++ -c $*.cpp -o $*.o -I$(CYGWIN_DIR)/usr/include -W -Wall

============== End of code ==================

The output from this is:

Foo::get_val() = 0
Calling Foo::set_val(7)
Foo::get_val(7)

::get_val() = 0                       <-- I want this to be 7.
Calling ::set_val(13)
::get_val() = 13

Foo::get_val() = 7                    <--I want this to be 13.


Under the --export-dynamic option in the ld man page,
it says:
". . .
If you use dlopen to load a dynamic object which needs
to refer back to the symbols defined by the program,
rather than some other dynamic object, then you will
probably need to use this option when linking the
program itself."

So it seems what I want to do should be possible.

Thanx.

- glen


_________________________________________________________________
Get your FREE download of MSN Explorer at http://explorer.msn.com/intl.asp


--
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple
Bug reporting:         http://cygwin.com/bugs.html
Documentation:         http://cygwin.com/docs.html
FAQ:                   http://cygwin.com/faq/


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]