多重引导规范具有以下结构:
struct multiboot_tag_mmap
{
multiboot_uint32_t type;
multiboot_uint32_t size;
multiboot_uint32_t entry_size;
multiboot_uint32_t entry_version;
struct multiboot_mmap_entry entries[0];
};
意图似乎是数组大小可能会有所不同。在引导加载程序传递之前,不知道该信息。在托管C ++中,建议的建议是“使用vector
”。好吧,我做不到。另一种方法是使用动态分配,但这需要在我拥有内存映射信息之前实现内核的重要部分(分页,MMU等)。有点像鸡肉或鸡蛋的问题。
“黑客”只是使用gnu++11
启用扩展程序。但我尽量避免使用扩展,以避免可能导致未定义行为的C-ish代码或代码。代码越便携,我认为错误的可能性就越小。
最后,你像这样迭代内存映射:
for (mmap = ((struct multiboot_tag_mmap *) tag)->entries;
(multiboot_uint8_t *) mmap
< (multiboot_uint8_t *) tag + tag->size;
mmap = (multiboot_memory_map_t *)
((unsigned long) mmap
+ ((struct multiboot_tag_mmap *) tag)->entry_size))
因此结构的大小为tag->size
。
只要语义相同,我就可以修改多引导头。关键在于它对引导加载程序的看法。我该怎么办?
答案 0 :(得分:2)
您可以使用1个大小的数组代替0大小的数组:
struct multiboot_tag_mmap
{
...
struct multiboot_mmap_entry entries[1];
};
这只会改变sizeof(struct multiboot_tag_mmap)
的结果,但在任何情况下都不应该使用它:分配结构的大小应该计算为
offsetof(struct multiboot_tag_mmap, entries) + <num-of-entries> * sizeof(struct multiboot_mmap_entry)
地图结构的对齐并不取决于条目数组中元素的数量,而是取决于条目类型。
严格确认备选方案: 如果有一个已知的数组大小的边界,可以使用这个边界进行类型声明:
struct multiboot_tag_mmap
{
...
struct multiboot_mmap_entry entries[<UPPER-BOUNDARY>];
};
对于此类声明,不适用下面描述的所有可能问题。
关于访问元素的说明: 要访问此类灵活数组中的元素(第一个元素之上),需要声明新的指针变量:
struct multiboot_mmap_entry* entries = tag->entries;
entries[index] = ...; // This is OK.
而不是直接使用entries
字段:
tag->entries[index] = ...; // WRONG! May spuriously fail!
问题在于,编译器知道entries
字段数组中只存在一个元素,可以将最后一种情况优化为:
tag->entries[0] = ...; // Compiler is in its rights to assume index to have the only allowed value
有关标准验证的问题:使用灵活数组方法,内存中存在类型为struct multiboot_tag_mmap
的无对象(在堆或堆栈)。我们所拥有的只是这种类型的指针,永远不会被解除引用(例如,用于制作对象的完整副本)。同样,数组类型struct multiboot_mmap_entry[1]
的无对象,对应于结构的entries
字段,此字段仅用于转换为类型{{的通用指针1}}。
因此,C标准中的短语,表示 Undefine Behavior
将对象分配给不完全重叠的对象或具有不兼容类型的完全重叠的对象
不适用于使用通用指针访问struct multiboot_mmap_entry*
数组字段:此处没有重叠对象。