Subject: Mozilla / dlopen
To: None <port-i386@netbsd.org, tech-toolchain@netbsd.org>
From: Frank van der Linden <frank@wins.uva.nl>
List: port-i386
Date: 12/08/1998 01:10:51
Quoting myself:
> I don't know why the global dlopen() fails, that looks like a bug. It
> happens for me too when running mozilla-current on i386-current with egcs,
> and I haven't looked into it any further.

[note follow-up to tech-toolchain]

I looked into this further, and noticed the following problem scenario,
which happens on a.out systems:

1) Create a C++ shared library with global constructors
2) In such a constructor, place a call to dlopen
3) Link a program with it, and start it
4) a.out crt0 gets called. it calls the dynamic linker. ld_entry (entry
   points for dl*() calls) are not yet set up.
5) The dynamic linker starts doing its job. It finds the C++ shared lib,
   and calls the .init section in it, which wakes care of the global
   constructors (if c++rt0 was linked in, anyway)
6) The global constructor calls dlopen(), which is in crt0
7) ld_entry has not yet been initialized, do dlopen() fails.
8) ld_entry will later be initialized, but this is too late.

So, anytime dlopen() is called in some way from a global constructor
in a shared library, it will fail, since it doesn't know it's actual
stub dl*() entry points yet.

I worked around this by giving ld_entry an extra indirection, setting
it before the runtime linker is called, and modified the runtime linker
to fill in the ld_entry field before it starts resolving libraries.

This appears to work, but it's just a quick hack, and I haven't even
checked if the rtld code is up to this task, i.e. if messing around
with dl*() in global constructors in a more serious way then
mozilla does will create havoc in its data structures (it probably will).

Workaround included below. I assume we may not want to spend too much
effort in cleaning dl*() up for a.out platforms, so it might do until
we switch to ELF in the future. Comments (esp. from people who know
the rtld code a little better)?

- Frank

P.S. About the original problem: mozilla crashes further along now,
     but that is a different problem.


*** lib/csu/common.c.orig	Tue Oct 20 21:14:00 1998
--- lib/csu/common.c	Tue Dec  8 00:36:25 1998
***************
*** 39,45 ****
  #ifdef DYNAMIC
  
  typedef int (*rtld_entry_fn) __P((int, struct crt_ldso *));
! static struct ld_entry	*ld_entry;
  
  static void
  __load_rtld(dp)
--- 39,45 ----
  #ifdef DYNAMIC
  
  typedef int (*rtld_entry_fn) __P((int, struct crt_ldso *));
! static struct ld_entry	**ld_entry;
  
  static void
  __load_rtld(dp)
***************
*** 134,139 ****
--- 134,140 ----
  	/* Call Sun's ld.so entry point: version 1, offset crt */
  	__call(CRT_VERSION_SUN, &crt, crt.crt_ba + sizeof hdr);
  #else
+ 	ld_entry = &crt.crt_ldentry;
  	entry = (rtld_entry_fn)(crt.crt_ba + sizeof hdr);
  	if ((*entry)(CRT_VERSION_BSD_4, &crt) == -1) {
  		/* Feeble attempt to deal with out-dated ld.so */
***************
*** 143,153 ****
  		if ((*entry)(CRT_VERSION_BSD_3, &crt) == -1) {
  			_FATAL("ld.so failed\n");
  		}
! 		ld_entry = dp->d_entry;
  		return;
  	}
! 	ld_entry = crt.crt_ldentry;
! 	atexit(ld_entry->dlexit);
  #endif
  
  #if defined(sun) && defined(DUPZFD)
--- 144,153 ----
  		if ((*entry)(CRT_VERSION_BSD_3, &crt) == -1) {
  			_FATAL("ld.so failed\n");
  		}
! 		ld_entry = &dp->d_entry;
  		return;
  	}
! 	atexit((*ld_entry)->dlexit);
  #endif
  
  #if defined(sun) && defined(DUPZFD)
***************
*** 168,187 ****
  	const char	*name;
  	int	mode;
  {
! 	if (ld_entry == NULL)
  		return NULL;
  
! 	return (ld_entry->dlopen)(name, mode);
  }
  
  int
  dlclose(fd)
  	void	*fd;
  {
! 	if (ld_entry == NULL)
  		return -1;
  
! 	return (ld_entry->dlclose)(fd);
  }
  
  void *
