Cannot modify data segment register. When tried General Protection Error is thrown - gcc

I have been trying to create an ISR handler following this
tutorial by James Molloy but I got stuck. Whenever I throw a software interrupt, general purpose registers and the data segment register is pushed onto the stack with the variables automatically pushed by the CPU. Then the data segment is changed to the value of 0x10 (Kernel Data Segment Descriptor) so the privilege levels are changed. Then after the handler returns those values are poped. But whenever the value in ds is changed a GPE is thrown with the error code 0x2544 and after a few seconds the VM restarts. (linker and compiler i386-elf-gcc , assembler nasm)
I tried placing hlt instructions in between instructions to locate which instruction was throwing the GPE. After that I was able to find out that the the `mov ds,ax' instruction. I tried various things like removing the stack which was initialized by the bootstrap code to deleting the privilege changing parts of the code. The only way I can return from the common stub is to remove the parts of my code which change the privilege levels but as I want to move towards user mode I still want them to stay.
Here is my common stub:
isr_common_stub:
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
xor eax,eax
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor
mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call isr_handler
xor eax,eax
pop eax
mov ds, ax ; This is the instruction everything fails;
mov es, ax
mov fs, ax
mov gs, ax
popa
iret
My ISR handler macros:
extern isr_handler
%macro ISR_NOERRCODE 1
global isr%1 ; %1 accesses the first parameter.
isr%1:
cli
push byte 0
push %1
jmp isr_common_stub
%endmacro
%macro ISR_ERRCODE 1
global isr%1
isr%1:
cli
push byte %1
jmp isr_common_stub
%endmacro
ISR_NOERRCODE 0
ISR_NOERRCODE 1
ISR_NOERRCODE 2
ISR_NOERRCODE 3
...
My C handler which results in "Received interrupt: 0xD err. code 0x2544"
#include <stdio.h>
#include <isr.h>
#include <tty.h>
void isr_handler(registers_t regs) {
printf("ds: %x \n" ,regs.ds);
printf("Received interrupt: %x with err. code: %x \n", regs.int_no, regs.err_code);
}
And my main function:
void kmain(struct multiboot *mboot_ptr) {
descinit(); // Sets up IDT and GDT
ttyinit(TTY0); // Sets up the VGA Framebuffer
asm volatile ("int $0x1"); // Triggers a software interrupt
printf("Wow"); // After that its supposed to print this
}
As you can see the code was supposed to output,
ds: 0x10
Received interrupt: 0x1 with err. code: 0
but results in,
...
ds: 0x10
Received interrupt: 0xD with err. code: 0x2544
ds: 0x10
Received interrupt: 0xD with err. code: 0x2544
...
Which goes on until the VM restarts itself.
What am I doing wrong?

The code isn't complete but I'm going to guess what you are seeing is a result of a well known bug in James Molloy's OSDev tutorial. The OSDev community has compiled a list of known bugs in an errata list. I recommend reviewing and fixing all the bugs mentioned there. Specifically in this case I believe the bug that is causing problems is this one:
Problem: Interrupt handlers corrupt interrupted state
This article previously told you to know the ABI. If you do you will
see a huge problem in the interrupt.s suggested by the tutorial: It
breaks the ABI for structure passing! It creates an instance of the
struct registers on the stack and then passes it by value to the
isr_handler function and then assumes the structure is intact
afterwards. However, the function parameters on the stack belongs to
the function and it is allowed to trash these values as it sees fit
(if you need to know whether the compiler actually does this, you are
thinking the wrong way, but it actually does). There are two ways
around this. The most practical method is to pass the structure as a
pointer instead, which allows you to explicitly edit the register
state when needed - very useful for system calls, without having the
compiler randomly doing it for you. The compiler can still edit the
pointer on the stack when it's not specifically needed. The second
option is to make another copy the structure and pass that
The problem is that the 32-bit System V ABI doesn't guarantee that data passed by value will be unmodified on the stack! The compiler is free to reuse that memory for whatever purposes it chooses. The compiler probably generated code that trashed the area on the stack where DS is stored. When DS was set with the bogus value it crashed. What you should be doing is passing by reference rather than value. I'd recommend these code changes in the assembly code:
irq_common_stub:
pusha
mov ax, ds
push eax
mov ax, 0x10 ;0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp ; At this point ESP is a pointer to where GS (and the rest
; of the interrupt handler state resides)
; Push ESP as 1st parameter as it's a
; pointer to a registers_t
call irq_handler
pop ebx ; Remove the saved ESP on the stack. Efficient to just pop it
; into any register. You could have done: add esp, 4 as well
pop ebx
mov ds, bx
mov es, bx
mov fs, bx
mov gs, bx
popa
add esp, 8
sti
iret
And then modify irq_handler to use registers_t *regs instead of registers_t regs :
void irq_handler(registers_t *regs) {
if (regs->int_no >= 40) port_byte_out(0xA0, 0x20);
port_byte_out(0x20, 0x20);
if (interrupt_handlers[regs->int_no] != 0) {
interrupt_handlers[regs->int_no](*regs);
}
else
{
klog("ISR: Unhandled IRQ%u!\n", regs->int_no);
}
}
I'd actually recommend each interrupt handler take a pointer to registers_t to avoid unnecessary copying. If your interrupt handlers and the interrupt_handlers array used function that took registers_t * as the parameter (instead of registers_t) then you'd modify the code:
interrupt_handlers[r->int_no](*regs);
to be:
interrupt_handlers[r->int_no](regs);
Important: You have to make these same type of changes for your ISR handlers as well. Both the IRQ and ISR handlers and associated code have this same problem.

Related

In Clang/LLVM x86-64 inline assembly, how do I say I clobbered the x87/media state?

I'm writing some x86-64 inline assembly that might affect the floating point and media (SSE, MMX, etc.) state, but I don't feel like saving and restoring the state myself. Does Clang/LLVM have a clobber constraint for that?
(I'm not too familiar with the x86-64 architecture or inline assembly, so it was hard to know what to search for. More details in case this is an XY problem: I'm working on a simple coroutine library in Rust. When we switch tasks, we need to store the old CPU state and load the new state, and I'd like to write as little assembly as possible. My guess is that letting the compiler take care of saving and restoring state is the simplest way to do that.)
If your coroutine looks like an opaque (non-inline) function call, the compiler will already assume the FP state is clobbered (except for control regs like MXCSR and the x87 control word (rounding mode)), because all the FP regs are call-clobbered in the normal function calling convention.
Except for Windows, where xmm6..15 are call-preserved.
Also beware that if you're putting a call inside inline asm, there's no way to tell the compiler that your asm clobbers the red zone (128 bytes below RSP in the x86-64 System V ABI). You could compile that file with -mno-redzone or use add rsp, -128 before call to skip over the red-zone that belongs to the compiler-generated code.
To declare clobbers on the FP state, you have to name all the registers separately.
"xmm0", "xmm1", ..., "xmm15" (clobbering xmm0 counts as clobbering ymm0/zmm0).
For good measure you should also name "mm0", ..., "mm7" as well (MMX), in case your code inlines into some legacy code using MMX intrinsics.
To clobber the x87 stack as well, "st" is how you refer to st(0) in the clobber list. The rest of the registers have their normal names for GAS syntax, "st(1)", ..., "st(7)".
https://stackoverflow.com/questions/39728398/how-to-specify-clobbered-bottom-of-the-x87-fpu-stack-with-extended-gcc-assembly
You never know, it is possible to compile withclang -mfpmath=387, or to use 387 vialong double`.
(Hopefully no code uses -mfpmath=387 in 64-bit mode and MMX intrinsics at the same time; the following test-case looks slightly broken with gcc in that case.)
#include <immintrin.h>
float gvar;
int testclobber(float f, char *p)
{
int arg1 = 1, arg2 = 2;
f += gvar; // with -mno-sse, this will be in an x87 register
__m64 mmx_var = *(const __m64*)p; // MMX
mmx_var = _mm_unpacklo_pi8(mmx_var, mmx_var);
// x86-64 System V calling convention
unsigned long long retval;
asm volatile ("add $-128, %%rsp \n\t" // skip red zone. -128 fits in an imm8
"call whatever \n\t"
"sub $-128, %%rsp \n\t"
// FIXME should probably align the stack in here somewhere
: "=a"(retval) // returns in RAX
: "D" (arg1), "S" (arg2) // input args in registers
: "rcx", "rdx", "r8", "r9", "r10", "r11" // call-clobbered integer regs
// call clobbered FP regs, *NOT* including MXCSR
, "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7" // MMX
, "st", "st(1)", "st(2)", "st(3)", "st(4)", "st(5)", "st(6)", "st(7)" // x87
// SSE/AVX: clobbering any results in a redundant vzeroupper with gcc?
, "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"
, "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15"
#ifdef __AVX512F__
, "zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", "zmm23"
, "zmm24", "zmm25", "zmm26", "zmm27", "zmm28", "zmm29", "zmm30", "zmm31"
, "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7"
#endif
#ifdef __MPX__
, "bnd0", "bnd1", "bnd2", "bnd3"
#endif
, "memory" // reads/writes of globals and pointed-to data can't reorder across the asm (at compile time; runtime StoreLoad reordering is still a thing)
);
// Use the MMX var after the asm: compiler has to spill/reload the reg it was in
*(__m64*)p = mmx_var;
_mm_empty(); // emms
gvar = f; // memory clobber prevents hoisting this ahead of the asm.
return retval;
}
source + asm on the Godbolt compiler explorer
By commenting one of the lines of clobbers, we can see that the spill-reload go away in the asm. e.g. commenting the x87 st .. st(7) clobbers makes code that leaves f + gvar in st0, for just a fst dword [gvar] after the call.
Similarly, commenting the mm0 line lets gcc and clang keep mmx_var in mm0 across the call. The ABI requires that the FPU is in x87 mode, not MMX, on call / ret, this isn't really sufficient. The compiler will spill/reload around the asm, but it won't insert an emms for us. But by the same token, it would be an error for a function using MMX to call your co-routine without doing _mm_empty() first, so maybe this isn't a real problem.
I haven't experimented with __m256 variables to see if it inserts a vzeroupper before the asm, to avoid possible SSE/AVX slowdowns.
If we comment the xmm8..15 line, we see the version that isn't using x87 for float keeps it in xmm8, because now it thinks it has some non-clobbered xmm regs. If we comment both sets of lines, it assumes xmm0 lives across the asm, so this works as a test of the clobbers.
asm output with all clobbers in place
It saves/restores RBX (to hold the pointer arg across the asm statement), which happens to re-align the stack by 16. That's another problem with using call from inline asm: I don't think alignment of RSP is guaranteed.
# from clang7.0 -march=skylake-avx512 -mmpx
testclobber: # #testclobber
push rbx
vaddss xmm0, xmm0, dword ptr [rip + gvar]
vmovss dword ptr [rsp - 12], xmm0 # 4-byte Spill (because of xmm0..15 clobber)
mov rbx, rdi # save pointer for after asm
movq mm0, qword ptr [rdi]
punpcklbw mm0, mm0 # mm0 = mm0[0,0,1,1,2,2,3,3]
movq qword ptr [rsp - 8], mm0 # 8-byte Spill (because of mm0..7 clobber)
mov edi, 1
mov esi, 2
add rsp, -128
call whatever
sub rsp, -128
movq mm0, qword ptr [rsp - 8] # 8-byte Reload
movq qword ptr [rbx], mm0
emms # note this didn't happen before call
vmovss xmm0, dword ptr [rsp - 12] # 4-byte Reload
vmovss dword ptr [rip + gvar], xmm0
pop rbx
ret
Notice that because of the "memory" clobber in the asm statement, *p and gvar are read before the asm, but written after. Without that, the optimizer could sink the load or hoist the store so no local variable was live across the asm statement. But now the optimizer needs to assume that the asm statement itself might read the old value of gvar and/or modify it. (And assume that p points to memory that's also globally accessible somehow, because we didn't use __restrict.)

