From mail at joeconway.com  Mon Apr 28 15:29:48 2003
From: mail at joeconway.com (Joe Conway)
Date: Mon Apr 28 23:30:38 2003
Subject: [Rd] Red Hat 9 regex symbol conflict
Message-ID: <3EAD9D4C.20305@joeconway.com>

Hello,

I've been struggling with a problem for the past several weeks, trying 
to get PL/R (R procedural language handler for PostgreSQL, 
http://www.joeconway.com/plr/) to work on Red Hat 9. In brief, R dumps 
core during the embedded library initialization, while in Rf_regcomp(), 
working on on Rprofile. Below I've included the important parts of a 
backtrace:

Program received signal SIGSEGV, Segmentation fault.
0x420b655c in re_compile_fastmap_iter () from /lib/tls/libc.so.6
(gdb) bt
#0  0x420b655c in re_compile_fastmap_iter () from /lib/tls/libc.so.6
#1  0x420b649d in re_compile_fastmap () from /lib/tls/libc.so.6
#2  0x403fcb3c in Rf_regcomp (preg=0xbfffb0e0, pattern=0x8ba68a0 
"[[:blank:]]*([[:alnum:]]+)", cflags=0) at regex.c:5729

{...snipped...]

#32 0x403b1015 in R_ReplFile (fp=0x83250d8, rho=0x8321e30, savestack=0, 
browselevel=0) at main.c:80
#33 0x403b17aa in R_LoadProfile (fparg=0x0, env=0x8321e30) at main.c:385
#34 0x403b1984 in setup_Rmainloop () at main.c:503
#35 0x4045c78f in Rf_initEmbeddedR (argc=4, argv=0xbfffcdb0) at system.c:332

 From the backtrace it is apparent that re_compile_fastmap() is being 
resolved to /lib/tls/libc.so.6 instead of the function by the same name 
in R's regex.c. On the advice of Ulrich Drepper (of the Red Hat glibc 
team) I was able to get a trace of the symbol resolution process:

# start PostgreSQL with glibc debugging turned on
env LD_DEBUG=all LD_DEBUG_OUTPUT=/home/postgres/dbg.out pg_ctl start

On Red Hat 8 (where everything works), the relevant output looks like this:

   19097: symbol=re_compile_fastmap;  lookup in file=postgres: postgres
          regression [local] SELECT
   19097: symbol=re_compile_fastmap;  lookup in file=/usr/lib/libz.so.1
   19097: symbol=re_compile_fastmap;  lookup in
          file=/usr/lib/libreadline.so.4
   19097: symbol=re_compile_fastmap;  lookup in file=/lib/libtermcap.so.2
   19097: symbol=re_compile_fastmap;  lookup in file=/lib/libcrypt.so.1
   19097: symbol=re_compile_fastmap;  lookup in file=/lib/libresolv.so.2
   19097: symbol=re_compile_fastmap;  lookup in file=/lib/libnsl.so.1
   19097: symbol=re_compile_fastmap;  lookup in file=/lib/libdl.so.2
   19097: symbol=re_compile_fastmap;  lookup in file=/lib/i686/libm.so.6
   19097: symbol=re_compile_fastmap;  lookup in file=/lib/i686/libc.so.6
   19097: symbol=re_compile_fastmap;  lookup in file=/lib/ld-linux.so.2
   19097: symbol=re_compile_fastmap;  lookup in
          file=/usr/local/pgsql/lib/plr.so
   19097: symbol=re_compile_fastmap;  lookup in
          file=/usr/local/lib/R/bin/libR.so
   19097: binding file /usr/local/lib/R/bin/libR.so to
          /usr/local/lib/R/bin/libR.so:
          normal symbol `re_compile_fastmap'

On Red Hat 9 (where I get core dumps), the relevant output looks like this:

   23521: symbol=re_compile_fastmap;  lookup in file=postgres: postgres
          regression [local] SELECT
   23521: symbol=re_compile_fastmap;  lookup in file=/usr/lib/libz.so.1
   23521: symbol=re_compile_fastmap;  lookup in
          file=/usr/lib/libreadline.so.4
   23521: symbol=re_compile_fastmap;  lookup in file=/lib/libtermcap.so.2
   23521: symbol=re_compile_fastmap;  lookup in file=/lib/libcrypt.so.1
   23521: symbol=re_compile_fastmap;  lookup in file=/lib/libresolv.so.2
   23521: symbol=re_compile_fastmap;  lookup in file=/lib/libnsl.so.1
   23521: symbol=re_compile_fastmap;  lookup in file=/lib/libdl.so.2
   23521: symbol=re_compile_fastmap;  lookup in file=/lib/tls/libm.so.6
   23521: symbol=re_compile_fastmap;  lookup in file=/lib/tls/libc.so.6
   23521: binding file /usr/local/lib/R/bin/libR.so to
          /lib/tls/libc.so.6: normal symbol `re_compile_fastmap'

