fastcall

pynoos의 이미지

http://coolengineer.com/133
원문은 제 블로그입니다만, 여기에도 포스팅합니다.

호출규약으로 번역되는 calling convention이라는 주제가 있다.
UNIX 쪽 C를 하는 사람들에게는 그다지 많이 다가오지 않는 주제일지 모르나, Windows 에서 프로그래밍을 하다보면, WINAPI라는 매크로를 사용할 때와 사용하지 않을 때가 있는 것을 볼 수 있는데, 저것은 실상 __stdcall 이라는 방식으로 선언하라는 것을 의미한다.

여기에는 중요한 두가지 요소가 있는데,

1. 인자 전달방식
2. 스택 청소 담당자

이다. 이런 차이에 의해 주위에서 많이 볼 수 있는 것이 다음 세가지이다.

1. cdecl
2. stdcall
3. fastcall

추가적으로 C++가 도입되면서 thiscall이라는 방식이 생겼지만, 이는 기본적으로 cdecl을 근간으로 하고 있으므로 생략하겠다. 또한 고생대의 pascal이나 far 등도 생략하겠다. 더불어 naked 로 선언되는 것도 생략한다.

cdecl과 stdcall은 다 안다고 가정하고... 흐흐흐...
요약정리하면, 스택을 비우는 책임이 cdecl은 호출자가 stdcall은 호출당한 놈이 있는데 그 차이가 있다. 이로 인해 가변인자를 처리하느냐(cdecl) 못하느냐(stdcall), 스택청소하는 코드가 곳곳에 산재하여 오브젝트 크기가 커지느냐(cdecl), 그렇지 않느냐(stdcall)의 차이로 구분할 수 있겠다.

요약한답시고 정리했지만 저게 다이므로 넘어가자. 내가 말하고 싶은 것은 fastcall이라는 것이다.
이 fastcall이라는 것은 내 기억상 borland c compiler에서 처음 도입되었다. 아니라면 지적해주시라.

아니, 도대체 뭐가 빠르단말이냐.

실상은 이렇다. 먼저 MSDN 문서를 살펴보면,
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclang/html/_core___fastcall.asp

Argument-passing order:
The first two DWORD or smaller arguments are passed in ECX and EDX registers; all other arguments are passed right to left.

아니, 이것은 스택을 이용하지 않고 레지스터를 이용하여 인자를 넘기겠다는 발상아니냐! 그렇다면, 메모리에 들어갔다 나갔다를 하지 않을 것이고, 게다가 스택을 청소하는 일도 없을 것 아닌가!
단, 두개까지만 허용한댄다.

재밌는 발상이다. 저 문서에서 더 발견할 수 있는 것은 funcion naming decoration이 추가적으로 일어난다는 것을 알 수 있다.
즉, dumpbin.exe 명령으로 확인할 수 있는 function의 심볼들 앞에 @로 시작하는 놈들은 모두 fastcall 이라는 것이다.

그렇다면, linux의 gnu c compiler에서는 어떠한가.
http://www.redhat.com/docs/manuals/enterprise/RHEL-4-Manual/gcc/function-attributes.html
에서 살펴보면, __attribute__안에 fastcall을 넣을 수가 있덴다.
그래서 사용해보니..

