Discussion:
Problem of execution order of entries in .fini_array
Jing Yu
2009-04-15 23:08:17 UTC
Permalink
Hi,

I am debugging one GCC-4.4 dejaGNU test case that fails on ARM-eabi target.
I build the toolchain with newlib and run the test on GNU arm-sim simulator.

The test case gcc/testsuite/gcc.dg/initpri1.c is testing whether the
compiler and linker process the constructor attribute and destructor
attribute correctly.

In initpri1.c, we see
void c1() __attribute__((constructor (500)));
void c2() __attribute__((constructor (700)));
void c3() __attribute__((constructor (600)));
void d1() __attribute__((destructor (500)));
void d2() __attribute__((destructor (700)));
void d3() __attribute__((destructor (600)));
void cd4() __attribute__((constructor (800), destructor (800)));

The correct execution order should be:
c1() -> c3() -> c2() -> cd4() -> cd4() -> d2() -> d3() -> d1()

However, on ARM-eabi target (GCC-4.4 + binutils-2.19 + newlib), the
sequence is wrong:
c1() -> c3() -> c2() -> cd4() -> d1() -> d3() -> d2() -> cd4()

I have checked both the assembly file (generated by arm-eabi-gcc -S)
and object dumped file (generated by arm-eabi-objdump -D). GCC puts
all constructors in .init_array section in increasing order and all
destructors in .fini_array section in increasing order.

Disassembly of section .init_array:

00013b10 <__init_array_start>:
13b10: 000081b8 .word 0x000081b8 <---- c1
13b14: 00008260 .word 0x00008260 <---- c3
13b18: 0000820c .word 0x0000820c <---- c2
13b1c: 000083b0 .word 0x000083b0 <--- cd4

00013b20 <__frame_dummy_init_array_entry>:
13b20: 00008058 .word 0x00008058 <--- frame_dummy

Disassembly of section .fini_array:

00013b24 <__do_global_dtors_aux_fini_array_entry>:
13b24: 00008018 .word 0x00008018 <-- __do_global_dtors_aux
13b28: 000082b4 .word 0x000082b4 <--- d1
13b2c: 0000834c .word 0x0000834c <--- d3
13b30: 00008300 .word 0x00008300 <--- d2
13b34: 000083b0 .word 0x000083b0 <--- cd4

However, newlib accesses entries in .fini_array in increasing order
too. So that we see a wrong order of destructors.

000084f8 <__libc_init_array>:
it calls entries in <__init_array_start> in increasing order.

000084b0 <__libc_fini_array>:
it calls entries in <__do_global_dtors_aux_fini_array_entry> in
increasing order.

000080ac <_mainCRTStartup>:
...
call <atexit> with argument <__libc_fini_array>
call <__libc_init_array>
call <main>
call <exit>

