-
Notifications
You must be signed in to change notification settings - Fork 145
/
Copy pathfile_ops.s
194 lines (165 loc) · 5.94 KB
/
file_ops.s
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
/* Perry Kivolowitz
Example of file operations.
*/
.text
.global main
.align 2
/* This program will
* open() a file in the current directory,
* write() some text to it,
* seek back to the beginning of the file,
* read() each line, printing it
* close() the file
*/
retval .req w27
fd .req w28
main: stp x29, x30, [sp, -16]!
stp x27, x28, [sp, -16]!
bl open_file
// w0 will contain either the file descriptor of the new
// file or -1 for a failure. Note that the value in w0
// has also been copied to "fd" - a register alias.
cmp w0, wzr
bge 1f
// If we get here, the open has failed. Use perror() to
// print a meaningful error and branch to exit. The return
// code of the program will be set to non-zero inside fail.
ldr x0, =fname
bl fail
b 99f
1: // When we get here, the file is open. Write some data to it.
// If write_file returns non-zero, it signifies an error. If
// so, branch to the file closing code since the file is open
// after printing an error message.
bl write_data
cbz w0, 10f
// If we get here, there was an error in write_data. Print
// a reasonable error message then branch to the clean usleep
// code.
ldr x0, =wf // load legend
bl fail // print error
b 50f // branch to clean up.
// Seek back to position zero preparing to read the file back.
// The return value in x0 (off_t) is the return value of
// lseek().
10: bl seek_zero
cbz x0, 20f
// If we get here, the seek failed. Cause a reasonable
// message to be printed then branch to the clean up code.
ldr x0, =sf
bl fail
b 50f
20: // When we get here, we have to read from the file and print
// the results. To ignore the complexity of memory allocation
// and buffer overrun potential, we'll read one character at a
// time looking the end-of-file.
// ssize_t read(int fildes, void *buf, size_t nbyte);
mov w0, fd
ldr x1, =buffer
mov x2, 1
bl read
// Check the return value - should be 1.
cbz x0,50f // zero means EOF - that's OK.
// If x0 is negative, that IS a problem.
cmp x0, xzr
bge 25f
// The return value is negative - this is an error.
ldr x0, =rf
bl fail
b 99f
25: // Write the character sitting in buffer to the console.
mov w0, 1
ldr x1, =buffer
mov x2, 1
bl write
// We will ignore the return value for the sake of brevity.
// There are plenty of examples of handling a potential error
// elsewhere in this code.
// --
b 20b
// When we get here, we are done. Close the file.
50: mov w0, fd
bl close
mov retval, wzr
99: ldp x27, x28, [sp], 16
ldp x29, x30, [sp], 16
mov w0, retval
ret
/* open_file()
This function attempts to open a file for both reading and
writing. Return values will be checked to ensure the file is
opened. If successful, the fd is returned (and is squirreled
away in register "fd"). If unsuccessful, the -1 returned by
open() is passed back to the caller.
Explanation of the magic numbers:
int open(const char *pathname, int flags, mode_t mode);
octal 102 for flags is O_RDRW | O_CREAT
octal 600 for mode is rw------- i.e. read and write for
the owner but no permissions for anyone else.
There is a version of open() that takes two parameters. However,
if O_CREAT is specified, the three parameter version is required.
*/
.equ O_FLAGS, 0102
.equ O_MODE, 0600
open_file:
stp x29, x30, [sp, -16]!
ldr x0, =fname
mov w1, O_FLAGS
mov w2, O_MODE
bl open
mov fd, w0
ldp x29, x30, [sp], 16
ret
/* This function uses perror() to print a meaningful error
message in the event of a failure. The string value
passed to perror() arrives to us as a pointer in x0.
*/
fail:
stp x29, x30, [sp, -16]!
bl perror
mov retval, 1
ldp x29, x30, [sp], 16
ret
/* ssize_t write(int fd, const void *buf, size_t count);
This function will write a string to the file descriptor contained
in "fd" (a register alias).
*/
write_data:
stp x29, x30, [sp, -16]!
str x20, [sp, -16]!
mov w0, fd // file descriptor
ldr x1, =txt // address to print from
ldr x2, =txt_s // load pointer to size
ldr x2, [x2] // dereference the pointer
mov w20, w2 // need this value for error check.
bl write
cmp x0, x20 // Did we write the expected amount?
bne 90f
// successful write - return 0
mov x0, xzr
b 99f
90: // failure - ensure we return non-zero!
mov x0, 1
99: ldr x20, [sp], 16
ldp x29, x30, [sp], 16
ret
/* off_t lseek(int fd, off_t offset, int whence);
*/
seek_zero:
stp x29, x30, [sp, -16]!
mov w0, fd // file descriptor
mov x1, xzr // beginning of file
mov w2, wzr // SEEK_SET - absolute offset
bl lseek
ldp x29, x30, [sp], 16
ret
.data
prog: .asciz "file_ops"
wf: .asciz "write failed"
rf: .asciz "read failed"
sf: .asciz "lseek failed"
fname: .asciz "test.txt"
txt: .asciz "some data\n"
txt_s: .word txt_s - txt - 1 // strlen(txt)
buffer: .word 0
.end