--- 168,187 ----
  	const char	*name;
  	int	mode;
  {
! 	if ((*ld_entry) == NULL)
  		return NULL;
  
! 	return ((*ld_entry)->dlopen)(name, mode);
  }
  
  int
  dlclose(fd)
  	void	*fd;
  {
! 	if ((*ld_entry) == NULL)
  		return -1;
  
! 	return ((*ld_entry)->dlclose)(fd);
  }
  
  void *
***************
*** 189,198 ****
  	void	*fd;
  	const char	*name;
  {
! 	if (ld_entry == NULL)
  		return NULL;
  
! 	return (ld_entry->dlsym)(fd, name);
  }
  
  int
--- 189,198 ----
  	void	*fd;
  	const char	*name;
  {
! 	if ((*ld_entry) == NULL)
  		return NULL;
  
! 	return ((*ld_entry)->dlsym)(fd, name);
  }
  
  int
***************
*** 200,209 ****
  	void	*fd, *arg;
  	int	cmd;
  {
! 	if (ld_entry == NULL)
  		return -1;
  
! 	return (ld_entry->dlctl)(fd, cmd, arg);
  }
  
  char *
--- 200,209 ----
  	void	*fd, *arg;
  	int	cmd;
  {
! 	if ((*ld_entry) == NULL)
  		return -1;
  
! 	return ((*ld_entry)->dlctl)(fd, cmd, arg);
  }
  
  char *
***************
*** 211,218 ****
  {
  	int     error;
  
! 	if (ld_entry == NULL ||
! 	    (*ld_entry->dlctl)(NULL, DL_GETERRNO, &error) == -1)
  		return "Service unavailable";
  
  	return ((char *)(error == 0 ? NULL : strerror(error)));
--- 211,218 ----
  {
  	int     error;
  
! 	if ((*ld_entry) == NULL ||
! 	    (*(*ld_entry)->dlctl)(NULL, DL_GETERRNO, &error) == -1)
  		return "Service unavailable";
  
  	return ((char *)(error == 0 ? NULL : strerror(error)));
***************
*** 223,232 ****
  	void	*addr;
  	Dl_info	*dli;
  {
! 	if (ld_entry == NULL || ld_entry->dladdr == NULL)
  		return (0);
  
! 	return (ld_entry->dladdr)(addr, dli);
  }
  
  /*
--- 223,232 ----
  	void	*addr;
  	Dl_info	*dli;
  {
! 	if ((*ld_entry) == NULL || (*ld_entry)->dladdr == NULL)
  		return (0);
  
! 	return ((*ld_entry)->dladdr)(addr, dli);
  }
  
  /*
*** gnu/usr.bin/ld/rtld/rtld.c.orig	Sun Oct 25 13:34:23 1998
--- gnu/usr.bin/ld/rtld/rtld.c	Tue Dec  8 00:37:33 1998
***************
*** 362,367 ****
--- 362,374 ----
  	LM_PRIVATE(smp)->spd_refcount++;
  	LM_PRIVATE(smp)->spd_flags |= _RTLD_RTLD;
  
+ 	/* Fill in some field in main's __DYNAMIC structure */
+ 	if (version >= CRT_VERSION_BSD_4)
+ 		crtp->crt_ldentry = &ld_entry;
+ 	else
+ 		crtp->crt_dp->d_entry = &ld_entry;
+ 
+ 
  	/* Handle LD_PRELOAD's here */
  	ld_preload_path = getenv("LD_PRELOAD");
  	if (ld_preload_path != NULL)
***************
*** 376,387 ****
  	}
  
  	init_maps(link_map_head);
- 
- 	/* Fill in some field in main's __DYNAMIC structure */
- 	if (version >= CRT_VERSION_BSD_4)
- 		crtp->crt_ldentry = &ld_entry;
- 	else
- 		crtp->crt_dp->d_entry = &ld_entry;
  
  	crtp->crt_dp->d_un.d_sdt->sdt_loaded = link_map_head->som_next;
  
--- 383,388 ----