summaryrefslogtreecommitdiff
path: root/PGU/CHAP9/memalloc.s
blob: 6f354de3f6b64c9bcc1b4004a25c12bee803a546 (plain)
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
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303

# Alloc and Dealloc memory as requested
#
# Programs using these routines will ask
# for a certain size of memory. We actually
# use more than that size for metadata,
# but we put it at the beginning, before the
# pointer we hand back. We add a size field and
# and AVAILABLE/UNAVAILABLE marker.
#
# The whole memory slot looks like this:
#
####################################################
# Available marker | Size | Actual memory location #
####################################################
#			    ^-- returned pointer points
#				here
#
# The calling program won't see our metadata.

### GLOBAL VARS ###
.section .data

############################### DEBUG INFO ################################

# Originally, these labels heap_begin and current_break are .long long...
# This implicitly means 4 bytes for each variable. This was causing the instructions
# in allocate_init to overlap the variables when writing to them:

# The following instructions were the problem:
#	movq %rax, current_break
#			allocated at 0x402011
#	movq %rax, heap_begin
#			allocated at 0x40200d
#
# Static allocation grows up in memory... But, when writing %rax to heap_begin,
# due the variable sizes being 4bytes, the instruction was corrupting
# current_break variable, causing the program to crash later in the allocation
# loop, because the heap_begin allocation, consequently caused current_break to be
# zero causing a NULL ptr dereference (or simply a SEGFAULT):

# =>   0x000000000040114e <+5>:	mov    0x8(%rax),%rdx
# Program received signal SIGSEGV, Segmentation fault.

# allocate_init() disassemble:
#   0x0000000000401105 <+0>:	push   %rbp
#   0x0000000000401106 <+1>:	mov    %rsp,%rbp
#   0x0000000000401109 <+4>:	mov    $0xc,%rax
#   0x0000000000401110 <+11>:	mov    $0x0,%rdi
#   0x0000000000401117 <+18>:	syscall
#   0x0000000000401119 <+20>:	inc    %rax
#   0x000000000040111c <+23>:	mov    %rax,0x402011
#   0x0000000000401124 <+31>:	mov    %rax,0x40200d
#=> 0x000000000040112c <+39>:	mov    %rbp,%rsp
#   0x000000000040112f <+42>:	pop    %rbp
#   0x0000000000401130 <+43>:	ret
#
# (gdb) info register rax
# rax            0x403001            4206593
# (gdb) p /x *0x40200d
# $5 = 0x403001
# (gdb) p /x *0x402011
# $6 = 0x0		<-- Here, the address of current_break got zeroed after
#			    heap_being has been changed

############################# END OF DEBUG INFO #############################

# Points to the beginning of the memory we are managing
heap_begin:
	.quad 0

# Points to one locaiton past the memory we are managing
current_break:
	.quad 0

### HEADER STRUCTURE INFORMATION ###

# To make things simpler, we use one word for
# each field in the header

.equ HEADER_SIZE, 16	 # size of space for memory region header
.equ HDR_AVAIL_OFFSET, 0 # Location of the 'available' flag in the header
.equ HDR_SIZE_OFFSET, 8	 # Location of the size field in the header


### CONSTANTS ###
.equ UNAVAILABLE, 0
.equ AVAILABLE, 1

.equ SYS_BRK, 12	# syscall number for brk() syscall in x86_64


.section .text

### FUNCTIONS ###

## allocate_init ##
#
# Call this function to initialize the functions by setting heap_begin and
# current_break. No parameters and no return value

.globl allocate_init
.type allocate_init, @function

allocate_init:
	pushq %rbp		# standard function stuff
	movq  %rsp, %rbp

	# If brk() syscall is called with a 0 in %rdi, it returns the last valid
	# usable address
	movq $SYS_BRK, %rax
	movq $0, %rdi
	syscall

	incq %rax		 # brk(0) returns the current break in %rax, we want the
				 # value after that

	movq %rax, current_break # Store the current break (actually the address
				 # after that)

	movq %rax, heap_begin	 # Our heap starts where the break is now. First
				 # address of uninitialized memory. This will
				 # cause the allocate function to get more memory
				 # from Linux the first time it is run.

	movq %rbp, %rsp		# Exit the function
	popq %rbp
	ret

## END of allocate_init ##


## allocate ##
#
# Grab a section of memory.
# - Checks to see if there are any free blocks
# - If not, asks Linux for more memory for the
#   heap through brk() syscall
#
# - Receives on parameter, the size of the memory block
#   we want to allocate
#
# - Returns the address of the allocated memory in %rax, or 0
#   if there is no memory available on the system
#
# ### Process
#
# %rcx - holds the size of requested memory (first/only parameter)
# %rax - current memory region being examined
# %rbx - Memory address past the end of the heap
# %rdx - size of the current memory region
#
# We scan through each memory region starting with heap_begin. We look at the
# size of each one and if it has been allocated. If it's big enough for the
# requested size, and it's available, we grab that one. If we do not find a
# region large enough, we ask Linux for more memory, which in case, moves the
# current_break up.