Cannot access stack elements from another method - Assembly [duplicate]

My questions pertain to the actions that seem to happen between the line when context is changed especially concerning RSP and RBP.
Given this very simple program:
Reading symbols from ./function_call...done.
(gdb) disass main
Dump of assembler code for function main:
0x00000000004004d6 <+0>: push rbp
0x00000000004004d7 <+1>: mov rbp,rsp
0x00000000004004da <+4>: mov esi,0x2
0x00000000004004df <+9>: mov edi,0x1
0x00000000004004e4 <+14>: call 0x4004b6 <add_and_7>
0x00000000004004e9 <+19>: mov eax,0x0
0x00000000004004ee <+24>: pop rbp
0x00000000004004ef <+25>: ret
End of assembler dump.
(gdb) disass add_and_7
Dump of assembler code for function add_and_7:
0x00000000004004b6 <+0>: push rbp
0x00000000004004b7 <+1>: mov rbp,rsp
0x00000000004004ba <+4>: mov DWORD PTR [rbp-0x14],edi
0x00000000004004bd <+7>: mov DWORD PTR [rbp-0x18],esi
0x00000000004004c0 <+10>: mov DWORD PTR [rbp-0x4],0x7
0x00000000004004c7 <+17>: mov edx,DWORD PTR [rbp-0x14]
0x00000000004004ca <+20>: mov eax,DWORD PTR [rbp-0x18]
0x00000000004004cd <+23>: add edx,eax
0x00000000004004cf <+25>: mov eax,DWORD PTR [rbp-0x4]
0x00000000004004d2 <+28>: add eax,edx
0x00000000004004d4 <+30>: pop rbp
0x00000000004004d5 <+31>: ret
End of assembler dump.
(gdb) list
1 int add_and_7( int num1, int num2 ) {
2 int seven = 7;
3 return num1 + num2 + seven;
4 }
5
6 int main() {
7 add_and_7( 1, 2 );
8 return 0;
9 }
All functions start off with push rbp which I as I understand it is preserving the parent context onto the stack. How does the parent function know how to rebuild itself? Are the necessary steps built into call and ret?
Then the rsp is always moved to rbp. As I have read this sets the new stack base to be in the context of the current function. What I can't seem to figure out is when or how stack pointer was set to that point in the first place. My best guess is the assembly function call does this, is that whats happening?
Lastly when a method returns it seems like eax is the register that is used for the parent function to utilize the return of its child function. Is eax explicitly used for this or is this just a convention with my compiler and architecture?
How does the parent function know how to rebuild itself ? Are the necessary steps built into call and ret?
Before calling a function, current status of registers are saved, as well as the return address. call instruction jumps to particular address, where the called function begins. The return address is pushed onto stack. When called function returns, ret instruction pops previously pushed return address and goes to that location.
Then the rsp is always moved to rbp
rbp is previously pushed onto stack to be able to restore rbp's value from caller's function. Then, rsp is moved to rbp to create a new stack frame for callee function. The new base pointer has been set up. So currently, rbp and rsp points to the same addresses. If there are other push instructions, esp is automatically adjusted. When function is done, the pop ebp instruction restores previously pushed stack base pointer address.
Push and Pop modify the stack pointer - SP.
Call pushes FLAGS - status register as well as the RA - return address.
Ret pops the FLAGS pops and jumps to the return address.
As rkhb said, the need to keep certain registers as they are comes from the calling conventions.

