|
메뉴릿
카테고리
최근 등록된 덧글
감사합니다. shell scrip..
by Mr.Carbin at 07/17 아.. 정말 감사드립니다.. by 송진영 at 03/15 조금 거리가 있는 주제.. by sio4 at 03/11 안녕하세요? 덧글 달아.. by 송진영 at 02/14 펄이나 파이썬은 진입장.. by kirrie at 02/13 최근 등록된 트랙백
Allergy to codeine s..
by Source of codeine. Online phentermine. by Lowest price for phen.. Tramadol ultram medic.. by Ultram addiction. Ephedra weight loss .. by Ephedra. Canada overnight am.. by Ambien manufacturer. Calico film music des.. by Zoo sex. Lexapro-ultracet-int.. by Extracting acetanin.. Ambien. by Ambien verses temac.. Scat eating shit sex. by Shit sex photos per.. Ordering adderall. by Adderall. 포토로그
이전블로그
이글루링크
이글루 파인더
|
2009년 05월 15일
원문 : http://www.drpaulcarter.com/pcasm/
번역 : 송진영( caddsjy@gmail.com ) 1.3.6 입력과 출력( Input and Output ) 입출력은 시스템에 의존적입니다. 이 것은 시스템의 하드웨어와 인터페이싱을 해야합니다. C언어와 같은 고급언어에서는 표준 라이브러리에서 기본적인 루틴을 제공하여 간단하고 편리하게 I/O 프로그램을 할 수가 있습니다. 어셈블리 언어에서는 제공되는 표준 라이브러리가 없습니다. 어셈블리에서는 OS가 제공하는 저수준의 루틴을 이용하거나 하드웨어에 직접 접근하게 됩니다. 어셈블리코드는 표준 C라이브러리의 I/O루틴을 사용할 수 가 있다. 하지만 C언어에서 사용되는 루틴으로 정보를 전달해야하는 규칙을 알아야 한다. 이 규칙은 여기서 설명하기에는 복잡하다.( 후에 다룰 것이다. ) 간단한 입출력 처리를 위해서 C의 복잡한 규칙을 숨기고 좀더 간단한 인터페이스를 가진 제작자만의 루틴을 개발한다. 표 1.4에 제공되는 루틴들이 나열되어있다. read루틴을 제외한 모든 루틴들은 모든 레지스터들의 값을 유지하고 있다. 이 루핀들은 EAX 레지스터의 값을 변경한다. 이 루틴들을 사용하기 위해서 어셈블러가 루틴을 사용하기 위해 필요한 정보들이 기록된 파일을 포함하여야 한다. NASM에서 파일을 포함하기 위해서는 %include 전처리 지시어를 사용하면 된다. 아래에 제작자의 I/O루틴 파일을 포함하는 예가 있다. %include "asm_io.inc"위 예에서 사용된 asm_io.inc파일은 asm_io라는 목적파일이 필요하다. 이 예제 코드는 아래의 웹페이지에서 다운 받을 수 있다. http://www.drpaulcarter.com/pcasm ---표 1.4 어셈블리 I/O 루틴들---- print_int EAX에 기록된 정수값을 스크린에 출력한다. print_char AL에 기록된 ASCII 문자를 스크린에 출력한다. print_string EAX에 기록된 주소에서 시작되는 문자열을 스크린에 출력한다. 문자열은 C언어 형식을 사용한다( 널 문자로 끝나는.. ) print_nl 스크린에 줄바꿈 문자를 출력한다. read_int 키보드에서 입력된 숫자를 읽고 EAX레지스터에 기록한다. read_char 키보드에서 한 문자를 입력 받고 ASCII코드를 EAX레지스터에 기록한다. ----------------------------------------- 1.3.7 디버깅(Debugging) 제작자의 라이브러리 또한 프로그램을 디버깅하는데 유용한 몇가지의 루틴들을 포함하고 있습니다. 디버깅 루틴들은 상태를 수정할 수없이 컴퓨터의 상태에 대한 정보를 출력하게 됩니다. 이 루틴들은 CPU의 현재 상태를 저장하는 Macro이며 서브루틴을 호출하게 됩니다. Macro는 위에서 언급 되었던 asm_io.inc파일에 정의 되어져 있습니다. Macro는 일반적인 명령어처럼 사용됩니다. 매크로들의 오퍼랜드들은 콤마로 구분됩니다. 아래는 4가지의 디버깅용 루틴들에 대해 설명되어져있습니다. 레지스터,메모리,스택등의 값들을 보여주게 됩니다.
1.4 프로그램 작성하기(Creating a Program) 어셈블리 언어로 독립적으로 작동하는 프로그램을 만들어보겠습니다. 고급언어는 어셈블리보다 매우 쉽게 프로그램을 작성할수 있다. 또한 어셈블리러 작성된 프로그램은 다른 플랫폼으로 이식하기가 매우 어렵다. 사실 모든 곳에서 어셈블리가 특별하게 사용되고 있다. 왜 어셈블리를 공부해야만 할까요? 1. 가끔 어셈블리로 작성된 코드는 컴파일러가 생성한 코드보다 작고 빠르다. 2. 어셈블리는 시스템의 하드웨어에 직접적으로 접근이 가능하다. 고급언어에서는 이것이 어렵거나 불가능하다. 3. 어셈블리를 공부함으로써 컴퓨터가 어떻게 작동되는지 구조에대해 쉽게 이해할 수 있다. 4. 어셈블리를 공부함으로써 C와 같은 고급언어와 컴파일러의 구조에대해 더 심도 있게 이해할 수 있다. 1.4.1 첫번째 프로그램( First program ) Figure 1.6의 simple C driver프로그램은 asm_main이라는 함수를 호출하는 간단한 프로그램입니다. 이것은 실제로 어셈블리로 작성될 것입니다. C driver루틴을 사용하면 몇가지의 이점이 있습니다. 첫번째는 C 시스템은 보호모드에서 동작합니다. 모든 세그먼트들과 대응하는 세그먼트 레지스터들은 C에 의해서 초기화 될것입니다. 어셈블리 코드는 이것들에 대한 신경을 쓰지 않아도 됩니다. 두번째로 C라이브러리 또한 어셈블리코드에서 사용이 가능합니다. 저작자의 I/O루틴들도 이 장점을 가지고 있습니다. 이 입출력 루틴들은 C의 I/O루틴을 사용합니다.(printf와 같은,.) 아래에 간단한 어셈블리 프로그램이 있습니다. ===Figure 1.6 : driver.c code=== int main() { int ret_status; ret_status = asm_main(); return ret_status; } ========================= ===first.asm=== ; file : first.asm ; 첫 번째 어셈블리 프로그램. 이 프로그램은 두개의 정수를 입력받고 ; 입력된 두 수의 합을 출력합니다. ; ; djgpp를 사용하여 실행파일을 만드는 법: ; nasm -f coff first.asm ; gcc -o first first.o driver.c asm_io.o %include "asm_io.inc" ; ; .data 세그먼트내에 데이터 초기화 코드를 넣습니다. segment .data ; ; 여기 label들은 출력될 문자열을 참조하게 됩니다. ; prompt1 db "Enter a number : " , 0 ; 널 문자열로 종료되야한다는 것을 기억하십시요! prompt2 db "Enter another number : " , 0 outmsg1 db "You entered ", 0 outmsg2 db " and ", 0 outmsg3 db ", the sum of these is " , 0 ; ; .bss 세그먼트에는 초기화되지 않는 데이터를 넣습니다. segment .bss ; ; 이 곳의 Label들은 입력된 값들을 저장할 double word 값을 참조합니다. ; input1 resd 1 input2 resd 1 ; ; code는 .text 세그먼트에 입력합니다. ; segment .text global _asm_main _asm_main: enter 0,0 ; 셋업 루틴 pusha mov eax, prompt1 ; prompt출력 call print_string call read_int ; 정수값 읽어오기 mov [input1], eax ; input1에 저장하기 mov eax, prompt2 ; 프롬프트 출력 call print_string call read_int ; 정수값 읽어오기 mov [input2], eax ; input2에 저장하기 mov eax, [input1] ; eax = input1 add eax, [input2] ; eax += input2 mov ebx, eax ; ebx = eax dump_regs 1 ; 레지스터의 값 출력 dump_mem 2, outmsg1, 1 ; 메모리 값 출력 ; ; 결과 메세지를 단계적으로 출력합니다. ; mov eax, outmsg1 call print_string ; 첫번째 메세지 출력 mov eax, [input1] call print_int ; input1 출력 mov eax, outmsg2 call print_string ; 두번째 메세지 출력 mov eax, [input2] call print_int ; input2 출력 mov eax, outmsg3 call print_string ; 세번째 메세지 출력 mov eax, ebx call print_int ; 합한 결과(ebx) 출력 call print_nl ; 줄바꿈 문자 출력 popa mov eax, 0 ; C로 리턴 leave ret ------------------------------- 13번째 줄의 섹션은 프로그램에서 사용하는 데이터가 저장될 메모리를 정의합니다.(.data) 이 세그먼트에 정의되는 데이터들은 초기화된 상태로 정의해야합니다. 17~21번째 줄에 몇개의 문자열들이 선언되어져있습니다. C라이브러리를 통하여 출력될 것이고 반드시 null 문자로 끝나는 문자열이여야만 합니다. 0과 '0'은 다르다는 것에 주의하십시요. 초기화되지 않은 데이터는 .bss 세그먼트내에 선언해야 합니다. 이 세그먼트의 이름은 UNIX기반의 어셈블러에서 "block started by symbol"이라는 것에서 따온 것입니다. 스택 세그먼트 또한 그렇습니다. 이것에 대해서는 후에 설명할 것입니다. 코드 세그먼트의 명칭은 .text 입니다. 이 것은 명령어들의 위치를 가리킵니다. 언더바(_)로 시작되는 메인 루틴(38번 라인)의 Label이 명령어들의 시작 Label을 가리킵니다. 이 것은 C calling convention의 일부분입니다. 이 관례는 코드를 컴파일 할 때 사용되는 C규칙입니다. 이 것은 C와 어셈블리가 상호 호환성을 가지기 위한 매우 중요한 점입니다. 37번째 줄의 global 지시문은 어셈블러에게 _asm_main이라는 전역 Label을 만들도록 지시합니다. C와는 다르게, Label은 기본적으로 내부적으로만(internal scope) 사용할 수 있습니다. 이 뜻은 오로지 동일한 모듈 내의 코드만 사용할 수 있는 지역 Label이라는 것입니다. global 지시자는 지정한 label(또는 label들)을 외부(external scope)에서도 사용할 수 있게 합니다. 이 Label의 형식은 프로그램내의 어떠한 모듈에서도 접근할 수 있게 됩니다. asm_io 모듈은 print_int라는 전역 레이블을 선언하였기에 first.asm 모듈에서도 사용할 수 있게 된 것입니다. 1.4.2 컴파일러 의존성( Compiler dependencies ) 위의 어셈블리 코드는 GNU(http://www.fsf.org)기반의 DJGPP C/C++ 컴파일러(http://www.delorie.com/djgpp) 기준으로 작성되었습니다. 이 컴파일러는 인터넷상에서 무료로 구할 수 있습니다. 386기반의 PC또는 DOS, Windows95/98 또는 NT에서 잘 동작합니다. 이 컴파일러는 COFF(Common Object File Format)포맷의 목적파일을 사용합니다. 이 형식으로 어셈블하기 위해서는 nasm에 -f coff옵션을 지정합니다.(위 코드의 주석문에도 나와있습니다.) 어셈블후의 목적파일의 확장자는 o입니다. 리눅스 C컴파일러 또한 GNU 컴파일러와 동일합니다. 위의 코드를 Linux상에서 변환시 간단하게 37,38번째라인의 언더바 접두사를 삭제하면 됩니다. 리눅스는 ELF(Excutable and Linkable Format) 형식의 목적코드를 사용합니다. 리눅스에서는 -f elf옵션으로 어셈블합니다. 출력된 목적코드의 확장자는 o입니다. 볼랜드 C/C++는 또 다른 대중적인 컴파일러입니다. 이 컴파일러는 마이크로소프트 OMF포맷을 목적코드의 형식으로 사용합니다. -f obj 옵션을 사용하면 됩니다. OMF 형식은 특별한 segment 지시자를 사용합니다. 13번째 줄의 data segment를 아래처럼 수정해야합니다. segment _DATA public align=4 class=DATA use32 26번째 줄의 bss segment도 아래와 같이 수정합니다. segment _BSS public align=4 class=BSS use32 36번째 줄의 text segment도 아래와 같이 수정합니다. segment _TEXT public align=1 class=CODE use32 36번째줄 이전에 아래와 같이 새로운 줄을 추가해야합니다. group DGROUP _BSS _DATA 마이크로소프트 C/C++ 컴파일러는 OMF형식 또는 Win32형식의 목적 파일 모두 사용가능합니다.( 만약 OMF형식이 주어지면 내부족으로 WIN32형식의 정보로 변환됩니다. ) Win32 형식은 segment들이 DJGPP와 Linux형태로 선언된 것을 허용합니다. -f win32옵션으로 이 기능을 사용가능합니다. 이 형식의 목적코드의 확장자는 obj입니다. 1.4.3 코드 어셈블하기( Assembling the code ) 첫번째 단계는 코드를 어셈블하는 것입니다. 커맨드라인 상에서 아래와 같이 입력하세요 : nasm -f object-format first.asm위 명령에서 object-format이라는 문자열은 사용하시는 C컴파일러의 맞게 수정하셔서 입력하십시요. (elf, coff, obj, win32) 1.4.4 C 코드 컴파일하기(Compiling the C code) C컴파일러로 driver.c 파일을 컴파일하세요. 만약 DJGPP를 사용하신다면 아래와 같이 컴파일하세요 : gcc -c driver.c-c옵션은 링크는 아직 하지 않고 오로지 컴파일만 한다는 의미입니다. Linux,Borland 그리고 Microsoft컴파일러에서도 동일한 옵션으로 동작합니다. 1.4.5 목적파일들 링크하기(Linking the object files) 링크작업은 기계코드와 목적파일내의 데이터들 그리고 라이브러리 파일들을 연결하여 실행가능한 파일을 생성하는 것입니다. 아래에서 이 작업을 보여줍니다. C코드를 실행하기 위해서는 표준 C라이브러리와 특수한 startup code가 필요합니다. 그것은 C컴파일러가 정확한 파라메터로 링커를 호출하는 것을 쉽게하고 링커를 직접 호출하게 합니다. 예를 들어 DJGPP를 사용하여 첫번째 프로그램을 링크하는 것은 아래와 같습니다. : gcc -o first driver.o first.o asm_io.o 이렇게 하면 윈도우에서는 first.exe, 리눅스에서는 first라는 실행파일이 생성됩니다. 볼랜드 컴파일러에서는 아래와 같이 링크합니다. bcc32 first.obj driver.obj asm_io.obj볼랜드 컴파일러는 파일 목록중 첫번째 파일의 이름으로 실행파일의 이름을 생성합니다. 위의 경우 첫번째 입력파일이 first.obj이므로 first.exe라는 실행파일을 생성하게 됩니다. 컴파일과 링크를 한번에 수행할 수 있습니다.: gcc -o first driver.c first.o asm_io.ogcc는 driver.c를 컴파일 한 뒤에 링크를 수행할 것입니다. 1.4.6 어셈블리 리스팅파일의 이해(Understanding an assembly listing file) nasm에서 -l listing-file 옵션을 사용하면 nasm은 주어진 이름의 리스팅 파일을 생성합니다. 이 파일은 코드가 어떻게 어셈블되었는지를 보여줍니다. 아래에 17,18번째 줄의(data 세그먼트내의) 코드에 대한 리스팅파일 내용입니다.(리스팅파일내의 줄번호는 원본 소스파일의 줄번호와 다를 수 있습니다.) 48 00000000 456E7465722061206E- prompt1 db "Enter a number: ", 0 각 줄의 첫 번째 컬럼에는 줄번호, 두번째 컬럼에는 세그먼트내의 데이터의 오프셋 값(16진수)을 나타냅니다. 세번째 컬럼은 raw hex값이 기록되어져 있습니다. 이 경우에 hex데이터와 ASCII코드가 일치합니다. 마지막으로 소스파일상의 텍스트를 보여줍니다.두번째 컬럼의 오프셋 목록들은 실제적인 오프셋과 다릅니다. 각각의 모듈은 데이터 세그먼트(그리고 다른 세그먼트들도)내의 label들을 각각 가지고 잇습니다. 링크 단계에서 (1.4.5섹션을 참고하세요) 모든 데이터 세그먼트 label 선언들이 하나의 data세그먼트로 합쳐집니다. 새로운 마지막 오프셋은 링커에서 계산된 값이됩니다. 아래에 리스팅파일의 text세그먼트 섹션의 일부분입니다.(소스파일의 54~56번째줄) 94 0000002C A1[00000000] mov eax, [input1]세번째 컬럼에서는 어셈블리가 생성한 기계코드입니다. 종종 최종 코드를 아직 생성할수 없을 때가 있습니다. 예를 들어 94번째 라인의 input1에 대한 offset(또는 어드레스)는 코드가 링킹되기 전까지는 알수가 없습니다. 어셈블러는 mov명령에대한 op-code를 생성할 수 잇습니다.(리스팅의 A1) 그러나 아직은 정확한 값을 계산할 수 없으므로 사각 괄호안에 오프셋이 기록됩니다. 이 경우는 임시로 0의 offset이 입력됩니다. input1은 소스파일의 시작부분 bss세그먼트내에 위치해있기 때문입니다. input1이 bss세그먼트의 제일 처음 지점에 있다는 것을 기억하십시요. 코드가 링크되었을 때 링커는 정확한 위치의 오프셋을 입력할 것입니다. 96번째 줄과 같은 다른 명령어들은 어떠한 label들도 참조하지 않습니다. 이런 것들은 어셈블러가 완성된 기계코드를 생성할 수가 있습니다. Big and Little Endian에 대한 설명 만약 95번째 줄 기계코드의 사각괄호 안의 오프셋값은 독특합니다. input2 레이블은 offset4에 위치합니다.(이 파일내에 정의되어져있습니다.) 하지만 메모리내의 오프셋값이 00000004가 아닌 04000000으로 되어져있습니다. 왜일까요? 여러 프로세서들은 다중바이트 정수들을 특별한 순서로 메모리에 기록합니다. 수를 기록하는 두가지의 대중적인 방식으로 big endian과 little endian이 있습니다. Big endian은 상위 바이트가 먼저 기록되고 다음으로 그다음 큰 바이트가 기록되는 방식입니다. 예를들어 dword 00000004은 4개의 바이트로 00 00 00 04순서로 기록됩니다. IBM구조와 대부분의 RISC 프로세서 그리고 모토롤라의 모든 프로세서들은 big endian을 사용합니다. 그러나 Intel기반의 프로세서들은 little endian방식을 사용합니다. 00000004값이 메모리에 저장될 때는 04 00 00 00으로 기록됩니다. 이 형식은 CPU에 내장되어있으므로 변경할 수가 없습니다. 일반적으로 프로그래머는 이 형식을 사용하는 것에 대해서 걱정할 필요가 없습니다. 하지만 아래의 상황들에서는 매우 중요합니다.
1.5 Skeleton File Figure 1.7은 어셈블리 프로그램을 작성하기 위한 시작점으로 사용하는 skeleton 파일을 보여줍니다. 어셈블리 프로그램의 가장 기본적인 골격 구조만을 가진 코드입니다. =====Figure 1.7 Skeleton Program===== %include "asm_io.inc" segment .data ; ; 초기화되는 데이터를 이 곳에 선언합니다. ; segment .bss ; ; 초기화 되지 않는 데이터를 이 곳에 선언합니다. ; segment .text global _asm_main _asm_main: enter 0, 0 ; setup routine pusha ; ; 코드는 text세그먼트에 기록됩니다. 이 주석문의 앞 또는 뒤의 ; 코드는 수정해서는 안됩니다. popa mov eax, 0 leave ret -------------------------
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
| |||||