BE can be extended with user written "extensions".
These are shared libraries with well-defined interfaces.
BE can be told to load them at start-up.
Memory extensions
Memory extensions allow BE to view and edit content in places other than in files.
The binary file arguments to BE are normally of the form :-
filename[@address]
This tells BE to load the file and whenever data at a memory address
from address
to address+filelength
is accessed,
to supply the data from the file.
However, it is possible to supply binary file arguments of the form :-
extension!args[@address]
Memory extensions may be written to provide either read-only, or read-write access to their data.
BE loads the memory extension DLL or shared library.
It then passes the args
and address
to the memory
extension, who does something of its own chosing with them.
The memory extension DLL can then supply data to BE on request.
The file bememext.h
documents the extension interface.
Memory extensions can of course cache the data they fetch. They might do this if it is inefficient to make request for the data, every time a new byte is requested. Often a memory extension will request data in blocks, around the specific address requested, taking advantage of the fact that subsequent requests are likely to be to nearby addresses, often in the same block.
When the user presses r (for refresh), BE simply re-requests the data, and it could come from a cache.
When the user presses R (for really refresh), BE tells memory extensions to discard any cache they could be holding, and then re-requests the data, thus ensuring a fresh view is presented.
Memory extensions which cache therefore have to support a function which makes them discard any cached data they might be holding.
When editing files, changes to the data are recorded in memory. When BE is closed down, it attempts to write back any changes back into the disk files where the data originally came from. This is referred to as flushing. BE will prompt you as to whether to save the changes back to disk.
If a memory extension is providing the data to BE for display, and the memory extension supports modification of the data, it has a choice :-
As most memory extensions provide a live view of some real-time data, they tend to opt for the first choice.
Memory extensions which don't apply updates immediately must therefore
support a function which makes them flush the data from memory to
whereever it should be stored.
Addressing
When BE initially accesses an address, it discards address bits which are
larger than the addressing mode in use (set via the -A
command
line argument, default is 32).
eg: if we try to access address 0x0123456789abcdef
and we are
operating in 48 bit addressing mode, BE will truncate the address to
0x0000456789abcdef
.
Then if the segmented mode flag is on (set via the -g
command
line argument), BE computes a new address according to the following formula,
which is the basically the Intel 8086 real mode mapping of
segment:offset
to physical address (with no A20 bit) :-
addr = ( ((addr&0xffff0000)>>12) + (addr&0xffff) ) & 0xfffff;
BE then attempts to satisfy the memory reference using this address by considering all memory sections (files and memory extensions) in turn, until one works.
If the address is greater than 0xffffffff
BE will not attempt
to satisfy it using a 32 bit memory extension.
When asked to access data at a given address, sophisticated memory extensions can call back into BE to access data at other address(es). They do this by providing a 'memory access' function, which BE calls at initialisation time to tell the extension the addresses of the callback functions.
The address does not go through the mappings explained in the above Addressing section again.
A memory extension which presents a shadow view of data is a simple use of this feature.
Multiple address decode is another use. Perhaps BE is being used as the data navigation part of a debugger, and the contents of a DRAM chip has been dumped. However, due to incomplete decoding of address bits, the same data appears at several places in the address map.
Summary statistics could be generated via memory extension. For example, when a memory access is made to one range of addresses, it reads another related range and counts how many bits are 1's. It then returns the count. This is an example of a memory extension providing a form of derived data.
Beware: Loops can easily be created. A memory extension that when asked to access a byte at a given address simply calls back into BE for data from the same address is going to recurse forever. BE detects excessive nesting and fails the nested memory access. Don't rely on BE doing this though...
BEBIG allows BE to edit files that are too large to load into memory. It ought to be noted that the author regularly uses BE on files of several megabytes in size without a problem, but several gigabytes would present a problem! The memory extension opens a file handle and reads bytes demanded by BE upon request. eg:
be big!verybigfile.dat
BEDEV is a memory extension which can allow BE to edit device files. This can be used to edit sectors on a disk. eg:
be dev!/dev/hde
BEEXEC supplies data to BE by invoking a user program and caching the results. eg:
be exec!0x1000,mycommand
BERAWIO is a memory extension which can read and write Windows block devices. Think of this as a Windows equivelent of BEDEV. eg:
be rawio!\\.\PhysicalDrive3
BEMEMU allows the live debug of programs running within the MEMU Memotech retro-computer emulator. eg:
be -A16 -Cz80 -ySpeculator.sym -iSpeculator.ini memu!TOKEN,0x80
The
Mosquitto
project (Really Small Messaging Broker, supporting the MQTT protocol)
uses a BE memory extension to examine its post-mortem dumps.
Their
git
includes rsmb.ini
and tools/be/bersmb.cpp
.
Another use is the in live-debug of running adapter cards,
or other embedded devices.
The memory extension can provide data bytes directly from the memory space
of the adapter.
args
could be used to identify the slot the adapter is in.
Alternatively, args
could identify IO base addresses,
memory window addresses, or a device driver to use to access the data.
Memory extensions which do this, do exist, and they almost turn BE into
a debugger (almost, because there is no run, stop, or single step).
Run, stop and single step of an adapter could be driven by the
options mechanism, if that were possible and/or desired.
When using these, a customised initialisation file is typically also used,
which understands all the structure definitions and variables used in the
firmware on the adapter.
Yet another use might be providing BE with access to physical or
virtual or process specific linear address spaces, perhaps via the use
of a device driver.
Shared memory windows might give addressibility of datastructures in
other programs.
A simple example of this is a memory extension which reads bytes from
the /dev/kmem
special device in the AIX or Linux environment.
Using this, kernel device drivers may be debugged.
Perhaps bytes sent down a communications port could be made to appear
as a stream of binary data.
Disassembler extensions
Disassembler extensions allow BE to disassemble code in its address space.
The -C dx
command line argument is a way of telling BE
to load and use a disassembler extension for displaying any code in the data.
The same rules for naming and locating disassembly extensions apply, as for memory extensions.
The file bedisext.h
documents the extension interface.
As the disassembler proceeds, it tests each address to see if it is
within any field in definition marked with the nocode
annotation.
If so, it inserts the field instead, and continues disassembly afterwards.
Sounds complicated, but this is a way of preventing the disassembly of data.
BEZ80 is available online, and disassembles Z80 opcodes. You can type :-
be -A 16 -C z80 rom.dat@0x0000 ram.dat@0x4000
BEI86 exists within IBM SSD Hursley, but I have yet to secure its release. It decodes 8086/80186 opcodes.
BEARM exists within IBM SSD Hursley, but I have yet to secure its release. It decodes ARM6 opcodes.
BEPPC exists within IBM SSD Hursley.
It decodes PowerPC opcodes.
How to build
Currently extensions may be built for :-
loadAndInit
API to load the extension, named
beextension
, searching along the directories listed in the
PATH
and LIBPATH
environment variables.
The IBM xlC C++ compiler can be used to compile the extension.
Unfortunately xlC C++ is expensive and I no longer have access to it.
I have binaries for AIX 4.1.5 and AIX 4.3.
loadAndInit
or dlopen
The IBM xlC C++ compiler can be used to compile the extension.
I have no binaries for this mode.
dlopen
to load
shared libraries named beextension.so
, searching
along the LIBPATH
environment variable.
g++ can be used to compile the extension.
I have an AIX 5.3 binary, although I think the same source would
compile fine on AIX 4.2.1 or later.
dlopen
API to load the extension, which is
named beextension.so
.
dlopen
locates shared libraries by looking along the
LD_LIBRARY_PATH
, directories listed in the
/etc/ld.so.cache
file, and in the /usr/lib
and /lib
directories.
shl_load
API to load the extension,
which is named beextension.sl
.
shl_load
locates shared libraries by looking along
the SHLIB_PATH
.
I reserve the right to switch-over-to or add-support-for using
dlopen
based shared library support.
dlopen
API to load the extension, which is
named beextension.so
.
dlopen
locates shared libraries by looking along the
LD_LIBRARY_PATH
(it may also look in other places).
dlopen
API to load the extension, which is
named beextension.dll
.
dlopen
API to load the extension, which is
named beextension.so
.
LoadLibrary
API to load
BEextension.DLL
.
DosLoadModule
API to load
BEextension.DLL
, which it finds by looking along the
LIBPATH
environment variable.
LoadLibrary
API to load
the extension, which is named BEextension.DLL
, which it seems
to find by looking in the current directory or along the PATH
.
.ndl
file extension.
The DLL remains in memory for as long as BE needs it.
The .ndl
files must be on the search path so BE can find them.