Why syscall doesn't work?

I'm on MAC OSX and I'm trying to call through assembly the execve syscall..
His opcode is 59 .
In linux I have to set opcode into eax, then parameters into the others registers, but here I have to put the opcode into eax and push parameters into the stack from right to left.
So I need execve("/bin/sh",NULL,NULL), I found somewhere that with assembly null=0, so I put null into 2nd and 3rd parameters.
global start
section .text
start:
jmp string
main:
; 59 opcode
; int execve(char *fname, char **argp, char **envp);
pop ebx ;stringa
push 0x0 ;3rd param
push 0x0 ;2nd param
push ebx ;1st param
add eax,0x3b ;execve opcode
int 0x80 ;interupt
sub eax,0x3a ; exit opcode
int 0x80
string:
call main
db '/bin/sh',0
When I try to execute it say:
Bad system call: 12
32-bit programs on BSD (on which OS/X is based) requires you to push an extra 4 bytes onto the stack if you intend to call int 0x80 directly. From the FreeBSD documentation you will find this:
By default, the FreeBSD kernel uses the C calling convention. Further, although the kernel is accessed using int 80h, it is assumed the program will call a function that issues int 80h, rather than issuing int 80h directly.
[snip]
But assembly language programmers like to shave off cycles. The above example requires a call/ret combination. We can eliminate it by pushing an extra dword:
open:
push dword mode
push dword flags
push dword path
mov eax, 5
push eax ; Or any other dword
int 80h
add esp, byte 16
When calling int 0x80 you need to adjust the stack pointer by 4. Pushing any value will achieve this. In the example they just do a push eax. Before your calls to int 0x80 push 4 bytes onto the stack.
Your other problem is that add eax,0x3b for example requires EAX to already be zero which is almost likely not the case. To fix that add an xor eax, eax to the code.
The fixes could look something like:
global start
section .text
start:
jmp string
main:
; 59 opcode
; int execve(char *fname, char **argp, char **envp);
xor eax, eax ;zero EAX
pop ebx ;stringa
push 0x0 ;3rd param
push 0x0 ;2nd param
push ebx ;1st param
add eax,0x3b ;execve opcode
push eax ;Push a 4 byte value after parameters per calling convention
int 0x80 ;interupt
sub eax,0x3a ; exit opcode
push eax ;Push a 4 byte value after parameters per calling convention
; in this case though it won't matter since the system call
; won't be returning
int 0x80
string:
call main
db '/bin/sh',0
Shellcode
Your code is actually called the JMP/CALL/POP method and is used for writing exploits. Are you writing an exploit or did you just find this code online? If it is intended to be used as shell code you would need to avoid putting a 0x00 byte in the output string. push 0x00 will encode 0x00 bytes in the generated code. To avoid this we can use EAX which we are now zeroing out and push it on the stack. As well you won't be able to NUL terminate the string so you'd have to move a NUL(0) character into the string. One way after zeroing EAX and popping EBX is to move zero to the end of the string manually with something like mov [ebx+7], al. Seven is the index after the end of the string /bin/sh. Your code would then look like this:
global start
section .text
start:
jmp string
main:
; 59 opcode
; int execve(char *fname, char **argp, char **envp);
xor eax, eax ;Zero EAX
pop ebx ;stringa
mov [ebx+7], al ;append a zero onto the end of the string '/bin/sh'
push eax ;3rd param
push eax ;2nd param
push ebx ;1st param
add eax,0x3b ;execve opcode
push eax
int 0x80 ;interupt
sub eax,0x3a ; exit opcode
push eax
int 0x80
string:
call main
db '/bin/sh',1
You are using a 64 bit syscall numbers and a 32 bit instruction to jump to the syscall. That is not going to work.
For 32 bit users:
opcode for Linux/MacOS execve: 11
instruction to call syscall: int 0x80
For 64 bit users:
opcode for Linux execve: 59 (MacOS 64-bit system calls also have a high bit set).
instruction to call syscall: syscall
The method for passing args to system calls is also different: 32-bit uses the stack, 64-bit uses similar registers to the function-calling convention.

