Subject: Re: Modifications of standard headers for PECOFF
To: TAMURA Kent <kent@netbsd.org>
From: Bang Jun-Young <junyoung@mogua.com>
List: tech-userlevel
Date: 05/25/2002 11:25:00
On Fri, May 24, 2002 at 10:42:03PM +0900, TAMURA Kent wrote:
> 
> In message "Re: Modifications of standard headers for PECOFF"
>     on 02/05/24, TAMURA Kent <kent@netbsd.org> writes:
> > > These changes don't even look reasonable to me. Why don't you use
> > > --enable-auto-export and --enable-auto-import features of recent
> > > binutils (the former is default now, AFAIK)?
> > 
> > Oh, I have not known --enable-auto-import.  I'll try it.  Thank
> > you.
> 
> I have tried it and found --enable-auto-import does not work in
> many cases.  It seems to work for simple variables but not for
> arrays and struct variables.

Arrgh. It's a known problem. ld(1) manual says (sorry for long 
excerption):

   --enable-auto-import
          Do  sophisticated  linking of _symbol to __imp__symbol for DATA
          imports  from  DLLs,  and create the necessary thunking symbols
          when  building the DLLs with those DATA exports. This generally
          will 'just work' - but sometimes you may see this message:
          
          "variable  '<var>'  can't  be  auto-imported.  Please  read the
          documentation for ld's --enable-auto-import for details."
          
          This  message  occurs  when  some  (sub)expression  accesses an
          address  ultimately  given  by  the sum of two constants (Win32
          import  tables  only allow one). Instances where this may occur
          include  accesses to member fields of struct variables imported
          from  a  DLL,  as  well as using a constant index into an array
          variable  imported  from a DLL. Any multiword variable (arrays,
          structs,  long  long,  etc)  may  trigger this error condition.
          However,  regardless  of  the  exact data type of the offending
          exported variable, ld will always detect it, issue the warning,
          and exit.
          
          There  are  several ways to address this difficulty, regardless
          of the data type of the exported variable:
          
          One  solution  is  to  force  one  of  the  'constants' to be a
          variable - that is, unknown and un-optimizable at compile time.
          For  arrays,  there  are two possibilities: a) make the indexee
          (the  array's  address)  a  variable, or b) make the 'constant'
          index a variable. Thus:
          
extern type extern_array[];
extern_array[1] -->
   { volatile type *t=extern_array; t[1] }

          or
          
extern type extern_array[];
extern_array[1] -->
   { volatile int t=1; extern_array[t] }

          For  structs  (and  most  other  multiword data types) the only
          option  is  to make the struct itself (or the long long, or the
          ...) variable:
          
extern struct s extern_struct;
extern_struct.field -->
   { volatile struct s *t=&extern_struct; t->field }

          or
          
extern long long extern_ll;
extern_ll -->
  { volatile long long * local_ll=&extern_ll; *local_ll }

          A  second  method of dealing with this difficulty is to abandon
          'auto-import'  for  the  offending  symbol  and  mark  it  with
          __declspec(dllimport). However, in practice that requires using
          compile-time  #defines  to  indicate whether you are building a
          DLL,  building client code that will link to the DLL, or merely
          building/linking  to  a  static  library.  In making the choice
          between  the  various  methods of resolving the 'direct address
          with  constant  offset'  problem,  you  should consider typical
          real-world usage:
          
          Original:
          
--foo.h
extern int arr[];
--foo.c
#include "foo.h"
void main(int argc, char **argv){
  printf("%d\n",arr[1]);
}

          Solution 1:
          
--foo.h
extern int arr[];
--foo.c
#include "foo.h"
void main(int argc, char **argv){
  /* This workaround is for win32 and cygwin; do not "optimize" */
  volatile int *parr = arr;
  printf("%d\n",parr[1]);
}

          Solution 2:
          
--foo.h
/* Note: auto-export is assumed (no __declspec(dllexport)) */
#if (defined(_WIN32) || defined(__CYGWIN__)) && \
  !(defined(FOO_BUILD_DLL) || defined(FOO_STATIC))
#define FOO_IMPORT __declspec(dllimport)
#else
#define FOO_IMPORT
#endif
extern FOO_IMPORT int arr[];
--foo.c
#include "foo.h"
void main(int argc, char **argv){
  printf("%d\n",arr[1]);
}

          A third way to avoid this problem is to re-code your library to
          use a functional interface rather than a data interface for the
          offending  variables  (e.g.  set_foo()  and  get_foo() accessor
          functions).
          
> I'll commit __libc_extern change later.

I still believe __declspec(dllexport) is not necessary at all since
--enable-auto-export is enabled by default, and __declspec(dllimport)
can be omitted for simple variables if --enable-auto-import is given
to ld(1).

BTW, how many multiword variables are exported from libc to
outer modules? 

Jun-Young

-- 
Bang Jun-Young <junyoung@mogua.com>