public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* An optimization bug in egcs 1.0.2?
@ 1998-03-23 15:11 H.J. Lu
  1998-03-23 19:24 ` John Carr
  0 siblings, 1 reply; 7+ messages in thread
From: H.J. Lu @ 1998-03-23 15:11 UTC (permalink / raw)
  To: egcs

Hi,

While compiling glibc 2.0.7 with egcs 1.0.2, I noticed that egcs 1.0.2
turns

if (imap->l_global)
  {
   /* This object is in the global scope list.  Remove it.  */
   struct link_map **tail = _dl_global_scope_end;
   do
     --tail;
    while (*tail != imap);
    --_dl_global_scope_end;
    memcpy (tail, tail + 1,
      (void *) _dl_global_scope_end -  (void *) tail);
    _dl_global_scope_end[0] = NULL;
    _dl_global_scope_end[1] = NULL;
  }

into

if (imap->l_global)
  {
   /* This object is in the global scope list.  Remove it.  */
   struct link_map **tail = _dl_global_scope_end;
   do
     --tail;
    while (*tail != imap);
    --_dl_global_scope_end;
    _dl_global_scope_end[0] = NULL;
    _dl_global_scope_end[1] = NULL;
    memcpy (tail, tail + 1,
      (void *) _dl_global_scope_end -  (void *) tail);
    _dl_global_scope_end[1] = NULL;
  }

when I use -O2 -fPIC. As the result, the resuling binary doesn't work
since _dl_global_scope_end[0] is within (void *) tail + 1 and 
(void *) tail + 1 + (void *) _dl_global_scope_end -  (void *) tail.
Is that normal for a C compiler to do it?

BTW, I changed it to

if (imap->l_global)
  {
   /* This object is in the global scope list.  Remove it.  */
   struct link_map **tail = _dl_global_scope_end;
   do
     --tail;
    while (*tail != imap);
    memcpy (tail, tail + 1,
      (void *) _dl_global_scope_end -  (void *) tail);
    --_dl_global_scope_end;
  }

since _dl_global_scope_end [0] and _dl_global_scope_end [1] are
always NULL to begin with. It works fine for me.

Thanks.


-- 
H.J. Lu (hjl@gnu.org)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: An optimization bug in egcs 1.0.2?
  1998-03-23 15:11 An optimization bug in egcs 1.0.2? H.J. Lu
@ 1998-03-23 19:24 ` John Carr
  1998-03-23 19:24   ` H.J. Lu
  0 siblings, 1 reply; 7+ messages in thread
From: John Carr @ 1998-03-23 19:24 UTC (permalink / raw)
  To: H.J. Lu; +Cc: egcs

The code calls memcpy with overlapping objects.  That is undefined in
ANSI C; use memmove instead.  (It also subtracts pointers to void so
the authors presumably didn't care about ANSI C, but the misuse of
memcpy is a real problem.)

There may also be a bug in egcs in addition to the bug in the code.
Can you generate a standalone test case with annotated assembly code?


> if (imap->l_global)
>   {
>    /* This object is in the global scope list.  Remove it.  */
>    struct link_map **tail = _dl_global_scope_end;
>    do
>      --tail;
>     while (*tail != imap);
>     --_dl_global_scope_end;
>     memcpy (tail, tail + 1,
>       (void *) _dl_global_scope_end -  (void *) tail);
>     _dl_global_scope_end[0] = NULL;
>     _dl_global_scope_end[1] = NULL;
>   }

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: An optimization bug in egcs 1.0.2?
  1998-03-23 19:24 ` John Carr
@ 1998-03-23 19:24   ` H.J. Lu
  1998-03-23 23:47     ` Jeffrey A Law
  1998-03-24 11:16     ` Richard Henderson
  0 siblings, 2 replies; 7+ messages in thread
From: H.J. Lu @ 1998-03-23 19:24 UTC (permalink / raw)
  To: John Carr; +Cc: egcs, Ulrich Drepper

> 
> 
> The code calls memcpy with overlapping objects.  That is undefined in
> ANSI C; use memmove instead.  (It also subtracts pointers to void so
> the authors presumably didn't care about ANSI C, but the misuse of
> memcpy is a real problem.)

It is from the dynamic linker in glibc 2. memcpy is safe there.
memcpy is used for optimization.

> 
> There may also be a bug in egcs in addition to the bug in the code.
> Can you generate a standalone test case with annotated assembly code?

I don't think there is a question on egcs. I don't want to spend
any more time on it since it has been fixed in glibc 2. If you really
want to look into it, I can do it when I find time.

> 
> 
> > if (imap->l_global)
> >   {
> >    /* This object is in the global scope list.  Remove it.  */
> >    struct link_map **tail = _dl_global_scope_end;
> >    do
> >      --tail;
> >     while (*tail != imap);
> >     --_dl_global_scope_end;
> >     memcpy (tail, tail + 1,
> >       (void *) _dl_global_scope_end -  (void *) tail);
> >     _dl_global_scope_end[0] = NULL;
> >     _dl_global_scope_end[1] = NULL;
> >   }
> 


-- 
H.J. Lu (hjl@gnu.org)

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: An optimization bug in egcs 1.0.2?
  1998-03-23 19:24   ` H.J. Lu