.globl allocate
.type allocate, @function
.equ ST_MEM_SIZE, 16	# Stack position of the memory size to allocate

allocate:
	pushq %rbp
	movq %rsp, %rbp

	movq ST_MEM_SIZE(%rbp), %rcx	# %rcx now holds the memory size we are
				# looking for (first/only parameter)

	movq heap_begin, %rax	# %rax will hold the current search location
	movq current_break, %rbx

	alloc_loop_begin:		# Scan memory regions

		cmpq %rbx, %rax		# heap needs more memory if these are
					# equal. %rax will hold the end of the
					# next memory region at each iteraction
					# until it hits the current_break.

		je move_break

		# Retrieve the size of this mem slot
		movq HDR_SIZE_OFFSET(%rax), %rdx

		cmpq $UNAVAILABLE, HDR_AVAIL_OFFSET(%rax) # If the space is
							  # unavailable, i.e.
							  # already in use.. Go
		je next_location			  # to the next one.

		cmpq %rdx, %rcx		# If the slot is available, check if
		jle allocate_here	# it's big enough.

	next_location:
		addq $HEADER_SIZE, %rax	# Total size of the memory region is the
					# sum of the size requested (currently
					# at %rdx), plus the 16 bytes for the
		addq %rdx, %rax		# header.

		jmp alloc_loop_begin	# Go look at the next location

	allocate_here:			# If we've made it here, that means that
					# the region header of the region to
					# allocate is in %rax

		# Mark space as unavailable
		movq $UNAVAILABLE, HDR_AVAIL_OFFSET(%rax)
		addq $HEADER_SIZE, %rax		# move %rax past the header, so it
						# points to the usable memory. Such
						# address is returned to the user.

		# Normal function return
		movq %rbp, %rsp
		popq %rbp
		ret

	move_break:	# We have exhausted all addressable memory, we need to
			# get more from Linux
			# %rbx holds the current endpoint of the data, and
			# %rcx its size.

		addq $HEADER_SIZE, %rbx	# We need to increase %rbx to where we _want_
					# memory to end. So we account for the header
		addq %rcx, %rbx		# and the user's requested size

		# Ask Linux for more memory

		# We'll need the values in these registers, so save them...
		pushq %rax
		pushq %rbx
		pushq %rcx

		movq $SYS_BRK, %rax	# Set new break by calling brk()
		movq %rbx, %rdi		# x86_64 uses %rdi for first parameter
		syscall

		# brk() returns 0 for error or the address we asked for (or larger if it
		# needs to be rounded up). We don't really care here where it ends up
		# setting the break as long as it isn't 0.

		cmpq $0, %rax		# Check for errors
		je error

		# Restore our registers
		popq %rcx
		popq %rbx
		popq %rax

		# Set this memory as unavailable since we are about to give it away
		movq $UNAVAILABLE, HDR_AVAIL_OFFSET(%rax)
		movq %rcx, HDR_SIZE_OFFSET(%rax) # Set memory size

		addq $HEADER_SIZE, %rax		# Set %rax to the address being
						# returned to the user (user
						# doesn't know anything about the headers

		movq %rbx, current_break	# Save the current break. In
						# reality it may be larger due
						# rounding, but we don't care
						# about memory footprint here

		movq %rbp, %rsp
		popq %rbp
		ret

	error:
		movq $0, %rax		# Return 0 on error
		movq %rbp, %rsp
		popq %rbp
		ret
##### END OF allocate ######

## deallocate ##
#
# Give back a region of memory to the pool after user is done
# with it
#
# Parameter: Address of the memory to be freed
# No return value
#
# The memory address returned to the user starts at 2 words beyond its header,
# all we need to do here is mark the memory slot as free (available).
# By now we don't care about moving the break back.
.globl deallocate
.type deallocate, @function

# Stack position of the memory region to be free (function's parameter)
.equ ST_MEMORY_SEG, 8	# We are not saving %rbp here so we just need to skip ret
			# address

deallocate:
	# Function is too simple, no need for fancy function stuff

	# Get the address of the memory to free (Normally this is 16(%rbp), but
	# since we didn't push %rbp or move %rsp to %rbp, we can just do 8(%rsp)
	movq ST_MEMORY_SEG(%rsp), %rax

	# Get the pointer to the beginning of the region (i.e. to the header)
	subq $HEADER_SIZE, %rax

	# Mark it as available
	movq $AVAILABLE, HDR_AVAIL_OFFSET(%rax)
	ret
##### END OF deallocate #####