Hard Disk handling using 32-bit system call in assembly language

friends, I need help regarding Harddisk handling using 32-bit system call. I am new to assembly language and I am using MASM assembler. I have tried some example codes but they are in 16-bit mode, and also i gives me error, while compling. I want to write in 32-bit. and i also read in a book that 32-bit is complicated, Is there any another procedure for 32-bit system call.
Example Code!
; This program calls INT 21h Function 7303h, to get free space information
; on a FAT-type drive volume. It displays both the volume size and free space.
; Runs under Windows 95/98/Me, but not under Windows NT/2000/XP.
; Last update: 12/11/01
INCLUDE Irvine16.inc
.data
buffer ExtGetDskFreSpcStruc <>
driveName BYTE "C:\",0
str1 BYTE "Volume size (KB): ",0
str2 BYTE "Free space (KB): ",0
str3 BYTE "Function call failed.",0dh,0ah,0
.code
main PROC
mov ax,#data
mov ds,ax
mov es,ax
mov buffer.Level,0 ; must be zero
mov di, OFFSET buffer ; ES:DI points to buffer
mov cx, SIZEOF buffer ; buffer size
mov dx, OFFSET DriveName ; ptr to drive name
mov ax, 7303h ; Get disk free space
int 21h
jc error ; Failed if CF = 1
mov dx,OFFSET str1 ; volume size
call WriteString
call CalcVolumeSize
call WriteDec
call Crlf
mov dx,OFFSET str2 ; free space
call WriteString
call CalcVolumeFree
call WriteDec
call Crlf
jmp quit
error:
mov dx,OFFSET str3
call WriteString
quit:
exit
main ENDP
;-------------------------------------------------------------------
CalcVolumeSize PROC
; Calculate and return the disk volume size, in kilobytes.
; Receives: buffer variable, a ExtGetDskFreSpcStruc structure
; Returns: EAX = volume size
; Remarks: (SectorsPerCluster * 512 * TotalClusters) / 1024
;--------------------------------------------------------------------
mov eax,buffer.SectorsPerCluster
shl eax,9 ; mult by 512
mul buffer.TotalClusters
mov ebx,1024
div ebx ; return kilobytes
ret
CalcVolumeSize ENDP
;-------------------------------------------------------------------
CalcVolumeFree PROC
; Calculate and return the number of available kilobytes on the
; given volume.
; Receives: buffer variable, a ExtGetDskFreSpcStruc structure
; Returns: EAX = available space, in kilobytes
; Remarks: (SectorsPerCluster * 512 * AvailableClusters) / 1024
;--------------------------------------------------------------------
mov eax,buffer.SectorsPerCluster
shl eax,9 ; mult by 512
mul buffer.AvailableClusters
mov ebx,1024
div ebx ; return kilobytes
ret
CalcVolumeFree ENDP
END main
Trying to assemble this program...
also i gives me error
Firstly, it cannot assemble because the struct ExtGetDskFreSpcStruc in the line
buffer ExtGetDskFreSpcStruc <>
is not defined before - not in this file and not in (the only) include file(s), in this case "Irvine16.inc", which doesn't contain the definition. Therefore all attempted accesses to "buffer.xxx" will throw errors.
Secondly, if you are trying to assemble this program for 32-bit, you are including a 16-bit include file in a 32-bit assembly file. You should use the 32-bit version "Irvine32.inc", because otherwise all indirect addressing like mov al, [si](16-bit) in the subroutines will fail.
Thirdly, you are calling DOS-legacy-interrupts, which are available only in Win95/98/Me, because their kernels are based on DOS. The newer NT kernel in Win2000 and above does not provide them, because it was a different OS architecture/codebase without/with reduced legacy code. The DOS-interrupt(21h) is 16-bit, because DOS was 16-bit. I don't know if there are implemented 32-bit versions of old DOS-interrupts in Win95/98/Me. A quick search yielded no indications of this having been realized. The BIOS interrupts used in "Irvine16.inc" are surely 16-bit and therefore unusable(without very complicated workarounds) in a 32-bit executable probably crashing the program.
Is there any another procedure for 32-bit system call[?]
Yes. Use the Win32-API, which is available in Win95 till Win8/10(?).
The easiest way to do this would be using the MASM-package by Hutch found at MOVSD.COM and then include kernel32.inc/lib from it. Then you can call the GetFreeDiskSpace function to get the desired result with a line similar to this:
invoke GetFreeDiskSpace,
offset driveName,
offset buffer.lpSectorsPerCluster, ; adjust to actual buffer layout names
offset buffer.lpBytesPerSector, ; "
offset buffer.lpNumberOfFreeClusters, ; "
offset buffer.lpTotalNumberOfClusters ; "

Assembly - Moving data from Register to Memory in MASM

I am trying to move stuff from a register to a variable in .CODE, but trying to do so makes my program start over in an infinite loop (no crash and no error message, but obviously broken). I don't understand what I'm doing wrong. Here is the beginning of my code where I am trying to move data; the program never even gets past this part when I include it:
.CODE
screenX DWORD 0
screenY DWORD 0
...
ProcName PROC
mov ebx, edx ;; Copy srcBitmap into ebx
mov eax, edi ;; Take given y-location (edi)
mov edx, (EECS205BITMAP PTR [ebx]).dwHeight
shr edx, 1 ;; Subtract dwHeight/2 to center
sub eax, edx
mov screenY, eax ;; Program jumps back to beginning with no error message
Seems like I'm missing something obvious, anyone have a clue?
Your application's code segment (which is actually it's .text section under Windows) isn't writable. If you want to modify these variables you need to put them in the data segment.

Resources