본문 바로가기
코스웨어/12년 내장형하드웨어

2012/8/24 금요일 디테일 정리 1

by 알 수 없는 사용자 2012. 8. 27.
728x90
반응형

정리일지가 있지만, 자세한 설명을 원하는 사람에게 정리한겸 올림니다. 틀린 것이 있을 수 있으니 공부와 확인을 요합니다.

지난 시간 어큐멀레이터에 대해 알아 보았다 intel에서만 특정한 레지스터의 성능을 좋게 만들어 놓은 것으로 ,

부호확장의 예를 c로 보자

signed 인 변수 a와 b 의 값은 (-) 이기 때문에 나머지 자리에 1로 채워진다. (2자리만 나올수 있게 할수 없는 이유)

unsigned 인 c와 같은 경우 부호가 + 이므로 2자리가 출력 된다.

- arm과 intel 의 함수 호출 비교

arm은 r1~r32 까지의 많은 레지스터를 보유 / intel 은 eax ebx ... 등 9개의 적은 레지스터가 있음

함수 호출 규약을 보면, arm은 함수인자를 4개 까지는 cpu안의 레지스터를 사용한다.

그래서 함수 인자가 4개 이상시 속도가 현저히 느려지는 것을 확인할 수 있다.

반대로 intel은 함수인자를 스택(메모리)역영에서 사용하므로, arm 보다는 속도가 현저히 느리고, 함수인자가 늘어나도 속도의 차이는 나타나지 않는다.

그러면, 함수 호출 규약에 대해서 알아보자.

함수 호출규약은 크게 함수인자를 스택에 푸쉬하는 방법, 반환하는 방법등이 큰 차이점

함수 호출 규약을 확인 하려면 우리가 해보았던, aip에서도 확인 할 수가 있다. 한 코드를

열어 확인해 보자.

위와 같은 api코드에서 본 winmain 앞의 APIENTRY가 정의 되어 있는 곳을 찾아보자 (F12)를 누르면, 그 아래와 같이 디파인된 것을 볼 수있고, 이 값은 다시 WINAPI로 디파인 되어 있다.

최종 디파인 된값은 __stdcall 이고, 이는 익숙한 CALLBACK도 이 값으로 디파인 된것을 확인 할 수 있다. 다른 값인 __cdecl이 보인다. 이 두 값이 규약으로 제일 많이 사용이 된다.

디폴트 호출규약으로 알려진 __cdecl와 표준 함수 호출규약으로 _stdcall에 대해 알아보자. 또한 이들을 설명함에 있어서, intel위주임을 발혀 두는 바이다. (여러경우가 있음을 인지하자.)

5가지의 함수 호출규약은 다음과 같다.

32bit인 경우이고, 64비트인 경우는 나중에 공부 하도록 하자.

1. __cdecl     :  parameter들을 역순으로((수식의)오른쪽에서 왼쪽 방향으로) 스택에 푸쉬한다.  스택 프레임 반환 작업은 호출자(Caller: 함수를 호출한 프로시저)가 한다.
* 스택 프레임 : 함수가 호출될 때마다 그 함수 호출을 위해 할당받는 메모리 덩어리\

2. __stdcall  : parameter들을 역순으로(오른쪽에서 왼쪽 방향으로) 스택에 푸쉬한다.
                  스택 프레임 반환 작업은 피호출자(Callee:호출을 당한 함수)가 한다.

3. __clrcall   :  parameter들을 순서대로(왼쪽에서 오른쪽 방향으로로)  CLR수식 스택에  적재한다. 스택 프레임 반환 작업을 하지 않는다.
* CLR : (common language runtime) ; 공통 언어 런타임


4. __fastcall : parameter들중 처음 2개 까지만 레지스터(ecx, edx)에 저장하고,
                   나머지는 스택에 푸쉬한다.
                  스택 프레임 반환 작업은 피호출자가 한다.

5. __thiscall : this 포인터만 레지스터 ecx에 저장하고, 나머지는 스택에 푸쉬한다.
                   스택 프레임 반환 작업은 피호출자가 한다 

나머지 두 규약에 대해서는 정리를 해 놓았으니 시간 나면, 공부를 하도록 하고, 정리를 계속 진행 하겠다.

함수 호출 규약을 c는 자동적으로 표준호출 규약을 사용 하지만 , 어셈블리는 우리가 최적화 하여 선택하여 사용한다. 이는 쉽지가 않은, 과정으로 이런 부분 까지 세세히 코딩을 해야 된다  (티끌모아 태산이라는 마인드를 잊지 말지어다. )

대부분의 옙은 os를 사용하고, os는 펌웨어를 (dc: 드라이버 컨트롤러)통해 각장치들을 제어한다. 이때, 어떻게 펌웨어를 만드는 냐에 따라 (최적화), 성능이 달라 지는 것처럼.

