3.2. Function call with stack switch

Stack »ç¿ë·®À» ¾î´À Á¤µµ ÀÌÇÏ·Î º¸ÀåÇϱâ À§Çؼ­ ¾î¶² ±â´ÉµéÀº stackÀ» ¹Ù²Ù¾î °¡¸é ½ÇÇàÇÏ´Â °æ¿ì°¡ ÀÖ½À´Ï´Ù. ¿¹¸¦ µé¾î, OS¿¡¼­ interrupt handler¿Í ±×¿¡ µû¶ó ½ÇÇàµÇ´Â bottom half handlerµéÀÇ °æ¿ì interrupt ¹ß»ý½ÃÀÇ kernel stack¿¡¼­ ½ÇÇàµÈ´Ù¸é interrupt nesting µîÀ» »ý°¢ÇÒ ¶§ ¸ðµç kernel threadÀÇ stack Å©±â°¡ ²Ï Ä¿Á®¾ß Çϴµ¥´Ù°¡ ÇÊ¿äÇÑ Å©±â¸¦ Á¤È®È÷ ¾Ë±âµµ Èûµì´Ï´Ù.

¾Æ·¡ÀÇ ÇÁ·Î±×·¥¿¡¼­ call_with_stack_switch´Â funcaddr·Î ÁÖ¾îÁø ÇÔ¼ö¸¦ arg¸¦ ÀÎÀÚ·Î ÇØ¼­ altstack¿¡¼­ ¼öÇàÇÏ°í ±× °á°ú°ªÀ» µ¹·ÁÁÝ´Ï´Ù.


#include <stdio.h>

#define fetch_esp(_esp) \
	__asm__ __volatile__("movl %%esp, %0" : "=g" (*_esp))

static __inline__ int
call_with_stack_switch(void *funcaddr, unsigned int arg, void *altstack)
{
	int a, b, c, d, D, S;

	__asm__ __volatile__(
		"pushl	%%ebp		\n\t"
		"movl	%%esp, %%eax	\n\t"
		"movl	%8, %%esp	\n\t"
		"pushl	%%eax		\n\t"
		"pushl	%7		\n\t"
		"call	*%6		\n\t"
		"addl	$4, %%esp	\n\t"
		"popl	%%esp		\n\t"
		"popl	%%ebp		"
		: "=&a" (a), "=b" (b), "=c" (c), "=d" (d), "=D" (D), "=S" (S)
		: "r" (funcaddr), "ri" (arg), "ri" (altstack));

		return a;
}

static int
say_hello(unsigned int arg)
{
	unsigned esp;

	fetch_esp(&esp);

	printf("say_hello : hello world... esp=%08x, arg=%d\n", esp, arg);
	arg *= arg;
	printf("say_hello : returning %d\n", arg);
	return arg;
}

static char _altstack[8192];
static void *altstack = _altstack + sizeof(_altstack);

int
main(void)
{
	unsigned esp;
	int rv, arg = 1096;

	fetch_esp(&esp);
	printf("main      : current esp=%08x, altstack=%08p-%08p\n",
	       esp, _altstack, altstack);
	printf("main      : calling say_hello w/ stack switch (arg=%d)\n",
	       arg);

	rv = call_with_stack_switch(say_hello, arg, altstack);

	fetch_esp(&esp);

	printf("main      : esp=%08x, arg=%d, rv=%d\n", esp, arg, rv);
	return 0;
}
      

call_with_stack_switch¿¡¼­ 6°³ÀÇ º¯¼ö°¡ ¼±¾ðµÇ¾î Àִµ¥ ÀÌ º¯¼öµéÀº ¸ðµÎ ·¹Áö½ºÅ͵éÀÇ outputÀ¸·Î ¾²ÀÔ´Ï´Ù. a:eax, b:ebx, c:ecx, d:edx, D:edi, S:edi·Î ´ëÀÀÀÌ µË´Ï´Ù. a¿Ü¿¡´Â outputÀ̶ó°í Á¤ÀÇµÈ ÈÄ ¾²ÀÌÁö ¾Ê´Âµ¥, ´ÜÁö ±× ·¹Áö½ºÅ͵éÀÇ °ªÀÌ ¹Ù²ï´Ù´Â °ÍÀ» ÄÄÆÄÀÏ·¯¿¡°Ô ¾Ë·ÁÁÖ´Â ¿ªÈ°¸¸À» ÇϰԵ˴ϴÙ. Clobber list¿Í °ÅÀÇ °°Àº ±â´ÉÀ̶ó°í ÇÒ ¼ö ÀÖÁö¸¸ clobber·Î ÁöÁ¤µÈ ·¹Áö½ºÅÍ´Â input, output ¾î´À°ÍÀ¸·Îµµ ¾²ÀÏ ¼ö ¾ø°í À§ÀÇ inline assembly¿¡ ÀÖ´Â ¼¼°³ÀÇ input º¯¼öµéÀÌ ±× ·¹Áö½ºÅÍ·Î ÇÒ´çµÉ ¼ö ¾ø°ÔµË´Ï´Ù. Áï, dummy º¯¼ö¸¦ ½á¼­ outputÀ¸·Î Á¤ÇØÁÖ°Ô µÇ¸é 'inputÀ¸·Î ÇÒ´çµÉ ¼ö ÀÖÁö¸¸ °á°úÀûÀ¸·Î °ªÀº º¯ÇÑ´Ù'¶ó´Â ¶æÀÔ´Ï´Ù.

