I was designing my kernel using C. While making the kprintf function (a function like printf but works with the kernel) I saw that signed integers (precisely the data type is long), va_args is converting them to unsigned long.
Here's a snippet of the code:
kPrint.c
#include <stdarg.h>
// skipped some lines not needed for this question
// ...
/******************
* global variables needed for number to string conversion
* ***************/
// store the converted string temporarily
static char convertedString[30];
// store the index of the filled portion
static char numberIndex;
// TODO: Create function like printf but which works here
_Bool kprintf(const char* stringFormat, ...) {
char* strPtr = (char*)stringFormat;
va_list arguments;
va_start(arguments, stringFormat);
while (*strPtr != 0) {
switch (*strPtr)
{
case '\e':
// printf() also ends the execution
return 1;
case '%':
switch (*(strPtr + 1))
{
// signed decimal integer
case 'd':
case 'i':
kprintf(IntToString(va_arg(arguments, long long))); // stringify the integer
// *problem* here
strPtr += 2;
break;
// unsigned decimal integer
case 'u':
kprintf(uIntToString(va_arg(arguments, uint64_t))); // stringify
strPtr += 2;
break;
// will implement U & L case later
// unsigned hexadecimal
case 'x':
case 'X':
kprintf(uHexToString(va_arg(arguments, uint64_t))); // stringify
// doesn't work now
strPtr += 2;
break;
// will implement U & L case later
// 6 numbers after decimal
case 'f':
case 'F':
kprintf(DoubleToString(va_arg(arguments, double), 6)); // stringify
// doesn't work now
strPtr += 2;
break;
// 2 numbers after pt
case 'g':
case 'G':
kprintf(DoubleToString(va_arg(arguments, double), 2));
strPtr += 2;
break;
case 'c':
kPrintChar((char)va_arg(arguments, int));
//
strPtr += 2;
break;
// another string
case 's':
kPrintfHelper(va_arg(arguments, char*)); // just to prevent recursion
strPtr += 2;
break;
case '%':
kPrintChar('%');
strPtr += 2;
break;
}
break;
default:
kPrintChar(*strPtr);
strPtr++;
}
}
va_end(arguments);
return 0;
}
void uIntToStrHelper(uint64_t num) {
if (num < 10)
{
convertedString[numberIndex++] = num + '0';
convertedString[numberIndex] = 0;
return;
}
uint8_t numIndexCpy = numberIndex;
while (num > 0) {
convertedString[numberIndex++] = (num % 10) + '0';
num /= 10;
}
char swpIndex = (numberIndex - 2 + numIndexCpy) / 2;
numberIndex = numberIndex - swpIndex - 1 + numIndexCpy;
while (swpIndex >= numIndexCpy) {
convertedString[swpIndex] = convertedString[swpIndex] +
convertedString[numberIndex];
convertedString[numberIndex] = convertedString[swpIndex] -
convertedString[numberIndex];
convertedString[swpIndex] = convertedString[swpIndex] -
convertedString[numberIndex];
swpIndex--;
numberIndex++;
}
convertedString[numberIndex] = 0;
}
char* uIntToString(uint64_t num) {
numberIndex = 0;
uIntToStrHelper(num);
return convertedString;
}
char* IntToString(long long num) {
numberIndex = 0;
if (num < 0) {
convertedString[numberIndex++] = '-';
num = -num;
}
uIntToStrHelper(num);
return convertedString;
}
EDIT: Added IntToString and uIntToString.
(I don't know the correct way to do this, but it suffices)
Overview of the problem:
cases for 'd' and 'i' show the place of the problem.
Function prototype of IntToString:
char* IntToString(long long num);
// part of the reason why I used long long as va_arg data type
// had also tried with long but with no luck :(
I have tried IntToString & uIntToString functions on a linux machine and they work the way intended.
NOTE: I have not tested kprintf(const char* stringFormat, ...) in a controlled & easily debuggable environment like the other 2 functions mentioned just above.
Other functions like DoubleToString and uHexToString have not yet been tested but it should not change/be related to the question anyway.
kPrintfHelper is a function just like kprintf but does not do any checking and just prints the string
My Environment
Compiler: x86_64-elf-gcc
Flags (aka CFLAGS): -ffreestanding -mno-red-zone -m64
Linker: x86_64-elf-ld
I use qemu-system-x86_64 to run the final executable.
Tests
I have tried the following cases:
- Normal unsigned case
kprintf("Number: %d\n", 1234);
Output:
Number: 1234
- Does not work but the output seems like
va_argsis doing some weird unsignedintmath (further proved by test 5 that it was indeedva_args, IMO)
kprintf("Number: %d\n", -1234);
Output:
Number: 4294966062
- Works as intended
kprintf("Number: %d\n", 1099511627775);
Output:
Number: 1099511627775
- BUT This
kprintf("Number: %d\n", -1099511627775);
Output:
Number: 1099511627775
- AND here
kprintf("Number: %s\n", IntToString(-1234));
Output:
Number: -1234