The problem exists when using both the 1.6.2 and 1.7.0 versions of 
libR.so. I have been unable to find a way to prevent this behavior on 
Red Hat 9 without patching R itself. The attached patch fixes the 
problem, hopefully in the least obtrusive way. It declares the 
internal-use-only regex.c functions 'static' instead of 'extern'. There 
is also a small correction to eval-etc.Rout.save which had an old copyright.

The patch is against 1.7.0, compiles cleanly, and passes `make check` on 
my Red Hat 9 machine. I'd appreciate it if you would consider it for 
inclusion in R-devel, and for any future R.1.7.x release. If a patch 
against R-devel is needed, I'd be happy to provide that. Alternatively, 
if someone can suggest a fix that I can apply directly to PL/R, I would 
also be most appreciative.

Thank you,

Joe
-------------- next part --------------
diff -cNr R-1.7.0/src/main/Rregex.h R-1.7.0.fixed/src/main/Rregex.h
*** R-1.7.0/src/main/Rregex.h	Fri May 10 00:11:47 2002
--- R-1.7.0.fixed/src/main/Rregex.h	Mon Apr 28 10:19:50 2003
***************
*** 452,463 ****
  
  /* Sets the current default syntax to SYNTAX, and return the old syntax.
     You can also simply assign to the `re_syntax_options' variable.  */
! extern reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
  
  /* Compile the regular expression PATTERN, with length LENGTH
     and syntax given by the global `re_syntax_options', into the buffer
     BUFFER.  Return NULL if successful, and an error string if not.  */
! extern const char *re_compile_pattern
    _RE_ARGS ((const char *pattern, size_t length,
               struct re_pattern_buffer *buffer));
  
--- 452,463 ----
  
  /* Sets the current default syntax to SYNTAX, and return the old syntax.
     You can also simply assign to the `re_syntax_options' variable.  */
! static reg_syntax_t re_set_syntax _RE_ARGS ((reg_syntax_t syntax));
  
  /* Compile the regular expression PATTERN, with length LENGTH
     and syntax given by the global `re_syntax_options', into the buffer
     BUFFER.  Return NULL if successful, and an error string if not.  */
! static const char *re_compile_pattern
    _RE_ARGS ((const char *pattern, size_t length,
               struct re_pattern_buffer *buffer));
  
***************
*** 465,471 ****
  /* Compile a fastmap for the compiled pattern in BUFFER; used to
     accelerate searches.  Return 0 if successful and -2 if was an
     internal error.  */
! extern int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
  
  
  /* Search in the string STRING (with length LENGTH) for the pattern
--- 465,471 ----
  /* Compile a fastmap for the compiled pattern in BUFFER; used to
     accelerate searches.  Return 0 if successful and -2 if was an
     internal error.  */