warning: `fastcall' attribute directive ignored

라는 좌절스러운 말이 전해온다. 그러나! 좀 위쪽에 regparm이라는 것이 있다..하하..

void __attribute__((regparm(3))) fastfunction( int x, int y, int z )
{
}

이와 같은 방식으로 사용하면 된단다. regparm(N)으로 인자를 N으로 주면 몇개는 인자를 레지스터로 넘기는 것이다.
저 문서에서 살펴보면, EAX, EDX, ECX가 사용된덴다.

심심하자나... 디스어셈블한번 해보자.

$ more a.c #include void __attribute__((regparm(3))) fastfunction( int x, int y, int z ) { printf("%d %d %d\n", x, y, z); } int main() { int x = 7; int y = 8; int z = 9; fastfunction( x, y, z ); return 0; }

야... 이거 gcc -O2 안해주면, fastcall 효과가 안난다.

$ gcc a.c -O2 -S $ vi a.s 1 .file "a.c" 2 .section .rodata.str1.1,"aMS",@progbits,1 3 .LC0: 4 .string "%d %d %d\n" 5 .text 6 .p2align 4,,15 7 .globl fastfunction 8 .type fastfunction, @function 9 fastfunction: 10 pushl %ebp 11 movl %esp, %ebp 12 subl $24, %esp 13 movl %eax, 8(%esp) 14 movl $.LC0, %eax 15 movl %eax, 4(%esp) 16 movl stdout, %eax 17 movl %ecx, 16(%esp) 18 movl %edx, 12(%esp) 19 movl %eax, (%esp) 20 call fprintf 21 movl %ebp, %esp 22 popl %ebp 23 ret 24 .size fastfunction, .-fastfunction 25 .p2align 4,,15 26 .globl main 27 .type main, @function 28 main: 29 pushl %ebp 30 movl $7, %eax 31 movl %esp, %ebp 32 subl $8, %esp 33 movl $9, %ecx 34 andl $-16, %esp 35 movl $8, %edx 36 call fastfunction 37 movl %ebp, %esp 38 xorl %eax, %eax 39 popl %ebp 40 ret 41 .size main, .-main 42 .ident "GCC: (GNU) 3.3.2"

대체 __attribute__((regparm(3))) 를 안쓰면 어떻게 되지?

1 .file "b.c" 2 .section .rodata 3 .LC0: 4 .string "%d %d %d\n" 5 .text 6 .globl fastfunction 7 .type fastfunction, @function 8 fastfunction: 9 pushl %ebp 10 movl %esp, %ebp 11 subl $24, %esp 12 movl 16(%ebp), %eax 13 movl %eax, 12(%esp) 14 movl 12(%ebp), %eax 15 movl %eax, 8(%esp) 16 movl 8(%ebp), %eax 17 movl %eax, 4(%esp) 18 movl $.LC0, (%esp) 19 call printf 20 leave 21 ret 22 .size fastfunction, .-fastfunction 23 .globl main 24 .type main, @function 25 main: 26 pushl %ebp 27 movl %esp, %ebp 28 subl $24, %esp 29 andl $-16, %esp 30 movl $0, %eax 31 subl %eax, %esp 32 movl $7, -4(%ebp) 33 movl $8, -8(%ebp) 34 movl $9, -12(%ebp) 35 movl -12(%ebp), %eax 36 movl %eax, 8(%esp) 37 movl -8(%ebp), %eax 38 movl %eax, 4(%esp) 39 movl -4(%ebp), %eax 40 movl %eax, (%esp) 41 call fastfunction 42 movl $0, %eax 43 leave 44 ret 45 .size main, .-main 46 .ident "GCC: (GNU) 3.3.2"

a.c의 fastfunction 내에서 printf에 대한 확장이 일어났었음을 감안해서 해석해야하는데, 위와 다른 것은 16(%ebp), 12(%ebp), 8(%ebp)로 접근하여 값을 가져온다는 것이다.

어, 이상하다? naming decoration (mangling)이 전혀 일어나지 않는다. 오브젝트만가지고 이것이 fastcall인지 알 수 있는 방법이 전혀없다.
MS의 오브젝트들은 dumpbin.exe의 심볼 덤프만으로 알 수 있는데 말이지.

그렇다면..? 장난한번 해볼까? 첫번째 인자가 eax로 넘어간다고 했으므로.. d.c 라는 프로그램을 다음과 같이 만들고

$ more d.c #include void __attribute__((regparm(1))) fastfunction( int x ) { printf("%d\n", x ); }

그리고 c.c라는 파일을 다음과 같이 만들되, 그 안의 fastfunction은 일반함수처럼 생기도록 선언하여 위 함수와 링크되게 한다.

$ more c.c #include void fastfunction( int x ); int main() { printf("hi?\n"); fastfunction( 0 ); printf("hello?\n"); fastfunction( 0 ); printf("I am Hojin?\n"); fastfunction( 0 ); printf("Welcome to real world\n"); fastfunction( 0 ); printf("Help me Gandalf!\n"); fastfunction( 0 ); return 0; }

원래 함수의 8 byte 이내의 크기를 가진 리턴값은 %eax에 저장되므로, printf의 return 값인 출력한 크기가 %eax에 남아 있게 되고 이것은 그대로 fastfunction에서 첫번째 인자로 동작할 것이다.

자, 위를 실행해보자..

$ ./a.out hi? 4 hello? 7 I am Hojin? 12 Welcome to real world 22 Help me Gandalf! 17

음.. 됐어. 됐어...

Forums: 
ed.netdiver의 이미지

재밌고 유익하게 잘 봤습니다. :)
그런데, 개인 blog에 적고 또 copy라니 뭔가 좀 어색하다는 느낌입니다.
원문은 다른 blog에 있고, 그걸 마치 이곳에 post한것처럼 link거는 방법은 없는걸까요?

\(´∇`)ノ \(´∇`)ノ \(´∇`)ノ \(´∇`)ノ
def ed():neTdiVeR in range(thEeArTh)

--------------------------------------------------------------------------------
\(´∇`)ノ \(´∇`)ノ \(´∇`)ノ \(´∇`)ノ
def ed():neTdiVeR in range(thEeArTh)

pynoos의 이미지

저도 어색합니다. :lol:
Drupal이 그러한 피딩기능은 제공하지 않기 때문에 어쩔 수 없는 것이죠.
RSS를 조합하여 피딩하는 다른 사이트처럼 가능하다면 괜찮겠습니다만,
다른사람이 퍼 나른것도 아니고, 저자인 제가 옮긴 것이므로 일반 펌질의 의미와는 조금 다를 것 같습니다.

저 말고도 많은 블로거들이 저와 비슷한 문제로 고민할 것도 같은데요...
일단은 실험모드로 해보려고합니다.

codebank의 이미지

아~ 어려워... 어려워... :sick:

그래도 모처럼 재미있는 글이었습니다. :-)

----------------------

좋은 하루되세요.

------------------------------
좋은 하루 되세요.

rollin96의 이미지

개인 사정으로 C++ 프로그래밍할 일이 줄어 들면서 이런 깊이 있는 예기들과 멀어졌었는데....
간만에 이런 포스트를 보니까 괜히 기분이 좋아지네요 ^^;
좋은 글 고맙습니다~

lovian의 이미지

물마시듯 순순히 이해가가는건 아니지만,
어느정도 흥미가 있던관계로 재미있게 읽었습니다.

:)
---------------------
한글을 사랑합니다.

-----------------
한글을 사랑합니다.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.