memcpy代码经过优化,使用-Os,因为缓冲区范围有限。

huangapple go评论74阅读模式
英文:

memcpy code optimized with -Os because of limited buffer scope

问题

使用"-O0"编译代码时,结果如下:

functionA
0 1 2 3 4 5 6 7 8 9 a b c d e f

然而,使用"-Os"或"-O3"(而不是"-O2")编译时,结果如下:

functionA
16 24 97 6b fc 7f 0 0 8d f2 b7 c ed 55 0 0

我在godbolt上使用不同的编译器,如ARM GCC 10.3.1进行了尝试,发现在"functionB"中的memcpy代码没有被编译。在"main"函数的开头更改"buff32"的作用范围似乎解决了这个问题。似乎GCC判断在其范围内不再使用该缓冲区,并将其视为未使用,因此进行了优化。

是否可能从编译器获得警告?这是否是未定义行为?

未定义行为通常是在C语言中不明确规定的行为,编译器可以自由地进行优化,而不受任何限制。因此,编译器可能不会生成警告,因为它认为未使用的变量可以安全地被丢弃。

要解决此问题,您可以尝试明确告诉编译器不要优化掉"buff32"变量。您可以使用"attribute((used))"来标记该变量,以确保编译器不会将其视为未使用。例如:

uint8_t buff32[32] __attribute__((used)) = {0x5};

这将告诉编译器"buff32"是被使用的,从而避免不必要的优化。这样,在使用"-Os"或"-O3"编译时,您应该会得到预期的结果。

英文:

I have the following C code, which works fine without optimization (-O0) but has unexpected behavior when using -Os:

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include <string.h>

static bool functionA(uint8_t* ptr1)
{
    for (int i = 0; i < 16; i++)
    {
        ptr1[i] = i;
    }
    printf("functionA\n");
    return true;
}

static bool functionB(uint8_t* ptr1)
{
    bool retVal = false;
    uint8_t ptr2[16] = {0};

    if (functionA(ptr2))
    {
        retVal = true;
        memcpy(ptr1, ptr2, 16);
    }

    return retVal;
}


int main(void)
{
    const uint8_t* p = NULL;
    const uint8_t BUF[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

    for (int i = 1; i <= 2; i++)
    {
        if (i == 1)
        {
            uint8_t buff32[32] = {0x5};

            if (functionB(buff32))
            {
                p = buff32;
                break;
            }

            continue;

        }
        else
        {
            p = BUF;
        }
    }

    for(int i = 0; i < 16; i++)
    {
        printf("%x ", p[i]);
    }

    printf("\n");
    return 0;
}

When compiling the code with "-O0", I have the result:

functionA
0 1 2 3 4 5 6 7 8 9 a b c d e f

However, with "-Os" or "-O3" (not "-O2"):

functionA
16 24 97 6b fc 7f 0 0 8d f2 b7 c ed 55 0 0

I tried on godbolt with different compilers like ARM GCC 10.3.1 and I see that the code of the memcpy in "functionB" is not compiled. Changing the scope of the buff32 at the beginning of the main seems to solve the issue. It seems that GCC judges that the buffer is no longer used in its scope and considers it as unused, hence optimizes it.

Is it possible to get a warning from the compiler?
Is it an undefined behavior?

答案1

得分: 3

在执行循环的第一次迭代时,如果 functionB 为真,那么将 buff32 的值赋给变量 p
现在,p 指向位于堆栈上的 buff32 的开头。当调用 break 并退出循环时,堆栈帧被释放,所以 p 指向不再使用的堆栈空间。
如果将 buff32 的定义移到循环之外(就像你在问题中提到的那样),那么该空间在程序退出之前是有效的,因此你就不会遇到这个问题。

这就是为什么 @Ted-Lyngmo 的评论中提到,检测工具报告了 stack-use-after-scope

英文:

When executing the first iteration of the loop if functionB is true then you assign the value of buff32 to the variable p.
Now p points to the beginning of buff32 that is an array on the stack. When you call break and exit the loop the stack frame is freed and so p points to a stack space that is not in use anymore.
If you move the definition of buff32 out of the loop (like you said you did in the question) that space is valid until the program exits so you don't get that problem.

That is why the @Ted-Lyngmo comment says that the sanitizers report a stack-use-after-scope.

答案2

得分: 0

buff32的作用是问题所在。
看到@Ted Lyngmo的评论

不知道您的问题是什么,很难说,但是
您的代码应该像这样读取:

int main(void)
{
    const uint8_t* p = NULL;
    const uint8_t BUF[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
    uint8_t buff32[32] = {0};  //<<--这里

    for (int i = 1; i <= 2; i++)
    {
        if (i == 1)
        {
            memset(buff32, '
int main(void)
{
    const uint8_t* p = NULL;
    const uint8_t BUF[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
    uint8_t buff32[32] = {0};  //<<--这里

    for (int i = 1; i <= 2; i++)
    {
        if (i == 1)
        {
            memset(buff32, '\0', sizeof(buff32)); //<<---这里
            buff32[0] = 0x5; //<<---这里
            if (functionB(buff32))
            {
                p = buff32;
                break;
            }

            continue;

        }
        else
        {
            p = BUF;
        }
    }

    for(int i = 0; i < 16; i++)
    {
        printf("%x ", p[i]);
    }

    printf("\n");
    return 0;
}
'
, sizeof(buff32)); //<<---这里
buff32[0] = 0x5; //<<---这里 if (functionB(buff32)) { p = buff32; break; } continue; } else { p = BUF; } } for(int i = 0; i < 16; i++) { printf("%x ", p[i]); } printf("\n"); return 0; }
英文:

The scope of buff32 is the issue.
See comment from @Ted Lyngmo

Not knowing your question it's difficult to say but
your code should probably read like this:

int main(void)
{
    const uint8_t* p = NULL;
    const uint8_t BUF[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
    uint8_t buff32[32] = {0};  //&lt;&lt;--HERE

    for (int i = 1; i &lt;= 2; i++)
    {
        if (i == 1)
        {
            memset(buff32, &#39;
int main(void)
{
const uint8_t* p = NULL;
const uint8_t BUF[] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};
uint8_t buff32[32] = {0};  //&lt;&lt;--HERE
for (int i = 1; i &lt;= 2; i++)
{
if (i == 1)
{
memset(buff32, &#39;\0&#39;, sizeof(buff32)); //&lt;&lt;---HERE
buff32[0] = 0x5; //&lt;&lt;---HERE
if (functionB(buff32))
{
p = buff32;
break;
}
continue;
}
else
{
p = BUF;
}
}
for(int i = 0; i &lt; 16; i++)
{
printf(&quot;%x &quot;, p[i]);
}
printf(&quot;\n&quot;);
return 0;
}
&#39;, sizeof(buff32)); //&lt;&lt;---HERE buff32[0] = 0x5; //&lt;&lt;---HERE if (functionB(buff32)) { p = buff32; break; } continue; } else { p = BUF; } } for(int i = 0; i &lt; 16; i++) { printf(&quot;%x &quot;, p[i]); } printf(&quot;\n&quot;); return 0; }

huangapple
  • 本文由 发表于 2023年6月16日 03:38:08
  • 转载请务必保留本文链接:https://go.coder-hub.com/76485022.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定