Sozobon ADB
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
Back to C