C에서 main 보다 먼저 호출되는 함수 만들기

pynoos의 이미지

아래 글은 http://writely.com/ 에서 작성하여 http://kldp.org/xmlrpc.php 를 통하여 포스팅한 글입니다. Full HTML로 속성을 수정했습니다. 테스트 겸 중복 포스팅이네요..
원문은 http://coolengineer.com/135 입니다.

C++ 언어는 그 특성상, 전역 개체의 초기화가 main 보다 먼저 이루어지므로 전역 개체의 생성자에 들어 있는 코드는 main 보다 먼저 호출된다.
이것은 여러가지 트릭으로 사용될 수 있는데, C에는 과연 그런 것이 없을까? 표준 명세에는 없다.
하지만, gcc의 __attribute__에는 그러한 일을 가능하게 해주는 지시자가 있는데, 바로 다음과 같이 사용된다.

void __attribute__((constructor)) before_main( void )
{
/* Things to do before main function */
}



또한 main 뒤에 호출되는 전형적인 방식은 atexit에 등록하는 것인데, 이것또한

void __attribute__((destructor)) after_main( void )
{
/* Things to do after main function */
}



와 같은 방식으로 호출된다.

간단한 샘플을 돌려보자면,

$ more c.c
#ifdef STRIP_ATTR
#define __attribute__(x)
#endif
void __attribute__((constructor)) before_main( void )
{
printf("I miss you Lorthlorien ever beauty.\n");
}

void __attribute__((constructor)) before_main_2nd( void )
{
printf("Bombadil, where have you been in the morning?\n");
}

void __attribute__((destructor)) after_main( void )
{
printf("Mithlandir, help me!\n");
}

int main()
{
printf("I am working, no touch!\n");
return 0;
}

$ gcc -o c1 -save-temps c.c
$ ./c1
Bombadil, where have you been in the morning?
I miss you Lorthlorien ever beauty.
I am working, no touch!
Mithlandir, help me!



근사하지 않은가? 다만 의심가는 것은 __attribute((constructor))로 지정한 함수들이 두 개 이상일때의 순서는 어떠하냐는 것인데, 외관상 stack형식으로 먼저 발견되는 것이 나중에 실행되는 것 같다.
자세한 것은 추후에 설명하지도 모르겠다.

이것이 수행되는 원리를 좀더 파헤쳐보자면,

우선 위 코드상에 STRIP_ATTR이라고 되어 있는 부분이 수행되도록하면 평범한 함수가 되는데, 그렇게 생성된 바이너리(c2)의 심볼들을 살펴보자.
nm 은 -n 옵션을 주어 번지로 정렬되도록 하였다.
두 nm 결과를 비교하는데는 unified diff를 사용하여 보자.
그리고 -save-temps 를 통해 생성되는 c.s 라는 어셈블리어 파일은 각각 c1.s c2.s로 이름을 바꾸는 과정이 들어가 있다.

$ mv c.s c1.s
$ gcc -o c1 c.c
$ gcc -o c2 -DSTRIP_ATTR -save-temps c.c
$ mv c.s c2.s
$ nm -n c1 > c1.nm
$ nm -n c2 > c2.nm
$ diff -u c1.nm c2.nm
--- c1.nm Thu Jan 5 13:29:16 2006
+++ c2.nm Thu Jan 5 13:29:20 2006
@@ -27,14 +27,14 @@
0804952c r __FRAME_END__
08049530 D _DYNAMIC
080495f8 d __CTOR_LIST__
-08049604 d __CTOR_END__
-08049608 d __DTOR_LIST__
-08049610 d __DTOR_END__
-08049614 d __JCR_END__
-08049614 d __JCR_LIST__
-08049618 D _GLOBAL_OFFSET_TABLE_
-08049630 A __bss_start
-08049630 A _edata
-08049630 b completed.1
-08049634 b object.2
-0804964c A _end
+080495fc d __CTOR_END__
+08049600 d __DTOR_LIST__
+08049604 d __DTOR_END__
+08049608 d __JCR_END__
+08049608 d __JCR_LIST__
+0804960c D _GLOBAL_OFFSET_TABLE_
+08049624 A __bss_start
+08049624 A _edata
+08049624 b completed.1
+08049628 b object.2
+08049640 A _end