°¢ ¶óÀÎÀ» »ìÆìº¸µµ·Ï ÇϰڽÀ´Ï´Ù.


 1: pushl	%%ebp

inline assemblyÀÇ ¾Õ°ú ³¡¿¡¼­ ebp¸¦ ÀúÀåÇÏ°í º¹±¸Çϴµ¥ ebp´Â ix86¿¡¼­ frame pointer·Î ¾²ÀÔ´Ï´Ù. ¸¸¾à -fomit-frame-pointer ¿É¼ÇÀ» ÁÖÁö¾Ê°í ÄÄÆÄÀÏÇϸé frame pointerÀÇ °ü¸®°¡ ÇÔ¼ö entry/exit¿¡¼­ µÇ¾î ½Å°æ ¾µ Çʿ䰡 ¾øÁö¸¸ frame pointer¸¦ »ý·«ÇÏ°Ô µÇ¸é ÄÄÆÄÀÏ·¯°¡ ebp¸¦ ´Ù¸¥ ¿ëµµ·Î ¾²°ÔµË´Ï´Ù. ÇÏÁö¸¸ gcc¿¡°Ô ebp°¡ º¯ÇÔÀ» ¾Ë·ÁÁÙ ¹æ¹ýÀÌ ¾ø±â¶§¹®¿¡ ÄÄÆÄÀÏ·¯°¡ ¸ð¸£´Â ü·Î ebpÀÇ °ªÀÌ ¹Ù²î¾î ¹ö¸± ¼ö°¡ ÀÖ½À´Ï´Ù. µû¶ó¼­ ´Ù¸¥ ·¹Áö½ºÅ͵é°ú ´Þ¸® µû·Î ÀúÀå/º¹±¸ ÇØ ÁÙ Çʿ䰡 ÀÖ½À´Ï´Ù.

ebp¿Í gcc¿¡ ´ëÇØ Á¶±Ý ´õ ¼³¸íÇϰڽÀ´Ï´Ù. ebp´Â ¿ÏÀüÇÑ ¹ü¿ë ·¹Áö½ºÅÍ´Â ¾Æ´ÏÁö¸¸ ´ëºÎºÐÀÇ ÁÖ¼Ò°è»ê¿¡ »ç¿ëµÉ ¼ö ÀÖ°í °ªµéÀ» Àá½Ã ÀúÀåÇÏ´Â Àå¼Ò·Îµµ »ç¿ëµÉ ¼ö ÀÖ½À´Ï´Ù. gcc´Â frame pointer·Î ¾²Áö ¾ÊÀ» ¶§ ebp¸¦ ÀÌ·± ¿ëµµ·Î »ç¿ëÇÏÁö¸¸ input/output¿¡¼­ Á÷Á¢ ebp¸¦ ÁöÁ¤ÇØÁÙ ¼ö ÀÖ´Â ¹æ¹ýÀÌ ¾ø°í, clobber list¿¡¼­ ÁöÁ¤À» ÇÒ ¼ö ÀÖÁö¸¸ ¹«½ÃµÇ±â¶§¹®¿¡ inline assembly¿¡¼­ ebpÀÇ °ªÀ» º¯È­½Ãų ¶§´Â ¹Ýµå½Ã ÀúÀå/º¹±¸ ÇØÁÖ¾î¾ß ÇÕ´Ï´Ù. GccÀÇ ¹ö±×¶ó°íµµ ÇÒ ¼ö ÀÖ½À´Ï´Ù.


 2: movl	%%esp, %%eax
      

ÇöÀç esp°ªÀ» eax¿¡ ÀúÀåÇÕ´Ï´Ù. altstackÀ¸·Î ¹Ù²Ù¾î ÇÔ¼ö¸¦ ½ÇÇàÇÏ°í ¿ø·¡ÀÇ stackÀ¸·Î µ¹¾Æ¿Í¾ßÇϱ⠶§¹®¿¡ ¿ø·¡ stack pointer¸¦ ±â¾ïÇϰí ÀÖ¾î¾ß ÇÕ´Ï´Ù. À̸¦ altstackÀ¸·Î ¹Ù²Ù°í Á¦ÀÏ Ã³À½¿¡ pushÇϱâ À§ÇØ eax¿¡ ÀúÀåÇØ µÎ´Â °ÍÀÔ´Ï´Ù.


 3: movl	%8, %%esp
      

