내장형 하드웨어/C언어

stack 구조, 어셈블리 (MASM, 코드 분석)

동화다아아 2011. 4. 29. 13:25

test(4,3);

⇒ push 3

   push 4

   call test(→push eip, → call test)


 

printf

 

 

test

 

main stack ebp(old ebp)

함수 호출 시 반드시 필요한 영역(overhead)

ret

4

3

main

 

 

 

← esp: main

 

 

B

 

A

 

stack ebp

RET(retrurn address)

 

init

← ebp: main

 

 

 

- disassemble

 test

 return

 push ebp

 mov ebp, esp

 sub esp, 50h

 mov esp, ebp

 pop ebp

 ret

 → test의 세 개의 코드를 entry code라 한다.

 → return의 세 개의 코드를 exit code라 한다.

 → 처음에 C에서 우리가 변수를 몰아 선언하듯이 esp도 메모리를 미리 일정부분 확보한 다. (sub esp, 50h)


※ stack 호출시 최소한 8byte의 메모리가 필요하다. 인자 2개가 4byte를 차지하는 것 이외에도 ret, old ebp가 들어가기 때문에

 → 즉, 함수를 너무 많이 호출하면 좋지 않다. 함수 호출을 통하여 2개의 인자(push 3, push 4) 이외에도 call test의 두가지 일(push eip, call test)가 발생하게 되기 때문.

 → ARM 칩의 경우에는 인자를 4개까지는 레지스터에 저장하므로 4개까지는 굉장히 빠르게 되지만 5개 부터는 메모리를 사용해야 하므로 속도가 급격히 느려진다.

 → 즉, stack 구조에서 인자는 없는게 사실상 가장 빠르다. 그러므로 최적화를 위해서는 인자의 개수를 너무 많이 사용하는 것은 조심 한다.


MASM 컴파일.

⇒ 컴파일 방법

1. 컴파일

ml /c /coff masm.asm

→ ml:microsoft assembler

→ /c 컴파일해라

→ /coff 공통 obj 파일을 생성해라

→ 실행파일


2. 링킹

link /subsystem:console /entry:embedded /out:실행파일.exe 실행파일.obj io.obj kernel32.lib

→ /subsystem:console(도스창에서 이기 때문에 console을 붙인다.)

→ /entry:embedded 함수가 시작점

→ /out: 최종적으로 만들어 내는 실행파일(.exe)

→ link 시킬 파일을 적는다.(실행파일.obj io.obj kernel32.lib)


3. 실행

실행파일.exe


※ C:\jbpup\Detmer에서

io.h io.obj kernel32.lib link.exe 를 복사해서 컴파일할 폴더에 붙이면 해당 폴더에서 컴파일 가능


 

.386 ; 386이상 호환, ‘.’은 특정 모델 명기.

.MODEL FLAT ; FLAT 메모리 방식.


ExitProcess PROTO NEAR32 stdcall, dxExitCode:DWORD

; ExitProcess → 기본으로 제공하는 함수.

; protov 선언.

; near32 → 32bit에 가깝다.

; stdcall → push 방법.

; dxExitCode:DWORD → 반환값이 4byte이다.


INCLUDE io.h

; C와 같은 역활

cr    EQU    0dh ; cr EQU(equal) 0DH(→ hexa)

                ;  #define cr 0x0D와 같다.(cr을 프로그램에서 0x0D로 대체한다.)

Lf    EQU    0ah ; 0A와 위의 0D는 아스키 코드를 뜻한다.


.STACK 4096 ; stack을 4096만큼 선언하겠다.(stack을 1024칸 사용가능 하도록 확보하겠다. ※C에서는 자동으로 해주지만 어셈블리에서는 직접 해주어야 한다.)


.DATA ; 전역변수(어셈블리에서 전역변수를 선언했다.)

number1    DWORD    ? ; 변수 이름, DWORD는 double word이므로 4byte

number2    DWORD    ? ; ? 는 쓰레기값을 뜻한다.