! static int re_compile_fastmap _RE_ARGS ((struct re_pattern_buffer *buffer));
  
  
  /* Search in the string STRING (with length LENGTH) for the pattern
***************
*** 473,486 ****
     characters.  Return the starting position of the match, -1 for no
     match, or -2 for an internal error.  Also return register
     information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
! extern int re_search
    _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
              int length, int start, int range, struct re_registers *regs));
  
  
  /* Like `re_search', but search in the concatenation of STRING1 and
     STRING2.  Also, stop searching at index START + STOP.  */
! extern int re_search_2
    _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
               int length1, const char *string2, int length2,
               int start, int range, struct re_registers *regs, int stop));
--- 473,486 ----
     characters.  Return the starting position of the match, -1 for no
     match, or -2 for an internal error.  Also return register
     information in REGS (if REGS and BUFFER->no_sub are nonzero).  */
! static int re_search
    _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
              int length, int start, int range, struct re_registers *regs));
  
  
  /* Like `re_search', but search in the concatenation of STRING1 and
     STRING2.  Also, stop searching at index START + STOP.  */
! static int re_search_2
    _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
               int length1, const char *string2, int length2,
               int start, int range, struct re_registers *regs, int stop));
***************
*** 488,500 ****
  
  /* Like `re_search', but return how many characters in STRING the regexp
     in BUFFER matched, starting at position START.  */
! extern int re_match
    _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
               int length, int start, struct re_registers *regs));
  
  
  /* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
! extern int re_match_2
    _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
               int length1, const char *string2, int length2,
               int start, struct re_registers *regs, int stop));
--- 488,500 ----
  
  /* Like `re_search', but return how many characters in STRING the regexp
     in BUFFER matched, starting at position START.  */
! static int re_match
    _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string,
               int length, int start, struct re_registers *regs));
  
  
  /* Relates to `re_match' as `re_search_2' relates to `re_search'.  */
! static int re_match_2
    _RE_ARGS ((struct re_pattern_buffer *buffer, const char *string1,
               int length1, const char *string2, int length2,
               int start, struct re_registers *regs, int stop));
***************
*** 512,518 ****
     Unless this function is called, the first search or match using
     PATTERN_BUFFER will allocate its own register data, without
     freeing the old data.  */
! extern void re_set_registers
    _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
               unsigned num_regs, regoff_t *starts, regoff_t *ends));
  
--- 512,518 ----
     Unless this function is called, the first search or match using
     PATTERN_BUFFER will allocate its own register data, without
     freeing the old data.  */
! static void re_set_registers
    _RE_ARGS ((struct re_pattern_buffer *buffer, struct re_registers *regs,
               unsigned num_regs, regoff_t *starts, regoff_t *ends));
  
diff -cNr R-1.7.0/src/main/regex.c R-1.7.0.fixed/src/main/regex.c
*** R-1.7.0/src/main/regex.c	Fri May 31 19:35:50 2002
--- R-1.7.0.fixed/src/main/regex.c	Mon Apr 28 10:22:30 2003
***************
*** 991,997 ****
     The argument SYNTAX is a bit mask comprised of the various bits
     defined in regex.h.  We return the old syntax.  */
  