%8Àº altstackÀÔ´Ï´Ù. altstackÀ¸·Î stackÀ» ¹Ù²ß´Ï´Ù.


 4: pushl	%%eax
      

¿ø·¡ÀÇ stack pointer¸¦ stack¿¡ ÀúÀåÇÕ´Ï´Ù.


 5: pushl	%7
      

%7Àº argÀÔ´Ï´Ù. ÇÔ¼ö È£ÃâÀ» À§ÇØ arg¸¦ »õ·Î ¹Ù²ï stack¿¡ pushÇÕ´Ï´Ù.


 6: call	*%6
      

funcaddrÀ» È£ÃâÇÕ´Ï´Ù. *´Â indirect callÀÓÀ» ³ªÅ¸³À´Ï´Ù. Input¿¡¼­ ´õ ÀÚ¼¼È÷ ¼³¸íÇϰڽÀ´Ï´Ù.


 7: addl	$4, %%esp
      

funcaddrÀÇ ½ÇÇàÀÌ ³¡³µÀ¸¹Ç·Î arg¸¦ ¾ø¾Û´Ï´Ù.


 8: popl	%%esp
      

¿ø·¡ÀÇ stackÀ¸·Î µ¹¾Æ¿É´Ï´Ù.


 9: popl	%%ebp
      

1¿¡¼­ ÀúÀåÇØµÐ ebp¸¦ º¹±¸ÇÕ´Ï´Ù.

Output¿¡¼­ a°¡ early clobber·Î ÁöÁ¤µÈ °Í À̿ܿ¡´Â Ưº°ÇÑ Á¡ÀÌ ¾ø½À´Ï´Ù. eax¸¦ Á¦¿ÜÇÑ ·¹Áö½ºÅ͵éÀº funcaddrÀÇ ÇÔ¼ö°¡ ½ÇÇàÇϸ鼭 Áï, ¸ðµç inputÀÌ ´Ù ¾²ÀÎ ÈÄ¿¡ ¹Ù²ð ¼ö Àֱ⠶§¹®¿¡ early clobber·Î ÁöÁ¤ÇÒ Çʿ䰡 ¾ø°í µû¶ó¼­ input¿¡ ÇÒ´çµÉ ¼ö ÀÖ½À´Ï´Ù.

Input¿¡¼­ funcaddrÀº ¹ü¿ë ·¹Áö½ºÅÍ, arg¿Í altstackÀº ¹ü¿ë ·¹Áö½ºÅÍ ¶Ç´Â immediate constraint¸¦ °¡Áö°í ÀÖ½À´Ï´Ù. Memory operand´Â esp¿¡ ´ëÇÑ offset addressingÀ¸·Î Ç¥ÇöµÉ ¼ö ÀÖ°í, esp¸¦ ¹Ù²Û ÈÄ¿¡ inputµéÀ» »ç¿ëÇϱ⠶§¹®¿¡ memory operand´Â Çô¿ëÇÒ ¼ö ¾øÀ¸¹Ç·Î ·¹Áö½ºÅͳª immediateÀ» »ç¿ëÇØ¾ß Çϴµ¥ ix86ÀÇ call instructionÀº immediate operand·Î´Â relative call¹Û¿¡ Áö¿øÇÏÁö ¾Ê±â ¶§¹®¿¡ indirect callÀ» ÇØ¾ßÇÏ°í µû¶ó¼­ 'r' constraint¸¦ ½á¾ßÇÕ´Ï´Ù. ³ª¸ÓÁö µÑÀº immediateÀ̾ °ü°è°¡ ¾ø±â ¶§¹®¿¡ 'ri' constraint¸¦ °¡Áö°í ÀÖ½À´Ï´Ù.

arg¿Í altstackÀÌ call_with_stack_switchÀÇ ÀÎÀÚÀ̱⠶§¹®¿¡ immediateÀÌ Àǹ̾ø´Ù°í »ý°¢ÇÒ ¼öµµ ÀÖÁö¸¸, __inline__À¸·Î ¼±¾ðµÇ¾î Àֱ⠶§¹®¿¡ ÀÎÀÚ°¡ compile time¿¡ °áÁ¤µÉ ¼ö ÀÖÀ¸¸é immediateÀÌ µË´Ï´Ù. ¾Æ·¡ÀÇ ÄÄÆÄÀÏÇÑ assembly¸¦ º¸¸é ¾Ë ¼ö ÀÖ½À´Ï´Ù.


	.file	"call_with_stack_switch.c"
	.version	"01.01"
