Overlaying a C Structure onto Memory

Wouldnt it be nice to be able to display memory as structures and linked lists? The problem with doing this action at the monitor level is that the monitor doesnt usually have access to the information that the compiler/linker provides regarding the format of a structure. Because I am now assuming I have a file system, one might think that I could put the tool set-generated data in a file and allow the monitor to parse through it. I could, but parsing this data (the symbol table generated by the compiler) can be complicated, especially when you consider the fact that the format of this file could be very different from one compiler to the next. Even if I limit myself to a particular file format (say, ELF), the symbol table format might not be the same from one compiler to the next . A simpler approach is to create a command in the monitor that can look to a structure-definition file in the file system to determine how to overlay a structured display on top of a block of memory on the target. This approach eliminates all dependency on some external file format; hence, it works regardless of CPU type or toolset.

The structure definition file is an ASCII file that contains structure definitions almost as they would be seen within a C header file. The command in the monitor, called cast , can then use this file as a reference when asked to display a particular block of memory. This structure information, combined with the monitors use of the symtbl file, allows me to issue commands like, such as

 cast abc %InBuf 

This command looks for the file structfile in the flash file system, and, if the file is found, the command overlays the structure defined as abc on top of the address associated with the symbol %InBuf (from the monitors symtbl file). By enhancing this command so that you can specify which member of the structure is the next pointer, you can turn the cast command into a linked list display tool with almost no additional coding effort.

MicroMonitor expects the structure definition to be in the file structfile . In general, the format of this file is similar to that of standard C structure definitions but with some limitations. The types char , short , and long are supported, and they are displayed as a 1-, 2-, or 4-byte decimal integers, respectively. Hexadecimal and character display formats are specified with a member-like notation: the types char.x , short.x , and long.x display hexadecimal representations; the char.c type displays the value as a character.

The example structure definition in Listing 12.5 displays the member i in decimal format, j in hexadecimal, c as a character, d in hexadecimal, and e as a one-byte decimal integer.

Listing 12.5: Structure Definition.
image from book
 struct abc {     long   i;      long.x j;     char.c c;     char.x d;     char   e; } 
image from book
 

If a structure contains an array, the user must define the array as one of the fundamental types described above and give it an appropriate size . The cast command does not display arrays within structures because of the complexity of the output generated. Thus, the size associated with an array member is treated like padding, and only the name and array size are displayed. Listing 12.6 is an example of a structure definition file that demonstrates all of the functionality of the cast command. Note that the # sign signifies a comment.

