5.3 Loading Maps

So we have written our =self.map=, which denotes a view or structure of ELF files we are interested on, and that resides in the current working directory. How to use it?

The first step is to fire up poke and open some object file. Let’s start with foo.o:

(poke) .file foo.o

Now, we can load the map using the .map load dot-command:

(poke) .map load self
[self](poke)

The .map load self command makes poke to look in certain directories for a file called self.map, and to load it. The list of directories where poke looks for map files is encoded in the variable map_load_path as a string containing a maybe empty list of directories separated by : characters. Each directory is tried in turn. This variable is initialized with suitable defaults:

(poke) map_load_path
"/home/jemarch/.poke.d:.:/home/jemarch/.local/share/poke:[...]"

Once a map is loaded, observe how the prompt changed to contain a prefix [self]. This means that the map self is loaded for the current IO space. You can choose to not see this information in the prompt by setting the prompt-maps option either at the prompt or in your .pokerc:

poke) .set prompt-maps no

By default prompt-maps is yes. This prompt aid is intended to provide a cursory look of the "views" or maps loaded for the current IO space. If we load another IO space and switch to it, the prompt changes accordingly:

(poke) [self](poke) .mem foo
The current IOS is now `*foo*'.
(poke) .ios $<*foo*>
The current IOS is now `./foo.o'.
[self](poke)

At any time the .info maps dot-command can be used to obtain a full list of loaded maps, with more information about them:

(poke) .info maps
IOS   Name   Source
0    self   ./self.map

In this case, there is a map self loaded in the IO space 0, which corresponds to foo.o.

Once we make foo.o our current IO space, we can ask poke to show us the entries corresponding to this map using another dot-command:

    : (poke) .map show self
    : Offset     Entry
    : 0x0UL#B    $self::ehdr
    : 0x208UL#B  $self::shdr

This tells us there are two entries for self in foo.o: $self::ehdr and $self::shdr= Note how map entries use names that start with the $ character, then contain the name of the map an the name of the entry we defined in the map file, separated by ::.

We can now use these entries at the prompt like if they were regular mapped variables:

[self](poke) $self::ehdr
Elf64_Ehdr {
  e_ident=struct {
    ei_mag=[0x7fUB,0x45UB,0x4cUB,0x46UB],
    [...]
  },
 e_type=0x1UH,
 e_machine=0x3eUH,
 [...]
}
(poke) $self::shdr'length
11UL

It is important to note, however, that map entries like $foo::bar are not part of the Poke language, and are only available when using poke interactively. Poke programs and scripts can’t use them.

Let’s now open another ELF file, and the =self= map in it:

(poke) .file /usr/local/lib/libpoke.so.0.0.0
(poke) .map load self
[self](poke)

So now we have two ELF files loaded in poke: foo.o and libpoke.so.0.0.0, and in both IO spaces we have the self map loaded. We can easily see that the map entries are different depending on the current IO space:

[self](poke) .map show self
Offset       Entry
0UL#B        $self::ehdr
3158952UL#B  $self::shdr
[self](poke) .ios 0
The current IOS is now `./foo.o'.
[self](poke) .map show self
Offset   Entry
0UL#B    $self::ehdr
520UL#B  $self::shdr

foo.o is an object file, whereas libpoke.so.0.0.0 is a DSO:

(poke) .ios 0
The current IOS is now `./foo.o'.
[self](poke) $self::ehdr.e_type
1UH
[self](poke) .ios 2
The current IOS is now `/usr/local/lib/libpoke.so.0.0.0'.
[self](poke) $self::ehdr.e_type
3UH

The interpretation of the map entry $self::ehdr is different depending on the current IO space. This makes it possible to refer to the “ELF header” of the current file.

Underneath, poke implements this by defining mapped variables and “redirecting” the entry names $foo::bar to the right variable depending on the IO space that is currently selected. It hides all that complexity from us.