After discussing with a few GCC gurus, I was told this maybe a bug in
newlib. Because ELF specification
(http://www.caldera.com/developers/gabi/2003-12-17/ch5.dynamic.html#init_fini)
says:
"The functions pointed to in the arrays specified by DT_INIT_ARRAY and
by DT_PREINIT_ARRAY are executed by the dynamic linker in the same
order in which their addresses appear in the array; those specified by
DT_FINI_ARRAY are executed in reverse order."

The code in newlib which takes care of the fini_array is in
newlib/libc/misc/init.c:

/* Run all the cleanup routines. */
void
__libc_fini_array (void)
{
size_t count;
size_t i;

count = __fini_array_end - __fini_array_start;
for (i = 0; i < count; i++) <--------- This is the place!
__fini_array_start[i] ();

_fini ();
}

Should we change the access order or fini_array?

Thanks,
Jing
Hans-Peter Nilsson
2009-04-16 10:40:26 UTC
Permalink
X-SWARE-Spam-Status: No, hits=-0.4 required=5.0 tests=AWL,BAYES_00,SARE_MSGID_LONG40,SPF_PASS
X-Spam-Check-By: sourceware.org
MIME-Version: 1.0
Date: Wed, 15 Apr 2009 16:08:17 -0700
(Long analysis cut short)
Should we change the access order or fini_array?
I'd suggest correcting the access order, as opposed to hacking
the linker. See for example glibc.

(Note that there are *three* files in newlib doing the access in
the wrong order. And I'm not counting the misguided
newlib/libc/sys/arm/crt0.S in which fini_array is treated like a
synonym for _fini!)

brgds, H-P
Jeff Johnston
2009-04-16 19:20:00 UTC
Permalink
Post by Hans-Peter Nilsson
X-SWARE-Spam-Status: No, hits=-0.4 required=5.0 tests=AWL,BAYES_00,SARE_MSGID_LONG40,SPF_PASS
X-Spam-Check-By: sourceware.org
MIME-Version: 1.0
Date: Wed, 15 Apr 2009 16:08:17 -0700
(Long analysis cut short)
Should we change the access order or fini_array?
I'd suggest correcting the access order, as opposed to hacking
the linker. See for example glibc.
(Note that there are *three* files in newlib doing the access in
the wrong order. And I'm not counting the misguided
newlib/libc/sys/arm/crt0.S in which fini_array is treated like a
synonym for _fini!)
brgds, H-P
Agreed. I have checked in a patch for misc/init.c. Where are the other
2 accesses?

-- Jeff J.
Jing Yu
2009-04-16 22:30:53 UTC
Permalink
I find under newlib/libc/sys/linux/dl directory, the dl-*.* files are
copied from glibc/libc/elf/ directory. These dl-* files have been
updated in glibc, but not in newlib.

I only search for "FINI", and find the following two places that may
need to change.

newlib/libc/sys/linux/dl/dl-close.c:
void internal_function _dl_close (void *_map)
{
...
if (imap->l_info[DT_FINI_ARRAY] != NULL)
{
ElfW(Addr) *array =
(ElfW(Addr) *) (imap->l_addr
+ imap->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
unsigned int sz = (imap->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
/ sizeof (ElfW(Addr)));
unsigned int cnt;

for (cnt = 0; cnt < sz; ++cnt)
<----------- changed in new glibc
((fini_t) (imap->l_addr + array[cnt])) ();
<------------changed
}
...

In glibc/libc/elf/dl-close.c, the arrowed two lines have been changed into:
while (sz-- > 0)
((fini_t) array[sz]) ();


newlib/libc/sys/linux/dl/dl-fini.c:
void internal_function _dl_fini (void)
{
...
if (l->l_info[DT_FINI_ARRAY] != NULL)
{
ElfW(Addr) *array =
(ElfW(Addr) *) (l->l_addr
+ l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);
unsigned int sz =
(l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val <---Changed
/ sizeof (ElfW(Addr)));
unsigned int cnt; <----- changed

for (cnt = 0; cnt < sz; ++cnt) <-----------
Changed in new glibc
((fini_t) (l->l_addr + array[cnt])) (); <----------- Changed
}

/* Next try the old-style destructor. */
if (l->l_info[DT_FINI] != NULL)
((fini_t) DL_DT_FINI_ADDRESS (l, l->l_addr +
l->l_info[DT_FINI]->d_un.d_ptr)) ();
}
}

The arrowed four lines have been updated in glibc/libc/elf/dl-fini.c:

unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val
/ sizeof (ElfW(Addr)));
while (i-- > 0)
((fini_t) array[i]) ();


Thanks,
Jing
Post by Hans-Peter Nilsson
Received-SPF: none (bart: domain of
sender hosts) client ip=209.132.176.174;
helo=sourceware.org;
X-SWARE-Spam-Status: No, hits=-0.4 required=5.0
tests=AWL,BAYES_00,SARE_MSGID_LONG40,SPF_PASS
X-Spam-Check-By: sourceware.org
MIME-Version: 1.0
Date: Wed, 15 Apr 2009 16:08:17 -0700
(Long analysis cut short)
Should we change the access order or fini_array?
I'd suggest correcting the access order, as opposed to hacking
the linker. See for example glibc.
(Note that there are *three* files in newlib doing the access in
the wrong order. And I'm not counting the misguided
newlib/libc/sys/arm/crt0.S in which fini_array is treated like a
synonym for _fini!)
brgds, H-P
Agreed. I have checked in a patch for misc/init.c. Where are the other 2
accesses?
-- Jeff J.
Loading...