@ 1998-03-23 23:47     ` Jeffrey A Law
  1998-03-24 11:16     ` Richard Henderson
  1 sibling, 0 replies; 7+ messages in thread
From: Jeffrey A Law @ 1998-03-23 23:47 UTC (permalink / raw)
  To: H.J. Lu; +Cc: John Carr, egcs, Ulrich Drepper

  In message < m0yHHxm-00058JC@ocean.lucon.org >you write:
  > > 
  > > 
  > > The code calls memcpy with overlapping objects.  That is undefined in
  > > ANSI C; use memmove instead.  (It also subtracts pointers to void so
  > > the authors presumably didn't care about ANSI C, but the misuse of
  > > memcpy is a real problem.)
  > 
  > It is from the dynamic linker in glibc 2. memcpy is safe there.
  > memcpy is used for optimization.
Is the compiler producing an inline expansion of memcpy?  If so
you'll need to compile with -fno-builtin.


jeff

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: An optimization bug in egcs 1.0.2?
  1998-03-24 11:16     ` Richard Henderson
@ 1998-03-24  8:29       ` H.J. Lu
  1998-03-24  9:27       ` H.J. Lu
  1 sibling, 0 replies; 7+ messages in thread
From: H.J. Lu @ 1998-03-24  8:29 UTC (permalink / raw)
  To: rth; +Cc: egcs, drepper

> 
> On Mon, Mar 23, 1998 at 04:53:09PM -0800, H.J. Lu wrote:
> > It is from the dynamic linker in glibc 2. memcpy is safe there.
> > memcpy is used for optimization.
> 
> No, memcpy on overlapping objects is wrong.  
> 
>     memcpy (tail, tail + 1,                                                  
>       (void *) _dl_global_scope_end -  (void *) tail);                       
> 
> This may happen to work on the particular implementation
> of memcpy used on i386, but will fail for others.
> 

I was wrong. The bug was in glibc 2.0.7. Here is the code in question.
As you can see, _dl_global_scope_end is adjusted before calling
memmove, which, BTW, is not inlined by egcs 1.0.2. The dynamic linker
will then call fixup () where there is

	*_dl_global_scope_end = NULL;

As the result, when memmove is entered, memory pointed to by tail + 1
has been already changed.

Ulrich, we should go over every place in dl where _dl_global_scope_end
is used to double check if there is a similar bug.

Thanks.

-- 
H.J. Lu (hjl@gnu.org)
----
#include <string.h>
#include <link.h>

void
_dl_remove (struct link_map *map)
{
  struct link_map **tail = _dl_global_scope_end;
  do
    --tail;
  while (*tail != map);
  --_dl_global_scope_end;
  memmove (tail, tail + 1,
  	(char *) _dl_global_scope_end - (char *) tail);
  _dl_global_scope_end [0] = NULL;
  _dl_global_scope_end [1] = NULL;
}

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: An optimization bug in egcs 1.0.2?
  1998-03-24 11:16     ` Richard Henderson
  1998-03-24  8:29       ` H.J. Lu
@ 1998-03-24  9:27       ` H.J. Lu
  1 sibling, 0 replies; 7+ messages in thread
From: H.J. Lu @ 1998-03-24  9:27 UTC (permalink / raw)
  To: rth; +Cc: egcs, drepper

> 
> On Mon, Mar 23, 1998 at 04:53:09PM -0800, H.J. Lu wrote:
> > It is from the dynamic linker in glibc 2. memcpy is safe there.
> > memcpy is used for optimization.
> 
> No, memcpy on overlapping objects is wrong.  
> 
>     memcpy (tail, tail + 1,                                                  
>       (void *) _dl_global_scope_end -  (void *) tail);                       
> 
> This may happen to work on the particular implementation
> of memcpy used on i386, but will fail for others.
> 

That has nothing to do with memcpy. I extracted the miscompiled
portion from glibc 2.0.7 into a separate file. I am enclosing
the source code and the asm output. I believe something is wrong.