이것을 잘 보아하니 __CTOR_LIST__ 라는 값까지는 같고 그 뒤가 달라지는 것을 볼 수가 있다.
아! __CTOR_LIST__에 내용이 채워지면서 조금씩 그 뒤로 밀려나는구나!

그럼 이 둘의 어셈블코드의 차이는 어떠할까?

$ diff -u c1.s c2.s
--- c1.s Thu Jan 5 13:31:04 2006
+++ c2.s Thu Jan 5 13:31:13 2006
@@ -15,9 +15,6 @@
leave
ret
.size before_main, .-before_main
- .section .ctors,"aw",@progbits
- .align 4
- .long before_main
.section .rodata
.align 32
.LC1:
@@ -34,9 +31,6 @@
leave
ret
.size before_main_2nd, .-before_main_2nd
- .section .ctors
- .align 4
- .long before_main_2nd
.section .rodata
.LC2:
.string "Mithlandir, help me!\n"
@@ -52,9 +46,6 @@
leave
ret
.size after_main, .-after_main
- .section .dtors,"aw",@progbits
- .align 4
- .long after_main
.section .rodata
.LC3:
.string "I am working, no touch!\n"



아앗! 이것은!
단지 section .ctor, .dtor에 함수 포인터만 추가하는 일을 하는 것 아닌가.
그렇다면, .ctor를 찾아서 하나씩 호출하는 부분은 어디에 있다는 것이지? ((계속))

댓글

ssif의 이미지

앗 신기하네요+_+ 다음편이 기대됩니다.
덤으로 어셈블코드를 이해 못해서,어셈블을 공부 할 필요성이 생기게되었습니다.
봄들판에서다

봄들판에서다

pynoos의 이미지

생각도 않고 가져왔는데, 마지막에 ((계속))이 있었군요..

youngminny의 이미지

재미있기는 한데...

家和萬事成

rgbi3307의 이미지

좋은 글 잘 봤습니다.
그런데, 소스 판독성 유지보수관리성 측면에서 꼭 필요할 것 같진 않은데,
C언어에서 main() 함수 이전에 이렇게 꼭 처리해야 하는 것들이 있을까요?
있다면 아주 유용할 듯...

From:
*알지비 (메일: rgbi3307(at)nate.com)
*커널연구회(http://www.kernel.bz/) 내용물들을 만들고 있음.
*((공부해서 남을 주려면 남보다 더많이 연구해야함.))

mirheekl의 이미지

가령 (실질적인 예가 있을진 모르겠지만) main함수를 이미 컴파일된 상태로 공급받는다든지. main함수가 들어있는 소스 파일 자체를 어떤 이유로든 수정하고 싶지 않다든지 뭐 이런 경우들 말이죠.

(좀 더 생각해보니 이런 것들도 컨스트럭터 없이 해결 가능할것 같군요.)

이런 경우가 아니라면, 컨스트럭터의 경우에는 그것 자체만으로는 뚜렷한 용도는 많지 않을 듯 합니다. shared library를 로드하기 전에 실행된다고 하니 여기서 뭔가 더 잇점이 생길 수는 있을 것 같네요.

요컨대 "컨스트럭터 없으면 절대 안돼" 하는 상황은 별로 없어 보입니다. 쓰면 더 편한 경우는 있겠습니다만.

반면에, 디스트럭터의 경우에는 여러 가지 사정으로 인해 일반적인 방법으로는 보장받기 힘든 자원 반납 등을 안정적으로 처리할 수 있는 요긴한 수단이 될 것으로 보이고,

어쨌든 디스트럭터를 쓰기로 결정했다면, 거기서 해제하는 자원은 처음부터 컨스트럭터에서 할당받는 게 아무래도 일관성도 있고 관리하기도 편하겠지요.

--

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.