gcc2_compiled.:
.section	.rodata
	.align 32
.LC0:
	.string	"say_hello : hello world... esp=%08x, arg=%d\n"
.LC1:
	.string	"say_hello : returning %d\n"
.text
	.align 4
	.type	 say_hello,@function
say_hello:
	subl $4,%esp
	pushl %ebx
	movl 12(%esp),%ebx
#APP
	movl %esp, 4(%esp)
#NO_APP
	pushl %ebx
	pushl 8(%esp)
	pushl $.LC0
	call printf
	imull %ebx,%ebx
	pushl %ebx
	pushl $.LC1
	call printf
	movl %ebx,%eax
	addl $20,%esp
	popl %ebx
	popl %ecx
	ret
.Lfe1:
	.size	 say_hello,.Lfe1-say_hello
.data
	.align 4
	.type	 altstack,@object
	.size	 altstack,4
altstack:
	.long _altstack+8192
.section	.rodata
	.align 32
.LC2:
	.string	"main      : current esp=%08x, altstack=%08p-%08p\n"
	.align 32
.LC3:
	.string	"main      : calling say_hello w/ stack switch (arg=%d)\n"
	.align 32
.LC4:
	.string	"main      : esp=%08x, arg=%d, rv=%d\n"
.text
	.align 4
.globl main
	.type	 main,@function
main:
	subl $4,%esp
	pushl %ebp
	pushl %edi
	pushl %esi
	pushl %ebx
#APP
	movl %esp, 16(%esp)
#NO_APP
	pushl altstack
	pushl $_altstack
	pushl 24(%esp)
	pushl $.LC2
	call printf
	pushl $1096
	pushl $.LC3
	call printf
	movl $say_hello,%edx
	movl altstack,%eax
	addl $24,%esp
	movl %eax,%ebp
#APP
	pushl	%ebp		
	movl	%esp, %eax	
	movl	%ebp, %esp	
	pushl	%eax		
	pushl	$1096		
	call	*%edx		
	addl	$4, %esp	
	popl	%esp		
	popl	%ebp		
	movl %esp, 16(%esp)
#NO_APP
	pushl %eax
	pushl $1096
	pushl 24(%esp)
	pushl $.LC4
	call printf
	xorl %eax,%eax
	addl $16,%esp
	popl %ebx
	popl %esi
	popl %edi
	popl %ebp
	popl %ecx
	ret
.Lfe2:
	.size	 main,.Lfe2-main
	.local	_altstack
	.comm	_altstack,8192,32
	.ident	"GCC: (GNU) 2.95.4 20010902 (Debian prerelease)"
      

call_with_stack_switch°¡ main¾È¿¡ inlining µÇ¾ú°í, altstackÀÌ %ebp·Î, arg´Â immediate operand·Î, funcaddrÀÌ %edx·Î ÇÒ´çµÈ °ÍÀ» º¼ ¼ö ÀÖ½À´Ï´Ù. ¶Ç, Dummy º¯¼öµéÀº ¸ðµÎ »ç¶óÁ³°í, return °ªÀÎ aµµ %eax¿¡ ÀÖ´Â ±×´ë·Î »ç¿ëµÇ°í ÀÖ½À´Ï´Ù.

À§ÀÇ ÇÁ·Î±×·¥À» ½ÇÇàÇÏ¸é ´ÙÀ½°ú °°Àº °á°ú°¡ ³ª¿É´Ï´Ù.

% ./call_with_stack_switch
main      : current esp=bffffc3c, altstack=0x80497c0-0x804b7c0
main      : calling say_hello w/ stack switch (arg=1096)
say_hello : hello world... esp=0804b7ac, arg=1096
say_hello : returning 1201216
main      : esp=bffffc3c, arg=1096, rv=1201216
      

Inline assembly¸¦ »ç¿ëÇÒ ¶§´Â ·¹Áö½ºÅÍ ÇÒ´çÀÌ Á¤È®È÷ ¾î¶»°Ô µÇ´ÂÁö ÇÁ·Î±×·¥À» ¾²¸é¼­´Â ¾Ë ¼ö ¾ø°í, ƯÈ÷ early clobber ¿É¼ÇÀº ÀرⰡ ½±°í À߸øµÇ¾úÀ» ¶§ ã±â°¡ »ó´çÈ÷ Èûµé±â ¶§¹®¿¡ Á¦´ë·Î ÀÛµ¿ÇÏ´Â °Í °°´õ¶óµµ -S ¿É¼ÇÀ» ÁÖ¾î ¿øÇÏ´Â Äڵ尡 »ý¼ºµÇ¾ú´ÂÁö¸¦ È®ÀÎÇØº¸´Â °ÍÀÌ ÁÁ½À´Ï´Ù.