Hex7CD
Hex7CD

Reputation: 1

Modify C array in external assembler routine

Summary: We have an int variable and 4 double arrays in C, 2 of which hold input data and 2 of which we want to write output data to. We pass the variable and arrays to a function in an external .asm file, where the input data is used to determine output data and the output data is written to the output arrays.

Our problem is, that the output arrays remain seemingly untouched after the assembly routine finishes its work. We don't even know, if the routine reads the correct input data. Where did we go wrong?

We compile with the following commands

nasm -f elf32 -o calculation.o calculation.asm
gcc -m32 -o programm main.c calculation.o

If you need any more information, feel free to ask.

C code:

// before int main()
extern void calculate(int32_t counter, double radius[], double distance[], double paper[], double china[]) asm("calculate");

// in int main()
double radius[counter];
double distance[counter];

// [..] Write Input Data to radius & distance [...]

double paper[counter];
double china[counter];

for(int i = 0; i < counter; i++) {
    paper[i] = 0;
    china[i] = 0;
}

calculate(counter, radius, distance, paper, china);
// here we expect paper[] and china[] to contain output data

Our Assembly code currently only takes in the values, puts them into the FPU, then places them into the output array.

x86 Assembly (Intel Syntax) (I know, this code looks horrible, but we're beginners, so bear with us, please; Also I can't get syntax highlighting to work correctly for this one):

BITS 32
GLOBAL calculate

calculate:
SECTION .BSS
; declare all variables
pRadius: DD 0
pDistance: DD 0
pPaper: DD 0
pChina: DD 0
numItems: DD 0
counter: DD 0

; populate them
POP DWORD [numItems]
POP DWORD [pRadius]
POP DWORD [pDistance]
POP DWORD [pPaper]
POP DWORD [pChina]

SECTION .TEXT
PUSH EBX ; because of cdecl calling convention
JMP calcLoopCond

calcLoop:
; get input array element
MOV EBX, [counter]
MOV EAX, [pDistance]
; move it into fpu, then move it to output
FLD QWORD [EAX + EBX * 8]
MOV EAX, [pPaper]
FSTP QWORD [EAX + EBX * 8]
; same for the second one
MOV EAX, [pRadius]
FLD QWORD [EAX + EBX * 8]
MOV EAX, [pChina]
FSTP QWORD [EAX + EBX * 8]

INC EBX
MOV [counter], EBX

calcLoopCond:
MOV EAX, [counter]
MOV EBX, [numItems]
CMP EAX, EBX
JNZ calcLoop

POP EBX
RET

Upvotes: 0

Views: 359

Answers (2)

Hex7CD
Hex7CD

Reputation: 1

Thanks to all your answers and comments and some heavy research, we managed to finally produce functioning code, which now properly uses stack frames and fulfills the cdecl calling convention:

BITS 32
GLOBAL calculate

SECTION .DATA
electricFieldConstant DQ 8.85e-12
permittivityPaper DQ 3.7
permittivityChina DQ 7.0

SECTION .TEXT
calculate:
PUSH EBP
MOV EBP, ESP
PUSH EBX
PUSH ESI
PUSH EDI

MOV ECX, 0 ; counter for loop
JMP calcLoopCond

calcLoop:
MOV EAX, [EBP +  12]
FLD QWORD [EAX + ECX * 8]
MOV EAX, [EBP + 20]
FSTP QWORD [EAX + ECX * 8]
MOV EAX, [EBP + 16]
FLD QWORD [EAX + ECX * 8]
MOV EAX, [EBP + 24]
FSTP QWORD [EAX + ECX * 8]

ADD ECX, 1 ; increase loop counter

calcLoopCond:
MOV EDX, [EBP +  8]
CMP ECX, EDX
JNZ calcLoop

POP EDI
POP ESI
POP EBX
MOV ESP, EBP
POP EBP
RET

Upvotes: 0

Florian Weimer
Florian Weimer

Reputation: 33757

There are a couple of problems in the assembler routine. The POP instructions are emitted into the .bss section, so they are never executed. In the sequence of POPs, the return address (pushed by the caller) is not accounted for. Depending on the ABI, you must leave the arguments on the stack anyway. Because the POPs are never executed, the loop exit condition always happens to be true.

And you really should not use global variables this way. Instead, create a stack frame and use that.

Upvotes: 2

Related Questions