24.16.5.2 Array maps bounded by size

While dealing with binary formats, it often happens that the number of entities in a collection is given by the space they occupy, rather than the count itself.

For example, consider ELF sections holding relocations. These sections contain a collection of zero or more relocations. The layout of each relocation is specified by the following type:

type Elf64_Rela =
  struct
  {
    offset<Elf64_Addr,B> r_offset;
    Elf64_Xword r_info;
    Elf64_Sxword r_addend;
  };

The section is described by an entry in the ELF sections header table:

type Elf64_Shdr =
  struct
  {
    Elf_Word sh_name;
    Elf_Word sh_type;
    Elf64_Xword sh_flags;
    Elf64_Addr sh_addr;
    Elf64_Off sh_offset;
    offset<Elf64_Xword,B> sh_size;
    Elf_Word sh_link;
    Elf_Word sh_info;
    Elf64_Xword sh_addralign;
    offset<Elf64_Xword,b> sh_entsize;
  };

The relevant elements of Elf64_Shdr are sh_offset and sh_size, which indicate the offset of the beginning of the section’s contents, and its size, respectively. At this point, if we wanted to get an array with all the relocations in the section, we could map an array bounded by number of elements like we saw in the previous section, like this:

(poke) Elf64_Rela[sh_size / 1#Elf64_Rela]
[... relocs ...]

However, this approach adoleces from two problems. First, it doesn’t work with any entity type. For an offset like 1#Elf64_Rela to work, it is required to know the size of the type specified as the unit at compile time. In the particular case of Elf64_Rela, that condition is satisfied, but too often that’s not the case. For example, think about a section containing NULL terminated strings: you can’t know the number of strings contained in the section until you actually read it.

Another problem is when the data in the header is corrupt. Using the mapping bounded by number of elements, we wouldn’t realize it. It would be good if the tool would tell us whether the specified size actually holds an exact number of the requested elements.

A mapping bound by size is what we need. Fortunately, as we saw when discussing array types, Poke allows you to specify an offset instead of an integral value, in the array type specification. The right amount of entities (in this case relocations) to strictly satisfy the provided size will be mapped in the IO space. So, in order to obtain an array containing all the relocations in the section, we simply write:

(poke) Elf64_Rela[ehdr.sh_size] @ ehdr.sh_offset
[... relocs ...]

The strictness mentioned above is important. GNU poke will complain (and abort the mapping) if it is not possible to map an exact number of elements. Thus the following mapping would not be successful:

(poke) int[33#b] @ 0#B
unhandled out of map bounds exception

Like in mappings bounded by number of elements, if a constraint fails while performing the mapping, an exception is raised and the map is aborted.

Likewise, if the exact size specified as the array boundaries can’t be achieved, then E_constraint is raised.