I want to write a simple x86 program which stores 2 arrays: one listing the names of students, and the other listing their grades. Assuming I want 8 bytes for each, I decided to simply allocate two segments of memory on the stack: (1) grade_ptr, which points to students' grades and holds 8 * [num_students] bytes; and (2) names_ptr, which points to an array of students' names and also holds holds 8 * [num_students] bytes.
I wrote the following code to accomplish this task:
mov rdi, [num_students]
mov rbp, rsp
shl rdi, 3
sub rsp, rdi
and rsp, -16 ; Align stack by rounding rsp down to multiple of 16.
mov [names_ptr], rsp
mov rdi, [num_students]
shl rdi, 3
sub rsp, rdi
and rsp, -16
mov [grades_ptr], rsp
When I run the program, names_ptr
overflows and begins to run into the space occupied by grades_ptr
, though I am unsure why. See the below output as an example.
Enter number students: 3
Enter student 0's name: 1
Enter student 0's grade: 60
Enter student 1's name: 2
Enter student 1's grade: 70
Enter student 2's name: 3
Enter student 2's grade: 80
Grade is 50 (ASCII code for "2")
Grade is 51 (ASCII code for "3")
Grade is 80
As you can see, each name overwrites the previous grade. I think the issue stems from how I allocated memory on the stack in the code at the top. Can someone identify my error and point me in the right direction?
section .data
num_students_prompt db "Enter number students: ",0
int_format db "%lld",0 ; lld to read 8-byte int.
stud_name_prompt db "Enter student %d's name: ",0
stud_name_format db "s",0 ; 10-char(byte) str.
stud_grade_prompt db "Enter student %d's grade: ",0
min_grade_format db "Min grade is: %d",10,0
max_grade_format db "Max grade is: %d",10,0
avg_grade_format db "Avg grade is: %d",10,0
ptr_format db "Ptr is: %d",10,0
grade_format db "Grade is %d",10,0
sum_format db "Sum grade is: %d",10,0
section .bss
num_students resq 1
grade_sum resq 1
min_grade resq 1
max_grade resq 1
avg_grade resq 1
names_ptr resq 2
grades_ptr resq 2
section .text
global main
extern scanf, printf, malloc
main:
push rbp
mov rbp, rsp
call _prompt_num_els
xor rax, rax ; al stores number vector params to scanf, so it must be empty
mov rdi, int_format ; before a printf/scanf call.
lea rsi, [num_students]
call scanf
mov rdi, [num_students]
shl rdi, 3
sub rsp, rdi
and rsp, -16
mov [names_ptr], rax
xor eax, eax
mov rdi, ptr_format
mov rsi, [names_ptr]
call printf
mov rdi, [num_students]
mov rbp, rsp
shl rdi, 3
sub rsp, rdi
and rsp, -16 ; Solve stack alignment by rounding stack ptr down to multiple of 16.
mov [grades_ptr], rsp
xor eax, eax
mov rdi, ptr_format
mov rsi, [grades_ptr]
call printf
xor rbx, rbx
_get_student:
call _prompt_name
xor rax, rax
mov rdi, stud_name_format
lea rsi, [names_ptr rbx*8]
call scanf
call _prompt_grade
xor rax, rax
mov rdi, int_format
lea rsi, [grades_ptr rbx*8]
call scanf
inc rbx
cmp rbx, [num_students]
jl _get_student
mov rax, [grades_ptr]
mov [min_grade], rax ; Before loop, we'll set min/max equal to first grade.
mov [max_grade], rax
xor rbx, rbx
_get_stats:
mov rax, [grades_ptr rbx*8]
add [grade_sum], rax
xor eax, eax
mov rdi, grade_format
mov rsi, [grades_ptr rbx*8]
call printf
cmp [min_grade], rax
jg _grade_is_min
cmp [max_grade], rax
jl _grade_is_max
jmp _loop_logic
_grade_is_min:
mov [min_grade], rax
jmp _loop_logic
_grade_is_max:
mov [max_grade], rax
_loop_logic:
inc rbx
cmp rbx, [num_students]
jl _get_stats
xor edx, edx ; rdx contains upper 64 bits of dividend.
mov rax, [grade_sum] ; rax contains lower 64 bits.
mov rcx, [num_students]
div rcx ; Compute (rdx:rax)/[num_students].
mov [avg_grade], rax
xor edx, edx
mov rdi, sum_format
mov rsi, [grade_sum]
call printf
xor rax, rax
mov rdi, max_grade_format
mov rsi, [max_grade]
call printf
xor rax, rax
mov rdi, min_grade_format
mov rsi, [min_grade]
call printf
xor rax, rax
mov rdi, avg_grade_format
mov rsi, [avg_grade]
call printf
mov rsp, rbp
pop rbp
mov rax, 60
mov rdi, 0
syscall ; Exit w/ return code 0.
_prompt_name:
xor rax, rax
mov rdi, stud_name_prompt
mov rsi, rbx
call printf
ret
_prompt_grade:
xor rax, rax
mov rdi, stud_grade_prompt
mov rsi, rbx
call printf
ret
_prompt_num_els:
xor eax, eax
mov rdi, num_students_prompt
call printf
ret
CodePudding user response:
In several places there is code like this:
lea rsi, [names_ptr rbx*8]
or
mov rax, [grades_ptr rbx*8]
That doesn't indirect through a pointer in memory like you want. What it does is index relative to the address of the variable names_ptr, rather than to the pointer stored in that variable.
To fix this, you have to load the pointer into a register and then do the index operation. So you could replace the first one with something like:
mov rsi, [names_ptr]
lea rsi, [rsi rbx*8]
Even better would be to take advantage of the registers available. Put names_ptr in r14 and grades_ptr in r15. Then you can use lea rsi, [r14 rbx*8]
without an additional load each time.
Be sure to push r14 and r15 (and also rbx) at the beginning of the function. It's not strictly necessary since you don't return from the function, but it's good habit.