2012-05-26 2 views
2
void myFunc(char dummy) { 
    char *addrFirstArg = &dummy; 
} 

int main() { 
    char dummy = 42; 
    myFunc(dummy); 
    return 0; 
} 

위에서 gdb를 실행하고 myFunc에 중단 점을 추가합니다. addrFirstArg 값을 계산하고 검토하기 위해 한 번 단계를 밟습니다.함수에 인수로 char을 전달하면 스택의 기본값이 아닌 위치에 저장됩니다.

또한

info frame
프레임 myFunc에 대한 정보를 출력합니다. 늘어나는만큼 C 스택 구현 내 이해가 간다, 그 addrFirstArg 프레임 myFunc에 대한 기본 포인터 위에 8 바이트 있어야합니다. 대신하는 0x14 바이트 EBP 이하 더미의 주소 0xffffd094 같이 위치 0xffffd0a8, 행

(gdb) p &dummy 
$1 = 0xffffd094 "*\202\f\b\032\004" 

(gdb) info frame 
Stack level 0, frame at 0xffffd0b0: 
eip = 0x8048330 in findStackBottom (reporter.c:64); saved eip 0x8048478 
called by frame at 0xffffd170 
source language c. 
Arglist at 0xffffd0a8, args: dummy=42 '*' 
Locals at 0xffffd0a8, Previous frame's sp is 0xffffd0b0 
Saved registers: 
ebp at 0xffffd0a8, eip at 0xffffd0ac 

(gdb) x/1c 0xffffd0b0 
0xffffd0b0:  42 'a' 

따라서, 프레임 MYFUNC 내부 EBP 점수

는 I 참조 출력 인 그 위에 0x8 바이트가됩니다.

더미가 int로 선언되고 myFunc이 int 인수로 선언되면이 '불일치'가 사라집니다.

저는이 동작에 정말 흥미가 있습니다. 재현 가능 - 여러 번 실행했습니다.

답변

2

gcc -S을 사용하면 차이점을 더 잘 볼 수 있습니다. 숯불의 경우에 우리는 함수를 입력

char case      int case (diffs) 

pushl %ebp 
movl %esp, %ebp 
subl $20, %esp    subl $16, %esp 
movl 8(%ebp), %eax   x 
movb %al, -20(%ebp)   x 
leal -20(%ebp), %eax   leal 8(%ebp), %eax 
movl %eax, -4(%ebp) 
leave 
ret 

이 스택은 (상단에 상단)입니다 :

단일 문자 스택이 길에 "밀어"때문에
esp  return address 
esp+4 2A 00 00 00 

입니다

movsbl -1(%ebp), %eax 
movl %eax, (%esp) 

이고 x86은 리틀 엔디안입니다.

은 "프리앰블"상황 (원래 값 "푸시"는 메인 의해 다음 EBP + 8에서 촬영한다 (32 비트 정수로서 저장)이

esp   (room for local char dummy - byte 42) ... 
... 
ebp-4   room for char * 
esp+20 = ebp ebp 
ebp+4   return addr 
ebp+8   2A 00 00 00  

은 "문자"처럼되면 "32 비트"로 표시)를 eax로 변환 한 다음 하위 중요도가 낮은 바이트를 로컬 저장소에 저장합니다.

우리가 정렬 할 필요가 없기 때문에 int의 경우가 더 간단하며 스택에 있던 주소를 "직접"가져올 수 있습니다.

esp    ... 
... 
ebp-4   room for int * 
esp+16 = ebp ebp 
ebp+4   return addr 
ebp+8   2A 00 00 00  

그래서 첫 번째 경우 (숯불 경우)에, ESP 하나의 문자를 개최 4 개 바이트 씩 감소 : 별도의 로컬 스토리지가있다.

왜 이렇게할까요?

본 것처럼 단일 문자는 32 비트 "정수"(eax)로 스택에 푸시되고 같은 방법으로 eax로 다시 가져옵니다. 이 조작에는 엔디안 문제가 없습니다.

하지만 숯불에 대한 ebp + 8 주소를 돌려 주면 컴퓨터가 리틀 엔디안이 아니겠습니까? 이 경우 ebp + 8은 00 00 00 2A을 가리키며 *dummy은 0이 아니라 42가됩니다.

"가짜 int"(CPU가 엔디안이 무엇이든 상관없이 CPU가 일관되게 처리하는 작업)을 취한 후에는 LSByte가 로컬 스토리지에 저장되어 주소가 해당 char을 가리 키도록 보장해야합니다 (하위 바이트). 이것이 여분의 코드와 ebp + 8이 사용되지 않는다는 사실입니다 : endianness 주소가 정렬되어야합니다 (예 : 빅 엔디안의 경우 00 00 00 2A의 2A는 홀수 주소를 갖습니다.)

+0

와우 .. 자세한 신속한 답변을 주셔서 감사합니다. 정말 통찰력이 있습니다. 질문을하기 전에 더 많은 생각을 가졌어야합니다. AHA 효과가 좋았을 것입니다. :) – Vighnesh

관련 문제