! reg_syntax_t
  re_set_syntax (syntax)
      reg_syntax_t syntax;
  {
--- 991,997 ----
     The argument SYNTAX is a bit mask comprised of the various bits
     defined in regex.h.  We return the old syntax.  */
  
! static reg_syntax_t
  re_set_syntax (syntax)
      reg_syntax_t syntax;
  {
***************
*** 3205,3211 ****
  
     Returns 0 if we succeed, -2 if an internal error.   */
  
! int
  re_compile_fastmap (bufp)
       struct re_pattern_buffer *bufp;
  {
--- 3205,3211 ----
  
     Returns 0 if we succeed, -2 if an internal error.   */
  
! static int
  re_compile_fastmap (bufp)
       struct re_pattern_buffer *bufp;
  {
***************
*** 3511,3517 ****
     PATTERN_BUFFER will allocate its own register data, without
     freeing the old data.  */
  
! void
  re_set_registers (bufp, regs, num_regs, starts, ends)
      struct re_pattern_buffer *bufp;
      struct re_registers *regs;
--- 3511,3517 ----
     PATTERN_BUFFER will allocate its own register data, without
     freeing the old data.  */
  
! static void
  re_set_registers (bufp, regs, num_regs, starts, ends)
      struct re_pattern_buffer *bufp;
      struct re_registers *regs;
***************
*** 3541,3547 ****
  /* Like re_search_2, below, but only one string is specified, and
     doesn't let you say where to stop matching. */
  
! int
  re_search (bufp, string, size, startpos, range, regs)
       struct re_pattern_buffer *bufp;
       const char *string;
--- 3541,3547 ----
  /* Like re_search_2, below, but only one string is specified, and
     doesn't let you say where to stop matching. */
  
! static int
  re_search (bufp, string, size, startpos, range, regs)
       struct re_pattern_buffer *bufp;
       const char *string;
***************
*** 3577,3583 ****
     found, -1 if no match, or -2 if error (such as failure
     stack overflow).  */
  
! int
  re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
       struct re_pattern_buffer *bufp;
       const char *string1, *string2;
--- 3577,3583 ----
     found, -1 if no match, or -2 if error (such as failure
     stack overflow).  */
  
! static int
  re_search_2 (bufp, string1, size1, string2, size2, startpos, range, regs, stop)
       struct re_pattern_buffer *bufp;
       const char *string1, *string2;
***************
*** 3802,3808 ****
  #ifndef emacs   /* Emacs never uses this.  */
  /* re_match is like re_match_2 except it takes only a single string.  */
  
! int
  re_match (bufp, string, size, pos, regs)
       struct re_pattern_buffer *bufp;
       const char *string;
--- 3802,3808 ----
  #ifndef emacs   /* Emacs never uses this.  */
  /* re_match is like re_match_2 except it takes only a single string.  */
  
! static int
  re_match (bufp, string, size, pos, regs)
       struct re_pattern_buffer *bufp;
       const char *string;
***************
*** 3848,3854 ****
     failure stack overflowing).  Otherwise, we return the length of the
     matched substring.  */
  
! int
  re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
       struct re_pattern_buffer *bufp;
       const char *string1, *string2;
--- 3848,3854 ----
     failure stack overflowing).  Otherwise, we return the length of the
     matched substring.  */
  
! static int
  re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop)
       struct re_pattern_buffer *bufp;
       const char *string1, *string2;
***************
*** 5525,5531 ****
  
     We call regex_compile to do the actual compilation.  */
  
! const char *
  re_compile_pattern (pattern, length, bufp)
       const char *pattern;
       size_t length;
--- 5525,5531 ----
  
     We call regex_compile to do the actual compilation.  */
  
! static const char *
  re_compile_pattern (pattern, length, bufp)
       const char *pattern;
       size_t length;
diff -cNr R-1.7.0/tests/eval-etc.Rout.save R-1.7.0.fixed/tests/eval-etc.Rout.save
*** R-1.7.0/tests/eval-etc.Rout.save	Tue Apr  2 06:15:19 2002
--- R-1.7.0.fixed/tests/eval-etc.Rout.save	Mon Apr 28 10:01:37 2003
***************
*** 1,6 ****
  
! R : Copyright 2002, The R Development Core Team
! Version 1.5.0 Under development (unstable) (2002-04-02)
  
  R is free software and comes with ABSOLUTELY NO WARRANTY.
  You are welcome to redistribute it under certain conditions.
--- 1,6 ----
  
! R : Copyright 2003, The R Development Core Team
! Version 1.7.0  (2003-04-16)
  
  R is free software and comes with ABSOLUTELY NO WARRANTY.
  You are welcome to redistribute it under certain conditions.

[Rd] Red Hat 9 regex symbol conflict
Joe Conway