3

The Multiboot Specification has structures like this:

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];  
};

The intent seems to be that the array size can vary. The information is not known until passed along by the boot loader. In hosted C++, the suggested advice is to "use vector". Well, I can't do that. The alternative is to use dynamic allocation, but that would require implementing a significant chunk of the kernel (paging, MMU, etc.) before I even have the memory map information. A bit of a chicken or egg problem.

The "hack" is to just enable extensions with gnu++11. But I try to avoid using extensions as much as possible to avoid C-ish code or code that could potentially lead to undefined behavior. The more portable the code is, the less chance of bugs in my opinion.

Finally, you iterate over the memory map like this:

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))

So the size of the structure is tag->size.

I can modify the multiboot header so long as the semantics are the same. The point is how it looks to the bootloader. What can I do?

  • 1
    Is it allowed to seperate `multiboot_tag_mmap` into two structs or something? It feels that the "header" part and "body" part should be seperated objects but placed in the same memory block successively. –  Nov 02 '15 at 04:05
  • As long as you don't ever declare a `multiboot_tag_mmap` variable or do `sizeof(multiboot_tag_mmap)`, who cares whether the entry array is defined with 0, 1, or 1000 entries? (I'm assuming you cast a preexisting buffer in memory to this type, then iterate through the entries) – Russ Schultz Nov 02 '15 at 06:15
  • @RussSchultz Zero-sized array isn't legal C++ at all. Also, the issue is I can't just waste memory. I should only use as much as I need. – user5514334 Nov 02 '15 at 06:47
  • 1
    In ISO C you can write `struct multiboot_mmap_entry entries[];` . Perhaps your compiler has this as an extension for C++? – M.M Nov 02 '15 at 07:09
  • 1
    Zero-sized arrays are not allowed in C, so I'm fairly certain they aren't allowed in C++ either. In C, you would use a [flexible array member](http://stackoverflow.com/questions/17344745/how-to-use-flexible-array-in-c-to-keep-several-values). Not sure if C++ supports that yet. – Lundin Nov 02 '15 at 07:16
  • @Lundin: No, not supported. C99 invention after C++'98 forked. Less needed in C++ because templates allow you to define the array length as a non-type template parameter. Sure, C allows length variation at runtime, but that makes for types whose size isn't properly fixed at compile time. – MSalters Nov 02 '15 at 09:05
  • depends on what you want to do with it. you can also do pointer arithmetic and dont declare the mmap entries at all... – Alexander Oh Nov 02 '15 at 10:13

1 Answers1

2

Instead of 0-sized array, you can use 1-sized array:

struct multiboot_tag_mmap
{
    ...
    struct multiboot_mmap_entry entries[1]; 
};

This will change only result of sizeof(struct multiboot_tag_mmap), but it shouldn't be used in any case: size of allocated structure should be computed as

offsetof(struct multiboot_tag_mmap, entries) + <num-of-entries> * sizeof(struct multiboot_mmap_entry)

Alignment of the map structure doesn't depends on the number of elements in the entries array, but on the entry type.

Strictly confirming alternative: If there is known bounary for array size, one can use this boundary for type declaration:

struct multiboot_tag_mmap
{
    ...
    struct multiboot_mmap_entry entries[<UPPER-BOUNDARY>]; 
};

For such declaration all possible issues described below are not applied.

NOTE about elements accessing: For accessing elements (above the first one) in such flexible array one need to declare new pointer variable:

struct multiboot_mmap_entry* entries = tag->entries;
entries[index] = ...; // This is OK.

instead of using entries field directly:

tag->entries[index] = ...; // WRONG! May spuriously fail!

The thing is that compiler, knowing that the only one element exists in the entries field array, may optimize last case it to:

tag->entries[0] = ...; // Compiler is in its rights to assume index to have the only allowed value

Issues about standard confirmance: With flexible array approach, there is no object of type struct multiboot_tag_mmap in the memory(in the heap or on the stack). All we have is a pointer of this type, which is never dereferenced (e.g. for making full copy of the object). Similarly, there is no object of the array type struct multiboot_mmap_entry[1], corresponded to the entries field of the structure, this field is used only for conversion to generic pointer of type struct multiboot_mmap_entry*.

So, phrase in the C standard, which denotes Undefine Behavior

An object is assigned to an inexactly overlapping object or to an exactly overlapping object with incompatible type

is inapplicable for accessing entries array field using generic pointer: there is no overlapping object here.

Tsyvarev
  • 60,011
  • 17
  • 110
  • 153
  • note: accessing beyond `entries[0]` will cause undefined behaviour – M.M Nov 02 '15 at 07:09
  • Why it cause UB? This is trick similar to allocating array with `entries = malloc( * sizeof(entry))` and using it up to `entries[ - 1]` . – Tsyvarev Nov 02 '15 at 07:14
  • @Tsyvarev Because of struct padding and perhaps also aliasing. You have two alternatives: C flexible array members, that are well-defined, or old, ugly struct hack tricks like this one, which come with undefined behavior. – Lundin Nov 02 '15 at 07:18
  • Arrays and pointers are different. An array of size 1 with some extra memroy after it is different to a pointer pointing at an array of size 20. – M.M Nov 02 '15 at 07:21
  • @Lundin, I am not sure about aliasing issues, but `sizeof()` takes into account padding, needed for use `` in an array. So ` * sizeof(entry)` bytes provide sufficient memory for being interpreted as an array. – Tsyvarev Nov 02 '15 at 07:41
  • BTW, paragrapgh 3.5.2.1 of C89 standard explicitely says: `There may also be unnamed padding at the end of a structure or union, as necessary to achieve the appropriate alignment were the structure or union to be a member of an array.` – Tsyvarev Nov 02 '15 at 07:52
  • @Tsyvarev: It causes UB when you're accessing (padding of) `multiboot_tag_mmap` via an expression of type `multiboot_mmap_entry entries`. – MSalters Nov 02 '15 at 09:02
  • @MSalters: Can you point place in the standard(C or C++) which says, that accessing padding cause UB? – Tsyvarev Nov 02 '15 at 09:05
  • @Tsyvarev: There's no specific wording for _padding_, it's contained within the general ban of accessing an object via an incompatible expression type. – MSalters Nov 02 '15 at 09:08
  • @MSalters: You probably mean these words: `An object is assigned to an inexactly overlapping object or to an exactly overlapping object with incompatible type`. I update my answer for show that this wording is inapplicable in the given situation. – Tsyvarev Nov 02 '15 at 10:09
  • [Is the "struct hack" undefined behavior?](http://stackoverflow.com/questions/3711233/is-the-struct-hack-technically-undefined-behavior). – Lundin Nov 02 '15 at 10:26
  • @Lundin: Answer to the SO question you link refers to [C FAQ](http://c-faq.com/struct/structhack.html), which summarize: `It's not clear if it's legal or portable`. Several lines below this FAQ states: `although it does seem to work under all known implementations`. So question about validness of given usage is still opened. – Tsyvarev Nov 02 '15 at 11:03
  • @Tsyvarev See the answer posted by ouah, I believe that one answers the question better. It is clear that the "struct hack" is questionable practice pre-C99. I do remember encountering a couple of strange bugs related to that hack, back in the days. – Lundin Nov 02 '15 at 14:05
  • Oh, I think I catch your(and others) point: accessing elements of such array through `tag->entries[index]` is actually not well-formed. I add explicit usage description, and alternative which is definitely free from confirmance issues. – Tsyvarev Nov 02 '15 at 17:26