prompt1    BYTE      "Enter first      number :     ", 0 ; 1byte이며 “” 내부는 배열이 선언되어있다. 즉, 각 글자가(띄어쓰기 포함) 1byte씩의 크기를 가진다. ,0(Null)은 문자열에서의 Null과 같다. 어셈블리는 자동으로 문자열에 Null이 들어가지 않으므로 적어주어야 한다.

→  “kkk”라는 문자열이 있다면 C는 4byte 어셈블리는 3byte이다. C는 Null을 자동으로 포함하기 때문이다.

prompt2    BYTE      "Enter second number :     ", 0

string     BYTE      40    DUP  (?) ; C에서 char string[40];과 같다. 즉, 초기화 안된 string을 넣었으므로 쓰레기값 ‘?’ 상태이다. DUP - 40에 ?를 복사해 넣어라. 즉, 40byte에 쓰레기값이 복사된다.

label1     BYTE      cr,    Lf,     "The Sum is " ; C에서 "\r\rthe sum is"인데 어셈블리에서 개행문자를 지원하지 않으므로 cr, lt를 이용한다. 줄을 바꾼다.

sum        BYTE      11    DUP  (?) ; FFFFFFFF가 10진수 열자리이므로 그 이상의 값을 주었다.

           BYTE      cr,    Lf,     0 ; 여기까지 변수(지역)가 총 7개

       

.CODE ; 코드영역의 시작을 선언

_embedded: ; 어셈블리 외부에서 쓰는 명령은 _(언더바)를 붙인다.

            ; 함수의 시작을 알린다.(임베디드 함수가 여기서 시작한다.-함수의 본체)

        output   prompt1 ; 어셈블리 문법이 아니다.(메크로), 뒤에것을 출력해준다.(표준이 아니며 교육용으로 선언된 것. printf와 비슷하다.)

        input    string, 40 ; 입력을 받는다. 문자열을 40 글자만큼 입력받을 수 있다.

        atod     string ; ASCII to decimal - 아스키를 10진수로 바꾼다. 그리고 eax라는 레지스터에 저장한다.(atod역시 메크로)

        mov      number1, eax ; eax의 값을 number1에 집어넣어라. mov는 뒤에서 앞으로 대입하라.(assembly 에서는 전송이라는 뜻)

        

        output   prompt2

        input    string, 40

        atod     string

        mov      number2, eax

       

        mov      eax, number1

        add      eax, number2 ; add - 덧셈하다. C로 표현하면 eax = eax+number2;와 같다. 즉, number1+number2를 eax에 넣는 것.

        dtoa     sum, eax ; decimal to ASCII 10진수를 아스키로 바꾼다.

        output   label1 ;

       

        INVOKE   ExitProcess, 0

       

PUBLIC  _embedded ; 위와 현재의 embedded는 C의 { }와 같다.

 

END ; 전체 소스가 끝났다는 뜻.




- 실습 (Hello world!!)

.386

.MODEL FLAT


ExitProcess PROTO NEAR32 stdcall, dxExitCode:DWORD


INCLUDE io.h


cr    EQU    0dh

Lf    EQU    0ah


.STACK 4096


.DATA

prompt1    BYTE      "Hello world!!", 0 ; 전역변수

                                         ; 어셈블리어 코드 주석은 ;(세미콜론)이다.

                                        ; 어셈블리는 “” 내의 문자에서 C처럼 null을 자동으로 첨부하지 않기 때문에 ,0을 표시해 주어야 한다.


       

.CODE

_embedded: ; 엔트리 포인트

        output   prompt1 ; output은 실제 어셈블리에 있는게 아니라 메크로로 만들어 둔 것이다. intput도 마찬가지. 이해를 돕기 위한 것으로 실재로는 printf와 scanf를 뜻한다.

        INVOKE   ExitProcess, 0 ; INVOKE는 exit 함수를 뜻하는 것으로써 어디서든 exit를 호출하게 되면 강제로 종료시키게 된다.

       

PUBLIC  _embedded


END