Tuuna Computer Science

[어셈블리] 파일 다루기 ( 소문자를 대문자로 바꾸기 ) 본문

Assembly

[어셈블리] 파일 다루기 ( 소문자를 대문자로 바꾸기 )

GuTTe 2018. 8. 6. 21:24

  • 파일 다루기 

유닉스 파일 :: 프로그램에 의해 생성되며 바이트의 연속된 스트림으로 읽을 수 있다. 

파일에 주어진 고유한 이름(번호)으로 파일에 접근할 수 있다 -> 파일 디스크립터 

  • open의 시스템 콜 :: 5번 

open은 파일의 이름, 모드, 권한등을 인자로 받는다. 

파일의 이름은 %ebx에 저장하고 

모드는 %ecx에 저장한다. 

읽기로 연다면 0을 쓰기위해서 03101을 저장하면 된다. (반드시 가장 앞에 0을 붙여야 함)

권한은 %edx에 저장 ex) 0666 모든 유저 읽기 쓰기 가능

리턴값은 %eax에 저장된다. :: 파일의 번호 리턴


  • read의 시스템 콜 3번 :: 리턴 값 읽어들인 바이트 수 

파일 지정자 :: %ebx에 저장 

읽어들인 데이터가 저장될 버퍼의 주소 :: %ecx에 주소값을 저장

버퍼의 크기 :: %edx에 저장 

버퍼는 section .bss를 이용해서 지정할 수 있다. 


  • write의 시스템 콜 4번 read함수와 기능 동일 (전달 제외) 


  • 버퍼와 .bss

버퍼 :: 대량의 데이터를 전달하기 위해서 사용되는 연속되는 블럭영역 

고정된 크기의 버퍼와 동적크기의 버퍼로 나뉜다. 

고정된 크기의 버퍼는 .long이나 .byte를 이용해서 버퍼로 사용될 크기를 직접 저장 

단점 :: 저장공간의 낭비, 자료형의 판단 미스 

해결 :: .text, .data 섹션을 사용 

.bss 섹션은 저장공간을 확보할 수 있지만 초기화 할수는 없다. 

.data 섹션의 경우 공간을 확보하고 값을 초기화 할 수 있다. 

이렇듯 초기화할 필요없는 공간을 확보하고자 할 때 주로 사요된다. (.bss) 

.section .bss
    .lcomm my_buffer, 500 # .lcomm은 500byte의 공간을 할당하고, 공간을 가리키기 위한 심볼로 my_buffer을생성 

movl $my_buffer, %ecx     #데이터가 저장될 버퍼의 주소 값 
movl 500, %edx                 #버퍼의 크기 
movl 3, %eax                     #시스템 콜 
int $0x80                           #해당 시스템 콜 처리 

위 코드는 read(fd, my_buffer, 500)이다. 

$my_buffer 함으로써 immediate모드 어드레스 상태가 되고 버퍼의 시작위치를 가리키게 된다. 




.equ는 일종의 별칭을 만들어 주기 위해서 사용되는 지시어 C언어의 #define과 비슷한 일

.equ  LINUX_SYSCALL 0x80 

int $LINUX_SYSCALL을 사용 가능 ($0x80)  

  1 # 하는일 : 파일로 부터 문자를 읽어들이고 대문자로 바꾼후 다른 파일로 저장한다.
  2
  3 #프로세스 : 1. 읽을 파일을 연다. 2. 쓸 파일을 연다. 3. 파일의 끝이 아니라면 반복한다
  4
  5 #반복 : 파일로 부터 문자열을 읽어서 메모리에 넣는다.
  6 #       메모리로 부터 각 문자를 대문자로 변경한다.
  7 #       변경된 문자는 파일로 저장한다.
  8
  9 .section .data