-- 
H.J. Lu (hjl@gnu.org)
----dl-remove.c---
#include <string.h>
#include <link.h>

void
_dl_remove (struct link_map *map)
{
  struct link_map **tail = _dl_global_scope_end;
  do
    --tail;
  while (*tail != map);
  --_dl_global_scope_end;
  memmove (tail, tail + 1,
  	(char *) _dl_global_scope_end - (char *) tail);
  _dl_global_scope_end [0] = NULL;
  _dl_global_scope_end [1] = NULL;
}
----
	.file	"dl-remove.c"
	.version	"01.01"
/ GNU C version egcs-2.90.27 980315 (egcs-1.0.2 release) (i586-unknown-linux-gnulibc1) compiled by GNU C version egcs-2.90.26 980308 (egcs-1.0.2 prerelease).
/ options passed:  -g -g0 -O2 -Wall -Winline -Wno-parentheses
/ -Wstrict-prototypes -Wwrite-strings -fno-exceptions -fPIC -fno-common
/ options enabled:  -fdefer-pop -fcse-follow-jumps -fcse-skip-blocks
/ -fexpensive-optimizations -fthread-jumps -fstrength-reduce -fpeephole
/ -fforce-mem -ffunction-cse -finline -fkeep-static-consts -fcaller-saves
/ -fpcc-struct-return -frerun-cse-after-loop -frerun-loop-opt
/ -fschedule-insns2 -fPIC -fverbose-asm -fgnu-linker -fregmove
/ -falias-check -fargument-alias -m80387 -mhard-float -mno-soft-float
/ -mieee-fp -mfp-ret-in-387 -mcpu=pentium -march=pentium

gcc2_compiled.:
.text
	.align 4
.globl _dl_remove
	.type	 _dl_remove,@function
_dl_remove:
	pushl %ebp
	movl %esp,%ebp
	pushl %ebx
	call .L9
.L9:
	popl %ebx
	addl $_GLOBAL_OFFSET_TABLE_+[.-.L9],%ebx
	movl _dl_global_scope_end@GOT(%ebx),%eax
	movl 8(%ebp),%ecx
	movl (%eax),%edx
	.align 4
.L5:
	addl $-4,%edx
	cmpl %ecx,(%edx)
	jne .L5
	movl _dl_global_scope_end@GOT(%ebx),%eax
	addl $-4,(%eax)
	movl _dl_global_scope_end@GOT(%ebx),%eax
	movl (%eax),%eax
	subl %edx,%eax
	pushl %eax
	leal 4(%edx),%eax
	pushl %eax
	pushl %edx
	call memmove@PLT
	movl _dl_global_scope_end@GOT(%ebx),%eax
	movl (%eax),%eax
	movl $0,(%eax)
	movl _dl_global_scope_end@GOT(%ebx),%eax
	movl (%eax),%eax
	movl $0,4(%eax)
	movl -4(%ebp),%ebx
	movl %ebp,%esp
	popl %ebp
	ret
.Lfe1:
	.size	 _dl_remove,.Lfe1-_dl_remove
	.ident	"GCC: (GNU) egcs-2.90.27 980315 (egcs-1.0.2 release)"

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: An optimization bug in egcs 1.0.2?
  1998-03-23 19:24   ` H.J. Lu
  1998-03-23 23:47     ` Jeffrey A Law
@ 1998-03-24 11:16     ` Richard Henderson
  1998-03-24  8:29       ` H.J. Lu
  1998-03-24  9:27       ` H.J. Lu
  1 sibling, 2 replies; 7+ messages in thread
From: Richard Henderson @ 1998-03-24 11:16 UTC (permalink / raw)
  To: H.J. Lu; +Cc: egcs, Ulrich Drepper

On Mon, Mar 23, 1998 at 04:53:09PM -0800, H.J. Lu wrote:
> It is from the dynamic linker in glibc 2. memcpy is safe there.
> memcpy is used for optimization.

No, memcpy on overlapping objects is wrong.  

    memcpy (tail, tail + 1,                                                  
      (void *) _dl_global_scope_end -  (void *) tail);                       

This may happen to work on the particular implementation
of memcpy used on i386, but will fail for others.


r~

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~1998-03-24 11:16 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
1998-03-23 15:11 An optimization bug in egcs 1.0.2? H.J. Lu
1998-03-23 19:24 ` John Carr
1998-03-23 19:24   ` H.J. Lu
1998-03-23 23:47     ` Jeffrey A Law
1998-03-24 11:16     ` Richard Henderson
1998-03-24  8:29       ` H.J. Lu
1998-03-24  9:27       ` H.J. Lu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).