I have the following main.c:
#include <stdio.h>
#include <stdlib.h>
extern unsigned short addsub(int op1, int op2, char what, int *res, int *bit_count);
void print_flags(unsigned short flags) {
printf("Flags:\n");
printf("%s", "O D I T S Z A P C\n");
int bits_to_display = 12; // Significant bits only (0 included)
for (int i = bits_to_display - 1; i >= 0; --i) {
printf("%d ", (flags >> i) & 1);
}
printf("\n\n");
}
void print_result(int op1, int op2, int result, unsigned short flags, char operation, int bit_count) {
// Signed interpretation
printf("Ergebnis und Operanden Signed:\n");
printf("%d %c %d = %d ", (signed char)op1, operation, (signed char)op2, (signed char)result);
if ((flags & 0x0800) != 0) { // Overflow flag (OF)
printf("(Ergebnis ist falsch!)\n");
} else {
printf("(Ergebnis ist richtig!)\n");
}
// Unsigned interpretation
printf("\nErgebnis und Operanden Unsigned:\n");
printf("%u %c %u = %u ", (unsigned char)op1, operation, (unsigned char)op2, (unsigned char)result);
if ((flags & 0x0001) != 0) { // Carry flag (CF)
printf("(Ergebnis ist falsch!)\n");
} else {
printf("(Ergebnis ist richtig!)\n");
}
// Print POPCNT result
printf("\nDie Anzahl der gesetzten Bits im Ergebnis beträgt: %d\n", bit_count);
}
int main(int argc, char *argv[]) {
if (argc != 4) {
fprintf(stderr, "Usage: %s <operand1> <+|-> <operand2>\n", argv[0]);
return 1;
}
int op1 = atoi(argv[1]);
int op2 = atoi(argv[3]);
char op = argv[2][0];
if (op != '+' && op != '-') {
fprintf(stderr, "Error: Invalid operator. Use '+' or '-'.\n");
return 1;
}
int res = 0;
int bit_count = 0;
unsigned short flags = addsub(op1, op2, op, &res, &bit_count);
if (bit_count == -1) {
printf("Diese CPU unterstützt den Befehl CPUID nicht!\n");
return 1;
} else if (bit_count == -2) {
printf("Diese CPU unterstützt den Befehl POPCNT nicht!\n");
return 1;
}
print_flags(flags);
print_result(op1, op2, res, flags, op, bit_count);
return 0;
}
which is combined with the following assembly module:
section .text
global addsub
; CPUID function genutzt um cpu informationen auszulesen
; EAX = 1 provides feature information in ECX
; Bit 23 of ECX = 1 indicates POPCNT support
addsub:
push ebp
mov ebp, esp
; Load function arguments
mov eax, [ebp+8] ; Load op1
mov ebx, [ebp+12] ; Load op2
mov ecx, [ebp+16] ; Load operation ('+' or '-')
mov edx, [ebp+20] ; Load address of res
mov esi, [ebp+24] ; Load address of bit_count
; Save registers
push eax
push ebx
push ecx
push edx
push esi
; CPUID for POPCNT support check
pushfd
pop eax
mov ecx, eax
xor eax, 0x00200000 ; Toggle ID bit in EFLAGS
push eax ; Save modified flags
popfd ; Load modified flags
pushfd ; Push modified flags to stack
pop eax ; Pop flags back into EAX
xor eax, ecx ; Check if ID bit was toggled
jz no_cpuid ; CPUID not supported
; CPUID supported, check POPCNT support (bit 23)
mov eax, 1 ; Set EAX to 1 (feature information)
cpuid
test ecx, 1 << 23
jz no_popcnt ; POPCNT not supported
; Restore registers
pop esi
pop edx
pop ecx
pop ebx
pop eax
cmp cl, '+'
je add
cmp cl, '-'
je sub
; Invalid operation
xor eax, eax
leave
ret
add:
add al, bl
mov [edx], eax ; Store result
pushfd
pop eax
jmp count_bits
sub:
sub al, bl
mov [edx], eax ; Store result
pushfd
pop eax
jmp count_bits
count_bits:
push ebx
push esi
mov ebx, [edx]
and ebx, 0xFF ; Hardcoded 8-bit limit
popcnt ecx, ebx ; Count the number of set bits in result
mov [esi], ecx ; Store bit count in variable
pop ebx
pop esi
jmp fin
no_cpuid:
mov dword [esi], -1 ; CPUID not supported flag for C program
jmp fin
no_popcnt:
mov dword [esi], -2 ; POPCNT not supported flag for C program
jmp fin
fin:
leave ; Restore stack frame (includes pop ebp)
ret
The purpose of the program is to read the flags of the CPU and return them. When I try to read the flags variable after the addsub()
method finishes the program crashes, and I can't find why. In gdb the correct value is stored in the flags variable. When I remove the print statement the program works as expected.
Maybe someone can help because I am going insane!
I suspect the issue is because you do not preserve the registers as per calling convention. I rearranged your code a little which should fix the problem:
global addsub
; CPUID function genutzt um cpu informationen auszulesen
; EAX = 1 provides feature information in ECX
; Bit 23 of ECX = 1 indicates POPCNT support
addsub:
push ebp
mov ebp, esp
; Save registers
push ebx
push esi
mov esi, [ebp+24] ; Load address of bit_count
; CPUID for POPCNT support check
pushfd
pop eax
mov ecx, eax
xor eax, 0x00200000 ; Toggle ID bit in EFLAGS
push eax ; Save modified flags
popfd ; Load modified flags
pushfd ; Push modified flags to stack
pop eax ; Pop flags back into EAX
xor eax, ecx ; Check if ID bit was toggled
jz no_cpuid ; CPUID not supported
; CPUID supported, check POPCNT support (bit 23)
mov eax, 1 ; Set EAX to 1 (feature information)
cpuid
test ecx, 1 << 23
jz no_popcnt ; POPCNT not supported
; Load rest of function arguments
mov eax, [ebp+8] ; Load op1
mov ebx, [ebp+12] ; Load op2
mov ecx, [ebp+16] ; Load operation ('+' or '-')
mov edx, [ebp+20] ; Load address of res
cmp cl, '+'
je add
cmp cl, '-'
je sub
; Invalid operation
xor eax, eax
jmp fin
add:
add al, bl
mov [edx], eax ; Store result
pushfd
pop eax
jmp count_bits
sub:
sub al, bl
mov [edx], eax ; Store result
pushfd
pop eax
jmp count_bits
count_bits:
mov ebx, [edx]
and ebx, 0xFF ; Hardcoded 8-bit limit
popcnt ecx, ebx ; Count the number of set bits in result
mov [esi], ecx ; Store bit count in variable
jmp fin
no_cpuid:
mov dword [esi], -1 ; CPUID not supported flag for C program
jmp fin
no_popcnt:
mov dword [esi], -2 ; POPCNT not supported flag for C program
fin:
pop esi
pop ebx
leave ; Restore stack frame (includes pop ebp)
ret