Source code:
--------------------------------
/* hello.c */
#include <stdio.h>
extern int hello_init() __attribute__ ((section(".init")));
extern int hello_finit() __attribute__ ((section(".finit")));
int hello_init()
{
printf("init hello\n");
return 0;
}
int hello()
{
printf("hello world\n");
return 0;
}
int hello_finit()
{
printf("finit hello\n");
return 0;
}
/* main.c */
int hello();
int main()
{
hello();
return 0;
}
Test result:
----------------------
ender@localhost ~/tmp
$ gcc -shared -Wl,-soname,libhw.so.1 -fPIC hello.c -o libhw.so.1
root@localhost ~/tmp
$ ln -s libhw.so.1 libhw.so
ender@localhost ~/tmp
$ gcc -L. -lhw main.c
ender@localhost ~/tmp
$ LD_LIBRARY_PATH=. ./a.out
init hello
Segmentation fault
Damn it, "Segmentation fault". Thanks for goole, I found this page
later,
http://www.flipcode.com/cgi-bin/fcarticles.cgi?show=63896
and then rewrite my hello.c:
#include <stdio.h>
int hello_init()
{
__asm__ (".section .init \n call hello_init \n .section .text\n");
printf("init hello\n");
return 0;
}
int hello()
{
printf("hello world\n");
return 0;
}
int hello_finit()
{
__asm__ (".section .finit \n call hello_finit \n .section .text\n");
printf("finit hello\n");
return 0;
}
And this is the test result:
---------------------------------------
ender@localhost ~/tmp
$ gcc -shared -Wl,-soname,libhw.so.1 -fPIC hello.c -o libhw.so.1
ender@localhost ~/tmp
$ LD_LIBRARY_PATH=. ./a.out
init hello
hello world
It won't segmentation fault now, but where is the hello_fini()? Seems
that it didn't run.
Could anybody tell my why, or show me a piece of demo code? Any telp
will be appreciated.
PS. I have also send this post to comp.lang.c yesterday, but they told
me it's off-topic there, and it's better to move the post to this
forum. So please be kindly forgive me if it's off-topic here too.
> I have writen following demo code, but it doesn't work :(
That's because you are doing it the wrong way.
> /* hello.c */
> #include <stdio.h>
> extern int hello_init() __attribute__ ((section(".init")));
> extern int hello_finit() __attribute__ ((section(".finit")));
Never do that. You can't just willi-nilly switch sections,
and other parts of glibc depend on certain things being initialized
from .init, which your code effectively disables, hence the crash.
Do this instead:
extern int hello_init() __attribute__ ((constructor));
extern int hello_finit() __attribute__ ((destructor));
> $ gcc -shared -Wl,-soname,libhw.so.1 -fPIC hello.c -o libhw.so.1
You don't need to muck about with SONAME, symlinks and
LD_LIBRARY_PATH just for this simple test. This gives you the
same result:
gcc -shared -fPIC hello.c -o hello.so
gcc main.c ./hello.so
> Damn it, "Segmentation fault". Thanks for goole, I found this page
> http://www.flipcode.com/cgi-bin/fcarticles.cgi?show=63896
The case of the blind leading the deaf :-(
Ignore that hackery and just do it proper way.
Cheers,
--
In order to understand recursion you must first understand recursion.
Remove /-nsp/ for email.
This works for me:
/* --- file ender.c --- */
#include <stdio.h>
int hello_init()
{
printf("init hello\n");
return 0;
}
int hello()
{
printf("hello world\n");
return 0;
}
int hello_fini()
{
printf("fini hello\n");
return 0;
}
/* --- file endertst.c --- */
int main()
{
hello();
return 0;
}
Now create the shared lib 'libender.so' from ender.c:
gcc -fPIC -shared -o libender.so
-Wl,-init,hello_init,-fini,hello_fini ender.c
Add the current directory to the Library search path
so that libender.so will be found by the linker:
export LD_LIBRARY_PATH=`pwd`
Later 'libender.so' should be moved to one of the
std lib dirs (/usr/lib, etc) searched by the linker.
Now compile the test program 'endertst', linking
it with libender.so:
gcc -o endertst -lender endertst.c
Now run the test program:
./endertst
Producing this output:
init hello
hello world
fini hello
Aaah, I didn't know that glibc depended on ".init".
So the example compile line below is the wrong approach?
gcc .... -Wl,-init,hello_init,-fini,hello_fini ...
Thanks for the info on "__attribute__ ((constructor))"
and friend.
Larry
And I have another question. Suppose we have multiple
constructors/destructors in multiple files, is there any desired
calling order for them? I can not find any description for this
subject in "info gcc".
Thanks in advance.
>> extern int hello_init() __attribute__ ((constructor));
>> extern int hello_finit() __attribute__ ((destructor));
>
> And I have another question. Suppose we have multiple
> constructors/destructors in multiple files, is there any desired
> calling order for them?
Generally, you can't depend on the order anymore than you can depend
on the order of construction for global C++ objects.
In practice, absent linker optimizations, on ELF they'll be called in
the (possibly reversed) order that objects appear on the link line.
> I can not find any description for this subject in "info gcc".
You are out of 'gcc' domain at that point, and into 'ld' domain.
But 'info ld' will give you any info either :-(
> Paul Pluzhnikov wrote:
>> Never do that. You can't just willi-nilly switch sections,
>> and other parts of glibc depend on certain things being initialized
>> from .init, which your code effectively disables, hence the crash.
>>
> Aaah, I didn't know that glibc depended on ".init".
It does (at least for some purposes), as can be observed by
disassembling the .init section:
(gdb) x/10i 0x00002aaaaaaad630
0x2aaaaaaad630 <_init>: sub $0x8,%rsp
0x2aaaaaaad634 <_init+4>: callq 0x2aaaaaaad680 <call_gmon_start>
0x2aaaaaaad639 <_init+9>: callq 0x2aaaaaaad6f0 <frame_dummy>
0x2aaaaaaad63e <_init+14>: callq 0x2aaaaaaad7a0 <__do_global_ctors_aux>
0x2aaaaaaad643 <_init+19>: add $0x8,%rsp
0x2aaaaaaad647 <_init+23>: retq
But it turns out that I mis-diagnosed the exact cause of the crash
in the OP test: when hello_init() is put into .init section,
the disassembly becomes:
0x2aaaaaaad618 <_init>: sub $0x8,%rsp
0x2aaaaaaad61c <_init+4>: callq 0x2aaaaaaad680 <call_gmon_start>
0x2aaaaaaad621 <_init+9>: callq 0x2aaaaaaad6f0 <frame_dummy>
0x2aaaaaaad626 <hello_init>: push %rbp
0x2aaaaaaad627 <hello_init+1>: mov %rsp,%rbp
0x2aaaaaaad62a <hello_init+4>: lea 429(%rip),%rdi # 0x2aaaaaaad7de
0x2aaaaaaad631 <hello_init+11>: callq 0x2aaaaaaad658 <puts@plt>
0x2aaaaaaad636 <hello_init+16>: mov $0x0,%eax
0x2aaaaaaad63b <hello_init+21>: leaveq
# the following return will return to "garbage", because stack
# has not been incremented to compensate for instruction at 0x2aaaaaaad618
0x2aaaaaaad63c <hello_init+22>: retq
0x2aaaaaaad63d <hello_init+23>: callq 0x2aaaaaaad780 <__do_global_ctors_aux>
# this is the adjustment and the "original" return:
0x2aaaaaaad642 <hello_init+28>: add $0x8,%rsp
0x2aaaaaaad646 <hello_init+32>: retq
Above is (obviously) x86_64, but I am pretty sure the same thing
would happen on x86 as well.
> So the example compile line below is the wrong approach?
>
> gcc .... -Wl,-init,hello_init,-fini,hello_fini ...
Well, with this approach, none of:
call_gmon_start(), frame_dummy(), __do_global_ctors_aux()
will happen.
This means no C++ global constructors for one thing, and no calls
to functions marked with __attribute__((constructor)) for another.
If you control *all* code in that DSO, and know what the consequences
are, then there is nothing particularly wrong with that approach. But
using __attribute__((constructor)) is simpler, and eliminates
negatives; so it is preferred way to do this.