마지막에 누가 댓글 달아줬는데 미처 확인을 못했네.. 봤으면 더 물어봤을텐데
전체 코드 첨부해서 다시 올림. 댓글달아준 친구가 float이 bp에서 -41위치에서 저장된다고 했는데
왜 하필 이 위치야? 보통 4의 배수가 오는게 자연스럽지 않나
내가 이 스택 모습을 직접 그림으로 그리려고 해서.. 잘 이해가 안됨
글구 선언된 순서대로 안쌓고 지 맘대로 float z가 x, y보다 먼저 스택에 추가되는 이유는?
P.S. 저기서 스택이 64바이트만큼 공간을 잡는 이유가 뭐냐 저거만 보면 거기까지 건드리지 않는건 같은데
참고로 난 어셈을 따로 배운적이 없음.. 빡머갈 양해 바랄게
해당 댓글은 삭제되었습니다.
putty라는 리눅스 서버 원격으로 접속하는게 있어
아아 char가 1바이트라서 41인거구나 ㅋㅋㅋㅋㅋㅋ 그럼 z는 41 y는 40 x는 36에 저장된다고 보면 될 듯?
나 이전글 답글단 사람인데 일단 나도 헷갈린 부분이 있어 정정할게. float위치는 -41이 아니고 -40임. 나도 변수 순서 바뀐걸 못봤음. 그리고 변수 선언 순서는 컴파일러가 임의로 바꾸기도하는데 대개 성능때문이고 일단 char이 1바이트 크기라 나머지 4바이트 변수들의 뒤로 빼서 최대한 4바이트 정렬을 도모한거라고 보면 됨.(참고로 바이트 정렬하면 액세스 성능향상이 있음.) 공식적으로 변수 순서에 대해 표준 기준은 없고 os, 컴파일러 맘대로인 부분중 하나임. (심지어 같은 코드에 대해서도 매 경우마다 다를 수 있는데 캐쉬 히트/미스율 때문에 정렬 무시하고 최대한 꾸겨넣는 경우도 있음.)
아아.. 고마워! 그럼 x와 y는 어디에 저장되는거야? 40에 char형이 들어와야 다음이 41 되는거 아닌가? - dc App
int x; |33|34|35|36|
float z; |37|38|39|40|
char y; |41|
이렇게일듯 함.
참고로 스택은 거꾸로 자라는 식이라 저렇게 됨
오호.. 결국엔 word allignment를 맞춰주기 위해서 저렇게 자기가 변수 넣는 순서를 바꿔서 넣어준건가 - dc App
leaq -32(%rbp), rax 이거는 arr 포인터 주소를 저장해주는건가? 흠..
그리고 스택 64바이트 에 대한건데 코드상으론 생략되었지만 모든 함수 콜은 리턴 시 리턴 주소 및 베이스 주소 같은 정보가 추가적으로 요구되고 이 경우에도 실제로 int arr이나 int x같은 변수가 -20 이후의 오프셋에서 할당된것이 그와 관련된 이유임. 64바이트만큼 예약된 건 일단 앞서 언급한 이러저러한 연유들로 인해 코드상으로 선언한 로컬 변수들보다 더 큰 영역이 필요하게 된 것이고, 스택의 기본 정렬 바운더리는 16바이트라 올림처리되서 64바이트 만큼 예약을 한 것임. (스택의 기본 정렬 바운더리는 32비트/64비트 환경과 관련있고 gcc에는 이를 조정할수 있는 옵션이 있는 걸로..)
리턴 주소는 base pointer 아래에 저장되는거잖아. 그럼 이런 정보들이 저장되는 코드는 어디에 있음? 내가 activation record 그리려고 하니까 빈 칸이 많이 남는데 어셈 코드만 보면 어디에 뭐가 들어가는지 이해가 너무 안돼.. - dc App
스택을 그려보면 아래에서 위로 리턴 주소-base 포인터-로컬 변수-stack pointer 이 순서가 되는게 맞는데 64바이트가 다 안채워지네 - dc App
위에 cfi관련된 라벨들 쪽을 보면 되는데 이부분은 댓글 한두줄로 설명이 안되고, 대신 링크하나 줄게.
http://egloos.zum.com/studyfoss/v/5347548
ㅇㅇ 64바이트 다 안채워질거야 올림해서 64가 된거라 남는부분이 있음
정말 고마워! 여기서 offset 얘가 나와서 또 질문.. 그림에는 안나와있는데 fun함수의 어셈 코드에서는 파라미터들이 있으니까 base 포인터에서 양수의 offset이 필요할거라 생각이 드는데 막상 코드를 보면 그런 부분이 없네. 이것도 잘 이해가 안돼 ㅠㅠ - dc App
fun한수의 어셈코드? 음.. fun 의 내부를 말하는거임? 아니면 call fun 호출시의 인자관련된거인가?
이해 안될까봐 사진 첨부했어 ㅇㅇ 세번째! - dc App
일단 이얘기를 물어보는건지는 잘 모르겠지만 일단 언급하고 넘어가자면 함수 호출 인자와 관련된거라면 64비트 리눅스환경에서는 함수 인자가 레지스터 RDI, RSI, RDX, RCX, R8, R9, XMM0~7 들에 순서대로 쌓임. 그리고 저 레지스터들의 갯수보다 더 많은 인자가 들어갈 때 비로소 추가적인 스택푸쉬가 일어나고.. 이번 fun함수의 경우 인자가 적어서 di, si, dx cx 로 퉁쳐질수 있는 상황임. main호출쪽과 fun내부쪽을 잘 보면 저 레지스터들에 값을 넣고 꺼내는 걸 확인할 수 있을거야. (근데 내가 위에 적은건 공식 표준문서 기준이고 이 예시에서만 보면 컴파일러가 cx대신 ax를 쓴거 같네. 음 이것도 컴파일러 맘인가. 암튼 맥락은 같다고 보면 됨.)
그리고 만일 저얘기가 아니라면.. 음 'base pointer에서의 양수의 오프셋' 부분은 내가 맞게 이해한건지 모르겠지만.. 일단 fun함수 내부의 윗부분에서 movq %rsp, %rbp 를 다시 하는 걸 보면 의문이 해소 될거 같아. 저기서 한번 rbp가 다시 현 rsp 값으로 덮어씌워지게 되니 main함수내에서의 rbp와는 엄연히 다른 값이되고 여기서부터는 이제 fun함수 내부의 로컬변수가 되는거니. main함수에서의 rbp와 연관지어서 볼 필요는 없게 되겠지.
아.. rbp가 바뀌는걸 생각해야하군. 내가 양수라고 얘기한 부분은 내가 배운대로라면 base pointer 기준으로 아래(+)에는 파라미터들이, 위(-)에는 로컬 변수들이 채워져야한다고 생각해서 ㅠ 그런데 rbp가 rsp로 갱신되는걸 생각해본다면 rsp=rbp-64니까 여기서 음수 offset이면 원래의 스택의 영역보다 더 위를 가리킬텐데.. - dc App
음 혹시 _cdecl, _stdcall 같은 호출 규약을 이야기하는거라면 그럴 수도 있지. 저 규약들은 함수 인자들은 무조건 스택에 넣고 호출을 하니깐. 그런데 저 둘은 32비트 컴퓨터에서만 쓰이는 방식이고 64비트(지금 어셈을 보면 64비트 환경인거 같은데 맞지?) 에서는 저 둘과는 달리 _fastcall과 비슷한 방식의 호출만을 하거든. 다음 링크 아래쪽에서 호출 규약쪽가면 32비트와 64비트에서의 차이를 알 수 있을거야.
https://ko.wikipedia.org/wiki/X86_%ED%98%B8%EC%B6%9C_%EA%B7%9C%EC%95%BD
https://ko.wikipedia.org/wiki/X86_호출_규약
링크가 잘 안걸린다 ㅋㅋ 복사붙여넣기 해야 됨
링크까지 보내주다니 감격이다.. 내가 gcc 컴파일러를 쓰는데 호출 규약중에 fastcall이 있구나. 그런데 fastcall은 인자 두개는 레지스터로 보내고 나머지는 스택에 push한다는데 내 코드에서는 인자 4개가 전부 레지스터로 바로 참조되는것 같아. 혹시 틀리면 지적해줘! - dc App
그리고 fun내부에서 subq rsp 같은거 없이 그냥 rsp보다 위쪽을 참조하는 거는 나도 걍 추측인데, 컴파일러 입장에서 그냥 안전하다고 판단해서 그렇게 한거 같기도.. 만일 fun내부에서 별도의 로컬변수가 선언되거나 또다른 제3의 함수 호출 같은걸 하게 되면 그때는 스택공간을 반드시 안전하게 확보시켜야 했겠지만 지금의 코드는 그럴 필요가 없어서 그런거 같기도... 걍 추측임.
맞어 내가 _fastcall "비스무리"하다고 지칭 한게 바로 그 뜻임. _cdecl, _stdcall, _fastcall 모두 32비트에서 쓰던 규약이고 64비트에서는 아예 fastcall을 더 확장해서 레지스터 사용 갯수를 엄청 늘려버렸음. 인자를 무쟈게 많이 늘리지 않는한 어지간해선 레지스터로 다 때울거야.
위의 위키문서에서 '시스템 V AMD64 ABI' 가 바로 64비트 리눅스 환경에서의 규약인 것.
이제 fun 어셈코드를 확인해보면 되는데 좀 아리까리하네. 처음에 레지스터의 값을 스택에 넣어주는데 이게 어떤의민지 궁금하고 또 나중에는 fun 함수 내부의 구문이 레지스터가 아니라 스택에 넣어주는 식으로 이루어지네.. movb 65, -16(%rbp) 처럼. 각 인자들이 로컬변수와 같이 대응되는 스택 위치가 정해짐 - dc App
이부분은 나도 잘 몰랐던 부분인데, redzone이 그 해답인거 같아.
https://jen6.tistory.com/77
fun함수는 저 링크에서 말하는 말단함수에 해당되고 그 런 경우 redzone을 적절히 활용한다고 하네. fun함수의 상황을 보면 레지스터에서 값을 꺼내서 연산을 위해 임시 저장해두는 공간으로 활용하고 있는것. 물론 약간 두단계를 거쳐서 하는 것처럼 보이는 부분은 그냥 최적화 플래그에 의해 조율될수도 있지 않을까 싶어. 특히 xmm0에서 -20[rbp]로 넣은뒤 잠시 후 다시 -20[rbp]에서 xmm0으로 넣는 부분같은거.
친구야 너무너무 고마워.. ㅜㅜ 처음엔 뭐가 뭔지 전혀 이해가 안됐는데 설명한거 천천히 읽어보고 하나씩 따라가면서 예상해보니까 어떻게 코드랑 똑같은 일을 하는건지 마침내 머릿속에 들어왔어. - dc App
위에 프로그램 말고도 다른 예제가 있는데 그것도 지금까지 알려준 정보를 바탕으로 따라오니까 이해됐다. 드디어 해방이야...여름이라 짜증나기 쉬운데 너무 열심이고 친절해서 감동이야! - dc App