IT 엔지니어링 학생이 여기 있습니다. 우리는 컨텍스트 스위칭을 가지고 놀아 달라는 요청을 받았고, 우리는 다소 조잡한 try/throw 시스템을 구현해야했습니다. 여기에 우리가 작성했던 코드입니다 :printf를 호출하면 분할 오류가 발생하지 않습니다.
struct ctx_s {
int esp;
int ebp;
};
struct ctx_s * pctx;
typedef int (func_t)(int); /* a function that returns an int from an int */
int try(func_t *f, int arg)
{
/* saving context by storing values of %esp and %ebp */
asm ("movl %%esp, %0"
: "=r"((*pctx).esp)
:
);
asm ("movl %%ebp, %0"
: "=r"((*pctx).ebp)
:
);
/* calling the function sent to try(), returning whatever it returns */
return f(arg);
}
int throw(int r)
{
printf("MAGIC PRINT\n");
static int my_return = 0;
/*^to avoid "an element from initialisation is not a constant" */
my_return = r;
/* restituting context saved in try() */
asm ("movl %0, %%esp"
:
: "r"((*pctx).esp)
);
asm ("movl %0, %%ebp"
:
: "r"((*pctx).ebp)
);
/* this return will go back to main() since we've restored try()'s context
so the return address is whatever called try... */
/* my_return is static (=> stored in the heap) so it's not been corrupted,
unlike r which is now the second parameter received from try()'s context,
and who knows what that might be */
return my_return;
}
pctx 두 INT 년대를 들고 단순한 구조로 글로벌 포인터는, f를 던져() (42)에 일부 반환 코드 #define을을 보내기를 호출하는 함수이며, main()은 본질적으로 pctx를 할당하고, result = try (f, 0)를 수행하고 결과를 출력합니다. 결과가 42 일 것으로 예상됩니다.
이제 MAGIC PRINT가 throw()에서 발견되었을 수 있습니다. 여기에 이유가 완전히 명확하지 않은 이유가 있습니다. 기본적으로 대부분의 학생들이 throw() 내부에서 segfaulting했다. 이 함수 내에서 printf()를 호출하면 프로그램이 겉으로보기에는 제대로 작동하고 교사는 시스템 호출이 효과가 있었을 것이라고 생각합니다.
실제로 설명을 얻지 못했기 때문에 gcc -S로 생성 된 어셈블리 코드를 두 버전 모두에서 비교해 보았습니다.하지만 printf()를 사용하거나 사용하지 않은 경우에는 많이 사용하지 못했습니다. printf와 함께
Breakpoint 1, throw (r=42) at main4.c:38
(gdb) disass
Dump of assembler code for function throw:
0x0804845a <throw+0>: push %ebp
0x0804845b <throw+1>: mov %esp,%ebp
0x0804845d <throw+3>: mov 0x8(%ebp),%eax
0x08048460 <throw+6>: mov %eax,0x8049720
0x08048465 <throw+11>: mov 0x8049724,%eax
0x0804846a <throw+16>: mov (%eax),%eax
0x0804846c <throw+18>: mov %eax,%esp
0x0804846e <throw+20>: mov 0x8049724,%eax
0x08048473 <throw+25>: mov 0x4(%eax),%eax
0x08048476 <throw+28>: mov %eax,%ebp
0x08048478 <throw+30>: mov 0x8049720,%eax
0x0804847d <throw+35>: pop %ebp
0x0804847e <throw+36>: ret
End of assembler dump.
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0xb7e846c0 in ??()
() :
Breakpoint 1, throw (r=42) at main4.c:34
(gdb) disassemble
Dump of assembler code for function throw:
0x0804845a <throw+0>: push %ebp
0x0804845b <throw+1>: mov %esp,%ebp
0x0804845d <throw+3>: sub $0x18,%esp
0x08048460 <throw+6>: movl $0x80485f0,(%esp)
0x08048467 <throw+13>: call 0x8048364 <[email protected]>
0x0804846c <throw+18>: mov 0x8(%ebp),%eax
0x0804846f <throw+21>: mov %eax,0x804973c
0x08048474 <throw+26>: mov 0x8049740,%eax
0x08048479 <throw+31>: mov (%eax),%eax
0x0804847b <throw+33>: mov %eax,%esp
0x0804847d <throw+35>: mov 0x8049740,%eax
0x08048482 <throw+40>: mov 0x4(%eax),%eax
0x08048485 <throw+43>: mov %eax,%ebp
0x08048487 <throw+45>: mov 0x804973c,%eax
0x0804848c <throw+50>: leave
0x0804848d <throw+51>: ret
End of assembler dump.
(gdb) c
Continuing.
MAGIC PRINT
result = 42
Program exited normally.
의 printf()없이
: 던져에 중단 점()의 여는 중괄호 (라인 33) 설정 및 GDB로 분해하는 것은 저에게이 준
나는 그것을 어떻게 만들어야할지 정말로 모른다. 분명히 일은 다르게 일어나고 있지만, 나는 두 경우 모두 무슨 일이 일어나고 있는지 이해하기가 어렵다. 그러니까 ... 내 질문은 본질적으로 : printf를 호출하는 것이 segfault를 던지지 않게하는 방법은 무엇입니까?
감사합니다! 이것은 의미가 있으며, 이제 나는 그것에 대해 생각해 보았습니다. 저는 실제로이 "지역 변수 없음"측면을 언급하는 선생님을 기억합니다. 로컬 변수와 함수 매개 변수 사이에서 혼란스러워하기 때문에 나는 그것을 기각 한 것 같습니다 - throw는 매개 변수를 받기 때문에 스택에 예약 된 공간이없는 이유는 없습니다. 물론 매개 변수가 먼저 푸시되고, ** 다음에 ** 반환 주소가 반환되고, 그런 다음 로컬 변수 ... – Peniblec