szadb primer
Michal Jaegermann
edited by
Anthony Howe
14 January 1992
(this line printer approximation produced by dvi2tty and slightly edited)
�
There is NO WARRANTY with respect to this publication, or the
program it describes, and disclaim any implied or explicit suggestions
of usefulness for any particular purpose. Use this program only if
you are willing to assume all risks, and damages, if any, arising as a
result, even if caused by negligence or other fault.
Sozobon C compiler and szadb debugger are both copyright (c)1989,1992
by Sozobon Ltd. Both can be freely copied and distributed provided
all copyright notices will remain intact and all modified versions will
be clearly marked as such.
Atari, ST, TOS and GEMDOS are trademarks of Atari Co.
GEM is a trademark of Digital Research Co.
Mark Williams C is a trademark or Mark Williams Co.
Unix is a trademark of AT&T.
�
1 Introduction
szadb is an assembly level debugger for programs written for
the TOS operating system on the Atari ST computer. It is
primarily meant to be a companion to the freely distributed
Sozobon C compiler, and can be copied and passed around under
the same conditions.
One of design goals of the original szadb project was to
provide a debugger closely resembling adb, which is available
on most Unix systems. There are some dissimilarities which
follow mostly from differences in target machines and
operating systems. Still users with an adb experience should
feel right at home.
The originally released version 1.0 was written by Johann
Ruegg and Don Dugger. Expanded to version 1.2 in a joint
effort of Anthony Howe and Michal Jaegermann. Further
development to version 1.4 by Michal Jaegermann. A document
describing version 1.2 was prepared by Anthony Howe and
Michal Jaegermann. Modified slightly by the last author to
reflect changes in versions 1.3, 1.4 and 2.1
This manual does not describe versions of szadb as they were
released as parts of Sozobon C compiler packages, but rather
modified relatives which are not products of Sozobon team and
which provide richer and more flexible user interface for a
price of an increased demand on computer resources.
Version 1.2 introduced multi-command input lines, which could
be attached to breakpoints and stepping commands. Other it
features included recording of a debugging sesion in a file,
definitions function keys, and other assorted niceties. In
addition to original Sozobon C symbols (Alcyon C style, also a
default for ST version of gcc compiler) it also accepted the
symbol table formats from Mark Williams C.
Version 1.3, not very widely distributed, added to this list a
"GST extension" of a symbol table format, with symbols up to
twenty two characters long. Executables with such tables are
produced by newer versions of gcc (ST) loader with -G option
to the compiler.
Version 1.4 expanded a range of supported screen displays. In
particular it is possible now to run szadb in all six TT
1
�
resolutions. Moniterm, in a tandem with a standard ST
monitor, also can be used in a particularly convenient manner.
If you have some graphic card which disallows switching of a
screen base check if Moniterm remarks will not apply to your
situation.
Version 2.1 added a support for "SozobonX" symbol table
format which showed up in version 2.0 of Sozobon C compiler.
If your compiler's symbol table format is not supported, or
there are other changes or additions you wish to add, the
complete source is provided free with the debugger. For
symbol tables check file adb1.c, a function setsym() and a
family.
The document you are reading now serves as a gentle
introduction to "adb way of debugging". It gives expanded
explanations and examples but it does not cover all points
described in a formal szadb description. Read the other
document in any case!
2 First steps
2.1 Starting
It is possible to run szadb either from a desktop or from a
text shell, in either low, medium or high resolution. In this
tutorial we will assume that szadb was launched from a command
line on a screen 80 characters wide (this setting affects only
the layout of some displays) and that the executable is called
adb.ttp, in tribute to its older brother.
The simplest way of starting szadb is to type something
similar to
adb.ttp program.ext
where program.ext is any executable. Note that the file
extension (tos, ttp,prg) must be provided. For debugging
purposes, it is preferable to compile your program with
debugger information (symbol tables). For Sozobon pass the
option -t to either cc or ld. It is possible to debug a
program without this information but this is not a trivial
2
�
task. This tutorial assumes that symbolic information is
provided.
For debugging purposes, it is much preferable not to strip off
symbol tables. In this tutorial we will always assume that
this was not done. To achieve that effect, while compiling
with Sozobon C, pass -t flag to cc.
If you do not have your favourite test program handy then you
may compile from the provided sources and use for experiments
a simplified version of unexpand.tos; it replaces, if
possible, runs of white space from stdin with tabs and writes
results on stdout. Further examples will use that program
compiled by Sozobon C compiler, version 1.2, with -t and -O
flags.
Once the debugger is started you should see on your screen a
display resembling the following:
Szadb version 2.1mj(english)
>
indicating that everything is in order. The character > is
the prompt. Its presence is the most striking visual feature
showing that we are not dealing with the "real" adb.
2.2 Where am I?
If you hit at this moment <Return> key szadb should respond
with
__start:
The debugger positioned itself at the very beginning of a
loaded program. szadb "remebers" the last command and its
current location, which is known as "dot". You may set
"dot" by typing any valid expression which will evaluate to
an address. Hitting <Return> by itself repeats the last typed
in command (not the executed one! This distinction will be
important later).
In this version the "dot" is set to low TPA, or a starting
execution address. The initial default command is /a which
will print the symbolic value of "dot" followed by a colon.
To see a numerical display of the "dot" try the following:
3
�
.=X <Return>
for a hexadecimal address, or
.=D <Return>
for the same one in a decimal form.
A note for Unix hackers. In szadb there is no distinction
objectspace and dataspace. Therefore prefixes ? and / in
commands are equivalent. Typing ?a will cause the same effect
as typing /a.
Addresses are printed in a form symbol+offset, where possible.
If there is no symbol table, or if offset is getting too big,
you will notice that addresses are printed as hexadecimal
values. The offset limit has initialy value of 0x400 but it
can be changed by $s request, in the form
$s <new_value>
The default number base for commands and displays is sixteen
(hexadecimal). Please refer to the documentation on how to
change the default base and use numbers in other bases.
White space, which is not a part of a literal string, is not
significant in szadb requests as long as a total number of
characters in the command line is below an input buffer length
(78 for this version). <Return> always terminates a current
line. Try adding some blanks and tabs to previously typed
commands.
2.3 Disassembling
Lets try something more exciting. A request for printing
machine language instructons is /i. It may be preceded by a
count. Try ,4/i. The comma is necessary and informs szadb
that what follows is a number of times to repeat the request.
If you will omit it then the debugger will decide that you
want to start with a location 4. If all is well then szadb
will respond
4
�
__start:
move.l sp,a5
move.l 4(sp),a4
move.l a4,__base
move.l 8(a4),d3
After this request a default command becomes /i. Therefore
to see the next four locations it is enough to type ,4 and
szadb will show
> ,4
__start+10:
add.l c(a4),d3
move.l d3,_etext
move.l 10(a4),d3
add.l 14(a4),d3
Note that the "dot" has moved. This is a common feature of
all memory examining requests. The "dot" will be set past
all already scanned memory. If you want to look at the same
memory area one more time, possibly using a different request,
use & to reset your position. It is a shorthand for the last
typed in address.
2.4 More on talking to szadb
A general szadb command has a form
address ,count request_with_its_modifiers
where each of three parts is optional or possibly not used.
Please refer to the documentation to see all available
requests. For all practical purposes count 1 means forever.
Read a little bit further before trying this.
Display format modifiers can be concatenated together. For
example, the following
main,9/ai
will print the first nine instructions, labelled by their
addresses, starting from _main. Like this:
_main: link a6,#-6
_main+4: movem.l [d3-5],-(sp)
5
�
_main+8: move.l #_tabs,-(sp)
_main+e: bsr _settab
_main+12: addq.w #4,sp
_main+14: clr.w d5
_main+16: clr.w d3
_main+18: move.l #__iob,-(sp)
_main+1e: jsr _fgetc
Note that the leading underscore, required to produce an
internal form of a symbol main was prepended automatically.
To get to an address of __main, if such symbol in your program
exists, you have to type its name in full. Similar but
slightly different rules will be in force if your program has
a symbol table in the MWC format.
If you do not know which symbols are available issue a request
$e. A display similar to the following will start to scroll
accross your screen.
> $e
__start: 77812
__exit: 778da
_gemdos: 778e2
_bios: 778ec
_xbios: 778f6
_bdos: 77900
_main: 77954
_settab: 77a72
__main: 77aac
_exit: 77b0c
................
The general method to stop a scrolling screen for a moment is
to use <^S> and any other key will continue. A <^C> will
cancel the command and any further output. Starting with
version 1.4 this includes also a paging. This means that
display stops after showing one screenful of symbols and szadb
waits for your keyboard input. Keys <q>, <Q> and <^C> break,
any other key continues. A processing of the request is
finished when you will get back a standard szadb prompt of
"> ".
6
�
Some hexadecimal numbers may look like symbols. For example,
if you happen to have a symbol abba in your program then szadb
will understand main+abba as a request for setting the "dot"
to an address which is a sum of addresses of main and abba,
even if you really meant an adress at offset of 0xabba from
main. To avoid this misinterpretation it is enough to type
main+0abba - number has a leading zero. It the symbol
abba is not defined then the ambiguity does not arise.
Assuming that the default base is sixteen abba will be taken
as a number. Otherwise such expression will be not accepted
and you will see only an error message. The form 0xabba has a
unique meaning and always works.
3 Running under szadb
3.1 How to run - with arguments
To run a loaded program, with a name passed as a szadb
argument, one has to type :c, which is a short for :continue.
When the program is running szadb switches to the program
screen which is different from that one used by the debugger.
In particular, all program keyboard input will be accepted
from the program screen. Unfortunately there is no way, at
least not in this version, to read the debugged program
standard input from a file. It has to be typed in. To switch
from the szadb screen the program window use <^W> and return
from the visit with any other character.
The program itself may have arguments. In principle there are
two methods with which they can be set. Firstly, you may
specify them when starting szadb. Everything which follows
the name of a loaded executable will be taken as its arguments
and copied verbatim to its basepage. A request $p will
display the whole basepage and it will allow you to check if
you really got what you expected. szadb does not allow for
any argument extending schemes and it will trunctate command
lines which are too long.
The second method allows you to specify arguments as an
optional tail of :c request. Once arguments were set, by any
of these methods, they cannot be changed and further attempts
to do so will be ignored.
7
�
Since both shell command lines and the input line inside of
szadb are limited in length, and partially already taken, it
may appear that there is no way to fill all available basepage
space with program arguments by any method, short of writing
directly to a computer memory. As we will see later this is
not true, even if it requires a little bit of trickery. (See
further descriptions how to define and execute function keys.)
When you are ready to quit, because you are done or lost and
wish to start afresh, enter $q. If the debugee process exited
of its own accord then szadb will terminate too.
3.2 Setting breakpoints
Executing a program under a debugger is of no great use
without breakpoints. Here is the simplest way in which they
can be set.
main:b
We can do even better than that. Try
main:b ="My first szadb breakpoint"n;.=XD;,8/ai
Everything which follows :b on the input line will be stored
and executed later on when the breakpoint is hit. Multiple
commands are separated by semicolons. By the way, you may use
semi-colons for immediate requests too.
Breakpoints also can have counts. Using as an example
unexpand.tos, and a fragment of its disassembled code shown in
a previous section, we may set a breakpoint
main+18,3:b="about to read"n
in a main loop of this program, just before fgetc() is called.
With this count an execution will stop only for every third
character to be accepted.
Setting a breakpoint on the top of an existing one is allowed
and it will simply cause a replacement - changing possibly a
count and commands to execute. A list of all current
breakpoints, with their counts and associated commands, is
8
�
produced by the $b request. Information shown at the bottom
of this list will be explained later.
It is not the best idea to set a breakpoint somewhere between
two program instructions. Nothing terrible will happen
immediately, but your debugging run may end up prematurely
amid an utter confusion. Sometimes it is possible to restart
a wayward program by writing a needed address directly into a
program counter with a address>pc request, but this is not
guaranteed to work. It is also advisable to keep breakpoints
on an execution path. They are a limited resource and there
is no point wasting it.
3.3 Displaying information
szadb provides many ways to display information about the
state of your program. Some of these requests were detailed
above. Another is $r, which will show the contents of all
registers and status flags. If you are interested in an
individual register then use something like
<a0=X
Replacing above = with /, or ?, will bring a hexadecimal
display of the long word stored at the location pointed to by
register a0. Careful here, the last form will move the
"dot". There are many other possible formats. Try, for
example, main,20/x and <b,2/s .
The second example uses one of four read-only variables
provided by szadb, which are
l lowest text address
t length of the text segment
b start of the bbs segment
d length of the data segment
With an exception of l names follow the Unix convention. They
will be particulary handy if you will have a misfortune of
debugging executable without a symbol table.
Formats in requests can be combined. Let us try something
like follows.
9
�
="Text memory dump"2n;main,<b-main%8+1/4x4^rr|rr8cn
and here are initial lines of a resulting display, where "."
replaces all non-printable characters.
Text memory dump
_main:
4e56 fffa 48e7 1c00 | NV..H...
2f3c 6 8ae6 6100 | /<....a.
10e 584f 4245 4243 | ..XOBEBC
2f3c 6 88a0 4eb9 | /<....N.
6 7c40 584f 3800 | ..|@XO8.
b87c ffff 6706 b87c | .|..g..|
4 6612 4267 4eb9 | ..f.BgN.
6 77d6 544f 4cdf | ..w.TOL.
Note that division is denoted by a % character and that 8
divides a difference <b-main and not only main, since all
expressions are evaluated in strict left-to-right order.
Let's break down the format modifiers to see what is actually
happening
4x print four short words in hex,
4^ backup the "dot" by four current fields (short words),
rr|rr print 2 blanks, vertical bar, and 2 more blanks,
8c print 8 characters,
n and a newline.
Displays that are wider then a current screen width (40 or 80)
will have lines split automatically.
3.4 Recording your session
A request $>filename starts writing a transcript of everything
which shows on your screen to the file filename. All examples
longer than a couple of lines were prepared this way. If the
filename is missing then the currently opened transcript will
10
�
be closed. The output is always appended to the given file,
so that it is possible to open, close, and re-open the same
file any number of times.
Because GEMDOS is not re-entrant it is not a very very good
idea to perform an actual file write while processing a GEMDOS
call. It is nearly certain you will crash your system. The
safest course in such spots is to turn recording temporarily
off. However, transcript output is buffered by default, so
actual writes occur only when the buffer is flushed when full
or because the file was closed. Therefore with proper care,
one can empty the buffer prior to dangerous spots and even
create a record of a GEMDOS call, provided it is not too
wordy. A handy definition for a function key to do this is
$>;$> (see the last section on function keys).
It is advisable not to write files to your hard drive, instead
use a RAM drive or a dedicated floppy (which can be
reformatted in case of disaster). Remember that a buggy
program and the debugger can write anywhere. Over system file
buffers and cached File Allocation Tables as well.
If you need all memory you can get it is possible to turn off
transcript buffering with command line option -nb. But then
you will have to be extremely careful about possible conflicts
with GEMDOS.
Note! After $>file request your default command is $> and not
the last command you were executing previously. It is
possible to execute $> inadvertently by hitting <Return> or by
making some mistake while typing the next line. This will
close your transcript with obvious results. When something
like thats happens, or when in doubt, issue another $>file.
4 Bug hunting
4.1 Compiling for szadb
Lets have a closer look at the C program below. Its stated
purpose is to replace, if possible, runs of white space with
tabs. The width of a tab is fixed and equal to a constant
TABSTOP. If a text line is getting too long then substitutions
11
�
are abandoned and remaining characters are copied without
modifications.
#include <stdio.h>
#define MAXLIN 132
#define TABSTOP 8
5
int tabs[MAXLIN];
main ()
{
10 int c, delay, col;
void settab ();
settab (tabs);
15 col = delay = 0;
while (EOF != (c = getchar ())) {
if (MAXLIN < col) {
/* copy remaining characters on a line */
do {
20 putchar (c);
} while ('\n' != c &&
EOF != (c = getchar ()));
col = 0;
}
25 else {
switch (c) {
case '\t':
while (!tabs[col])
col++;
30 case ' ': /* fallthrough */
if (!tabs[col]) {
col += 1;
continue; /* keep delay */
}
35 putchar ((delay == col) ? ' ' : '\t');
col += 1;
12
�
break;
default:
while (delay < col) {
40 putchar (' ');
delay += 1;
}
putchar (c);
col = ('\n' == c ? 0 : col + 1);
45 break;
} /* switch */
} /* if (MAXLIN < col) */
delay = col;
}
50 exit (0);
}
void
settab (tab_pt)
55 short *tab_pt;
{
int i;
for (i = 0; i < MAXLIN; i++) {
60 *tab_pt++ = (0 == (i % TABSTOP));
}
}
This program has actually two bugs. See if you can find them
just examinig the source.
Here is how szadb can help. Compile source as follows (these
commands are for Sozobon C)
cc -t -O -o unexpand.tos unexpand.c
Flag -O is not necessary but with this particular compiler you
will probably find a disassembled code easier to follow. You
may test the compiled executable on its own source.
unexpand.tos <unexpand.c >output
13
�
In the first moment the program appears to work, but a closer
examination of the output reveals that the indentation is not
exactly right. Moreover, some lines start with a blank,
followed by a tab, which is not really what was intended.
There are also other problems. Check yourself. The likely
suspect will be an array tabs of tabstops filled by a function
settab().
4.2 The first bug
Start the program under szadb control
adb.ttp unexpand.tos
and set a breakpoint at settab+4, just after link instruction.
Run the program with :c ; $C. This will produce the following
display
break at _settab+4
_settab+4: movem.l [d3-4],-(sp)
_settab+4(0006,8bb4)
_main+12(0001,0006,8cbe,0006,73a2)
__main+4e(? at 69cc6)
In the absence of better information all arguments shown in
the stack backtrace are assumed to be two bytes wide. We know
from the source that settab() actualy expects one pointer.
Confirm that it got a right one by putting it together from
two halfs and using the request 68bb4=p to print it as the
symbol. You should see _tabs in response. To continue
execution of the current function till it returns to its
caller, use :f which stands for :finish.
It is clear from lines 30 and 35 that the first tabstop in
_tabs is expected to be on a position TABSTOP - 1 . Dumping
some initial fragment of the just initialized array we see the
following:
> tabs,3/4x
_tabs:
1 0 0 0
0 0 0 0
1 0 0 0
14
�
This is clearly wrong and one bug becomes obvious. To repair
it line 59 should be changed to
for (i = 1; i <= MAXLIN; i++) {...}
4.3 ...and the other one
The second bug is harder to spot, since for most of test
inputs our program will work correctly. This is a typical
example of a program broken for boundary conditions. To make
it easier to track the problem set MAXLIN to some small
integer (around 10 should be good), recompile the program,
restart the debugging session and set a breakpoint at main+28,
just after a character was read. Set this breakpoint with a
count and a request to show a received character with
main+28,5:b <d0=cx
With carefuly chosen input this will show where you are in the
program and will skip unnecessary stops in the same time. You
have to provide an input by typing it yourself. Remember that
to repeat the last command :c it is enough to hit <Return>.
For execution defaults szadb will use the most recently typed
command even if some other requests were executed by
breakpoints.
In order to see how the received character is processed you
can single step with :s, which will follow program execution.
The request :n will single step like :s but will execute
function calls at full speed and so have the effect of
stepping over them. Breakpoints set on skipped levels still
will be obeyed.
Tracing some tests inputs with the value of the variable col
around MAXLIN should reveal the second error soon enough. If
you want to try it yourself, do not read further.
Looking at line 17 we find a sharp less-than inequality in the
test. It should be replaced by a less-than-equal-to.
Otherwise, when the value of col equals MAXLIN, and your input
is "right", the program is trying to read, in lines 28 and 31,
from the location &tabs[col], which is one past the end of the
array. The remaining analysis of this bug is left as an
exercise to the reader.
15
�
4.4 Breaking out
Another stepping command is the :j jump request. Its purpose
it to short-circuit loops. It behaves exactly like :n in that
it skips-over function calls but unlike the :n request which
follows branch instructions, the :j will place a temporary
breakpoint at the instruction immediately following the
current instruction in memory. Think of :n as
step-next-logical instruction and :j as step-next-physical
instruction. The idea is that you only use :j to step-out of
loops where you are sitting on the loop-back branch.
...
bra .test
.body:
...
.test:
cmp d0, d1
bnz .body
.end
...
In the example above, if you are sitting on the bnz
instruction and you wish to execute the remainder of the loop
at full speed then you use the :j, which places a temporary
breakpoint at the location .end. However care must be taken
when using this command because it is possible that the
flow-of-control never reaches the temporary breakpoint.
...
bra .snert
.never:
... ; might be local data here
.snert:
rts
This is a case where NOT to use the :j command. Basically to
use the :j request you should know how your C compiler sets up
its loops or where your assembler code is meant to go. It is
sometime a good idea to put a safety breakpoint somewhere you
know you will end up.
16
�
Because of the unusual nature of the :j request, it will not
autorepeat if the next command is just a <Return> key. Instead
:n is performed. Also it is also considered a variant of :n
when attaching execution requests to stepping commands (see
further down).
It should be also mentioned that all stepping commands have
upper case counterparts, which are noisier. When used they
return the a full register display after the step (:S is like
doing :s;$r).
5 More fun and games
This section covers some finer points of szadb use. You may
put them aside when just starting first experiments with the
debugger. But probably one day you will find some of that
information very useful.
5.1 Advanced steps
It was already mentioned that all stepping commands may have
also attached szadb requests. As a matter of fact there is
even one default, for :f. To get a similar effect for other
steps just type what you want to be executed after a given
command on the szadb input line. For example
:s ="this string printed by :s command"n
You will notice that this form of :step does not move you
ahead in the program. This gives an opportunity to set and
modify requests in advance, without an immediate execution.
It also saves your nerves if you are not a very good typist.
To really step by one instruction forward hit <Return> and
observe what will happen. As noted before :j is a variant of
:n and setting request for it will really change what is
attached to :n.
The example above is not tremendously useful, but tracking
values of a chosen registers can be. Or anything else that
you need and that will fit on the command line. This
condition is much less limiting than it appears when used in
conjunction with function keys.
17
�
There are two points to remember. There is no syntax check
while you are setting requests. Watch what you are typing if
you do not want to see only error messages. And there is
nothing to prevent a direct or indirect recursion. Since a
depth of szadb stack is limited and <^C> does not always work
it is better to avoid such constructs.
If you came to a conclusion that the only way to change
requests attached to a stepping command is to overtype them
with something else then you are correct. Moreover an empty
string is not a good replacement since it causes an execution.
Luckily there is some other way to turn a noise off. Try
typing ::s- and similar commands for :n and :f. Switched off
requests are not gone, unless later redefined. They can be
brought back by a similar command as above in which - was
replaced with +. The default request for :f is special. It
cannot be overtyped or turned on by ::f+. To bring it back
use ::f` instead.
There is one more way of request switching. If you will type,
for example, ::n_, then whatever was attached to :s will be
performed also for :n, instead of a "native" command. This
gives an opportunity to create, say, a quite complicated
request for :n and most of the time execute a simpler request
for :s. When an original request for :n is needed it is
enough to type ::n+ to get it back. The same mechanism works
for :f, temporarily redirecting its requests "down" to the
first active one. There is no similar switching going the
other way. To gain a better understanding try all of this
after defining various requests for each of stepping commands
and examine effects with the $b command after every switch.
It is also possible to make user breakpoint silent or not.
For example
main:b ="entering main"
main::b- and main::b+ turns the action off and on.
5.2 How to use function keys
Function keys can be defined, only once per debugging session,
by reading their definitions from a file. The name of the
18
�
file can be passed on the command line with -k filename. The
file may be as simple as this
# flush buffers of a transcript file record
F1 $>;$>record
F2 ="this is pretty long string which will be ins\
erted when you will hit function key F2"n;
F1 ="an attempt to redefine F1\n"
Note that an uppercase F, which starts key definition, has to
be in the first column and it has to be immediately followed
by a valid key number. Shifed functions keys have numbers
between 11 and 20. A definition of <F2> is continued on the
next line since a terminating newline is escaped with a \
character. Otherwise this continuation line would be simply
ignored. szadb also does not pay any attention to the second
definition of <F1>. To see a list of all function keys defined
type $k.
Defined function keys can be used in two ways. Just hitting a
function key will insert into the current input line as many
characters from a corresponding string as it will fit. The
resulting input text can be edited. In order to execute a
full definition use $k followed by a key number - always in
decimal. For example, a request $k2 will print twice the
message, which was just defined in the function key file.
Note that there no way to specify that a function key should
execute immediately instead of waiting for a <return>. Also is
there no default function key file name like - adb.key -
so you'll have to specify the -k file option on the command line
each time or use a command alias. Note also that requests of
a form $k<number> do not autorepeat.
It is clear that strings of commands attached to function keys
can be longer than a lenght of the input line. Actually
around 2K will be accepted. This is hopefuly longer than any
szadb script you ever want to write. Especially if you take
into account that one script can call another. Warnings
against recursion apply as well.
Such long scripts can be, of course, attached to breakpoints
or stepping commands. They give also, previously mentioned,
19
�
opportunity to fill all available space on a base page with
command line arguments. Just define one of function keys as
:c <text to put on a base page>
and execute at the beggining of your session. Coupled with a
possibility of directly modifying the memory this allows for a
preparation of scripts which will emulate any extended
arguments scheme, even if szadb directly does not support
directly any of these.
5.3 Other symbol table formats
This debugger was designed as a companion for Sozobon C and
therefore it understands its symbol table format, which was
inherited from Alcyon compiler. Starting with version 2.0 of
Sozobon C compiler a new format - "SozobonX" - was
introduced and which allows for an unlimited length of symbol
names (there is an internal compiler limit of 80 characters,
but it can be raised if you really need to do that). From
2.1mj szadb recognizes this format as well. The version for
which this document was written also supports MWC -
currently the official Atari development compiler. It will
cooperate also with the ST version of GNU compiler gcc. If
you happen to have an older version of gcc loader it may be
necessary for you to get your hands dirty in a source code.
Even better idea would be to update your compiler. The
current gcc loader can optionally produce symbol tables in
"GST format" where symbols can be up to twenty two
characters long. A support for this format exists in versions
of szadb 1.3 and up.
5.3.1 Mark Williams C support
Symbols created by MWC are outwardly different from those
produced by Sozobon C in that that names can be longer - up
to sixteen characters - and an underscore character is
appended instead of beeing prepended. When szadb will detect
an MWC produced object it will apply its conventions. That
means that if you type main it will first try to find a symbol
20
�
main. If this fails it will search for main_ next (not for
_main). If a symbol name has a leading underscore, or more
than one trailing, you have to type them yourself.
The debugger is trying to guess by itself which compiler
produced the current executable. If it guesses wrong you may
always override its choice by dropping a hint on the command
line. It consists of an -os flag for the Sozobon format and
-om for MWC. Remember that if you work in an assembler the
guessing code can always be fooled. The flags always provide
a way to set things straight.
When writing this guessing code I had no official description
of the format used by MWC. All necessary information was
inferred from an examination of MWC produced binaries. The
code worked so far on everything I tried, but it may happen to
be wrong for your version. Since szadb comes complete with
source you can modify it accordingly and recompile (look for
all places where a global variable swidth is modified).
5.3.2 How to work with gcc
(Some of an information in this section is likely to be of a
historical value only. On the other hand maybe there are
still some stray copies of an old gcc loader still in use.
Who knows?)
A default form of executables created by this compiler is with
a symbol table attached. You need to use -s flag if you
really do not want it produced. A default format for TOS
version of this compiler is basically the same as for Sozobon
C or Alcyon. If you happen to be an owner of an old version
there is one subtle difference. A bit which carries an
information that a symbol is global one, known as S_EXT in
szadb parlance, is not set. This makes the debugger blind to
a symbol presence.
Here are instructions how to modify an old version of a file
ld.c which contains sources for gcc linker. In a function
write_atari_sym(p, str) for TOS version add the following
if (p->n_type & N_EXT)
sym.a_type |= A_GLOBL;
21
�
just before a line which reads
sym.a_value = p->n_value;
Recompile and reinstall linker and from this moment on szadb
recognizes gcc produced symbols.
Even better idea would be to upgrade your compiler to a newer
version. The modification described above will be already
present, but there is more. Currently gcc, upon a presence of
-G flag, may produce symbol tables with names up to twenty two
characters long. Versions 1.3 and up of szadb will correctly
recognize such symbols making for much nicer debugging (even
if this will require more typing from time to time).
Internally this is a version of Sozobon format. Use -os flag
in case of confusion.
If for some reasons you cannot upgrade, you do not have linker
sources or you cannot recompile them - because you do not
have enough memory, for example - not everyting is lost. It
is quite feasible to disable S_EXT check in setsym() (look in
the file adb1.c). You will not notice any change for Sozobon
C created executables. All symbols occuring in their symbol
tables are actually always global. This is not quite true for
gcc and some new, sometimes strange, symbols will appear but
usually this will not create any problems. In order to have
only globals in a gcc produced symbol table pass -x flag
either to gcc or to its linker.
5.3.3 Command line interpretation
As mentioned before szadb supports only "vanilla" command
line without any extension schemes. Other compilers, like
gcc, may expect something different and there could be some
disagreements about an interpretation of a command line.
Usually this can be fixed quite easily. Here is a file, named
fixargs.adb, which can be used with a command line following
this pattern
szadb.ttp -k fixargs.adb <program> [argument, ...]
where <program> was compiled by gcc.
22
�
# an example of a function keys definition file used
# to correct an argument interpretation for gcc
# compiled program
F1 $k11; $k13 ; $k14; $k15; $b; :c
F11 main:b <sp,10/x; $k12
F12 <sp/W *(<sp+4)-3; <sp+4/W *(<sp+8)+c; <sp,10/x
# for 16 bit
#F12 <sp/w *(<sp+2)-3; <sp+2/W *(<sp+6)+c; <sp,10/x
F13 getitime:b
This file defines some function keys. The fact that commands
associated with keys <F14> and <F15> do not exist, even if they
are referenced in a line for <F1>, is not harmful in any way.
You may define these keys later for your advantage. After
szadb started hit <F1> followed by <Return>. This will cause,
among others, an execution of a command attached to <F11>, i.e
<F1-shifted>. This command will set a breakpoint at a start of
real program with a fixup command attached. In turn, this
will modify your stack causing your program to see proper
arguments. Requests in a form of <sp,10/x are added only for
reassurance and to show what really happens.
The example above assumes that a symbol getitime is defined in
your program. Replace with something which is really present
or you will see some error messages. They are not fatal. All
of this is not going to work if main is not present in your
symbol table. This is not really likely for a program which
has a symbol table and was compiled from C sources, but may
happen for other languages. Modify accordingly.
5.3.4 Other compilers
If you own a compiler which produces symbolic information in
an unsupported format you can modify szadb yourself to support
it. For a model of how to do this look for setsym() and
mwsetsym() in the file adb1.c. Both of them call addsym()
which performs an actual insertion of a new symbol and its
value into a linked list of supported symbols. A current
version of addsym() will handle symbol names of any length but
internally they will be chopped off to something not longer
23
�
than the current value of the global variable swidth. Ensure
that this value is set properly for your needs.
The mechanism above can be easily extended to allow reading
some symbols and their values from a user suplied text file.
Some may find it very handy. The current version of szadb
does not support this feature. It is possible to roll your
own if you really need it.
5.3.5 Screens of not a standard ST size
Starting with a version 1.4 szadb does not have standard ST
screen parameters embedded into its code. Instead it reads
all necessary information from line-A variables and reserves
all needed buffers accordingly. In particular it follows that
szadb will run correctly in all six TT resolutions. It may
also work with different overscan utilities, provided screen
geometry variables in a machine were updated properly. Note
that fonts used are resolution dependent but fixed for a given
resolution. This means that putting monochrome ST into 50
line mode will not give nice results without code
modifications (see w_init() in window.c).
A special support is provided for Moniterm monitors. Because
of limitations of a Moniterm driver you will need also a
standard ST monitor connected in parallel. In such setup your
program screen will be displayed on Moniterm and szadb will
print its information to a side monitor; no screen flipping
with <^W> is needed or possible.
Since the debugger tries to limit use of a dynamic memory
allocations the code which reserves szadb work screen and
necessary buffers does that in a "false" program space and
is located in start.s. Should you decide to modify it for any
reasons look for comments which attempt to describe what
really happens.
Note: despite of the fact that szadb can be used on TT the
version 2:1 is still ST debugger in that sense that it does not
support yet any of 68020/30 specific codes. This should be
fine for most programs in the nearest future. An expansion
will be easy and straightforward provided you will have
necessary reference handy.
24
�
5.4 Customization
Some possible customizations were already mentioned. The
other obvious one is a version of szadb without on-line help.
If you feel brave enough remove the definition of the compile
time constant HELP from the makefile. A size of the
executables will undoubtely go down.
In order to create a version in some other language edit a
file lang.h. It contains all messages which szadb may
display.
You can adjust to your taste some other points. For example,
what are default requests attached to stepping commands and
what is their status (look in stepping.c for bpstat(),
findcmds(), bpt_list[]). Which characters are used for
requests switching (see getrequs() in pcs.c). Some features,
like support for functions keys or other symbol table formats,
can be taken out easily without affecting the whole design.
If you try to recreate szadb with another compiler you should
note that essential parts of this program were written in
assembler and may have to be translated to something your
tools can accept. Here is another point to watch for. A
variable _BLKSIZ sets size of Malloc'ed blocks. If a library
you use supports something similar modify accordingly. There
are also some constructs in C code, like a static
initialization of a union member (bpt_list in stepping.c) and
an array of size 0 in a definition of struct symbol in adb.h,
which are accepted by Szozobon C but can give a hiccup to some
other compilers. These points can be modified without undue
strain. Otherwise the code should be pretty portable,
although it is ST specific by its very nature.
5.5 Running on a verge
You may find yourself in a situation when your program wants
all memory it can get and together with the debugger does not
exactly fits into available space. There are still some
things which can be done.
Keep in mind that szadb grabs all memory it needs before a
program to debug is loaded and it does not make any claims
25
�
later. These requirements can be minimized. Function key
definitions if non-existent will not use memory at all,
discounting the supporting code. This code by itself is
mostly contained in a file fkeydefs.c and it is easy to
remove. The flag -nc, for no commands, gives some memory for
a price of missing breakpoint requests. A more substantial
memory chunk can be released with -nb, for no buffering on
transcript, flag. You still can open a transcript file but
all output will be direct. Beware of GEMDOS when using
transcripts without buffering.
If this does not help then there is a time to trim some fat
from szadb itself. You will have to recompile it leaving some
features out. On-line help is probably the first candidate.
All this effort can be wasted if you will forget about one
thing. Due to an infamous design bug in TOS a Malloc system
call can be invoked only a small fixed number of times. When
this pool is exhausted you will get "out of memory
condition" even if memory is still plentiful. Therefore all
allocation functions, from smart libraries, request a memory
from the system in bigger pieces and later try to satisfy all
requests chopping from an already owned resource. The name of
the game for szadb is to use for all its needs only one chunk
of a system memory which is just big enough, so not too much
of an unused memory will be left. The whole symbol table of a
debugged program must fit there and all szadb requests for a
space for internal structures have to be statisfied. To
adjust sizes properly you may want to change a constant CHUNK
which is defined at the top of a file adb.c.
If everything else fail you may still try to change a value of
a global variable __STKSIZ from start.s but this would be
probably the last stand.
5.6 Writing to memory
It can be done. This is left as an exercise to the reader.
Check your documentation.
26
�
Contents
1 Introduction 1
2 First steps 2
2.1 Starting . . . . . . . . . . . . . . . . . . . . . . . . 2
2.2 Where am I? . . . . . . . . . . . . . . . . . . . . . . . 3
2.3 Disassembling . . . . . . . . . . . . . . . . . . . . . . 4
2.4 More on talking to szadb . . . . . . . . . . . . . . . . 5
3 Running under szadb 7
3.1 How to run - with arguments . . . . . . . . . . . . . . 7
3.2 Setting breakpoints . . . . . . . . . . . . . . . . . . . 8
3.3 Displaying information . . . . . . . . . . . . . . . . . 9
3.4 Recording your session . . . . . . . . . . . . . . . . . 10
4 Bug hunting 11
4.1 Compiling for szadb . . . . . . . . . . . . . . . . . . . 11
4.2 The first bug . . . . . . . . . . . . . . . . . . . . . . 14
4.3 ...and the other one . . . . . . . . . . . . . . . . . . 15
4.4 Breaking out . . . . . . . . . . . . . . . . . . . . . . . 16
5 More fun and games 17
5.1 Advanced steps . . . . . . . . . . . . . . . . . . . . . . 17
5.2 How to use function keys . . . . . . . . . . . . . . . . . 18
5.3 Other symbol table formats . . . . . . . . . . . . . . . . 20
5.3.1 Mark Williams C support . . . . . . . . . . . . . . 20
5.3.2 How to work with gcc . . . . . . . . . . . . . . . 21
5.3.3 Command line interpretation . . . . . . . . . . . . 22
5.3.4 Other compilers . . . . . . . . . . . . . . . . . . 23
5.3.5 Screens of not a standard ST size . . . . . . . . . 24
5.4 Customization . . . . . . . . . . . . . . . . . . . . . . 25
5.5 Running on a verge . . . . . . . . . . . . . . . . . . . . 25
5.6 Writing to memory . . . . . . . . . . . . . . . . . . . . 26
i