va_args不接受C中的签名整数

发布于 2025-01-18 22:24:13 字数 6277 浏览 2 评论 0原文

我正在使用C设计内核。在制作kprintf函数(printf之类的函数)时,我看到了签名的整数(精确的数据类型是<代码> long ),va_args将它们转换为无签名的长

这是代码的片段: 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;
}

编辑:添加inttoStringuinttoString

(我不知道这样做的正确方法,但这足够了)

问题的概述: “ D”和“我”的案例显示了问题的位置。
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 :(

我尝试了inttostring&amp; uintToString在Linux计算机上功能,它们以预期的方式工作。

注意:我尚未测试kprintf(const char* stringformat,...)在受控&amp;像上面提到的其他两个功能一样,易于辩论的环境。

doubleTostostringuhextoString等其他功能尚未进行测试,但无论如何它都不应更改/与问题相关。

kprintfhelper是一个函数,就像kprintf,但不进行任何检查,只打印字符串

我的环境
编译器:x86_64-elf-fcc
flags(又名cflags):-ffreestanding -mno-red-Zone -m64

链接器:x86_64-fld-ld

i使用QEMU-SYSTEM-X86_64运行最终可执行文件。

测试

我尝试了以下情况:

  1. 正常的未签名案例
kprintf("Number: %d\n", 1234);

输出:

编号:1234

  1. 不起作用,但输出似乎va_args正在做一些奇怪的无符号int数学(通过测试5进一步证明,确实是va_args,IMO)
kprintf("Number: %d\n", -1234);

输出:

编号:4294966062

  1. 按预期
kprintf("Number: %d\n", 1099511627775);

输出工作:

编号:1099511627775

kprintf("Number: %d\n", -1099511627775);

输出:

编号:1099511627775

  1. ,此处
kprintf("Number: %s\n", IntToString(-1234));

输出:

编号:-1234

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:

  1. Normal unsigned case
kprintf("Number: %d\n", 1234);

Output:

Number: 1234

  1. Does not work but the output seems like va_args is doing some weird unsigned int math (further proved by test 5 that it was indeed va_args, IMO)
kprintf("Number: %d\n", -1234);

Output:

Number: 4294966062

  1. Works as intended
kprintf("Number: %d\n", 1099511627775);

Output:

Number: 1099511627775

  1. BUT This
kprintf("Number: %d\n", -1099511627775);

Output:

Number: 1099511627775

  1. AND here
kprintf("Number: %s\n", IntToString(-1234));

Output:

Number: -1234

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

还在原地等你 2025-01-25 22:24:13

您要传递的%d指定符的值类型与va_arg期望与之匹配。

您正在告诉va_arg期望,但是1234-1234有类型int。这些类型是不同的尺寸,因此va_arg从呼叫堆栈中拉出的字节比您所付出的字节更多。

标准格式指定器具有尺寸修饰符,因此可以处理不同的尺寸类型。您需要实施类似的东西。

The type of the value you're passing in for your %d specifier doesn't match what va_arg is expecting.

You're telling va_arg to expect a long long, but 1234 and -1234 have type int. These types are different sizes, so va_arg is pulling more bytes from the call stack than you put on.

The standard format specifiers have size modifiers so they can handle different size types. You'll need to implement something similar.

杀手六號 2025-01-25 22:24:13

呼叫kprintf(“编号:%d \ n”,-1234);是不正确的,因为%d提取long long long。它必须是kprintf(“编号:%d \ n”,-1234ll);

-1234是32位操作数。问题可能是,这是用一个64位对齐的单词传递的,但没有符号扩展到64位。

也就是说,64位中的-1234值需要为ffffffffffffffb2e,但是32位参数正在堆栈上产生00000000000000000000FFFFFB2E图像,该图像是429496666662。

根据这一假设,我们必须通过-1000才能获得观察到的429496629。它与-1234没有关系。其他事情可能正在发生,例如被解释为数据的垃圾位。

毕竟,这种行为的定义不明确:您将一个大小的整数推入完全不典型且不安全的参数传递机构,并拔出一个不同尺寸的整数。

The call kprintf("Number: %d\n", -1234); is incorrect because %d extracts a long long. It must be kprintf("Number: %d\n", -1234LL);.

-1234 is a 32 bit operand. The problem could be that this is being passed in a 64 bit aligned word, but not being sign extended to 64 bits.

So that is to say, the -1234 value in 64 bits needs to be fffffffffffffb2e, but the 32 bit parameter is producing a 00000000fffffb2e image on the stack, which is 4294966062.

According to this hypothesis, we would have to pass -1000 to obtain the observed 429496629, however. It bears no relation to -1234. Something else could be going on, like garbage bits being interpreted as data.

The behavior is not well-defined, after all: you're shoving an integer of one size into a completely typeless and unsafe parameter passing mechanism and pulling out an integer of a different size.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文