kldp에 올라온 질문인데.. 흥미로운게 있어서 제가
몇가지 실험후에 답글을 달았습니다.
#include <stdio.h>
int arr_data[1000] = { 0, };
int arr_bss[1000];
int main()
{
int arr_local1[1000];
int arr_local2[1000] = { 0, };
printf("Hello World!\n");
return 0;
}
위의 소스의 변수들을 각각 추가하면서 size로 실행파일의 각 segment의
크기를 확인해보았더니,
data segment의 크기는 4*1000 + 16만큼 늘어나구요.
bss segment의 크기는 4*1000 + 8만큼 늘어나구요.
text segment의 크기는 초기화안한 경우에는 변화가 없고,
변수를 초기화하면서 선언하면, 8만큼 늘어납니다.
여기서, 변수크기외에 늘어난 크기(16, 8, 8)는
어떤 정보가 추가되기 때문에 생기는 건가요??
-----------------------------------------------------------------
디어셈블 하는 것은 gdb를 사용해도 되고
objdump란 유틸을 사용해도 됩니다.
그럼 일딴 각각의 상태를 정리를 해보지요..
gcc elf.c -s 하면 어셈블리 코드로 생성 해주기는 하는데
실제적으로 어셈블러가 바이너리 정렬같은 것을 하니까..
그냥 바이너리 상태를 덤프해보는게 정확합니다.
-----------------------------------------------------------------
//#include <stdio.h>
int arr_data[1000] ;
int arr_bss[1000];
int main()
{
int arr_local1[1000];
int arr_local2[1000] ;
// printf("Hello World!\n");
return 0;
}
gcc elf.c -o elf
objdump -h elf
Sections: elf init 없음
Idx Name Size VMA LMA File off Algn
11 .text 000000fc 080482f0 080482f0 000002f0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .data 0000000c 08049410 08049410 00000410 2**2
CONTENTS, ALLOC, LOAD, DATA
20 .bss 00001f60 08049500 08049500 00000500 2**5
ALLOC
-----------------------------------------------------------------
//#include <stdio.h>
int arr_data[1000] = {0,};
int arr_bss[1000];
int main()
{
int arr_local1[1000];
int arr_local2[1000] ={0,} ;
// printf("Hello World!\n");
return 0;
}
gcc elf2.c -o elf2
objdump -h elf2
Sections: elf2 init 있음
Idx Name Size VMA LMA File off Algn
11 .text 0000010c 080482f0 080482f0 000002f0 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
14 .data 00000fc0 08049420 08049420 00000420 2**5
CONTENTS, ALLOC, LOAD, DATA
20 .bss 00000fc0 0804a4c0 0804a4c0 000014c0 2**5
ALLOC
-----------------------------------------------------------------
elf, elf2의 사이즈 비교
elf elf2
Idx Name Size Algn Size Algn
11 .text 000000fc(252) 2**4 0000010c(268) 2**4
14 .data 0000000c(12) 2**2 00000fc0(4032) 2**5
20 .bss 00001f60(8032) 2**5 00000fc0(4032) 2**5
일딴 main에서 약간의 코드 차이가 나는데
바이너리를 덤프해봤습니다. objdump -s elf
이런식으로 하면되는데...
아래를 보시면 알겠지만,
초기화 루틴 때문에 조금 차이가 있습니다.
즉 elf.c같은 경우엔 스택에 공간을 잡아두고
(80483a3: 81 ec 40 1f 00 00 sub $0x1f40,%esp)
바로 사용을 하지만
elf2.c의 경우엔 elf.c와 같이 스택에 자리를 잡아주고
아래와 같이 클리어해주는 작업을 합니다.
80483b0: 31 c0 xor %eax,%eax
80483b2: fc cld
80483b3: b9 e8 03 00 00 mov $0x3e8,%ecx
80483b8: f3 ab repz stos %eax,%es:(%edi)
레지스터 eax를 0으로 만든다음
플래그를 리셋하고
ecx에 반복할 횟수 (4000)를 지정하고
eax의 값을 %es:(%edi)에 전송하는 거죠..
repz명령어에 의해서 ecx는 하나씩 줄고,, edi는 하나씩 증가하니까
결과적으로는 스택에 자리잡은 arr_local2가 0으로 클리어 되겠지요..
여기서 한가지 집고 넘어갈 사항이 있는데,
만약에 arr_data를 0이 아닌 특정 다른 데이터로 초기화 한다면
arr_local2[1024] = {1,2,3,4,5,6};
이 내용으 rdata 세그먼트에 저장이 되고
지금처럼 스트링일 0 밀어버리는게 아니라...
메모리 카피하는 어셈블리가 추가됩니다.
그럼 글로벌에 있는 arr_data는 어떨까요?
이것도 초기화 하는 루틴이 있을까요?
요것은 잘 생각해보면 그럴필요가 없습니다.
arr_data는 글로벌 데이터로., 컴파일 타임때 미리 그 값이 정해져서
바이너리에 있는 내용이 그대로 메모리로 올라가니까
초기화 루틴이 필요 없는것이죠..
그럼 결론적으로 사이즈 차이는 왜 생겼을까요?
바로... 이 초기화 하는 루틴때문에 코드가 더 추가 되었기 때문이죠..
그런데 보기좋게 16이란 값으로 차이가 나는걸까요?
그것은... 세그먼트가 효율적인 접근을 위해서 특정 바이트로 정렬이
되기 때문입니다.
즉 elf2는 000000fc 코드사이즈의 임계를 넘어버려서
다음 임계점인 0000010c까지 사이즈가 증가해 버린것이지요..
이제 그럼 아래표에서 왜 Algn값을 찍었는지 알겠지요?
Idx Name Size Algn Size Algn
11 .text 000000fc(252) 2**4 0000010c(268) 2**4
14 .data 0000000c(12) 2**2 00000fc0(4032) 2**5
20 .bss 00001f60(8032) 2**5 00000fc0(4032) 2**5
자 아래는 main function의 어셈블리 자료입니다.
080483a0 <main>: elf
80483a0: 55 push %ebp
80483a1: 89 e5 mov %esp,%ebp
80483a3: 81 ec 40 1f 00 00 sub $0x1f40,%esp
80483a9: 31 c0 xor %eax,%eax
80483ab: eb 03 jmp 80483b0 <main+0x10>
80483ad: 8d 76 00 lea 0x0(%esi),%esi
80483b0: c9 leave
80483b1: c3 ret
80483b2: 90 nop
80483b3: 90 nop
80483b4: 90 nop
80483b5: 90 nop
80483b6: 90 nop
80483b7: 90 nop
80483b8: 90 nop
80483b9: 90 nop
80483ba: 90 nop
80483bb: 90 nop
80483bc: 90 nop
80483bd: 90 nop
80483be: 90 nop
80483bf: 90 nop
080483a0 <main>: elf2
80483a0: 55 push %ebp
80483a1: 89 e5 mov %esp,%ebp
80483a3: 81 ec 40 1f 00 00 sub $0x1f40,%esp
80483a9: 57 push %edi
80483aa: 8d bd c0 e0 ff ff lea 0xffffe0c0(%ebp),%edi
80483b0: 31 c0 xor %eax,%eax
80483b2: fc cld
80483b3: b9 e8 03 00 00 mov $0x3e8,%ecx
80483b8: f3 ab repz stos %eax,%es:(%edi)
80483ba: 31 c0 xor %eax,%eax
80483bc: eb 02 jmp 80483c0 <main+0x20>
80483be: 89 f6 mov %esi,%esi
80483c0: 8b bd bc e0 ff ff mov 0xffffe0bc(%ebp),%edi
80483c6: c9 leave
80483c7: c3 ret
80483c8: 90 nop
80483c9: 90 nop
80483ca: 90 nop
80483cb: 90 nop
80483cc: 90 nop
80483cd: 90 nop
80483ce: 90 nop
80483cf: 90 nop
자 그럼 data는 8이 차이가 난다고 했는데
제가 실험을 해보니까. 저는 20이 차이가 나네요..
음.. 이것 역시 정렬때문입니다.
아래의 덤프내역을 보면, elf는 약간의 데이터 영역이
4바이트 단위로 정렬이 되어서... 12바이트로 존재를 합니다.
하지만,,
elf2의 경우엔 12 + 4000 인데. 이것이 4000을 넘어버러서
다음 임계점은 4032까지 커져버린거죠..(elf2의 데이터 정렬은 2^5)
Contents of section .data: elf
8049410 00000000 2c940408 00000000 ....,.......
Contents of section .data: elf2
8049420 00000000 f0a30408 00000000 00000000 ................
8049430 00000000 00000000 00000000 00000000 ................
8049440 00000000 00000000 00000000 00000000 ................
8049450 00000000 00000000 00000000 00000000 ................
8049460 00000000 00000000 00000000 00000000 ................
8049470 00000000 00000000 00000000 00000000 ................
8049480 00000000 00000000 00000000 00000000 ................
8049490 00000000 00000000 00000000 00000000 ................
....
804a380 00000000 00000000 00000000 00000000 ................
804a390 00000000 00000000 00000000 00000000 ................
804a3a0 00000000 00000000 00000000 00000000 ................
804a3b0 00000000 00000000 00000000 00000000 ................
804a3c0 00000000 00000000 00000000 00000000 ................
804a3d0 00000000 00000000 00000000 00000000 ................
elf2에서 일종의 filler가 어디에 들어갔을까요?
그것을 알아보기 위해서 main에 코드를 약간 추가했습니다,
단 여기서 주의할 사항은 코드가 정렬을 위해서 남겨둔
여분보다 커져버리면 전체 코드가 증가해 버리니
절대값 비교가 안됩니다...
//#include <stdio.h>
int arr_data[1000] = {0,};
int arr_bss[1000];
int main()
{
int arr_local1[1000];
int arr_local2[1000] ={0,} ;
arr_data[0] = 1;
// printf("Hello World!\n");
return 0;
}
080483a0 <main>:
80483a0: 55 push %ebp
80483a1: 89 e5 mov %esp,%ebp
80483a3: 81 ec 40 1f 00 00 sub $0x1f40,%esp
80483a9: 57 push %edi
80483aa: 8d bd c0 e0 ff ff lea 0xffffe0c0(%ebp),%edi
80483b0: 31 c0 xor %eax,%eax
80483b2: fc cld
80483b3: b9 e8 03 00 00 mov $0x3e8,%ecx
80483b8: f3 ab repz stos %eax,%es:(%edi)
80483ba: c7 05 40 94 04 08 01 movl $0x1,0x8049440
80483c1: 00 00 00
80483c4: 31 c0 xor %eax,%eax
80483c6: eb 00 jmp 80483c8 <main+0x28>
80483c8: 8b bd bc e0 ff ff mov 0xffffe0bc(%ebp),%edi
80483ce: c9 leave
80483cf: c3 ret
자.. 답이 나왔네요.
80483ba: c7 05 40 94 04 08 01 movl $0x1,0x8049440
보시면 알겠지만. elf때 있던 12바이트 뒤를 20바이트 0으로 깔고
그다음 부터 0x8049440주욱 연속된 공간을 할당하게 됩니다.
제 실험에서는 bss는 제가 원하는 크기 만큼 동일한 사이즈로 줄었네요..
음... 보시고 다시 잘 이해가 안되면 질문해주세요..
덕분에... 저도 옛날에 배운거 복습도 하고 정리도 하고 좋네요. ^^
ps.
아 한가지 추가.. 자료가 커졌을때 32로 정렬하는것은 무엇 때문일까요?
ㅋㅋ
그건 아마도 캐쉬랑 연관이 클껍니다.
제가 gcc소스를 분석해 보지는 않았지만..
제가 컴파일한 환경의 cpu는 다음과 같은 캐쉬라인을 가지고 있거든요
TLB and cache info:
01: Instruction TLB: 4KB pages, 4-way set assoc, 32 entries
02: Instruction TLB: 4MB pages, 4-way set assoc, 2 entries
03: Data TLB: 4KB pages, 4-way set assoc, 64 entries
43: 2nd-level cache: 512KB, 4-way set assoc, 32 byte line size
08: 1st-level instruction cache: 16KB, 4-way set assoc, 32 byte line size
04: Data TLB: 4MB pages, 4-way set assoc, 8 entries
0c: 1st-level data cache: 16KB, 4-way set assoc, 32 byte line size
그래서 캐쉬라인에 걸치지 말라고 저렇게 해주는거 같네요..
[출처] : http://www.ezdoum.com/stories.php?story=02/08/24/6098584
'QnA' 카테고리의 다른 글
Reentrant 와 Thread-safe 의 차이 (0) | 2010.05.28 |
---|---|
arm7에서 arm9으로 넘어가면서 pipeline이 두단계 들어난 것이 폰노이만 구조에서 하버드 구조로 넘어간 것과 연관이 있나? (0) | 2010.05.08 |
[Q/A] C언어에서 (void *)0, (const void *)0, (void * const)0 세가지의 차이점 (0) | 2010.03.01 |
[Q/A] 데이터 타입이 바뀌면 일일이 다시 짜야하는건가요? (0) | 2010.02.27 |
[Q/A] container_of() 매크로 (0) | 2010.02.27 |
WRITTEN BY
- RootFriend
개인적으로... 나쁜 기억력에 도움되라고 만들게되었습니다.
,