Listing 12.6: A Structure Definition File Used by the cast Command.
image from book
 struct abc {     long       I;     char.c     c1;     pad[3];         # Not displayed, just adds padding     struct def d; } struct def {     short   s1;     short.x s2;     long    ltbl[5];  # Data is not displayed     short   s3; } 
image from book
 

In Listing 12.6, notice the embedded structures, the use of the .x suffix, and the pad[] descriptor. The pad[] descriptor is used for CPU/compiler-specific padding. The cast command is totally unaware of compiler-specific padding and CPU-specific alignment requirements. If the structure definition puts a long on an odd boundary and the CPU does not support that, cast still attempts the access, generating an exception. The user must add the appropriate padding to reflect actual alignment requirements. If the member is of type char.c * or char.c [] , cast displays the ASCII string. (If you dont want the pointer to be dereferenced, use char.x * .)

Listing 12.7: cast Command.
image from book
 static  ulong memAddr; static  int castDepth; #define OPEN_BRACE  '{' #define CLOSE_BRACE '}' #define STRUCT_SEARCH   1 #define STRUCT_DISPLAY  2 #define STRUCT_ALLDONE  3 #define STRUCT_ERROR    4 #define STRUCT_SHOWPAD  (1<<0) #define STRUCT_SHOWADD  (1<<1) #define STRUCT_VERBOSE  (1<<2) #define STRUCTFILE "structfile" struct mbrinfo {     char *type;     char *format;     int size;     int mode; }; struct mbrinfo mbrinfotbl[] = {     { "char",       "%d",       1 },        /* decimal */     { "char.x",     "0x%02x",   1 },        /* hex */     { "char.c",     "%c",       1 },        /* character */     { "short",      "%d",       2 },        /* decimal */     { "short.x",    "0x%04x",   2 },        /* hex */     { "long",       "%ld",      4 },        /* decimal */     { "long.x",     "0x%08lx",  4 },        /* hex */     { 0,0,0 } }; char *CastHelp[] = {     "Cast a structure definition across data in memory.",     "-[apv] {struct type} {address}",     "Options:",     " -a   show addresses",     " -l{linkname}",     " -n{structname}",     " -p   show padding",     " -t{tablename}",     0, }; int Cast(int argc,char *argv[]) {     long    flags;     int     opt, tfd, index;     char    *structtype, *structfile, *tablename, *linkname, *name;     flags = 0;     name = (char *)0;     linkname = (char *)0;     tablename = (char *)0;     while((opt=getopt(argc,argv,"apl:n:t:")) != -1) {         switch(opt) {         case 'a':             flags = STRUCT_SHOWADD;             break;         case 'l':             linkname = optarg;             break;         case 'n':             name = optarg;             break;         case 'p':             flags = STRUCT_SHOWPAD;             break;         case 't':             tablename = optarg;             break;         default:             return(0);         }     }     if (argc != optind + 2)         return(-1);     structtype = argv[optind];     memAddr = strtoul(argv[optind+1],0,0); 
image from book
 

The cast command begins in Listing 12.7. The command supports a few options that offer display of linked lists, inclusion of the address of the data in the display, and display of the padding. As with most of MicroMonitor commands, the cast function begins by performing some default initialization, a call to getopt() to override the defaults, and retrieval of the necessary command line arguments.

Listing 12.8: Checking for a Structure File.
image from book
 /* Start by detecting the presence of a structure definition file... */     structfile = getenv("STRUCTFILE");     if (!structfile) {         structfile = STRUCTFILE;     }     tfd = tfsopen(structfile,TFS_RDONLY,0);     if (tfd < 0) {         printf("Structure definition file '%s' not found\n",structfile);         return(0);     } 
image from book
 

Because this command requires a structure definition file, the command must verify that a structure definition file is present (Listing 12.8). The default name of the file is defined by STRUCTFILE , but can be overridden by the environment variable STRUCTFILE if present. This technique is used in several places in MicroMonitor, where a hardcoded value is available but can be overridden by the content of a shell variable.

Listing 12.9: Displaying Multiple Structures.
image from book
 index = 0;     do {         castDepth = 0;         showStruct(tfd,flags,structtype,name,linkname);         index++;         if (linkname) {             printf("Link #%d = 0x%lx\n",index,memAddr);         }         if (tablename  linkname) {             if (askuser("next?"))  {                 if (tablename) {                     printf("%s[%d]:\n",tablename,index);                 }             }             else {                 tablename = linkname = (char *)0;             }         }     } while(tablename  linkname);     tfsclose(tfd,0);     return(0); } 
image from book
 

The loop at the end of the cast command (see Listing 12.9) allows multiple structures or table entries to be displayed by calling showStruct() . After each structure display, the user can abort or continue with the next entry.