10
11 #상수들
12
13 .equ SYS_OPEN, 5
14 .equ SYS_WRITE, 4
15 .equ SYS_READ, 3
16 .equ SYS_CLOSE, 6
17 .equ SYS_EXIT, 1
18
19 #open()에 사용할 옵션
20
21 .equ O_RDONLY, 0 #읽기만 한다?
22 .equ O_CREATE_WRONLY_TRUNC, 03101 #쓰기 전용인데 없으면 만들고 초기화한 상태에서 쓰기 전용
23
24 #표준 파일 지정자
25
26 .equ STDIN, 0
27 .equ STDOUT, 1
28 .equ STRERR, 2
29
30 #시스템 콜 중단(interrupt)
31
32 .equ LINUX_SYSCALL, 0x80
33 .equ END_OF_FILE, 0 #파일의 끝을 검사하기 위해서 사용
34                     #read()의 리턴값과 비교한다.
35
36 .equ NUMBER_ARGUMENTS, 2 #?
37
38 .section .bss
39 #버퍼 - 파일로 부터 데이터를 읽어들인 데이터를 저장하기 위한 목적으로 사용
40
41 #버퍼의 크기는 16000을 초과할 수 없다
42 .equ BUFFER_SIZE, 500
43 .lcomm BUFFER_DATA, BUFFER_SIZE #BUFFER_DATA라는 버퍼가 500byte크기의 공간을 할당한다.
44
45 .section .text
46
47 # 스택 위치
48
49 .equ ST_SIZE_RESERVE, 8 #?
50 .equ ST_FD_IN, -4
51 .equ ST_FD_OUT, -8
52 .equ ST_ARGC, 0 #인자의 개수
53 .equ ST_ARGV_0, 4 #프로그램의 이름       파라미터들이네  
54 .equ ST_ARGV_1, 8 #읽어들일 파일의 이름
55 .equ ST_ARGV_2, 12# 저장할 파일의 이름
56
57
58 .global _start
59
60 _start:
61
62 ###초기화 관련###
42 .equ BUFFER_SIZE, 500
43 .lcomm BUFFER_DATA, BUFFER_SIZE #BUFFER_DATA라는 버퍼가 500byte크기의 공간을 할당한다.
44
45 .section .text
46
47 # 스택 위치
48
49 .equ ST_SIZE_RESERVE, 8 #?
50 .equ ST_FD_IN, -4
51 .equ ST_FD_OUT, -8
52 .equ ST_ARGC, 0 #인자의 개수
53 .equ ST_ARGV_0, 4 #프로그램의 이름       파라미터들이네  
54 .equ ST_ARGV_1, 8 #읽어들일 파일의 이름
55 .equ ST_ARGV_2, 12# 저장할 파일의 이름
56
57
58 .global _start
59
60 _start:
61
62 ###초기화 관련###
63 #스택포인터를 저장한다.
64 movl %esp, %ebp
65
66 #스택에 파일 지정자를 저장하기 위한 공간을 할당한다.
67 subl $ST_SIZE_RESERVE, %esp
68
69 open_files:
70
71 open_fd_in:
72 ### 읽어들일 파일을 연다.
73 movl $SYS_OPEN, %eax #system call
74 movl ST_ARGV_1(%ebp), %ebx #ebx에 파일이름을 넣는다.
75 movl $O_RDONLY, %ecx #read_only flag
76 movl $0666, %edx #파일권한을 0666
77 #위 3개는 모두 open함수의 인자들임
78 int $LINUX_SYSCALL #리눅스 호출
79
80 store_fd_in:
81 movl %eax, ST_FD_IN(%ebp) #리턴된 파일 지정자를 저장한다.
82
83 open_fd_out:
84 #저장할 파일 열기
85
86 movl $SYS_OPEN, %eax #파일 열기
87 movl ST_ARGV_2(%ebp), %ebx #열 파일이름을 지정
88 movl $O_CREATE_WRONLY_TRUNC, %ecx #쓰기위한 플래그 설정
89 movl $0666, %edx #파일 권한
90 int $LINUX_SYSCALL
91
92 store_fd_out:
93 movl %eax, ST_FD_OUT(%ebp) #파일 지정자를 저장한다.
94
95
96 read_loop_begin:
97
92 store_fd_out:
93 movl %eax, ST_FD_OUT(%ebp) #파일 지정자를 저장한다.
94
95
96 read_loop_begin:
97
98 ### 파일로 부터 읽어들이는 부분
99 movl $SYS_READ, %eax
99 movl $SYS_READ, %eax
100 movl ST_FD_IN(%ebp), %ebx #읽어들일 파일 지정자
101 movl $BUFFER_DATA, %ecx #버퍼의 주소 값을 넘김
102 movl $BUFFER_SIZE, %edx #읽어들일 크기를 준다.
103 int $LINUX_SYSCALL #리턴 값은 %eax에
104
105 cmpl $END_OF_FILE, %eax #파일의 끝인지 검사한다.
106 jle end_loop #만약 끝이라면
107
108
109 continue_read_loop:
110 ###입력된 문자를 대문자로 변경하는 부분 ###
111 pushl $BUFFER_DATA #버퍼의 위치
112 pushl %eax #버퍼의 사이즈 ( read함수의 리턴값은 읽어들인 바이트수다)
113 call convert_to_upper #대문자 변경함수 호출
114 popl %eax
115 addl $4, %esp
116
117 ###변경된 문자를 파일에 쓴다. ###
118
119 movl %eax, %edx #버퍼의 크기
120 movl $SYS_WRITE, %eax
121 movl ST_FD_OUT(%ebp), %ebx #저장에 사용할 파일 지정자
122 movl $BUFFER_DATA, %ecx #버퍼의 위치를 준다
123 int $LINUX_SYSCALL
124
125 ###루프를 수행
126 jmp read_loop_begin #이거 어떻게 탈출 그럼?
127
128 end_loop:
129 ###파일을 닫는다 ###
130 movl $SYS_CLOSE, %eax
131 movl ST_FD_OUT(%ebp), %ebx #왜 2번 파일을 닫지 write하는 파일
132 int $LINUX_SYSCALL
133
134 ### 종료 코드 ###
135
136 movl $SYS_EXIT, %eax
137 movl $0, %ebx
138 int $LINUX_SYSCALL
139
140 # 소문자를 대문자로 변경
141
142 .equ LOWERCASE_A, 'a'
143 .equ LOWERCASE_Z, 'z'
144 .equ UPPER_CONVERSION, 'A' - 'a'
145
146 .equ ST_BUFFER_LEN, 8
147 .equ ST_BUFFER, 12
148
149 convert_to_upper:
150
151 pushl %ebp
152 movl %esp, %ebp
153 movl ST_BUFFER(%ebp), %eax #12면 메모리를 그렸을 때 read했던 파일디스크립터다
154 movl ST_BUFFER_LEN(%ebp), %ebx
155 movl $0, %edi
156
157 cmpl $0, %ebx #loop count?
158 je end_convert_loop
159
160
161 convert_loop:
162
163 movb (%eax, %edi, 1), %cl #cl = eax에서 + edi * 1이렇게네
164
165 cmpb $LOWERCASE_A, %cl
166 jl next_byte #cl이 더크다면
167 cmpb $LOWERCASE_Z, %cl
168 jg next_byte #cl이 더 작다면
169
170 addb $UPPER_CONVERSION, %cl #아스키 값을 더함
171 movb %cl, (%eax, %edi, 1) #다시 그 위치에 가져다 놓는다.  
172
173 next_byte:
174
175 incl %edi
176 cmpl %edi, %ebx # %ebx는 버퍼에 적인 문자열의 길이
177 jne convert_loop
178
179 end_convert_loop:
180
181 movl %ebp, %esp
182 popl %ebp
183 ret





밑에 사진은 위 코드의 메모리를 그림으로 본떴다.

여러분도 어셈블리공부하다가 메모리구조를 까먹었다면 그리길 추천합니다...



Comments