자 그럼면, 이제부터는 masm컴파일 문법을 이용해서, 덧셈을 하는 프로그램을 만들어 보자.

 

먼저 nasm 으로 작성한 코드이다. 컴파일 코드는 nasm -f win32 first.asm으로 커파일만을 실행하고, 나머지 obj파일을 링크 시켜 실행한다. cl main.obj first.obj asm_io.obj

코드의 설명은 빨간색과 같고, 여기서 중요한 것은 반환값으로 C에서 출력하려 했지만, popa로 전부다 초기화가 되어, assme에서 call print_int로 출력 했다는 점이다.

그리고 여기서 print_int 는 eax의 정수 값을 출력한다. sub ebx, eax를 하면, 8이 출력되는 것을 알수 있다.

.386  //386이상의 기능을 사용 하겠다.
.MODEL FLAT 
//메모리를(세그먼트단위가 아닌) 오리지널주소 통째로 보겠다. (0번지가 하나다)

ExitProcess PROTO NEAR32 stdcall,dwExitCode:DWORD  //함수선언
//함수이름   시그니쳐(설명) 함수모양이 이렇타. /32비트를 쓰기 /,4byte(dw)함수인자

INCLUDE io.h   //메크로사용 하기 위해

cr  Equ 0Dh  //#define 용법 아스키 코드값 확인
Lf  Equ 0Ah

.STACK 4096   // 스택을 정해준다 적당한 값찾기 / 지금은 4byte

.DATA
number1 DWORD ?  //  4byte 초기값안넣는다.
number2 DWORD ?
prompt1 BYTE  "Enter First Number : ",0
prompt2 BYTE   "Enter Second Number : " ,0
string  BYTE   40  DUP  (?) 
 //  1byte를 40개 확보(40byte) /누르면 /초기값은x

label1   BYTE   cr,Lf, "The Sum is"
 // printf("\r\nThe Sum is") / 와 같음
sum  BYTE   11  DUP  (?) 
 // 공간만 확보 한다.
  BYTE  cr,Lf,0 // 개행문자와 널문자 (총3byte) 
 // 3개가 한덩어리로 출력 (널문자가 없기 때문)

.CODE
_start:
  output   prompt1 // 만든 함수 뒤의 주소를 출력한다.
  input   string,40  // scanf와 같은 함수
  atod   string  // 문자를 숫자로 변환 eax로 숫자를 대입
  mov  number1,eax // 최종적으로 number2에 넣는다.

  output   prompt2
  input  string,40
  atod   string
  mov   number2,eax

  mov   eax, number1
  add    eax, number2
  dtoa  sum, eax
  output   label1 

  INVOKE  ExitProcess,0 // 정상종료할 수있게 해주는 코드 (exitzore호출),(0)의 값

PUBLIC _start // 외부에서 보일수 있게 

END  
// 끝

자, 위의 코드는 masm 문법을 이용한 코드이다. 내용을 주석을 통해서 복습하도록 하자.

두번째, .MODEL FLAT 을 보면, 세그먼트 단위가 아니라 통쩨로 본다라고 주석이 달려 있는데, 이는 각 프로그램이 실행되며, 해당하는 프로그램에 맞게 메모리를 할당하는데, 

이때, 각 메모리를 구분하기 위해 세그먼트들이(cs, ds, ss) 시작주소를 갖게 되는데, 이를 위주로 보면,  0번지가 3개로 따로 볼수 있는데, 이를 따로 보지 않고, 실제 주소와 같이 (0번지가 하나) 뭉덩이로 본다는 코드 이다. 

 

데이터 영역을 보면, 아래와 같은 코드가 있는데, 이는 문자열로 출력하면, 한덩어리 처럼 출력된다. 이는 널문자가 없기 때문이란, 아주 기초적 사실때문이 라는 것

 string  BYTE   40  DUP  (?) 
label1   BYTE   cr,Lf, "The Sum is"
sum  BYTE   11  DUP  (?) 
  BYTE  cr,Lf,0 

 변수들의 선언을 하는데, ? 코드는 초기화를 하지 않겠다는 코드이고, string DUP 40 바이트를 초기화 하지 않은 공간을 확보하고 (DUP는 복사하라. (?)를 40번 )

메모리상태를 그림으로 나타낸 것이다. 이해하기 쉽게

컴파일 옵션 코드는 각각 ml /c /coff First.asm 로 컴파일만, 나머지를 링크시키면,
link /subsystem:console /entry:start /out:main.exe First.obj io.obj Kernel32.Lib 코드로 링크 시켜 실행 파일을 만들어 실행 시킨다.

실행 결과

728x90