Listing 12.10: showStruct() .
image from book
 /* castIndent():  *  Used to insert initial whitespace based on the depth of the  *  structure nesting.  */ void castIndent(void) {     int i;     for(i=0;i<castDepth;i++) {         printf("  ");     } } /* strAddr():  *  Called by showStruct().  It will populate the incoming buffer pointer  *  with either NULL or the ascii-hex representation of the current address  *  pointer.  */ char * strAddr(long flags, char *buf) {     if (flags & STRUCT_SHOWADD) {         sprintf(buf,"0x%08lx: ",memAddr);     }     else {         buf[0] = 0;     }     return(buf); } int showStruct(int tfd,long flags,char *structtype,char *structname,char *linkname) {     struct mbrinfo *mptr;     ulong  curpos, nextlink;     int    i, state, snl, retval, tblsize;     char   line[96], addrstr[16], format[64];     char   *cp, *eol, *type, *eotype, *name, *bracket, *eoname, tmp;     type = (char *)0;     retval = nextlink = 0;     curpos = tfsctrl(TFS_TELL,tfd,0);     tfsseek(tfd,0,TFS_BEGIN);     castIndent();     if (structname) {         printf("struct %s %s:\n",structtype,structname);     }     else {         printf("struct %s @0x%lx:\n",structtype,memAddr);     }     castDepth++; 
image from book
 

The function showStruct() (Listings 12.1012.15) is the workhorse of the cast command. The showStruct() function uses recursion to display structures within structures. The showStruct() function parses the structure definition file ( tfd is the descriptor), while looking for the structure type specified by structtype . The function then attempts to display the memory block that begins at memAddr as if it were the structure. Note that showStruct() does not verify the syntax of the structure definition in structfile . It is up to the user to avoid confusing this function.

The showStruct() function records the current position in structfile so the position can be retrieved in the event of recursion. The function then seeks to the beginning of the file and calls castIndent() . The castIndent() function keeps track of the depth (or recursion level) of showStruct() and, based on that depth, prints out a related number of spaces. As the recursion depth increases, the space count increases . This process allows the embedded members to be indented, as one would expect. Next, showStruct() prints out either the name of the structure or its address and starts searching for the incoming structure type in the structure definition file.

Listing 12.11: Searching Through the Structure Definition File
image from book
 state = STRUCT_SEARCH;     snl = strlen(structtype);     while(1) {         if (tfsgetline(tfd,line,sizeof(line)-1) == 0) {             printf("Structure definition '%s' not found\n",structtype);             break;         }         if ((line[0] == '\r')  (line[0] == '\n')) { /* empty line? */             continue;         }         eol = strpbrk(line,";#\r\n");         if (eol) {             *eol = 0;         }         if (state == STRUCT_SEARCH) {             if (!strncmp(line,"struct",6)) {                 cp = line+6;                 while(isspace(*cp)) {                     cp++;                 }                 if (!strncmp(cp,structtype,snl)) {                     cp += snl;                     while(isspace(*cp)) {                         cp++;                     }                     if (*cp == OPEN_BRACE) {                         state = STRUCT_DISPLAY;                     }                     else {                         retval = -1;                         break;                     }                 }             }         } 
image from book
 

Initially, showStruct() is in state ( STRUCT_SEARCH ), looking for the specified structure. In this state, the function steps through each line of the file (using the TFS API function tfsgetline() ), until the structure definition is found (Listing 12.11). After the definition is found, showStruct() changes state to STRUCT_DISPLAY to indicate that it is now displaying the specified memory using the specified structure.

Listing 12.12: Displaying the Structure.
image from book
 else if (state == STRUCT_DISPLAY) {             type = line;             while(isspace(*type)) {                 type++;             }             if (*type == CLOSE_BRACE) {                 state = STRUCT_ALLDONE;                 break;             }             eotype = type;             while(!isspace(*eotype)) {                 eotype++;             }             *eotype = 0;             name = eotype+1;             while(isspace(*name)) {                 name++;             }             bracket = strchr(name,'[');             if (bracket) {                 tblsize = atoi(bracket+1);                                   }             else {                 tblsize = 1;             }             if (*name == '*') {                 castIndent();                 printf("%s%-8s %s: ",strAddr(flags,addrstr),type,name);                 if (!strcmp(type,"char.c")) {                     printf("\"%s\"\n",*(char **)memAddr);                 }                 else {                     printf("0x%lx\n",*(ulong *)memAddr);                 }                 memAddr += 4;                 continue;             } 
image from book
 

After showStruct() is in the state STRUCT_DISPLAY (Listing 12.12), each line is parsed to filter white space and properly align character pointers on tokens within the line. Note that this mechanism is not very robust. To maintain a reasonable level of simplicity, the code assumes a sane structure definition file.

The parsing code first looks for a leading asterisk, which indicates that a pointer needs to be dereferenced. If there is no leading asterisk, the raw memory is formatted, based on each member of the structure. The table mbrinfotbl[] (see Listing 12.13) is used to scan the line of the structure definition file for one of the possible data display formats supported. Listing 12.12 also detects the presence of square brackets ( '[' & ']' ) to support array specifications.

Listing 12.13: Using mbrinfotbl[].
image from book
 mptr = mbrinfotbl;                       while(mptr->type) {                 if (!strcmp(type,mptr->type)) {                     castIndent();                     eoname = name;                     while(!isspace(*eoname)) {                         eoname++;                     }                     tmp = *eoname;                     *eoname = 0;                     if (bracket) {                         if (!strcmp(type,"char.c")) {                             printf("%s%-8s %s: ",                                 strAddr(flags,addrstr),mptr->type,name);                             cp = (char *)memAddr;                             for(i=0;i<tblsize && isprint(*cp);i++) {                                 printf("%c",*cp++);                             }                             printf("\n");                         }                         else {                             printf("%s%-8s %s\n",                                 strAddr(flags,addrstr),mptr->type,name);                         }                         memAddr += mptr->size * tblsize;                     }                     else {                         sprintf(format,"%s%-8s %%s: %s\n",                             strAddr(flags,addrstr),mptr->type,mptr->format);                         switch(mptr->size) {                             case 1:                                 printf(format,name,*(uchar *)memAddr);                                 break;                             case 2:                                 printf(format,name,*(ushort *)memAddr);                                 break;                             case 4:                                 printf(format,name,*(ulong *)memAddr);                                 break;                         }                         memAddr += mptr->size;                     }                     *eoname = tmp;                     break;                 }                 mptr++;             } 
image from book
 
Listing 12.14: Dealing with an Embedded Structure.
image from book
 if (!(mptr->type)) {                 int padsize;                 char *subtype, *subname, *eossn;                 if (!strcmp(type,"struct")) {                     subtype = eotype+1;                     while(isspace(*subtype)) {                         subtype++;                     }                     subname = subtype;                     while(!isspace(*subname)) {                         subname++;                     }                     *subname = 0;                                          subname++;                     while(isspace(*subname)) {                         subname++;                     }                     eossn = subname;                     while(!isspace(*eossn)) {                         eossn++;                     }                     *eossn = 0;                     if (*subname == '*') {                         castIndent();                         printf("%s%s %s %s: 0x%08lx\n",                             strAddr(flags,addrstr),                             type,subtype,subname,*(ulong *)memAddr);                         if (linkname) {                             if (!strcmp(linkname,subname+1))                                 nextlink = *(ulong *)memAddr;                         }                         memAddr += 4;                     }                     else {                         for (i=0;i<tblsize;i++) {                             if (bracket) {                                 sprintf(bracket+1,"%d]",i);                             }                             if (showStruct(tfd,flags,subtype,subname,0) < 0) {                                 state = STRUCT_ALLDONE;                                 goto done;                             }                         }                     }                 }                 else if (!strncmp(type,"pad[",4)) {                     padsize = atoi(type+4);                     if (flags & STRUCT_SHOWPAD) {                         castIndent();                         printf("%spad[%d]\n",strAddr(flags,addrstr),                             padsize);                     }                     memAddr += padsize;                 }                 else  {                     retval = -1;                     break;                 }             }         }         else {             state = STRUCT_ERROR;             break;         }     } 
image from book
 

If showStruct() cannot find a match between the content of the line and the mbrinfotbl[] array, the only other acceptable possibilities are another structure (in which case recursion occurs and the process just deepens) or an invisible padding request (Listing 12.14). The pad entry in the structure definition file tells showStruct() to skip over some specified number of bytes without displaying anything. This option is useful for two reasons:

  1. Sometimes you must insert padding to align members of a structure properly, as the C compiler would.

  2. Sometimes you are only interested in a small section of a large structure. This pad[] feature reduces the unwanted visual noise.

When the loop ends (see Listing 12.15), if all went well, the value of state is STRUCT_ALLDONE . Otherwise, the switch statement prints an error message.

Listing 12.15: showStruct() .
image from book
 done:     switch(state) {         case STRUCT_SEARCH:             printf("struct %s not found\n",structtype);             retval = -1;             break;         case STRUCT_DISPLAY:             printf("invalid member type: %s\n",type);             retval = -1;             break;         case STRUCT_ERROR:             printf("unknown error\n");             retval = -1;             break;     }     tfsseek(tfd,curpos,TFS_BEGIN);     if (linkname)         memAddr = nextlink;     castDepth--;     return(retval); } 
