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 #0 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.