1

我正在尝试从头开始创建一个内核(只是尝试一些新的东西)

现在一切都准备好了,我正在测试输出并注意到一些非常奇怪的东西

我构建我的文件:

gcc ./kernel/kernel.c -ffreestanding -O0 -m32 -c -o./bin/kernel.o -fno-pie

并将它们链接在一起:

ld -nostdlib -nodefaultlibs -Tlink.ld ./bin/kernel_ep.elf.bin ./bin/kernel.o -o ./bin/kernel.bin

所以根据我的理解,我已经告诉编译器不要优化我的代码。

现在是C部分

#define BYTE unsigned char
#define VIDMEM ((BYTE*)0xb8000)

void init();
void main() {
    init();
    while(1);
}

void print(char *msg)
{
    volatile BYTE *screen = VIDMEM;
    for(const char *msgPtr = msg; *msgPtr; ++msgPtr)
    {
        *(screen++) = *msgPtr;
        screen++;
    }
}

void init() {
    //volatile char test[] = "Test";
    //print(test);
    print("Test");    
}

如果我运行它,什么都不会发生,我在 ghidra 中检查了整个事情 - “测试”的 char 数组在内存中,但我没有引用 init() -> 所以print永远不会被调用。

如果我现在使用注释文本(和注释print("Test"))一切正常,文本会以我想要的方式打印。

但真正的问题是:是否有某种“技巧”告诉编译器除了使用 volatile 之外不要优化此代码?因为我不认为清除所有可能被打印为易失性的东西是我应该这样做的方式。

据我了解,主要问题是该print函数基本上什么都不做,因为编译器似乎不知道 0xb8000 是一个有点……特殊的地址。

4

3 回答 3

3
  1. 这种定义类型的 Microsoft 风格实在是太可怕了。使用精确尺寸类型,例如uint8_tint32_t。定义BYTE是一个非常糟糕的习惯。它不会在这里引起问题,但问题始于不同系统上更广泛的类型和不同的大小。

  2. 在这个简单的示例中,编译器将内联这两个函数。所以你不会看到initorprint函数调用。如果你想让它非内联使用__attribute__((noinline)).

  3. 你不需要 volatile 因为代码不会被优化。

#include <stdint.h>

#define VIDMEM ((uint8_t*)0xb8000)


void __attribute__((noinline)) print(const char *msg)
{
    uint8_t *screen = VIDMEM;
    for(const char *msgPtr = msg; *msgPtr; ++msgPtr)
    {
        *screen++ = *msgPtr; 
    }
}


void __attribute__((noinline)) init() {
    print("Test");    
}


void main() 
{
    init();
    while(1);
}

https://godbolt.org/z/iqNadv

在这里你有内联版本。函数是静态的,以防止编译器为外部链接拥有另一个副本。

https://godbolt.org/z/giKAnC

于 2020-02-29T23:25:43.493 回答
0

您的volatilechar 数组在堆栈上,而您的字符串文字不在。我怀疑您错误地设置了数据段寄存器。我将继续克隆您的存储库并测试该理论。

于 2020-02-29T23:42:39.117 回答
-2

虽然编译器过去将整数到指针的转换视为指示编译器不应该对结果指针做任何假设的点(在每个转换行为的结果仅使用一次的情况下,本质上暗示易失性语义),但无论是 clang 还是 gcc提供任何选项来支持这种语义,除了-O0.

于 2020-02-29T23:21:08.117 回答