image from book
 

Some Example Output

To illustrate the flexibility of the cast and dm commands, I will use them to generate some example displays. Ill first show a memory dump using dm and then display the same memory using cast .

Assume I want to look at the structure type abc (as defined in Listing 12.6), located at location 0x5d000 in system memory. If I dump the data as raw memory, using dm it displays as

 uMON> dm 0x5d000 48 0005d000: 00 01 00 00 64 00 00 00   04 01 12 34 00 00 00 00   ....d......4.... 0005d010: 00 00 00 00 00 00 00 00   00 00 00 00 00 00 00 00   ................ 0005d020: be ef 00 00 00 00 00 00   00 00 00 00 00 00 00 00   ................ 

The output format is clean and easy to read, as long as you want to read raw memory. Each line starts with a hexadecimal address, followed by 16 bytes of ASCII-coded hexadecimal data, followed by the same 16-byte block displayed in ASCII. If the ASCII is a non-printable character, then a dot ( . ) is printed as a placeholder. If you look closely, you can see the data in the block of memory. Wouldnt it be nicer to look at output such as the following

 uMON>cast abc 0x5d000 struct abc @0x5d000:     long     I: 65536     char.c   c1: d     struct def d:         short    s1: 1025         short.x  s2: 0x1234         long     ltbl[5]         short    s2: 48879 

Notice member I printed as a long decimal value ( 0x10000 = 65536 )

 0005d000:   00 01 00 00   64 00 00 00   04 01 12 34 00 00 00 00   ....d......4.... 

Notice member c1 printed as an ASCII character ( 0x64 = d )

 0005d000: 00 01 00 00   64   00 00 00   04 01 12 34 00 00 00 00   ....d......4.... 

Notice that the three bytes of padding were silently ignored

 0005d000: 00 01 00 00 64   00 00 00   04 01 12 34 00 00 00 00   ....d......4.... 

Notice short member s1 displayed as a decimal number ( 0x401 == 1025 )

 0005d000: 00 01 00 00 64 00 00 00   04 01   12 34 00 00 00 00   ....d......4.... 

and so on through the structure. As you can see, it is easier to look at a structure with cast than through a raw memory dump.



Embedded Systems Firmware Demystified
Embedded Systems Firmware Demystified (With CD-ROM)
ISBN: 1578200997
EAN: 2147483647
Year: 2002
Pages: 118
Authors: Ed Sutter

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net