Hi! With the latest dumpbin work in place, libtool starts to add symbols from import libraries to the generated fooS.c files in func_generate_dlsyms, as needed by the preloader. And then the next problem naturally rears its head. This time it is the age-old data exports problem responsible for code such as this (in demo.at):
else if (STREQ ("nothing", name)) #ifndef _WIN32 /* In an ideal world we could do this... */ pnothing = (int*)s->address; #else /* !_WIN32 */ /* In an ideal world a shared lib would be able to export data */ pnothing = (int*)¬hing; #endif I.e., demo.at isn't even trying to use the data symbol from the preloader, it just picks data up directly using the import library on Windows. Which is of course cheating and circumvents the API etc etc. However, MinGW has long supported pseudo-relocs and anto-import and the "Ideal world" case should work just fine with gcc even on _WIN32. But not with MSVC, which fails to link since data symbols from import libraries are not declared __declspec(dllimport). Example from demo.at: helldl.exeS.obj : error LNK2001: unresolved external symbol _nothing Fixing that, and making data symbols __declspec(dllimport) is however not the entire solution, as that yields: helldl.exeS.c(47) : error C2099: initializer is not a constant during compilation instead. The underlying problem is that MSVC does not have pseudo-relocs. So, this patch adds an @INIT@ entry to the preloader symbol table, with the address of a function that is intended to do the relocations of data items from user-provided code instead of relying on the tools. This function is then filled with relocation code for MSVC, but left empty everywhere else where it is not needed. This is clearly not a finalized patch (it's actually pretty rough), it's just proof of concept. I have e.g. not optimized it, but it is clearly possibly to set the @INIT@ address to NULL and don't call @INIT@ at all in non-MSVC cases. It should also not be necessary to relocate data symbols from static libraries from user code, and it should be possible to add a "done" variable to the relocation function, so that it only does the work once. What I'm looking for is feedback. Is it acceptable to add a new virtual @INIT@ entry like this? I.e. is this approach workable or should I just drop it? With the patches, the testsuite runs cleanly with .../configure \ CC="/home/peda/automake/lib/compile cl -nologo" \ CFLAGS="-MD -Zi -EHsc" \ CXX="/home/peda/automake/lib/compile cl -nologo" \ CXXFLAGS="-MD -Zi -EHsc" \ NM="dumpbin -symbols -headers" \ LD=link \ STRIP=: \ AR="/home/peda/automake/lib/ar-lib lib" \ RANLIB=: \ F77=no \ FC=no \ GCJ=no (and it is also clean for MinGW and Cygwin with a default no-args .../configure, and the "Ideal world" branch from demo.at above works for MSVC as well, not sure about WinCE though, no access to that.) For reference, here is how the generated symbol file ends up for demo.at in a MSVC build: ------------------8<-------------------- /* helldl.exeS.c - symbol resolution table for `helldl.exe' dlsym emulation. */ /* Generated by libtool (GNU libtool) 2.4.2.282-b3e9-dirty */ #ifdef __cplusplus extern "C" { #endif #if defined __GNUC__ && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)) || (__GNUC__ > 4)) #pragma GCC diagnostic ignored "-Wstrict-prototypes" #endif /* Keep this code in sync between libtool.m4, ltmain, lt_system.h, and tests. */ #if defined _WIN32 || defined __CYGWIN__ || defined _WIN32_WCE /* DATA imports from DLLs on WIN32 can't be const, because runtime relocations are performed -- see ld's documentation on pseudo-relocs. */ # define LT_DLSYM_CONST #elif defined __osf__ /* This system does not cope well with relocations in const data. */ # define LT_DLSYM_CONST #else # define LT_DLSYM_CONST const #endif #if defined _WIN32 && defined _MSC_VER #define LT_DLSYM_DATA extern __declspec(dllimport) #else #define LT_DLSYM_DATA extern #endif /* External symbol declarations for the compiler. */ extern int foo(); extern int hello(); LT_DLSYM_DATA char nothing; /* The mapping between symbol names and symbols. */ typedef struct { const char *name; void *address; } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt__PROGRAM__LTX_preloaded_symbols[]; static void lt_syminit(void) { #if defined _WIN32 && defined _MSC_VER LT_DLSYM_CONST lt_dlsymlist *symbol = lt__PROGRAM__LTX_preloaded_symbols; for (; symbol->name; ++symbol) { if (!strcmp (symbol->name, "nothing")) symbol->address = (void *) ¬hing; } #endif } LT_DLSYM_CONST lt_dlsymlist lt__PROGRAM__LTX_preloaded_symbols[] = { {"@PROGRAM@", (void *) 0}, {"@INIT@", (void *) <_syminit}, {"hello-2.dll", (void *) 0}, {"foo", (void *) &foo}, {"hello", (void *) &hello}, {"nothing", (void *) 0}, {0, (void *) 0} }; /* This works around a problem in FreeBSD linker */ #ifdef FREEBSD_WORKAROUND static const void *lt_preloaded_setup() { return lt__PROGRAM__LTX_preloaded_symbols; } #endif #ifdef __cplusplus } #endif ------------------8<-------------------- Cheers, Peter
>From 3e840b158f7a9949457273ec070f35ab03b04ade Mon Sep 17 00:00:00 2001 From: Peter Rosin <p...@lysator.liu.se> Date: Fri, 19 Oct 2012 22:49:56 +0200 Subject: [PATCH] libtool: add @INIT@ to the preloader, for data imports on Windows * build-aux/ltmain.in (func_generate_dlsyms) [MSVC]: Mark all data symbols __declspec(dllimport) and handle static libraries and import libraries equivalently. Generate a relocation function lt_syminit that fills in the addresses of data items and point it out with a new virtual @INIT@ entry in the symbol list. * m4/libtool.m4 (_LT_CMD_GLOBAL_SYMBOLS) [MSVC]: Adjust lt_cv_sys_global_symbol_to_c_name_address and lt_cv_sys_global_symbol_to_c_name_address_lib_prefix so that they fill in "(void*) 0" for data symbols which need to be relocated at runtime by the above lt_syminit. * libltdl/loaders/preopen.c (add_symlist): Look up the virtual @INIT@ symbol (i.e. lt_syminit) and call it if non-zero. (vm_sym, lt_dlpreload_open): Step past the @INIT@ symbol. * tests/demo.at (dlmain.c): Call the @INIT@ symbol if non-zero. --- build-aux/ltmain.in | 26 +++++++++++++++++++++++--- libltdl/loaders/preopen.c | 22 ++++++++++++++++++++-- m4/libtool.m4 | 2 ++ tests/demo.at | 2 ++ 4 files changed, 47 insertions(+), 5 deletions(-) diff --git a/build-aux/ltmain.in b/build-aux/ltmain.in index ed4a4b1..a153df0 100644 --- a/build-aux/ltmain.in +++ b/build-aux/ltmain.in @@ -2659,6 +2659,11 @@ extern \"C\" { #else # define LT_DLSYM_CONST const #endif +#if defined _WIN32 && defined _MSC_VER +#define LT_DLSYM_DATA extern __declspec(dllimport) +#else +#define LT_DLSYM_DATA extern +#endif /* External symbol declarations for the compiler. */\ " @@ -2793,7 +2798,7 @@ extern \"C\" { fi if test -f "$nlist"S; then - eval "$global_symbol_to_cdecl"' < "$nlist"S >> "$output_objdir/$my_dlsyms"' + eval "$global_symbol_to_cdecl"' < "$nlist"S' | $SED 's/^extern char /LT_DLSYM_DATA char /' >> "$output_objdir/$my_dlsyms" else echo '/* NONE */' >> "$output_objdir/$my_dlsyms" fi @@ -2807,10 +2812,25 @@ typedef struct { } lt_dlsymlist; extern LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[]; +static void lt_syminit(void) +{ +#if defined _WIN32 && defined _MSC_VER + LT_DLSYM_CONST lt_dlsymlist *symbol = lt_${my_prefix}_LTX_preloaded_symbols; + for (; symbol->name; ++symbol) + {\ +" + if test -f "$nlist"S; then + eval "$global_symbol_to_cdecl"' < "$nlist"S' | $SED -n 's/extern char \([^;]*\).*/if (!strcmp (symbol->name, \"\1\")) symbol->address = (void *) \&\1;/p' >> "$output_objdir/$my_dlsyms" + fi + + echo >> "$output_objdir/$my_dlsyms" "\ + } +#endif +} LT_DLSYM_CONST lt_dlsymlist lt_${my_prefix}_LTX_preloaded_symbols[] = -{\ - { \"$my_originator\", (void *) 0 }," +{ {\"$my_originator\", (void *) 0}, + {\"@INIT@\", (void *) <_syminit}," case $need_lib_prefix in no) diff --git a/libltdl/loaders/preopen.c b/libltdl/loaders/preopen.c index 1670085..432f552 100644 --- a/libltdl/loaders/preopen.c +++ b/libltdl/loaders/preopen.c @@ -210,7 +210,7 @@ vm_sym (lt_user_data LT__UNUSED loader_data, lt_module module, const char *name) { lt_dlsymlist *symbol = (lt_dlsymlist*) module; - symbol +=2; /* Skip header (originator then libname). */ + symbol +=3; /* Skip header (originator, init then libname). */ while (symbol->name) { @@ -270,9 +270,25 @@ add_symlist (const lt_dlsymlist *symlist) if (tmp) { + const lt_dlsymlist *symbol; + tmp->symlist = symlist; tmp->next = preloaded_symlists; preloaded_symlists = tmp; + + for (symbol = symlist; symbol->name; ++symbol) + { + if (STREQ (symbol->name, "@INIT@")) + { + if (symbol->address) + { + void (*init_symlist)(void); + *(void **)(&init_symlist) = symbol->address; + init_symlist(); + } + break; + } + } } else { @@ -345,7 +361,9 @@ lt_dlpreload_open (const char *originator, lt_dlpreload_callback_func *func) ++found; /* ...load the symbols per source compilation unit: - (we preincrement the index to skip over the originator entry) */ + (we increment the index once to skip over the originator entry + and then preincrement to also skip the init entry) */ + ++idx; while ((symbol = &list->symlist[++idx])->name != 0) { if ((symbol->address == 0) diff --git a/m4/libtool.m4 b/m4/libtool.m4 index c85a85f..a7c3f4f 100644 --- a/m4/libtool.m4 +++ b/m4/libtool.m4 @@ -3674,6 +3674,8 @@ for ac_symprfx in "" "_"; do # Write the raw and C identifiers. if test "$lt_cv_nm_interface" = "MS dumpbin"; then + lt_cv_sys_global_symbol_to_c_name_address="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^T \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^D \([[^ ]]*\) \([[^ ]]*\)$/ {\"\2\", (void *) 0},/p'" + lt_cv_sys_global_symbol_to_c_name_address_lib_prefix="sed -n -e 's/^: \([[^ ]]*\)[[ ]]*$/ {\\\"\1\\\", (void *) 0},/p' -e 's/^T \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) \&\2},/p' -e 's/^T \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) \&\2},/p' -e 's/^D \([[^ ]]*\) \(lib[[^ ]]*\)$/ {\"\2\", (void *) 0},/p' -e 's/^T \([[^ ]]*\) \([[^ ]]*\)$/ {\"lib\2\", (void *) 0},/p'" # Fake it for dumpbin and say T for any non-static function # and D for any global variable. # Also find C++ and __fastcall symbols from MSVC++, diff --git a/tests/demo.at b/tests/demo.at index b3d2532..1c4e91e 100644 --- a/tests/demo.at +++ b/tests/demo.at @@ -295,6 +295,8 @@ int main () if (s->address) { const char *name = s->name; printf ("found symbol: %s\n", name); + if (STREQ ("@INIT@", name) && s->address) + ((void(*)())s->address)(); if (STREQ ("hello", name)) phello = (int(*)())s->address; else if (STREQ ("foo", name)) -- 1.7.9