用于实模式DOS .COM的自定义内存分配器(独立) - 如何调试?

时间:2015-08-05 20:29:08

标签: c memory-management dos freestanding

首先有一点背景:在this blog post上磕磕绊绊,我了解到有可能用GNU链接器创建DOS .COM文件,它甚至不是火箭科学。使用clang-m16开关(通过相应地为32位指令添加前缀来创建实模式兼容的32位代码),这非常好用。所以我有想法尝试实现足够的运行时来获得我最近编写的little curses game编译到.COM并在实模式 DOS中运行。游戏足够小,所以压缩64KB的所有内容(文本,数据,bss,堆,堆栈)似乎是可行的。当然,它使用malloc()。所以我不得不想出自己的实现。这就是它的样子:

typedef unsigned short size_t; /* from stddef.h */

typedef struct hhdr hhdr;
struct hhdr
{
    void *next;
    int free;
};

extern char _heap;
static char *hbreak = &_heap;
static hhdr hhead = { &_heap, 0 };

static void *newchunk(size_t size)
{
    char *stack;
    __asm__("mov %%esp, %0": "=rm" (stack));
    if (hbreak + size > stack - 0x40) return 0;
    if (size < 1024) size = 1024;
    hhdr *chunk = (hhdr *)hbreak;
    hbreak += size;
    if (hbreak > stack - 0x40) hbreak = stack - 0x40;
    chunk->next = hbreak;
    chunk->free = 1;
    return chunk;
}

void *malloc(size_t size)
{
    if (!size) return 0;
    if (size % sizeof(hhdr)) size += sizeof(hhdr) - (size % sizeof(hhdr));

    hhdr *hdr = &hhead;
    while ((char *)hdr->next < hbreak)
    {
        hdr = hdr->next;
        if (hdr->free && 
                (char *)hdr->next - (char *)hdr - sizeof(hhdr) >= size)
        {
            if ((char *)hdr->next - (char *)hdr - 2*sizeof(hhdr) > size)
            {
                hhdr *hdr2 = (hhdr *)((char *)hdr + sizeof(hhdr) + size);
                hdr2->free = 1;
                hdr2->next = hdr->next;
                hdr->next = hdr2;
            }
            hdr->free = 0;
            return (char *)hdr + sizeof(hhdr);
        }
    }

    if (!(hdr->next = newchunk(size + sizeof(hhdr)))) return 0;
    return malloc(size);
}

void free(void *ptr)
{
    if (!ptr) return;
    hhdr *hdr = (hhdr *)((char *)ptr - sizeof(hhdr));
    hdr->free = 1;
    if ((void *)hdr != hhead.next)
    {
        hhdr *hdr2 = hhead.next;
        while (hdr2->next != hdr) hdr2 = hdr2->next;
        if (hdr2->free) hdr = hdr2;
    }
    hhdr *next = hdr->next;
    while ((char *)next < hbreak)
    {
        if (!next->free) break;
        hdr->next = next;
        next = next->next;
    }
    if ((char *)next == hbreak) hbreak = (char *)hdr;
}

_heap符号由链接器定义。此处未显示realloc(),因为它现在无法使用(因此完全未经测试)。

现在的问题是:我在这里创建了我的runtime(malloc在src / libdos / stdlib.c中),写了很多测试内容,最后,一切似乎都运行得很好。另一方面,我的游戏经过全面测试,并使用valgrind检查无效的内存访问。尽管如此,将两个部分放在一起,只是崩溃。 (尝试使用make -f libdos.mk从git构建游戏,你需要安装llvm / clang。

当我第一次经历一个奇怪的 heisenbug 时(我现在就在它身边工作),我想它可能是优化者在编译真实模式时出错的错误,这确实不常见。但我无法确定,下一个敏感的候选人可能是我的记忆管理,见上文。

现在这是一个棘手的问题:我如何调试这样的事情?只有我自己的测试代码,它的效果非常好。我无法在没有优化的情况下编译我的游戏,因为这样做会超过64KB。有什么建议?或者任何人都可以发现上述代码明显错误的东西?

1 个答案:

答案 0 :(得分:0)

如果这是实模式DOS,我不确定esp的高位。对于malloc(),使用ss:sp and 0xa000:0000之间的内存,堆栈顶部和640k边界之间的内存。我不记得MS-DOS是否为.COM程序分配了所有640k区域。有两个DOS调用,INT 21H, ah = 04Ah releases memory, ah = 048H allocates memory,但我不记得这些是用于.COM还是.EXE程序。