Go to the first, previous, next, last section, table of contents.

Aggregate Types

Now let's look at some variable definitions involving complex types. This involves understanding better how types are described. In the examples so far types have been described as references to previously defined types or defined in terms of subranges of or pointers to previously defined types. The section that follows will talk about the various other type descriptors that may follow the = sign in a type definition.

Array types

Symbol Descriptor:
Type Descriptor:

As an example of an array type consider the global variable below.

15 char char_vec[3] = {'a','b','c'};

Since the array is a global variable, it is described by the N_GSYM stab type. The symbol descriptor G, following the colon in stab's string field, also says the array is a global variable. Following the G is a definition for type (19) as shown by the equals sign after the type number.

After the equals sign is a type descriptor, ar, which says that the type being defined is an array. Following the type descriptor for an array is the type of the index, a null field, the upper bound of the array indexing, and the type of the array elements.

The array definition above generates the assembly language that follows.

<32> N_GSYM - global variable
.stabs "name:sym_desc(global)type_def(19)=type_desc(array)

32 .stabs "char_vec:G19=ar1;0;2;2",32,0,0,0
33      .global _char_vec
34      .align 4
35 _char_vec:
36      .byte 97
37      .byte 98
38      .byte 99


Symbol Descriptor:
Type Descriptor:

The source line below declares an enumeration type. It is defined at file scope between the bodies of main and s_proc in example2.c. Because the N_LSYM is located after the N_RBRAC that marks the end of the previous procedure's block scope, and before the N_FUN that marks the beginning of the next procedure's block scope, the N_LSYM does not describe a block local symbol, but a file local one. The source line:

29 enum e_places {first,second=3,last};

generates the following stab, located just after the N_RBRAC (close brace stab) for main. The type definition is in an N_LSYM stab because type definitions are file scope not global scope.

    <128> N_LSYM - local symbol
    .stab "name:sym_dec(type)type_def(22)=sym_desc(enum)
           N_LSYM, NIL, NIL, NIL
104 .stabs "e_places:T22=efirst:0,second:3,last:4,;",128,0,0,0

The symbol descriptor (T) says that the stab describes a structure, enumeration, or type tag. The type descriptor e, following the 22= of the type definition narrows it down to an enumeration type. Following the e is a list of the elements of the enumeration. The format is name:value,. The list of elements ends with a ;.

Structure Tags

Symbol Descriptor:
Type Descriptor:

The following source code declares a structure tag and defines an instance of the structure in global scope. Then a typedef equates the structure tag with a new type. A seperate stab is generated for the structure tag, the structure typedef, and the structure instance. The stabs for the tag and the typedef are emited when the definitions are encountered. Since the structure elements are not initialized, the stab and code for the structure variable itself is located at the end of the program in .common.

6  struct s_tag {
7    int   s_int;
8    float s_float;
9    char  s_char_vec[8];
10   struct s_tag* s_next;
11 } g_an_s;
13 typedef struct s_tag s_typedef;

The structure tag is an N_LSYM stab type because, like the enum, the symbol is file scope. Like the enum, the symbol descriptor is T, for enumeration, struct or tag type. The symbol descriptor s following the 16= of the type definition narrows the symbol type to struct.

Following the struct symbol descriptor is the number of bytes the struct occupies, followed by a description of each structure element. The structure element descriptions are of the form name:type, bit offset from the start of the struct, and number of bits in the element.

   <128> N_LSYM - type definition 
   .stabs "name:sym_desc(struct tag) Type_def(16)=type_desc(struct type) 
        elem_name:type_def(17)=type_desc(dynamic array) index_type(int);NIL;

30 .stabs "s_tag:T16=s20s_int:1,0,32;s_float:12,32,32;

In this example, two of the structure elements are previously defined types. For these, the type following the name: part of the element description is a simple type reference. The other two structure elements are new types. In this case there is a type definition embedded after the name:. The type definition for the array element looks just like a type definition for a standalone array. The s_next field is a pointer to the same kind of structure that the field is an element of. So the definition of structure type 16 contains an type definition for an element which is a pointer to type 16.


Symbol Descriptor:

Here is the stab for the typedef equating the structure tag with a type.

    <128> N_LSYM - type definition 
    .stabs "name:sym_desc(type name)type_ref(struct_tag)",N_LSYM,NIL,NIL,NIL
31 .stabs "s_typedef:t16",128,0,0,0

And here is the code generated for the structure variable.

    <32> N_GSYM - global symbol 
    .stabs "name:sym_desc(global)type_ref(struct_tag)",N_GSYM,NIL,NIL,NIL
136 .stabs "g_an_s:G16",32,0,0,0
137     .common _g_an_s,20,"bss"

Notice that the structure tag has the same type number as the typedef for the structure tag. It is impossible to distinguish between a variable of the struct type and one of its typedef by looking at the debugging information.


Symbol Descriptor:
Type Descriptor:

Next let's look at unions. In example2 this union type is declared locally to a procedure and an instance of the union is defined.

36   union u_tag {
37     int  u_int;
38     float u_float;
39     char* u_char;
40   } an_u;

This code generates a stab for the union tag and a stab for the union variable. Both use the N_LSYM stab type. Since the union variable is scoped locally to the procedure in which it is defined, its stab is located immediately preceding the N_LBRAC for the procedure's block start.

The stab for the union tag, however is located preceding the code for the procedure in which it is defined. The stab type is N_LSYM. This would seem to imply that the union type is file scope, like the struct type s_tag. This is not true. The contents and position of the stab for u_type do not convey any infomation about its procedure local scope.

     <128> N_LSYM - type
     .stabs "name:sym_desc(union tag)type_def(22)=type_desc(union)
     elem_name:type_ref(ptr to char),bit_offset(0),bit_size(32);;"
105 .stabs "u_tag:T23=u4u_int:1,0,32;u_float:12,0,32;u_char:21,0,32;;",

The symbol descriptor, T, following the name: means that the stab describes an enumeration struct or type tag. The type descriptor u, following the 23= of the type definition, narrows it down to a union type definition. Following the u is the number of bytes in the union. After that is a list of union element descriptions. Their format is name:type, bit offset into the union, and number of bytes for the element;.

The stab for the union variable follows. Notice that the frame pointer offset for local variables is negative.

    <128> N_LSYM - local variable (with no symbol descriptor)
    .stabs "name:type_ref(u_tag)", N_LSYM, NIL, NIL, frame_ptr_offset
130 .stabs "an_u:23",128,0,0,-20

Function types

type descriptor f

The last type descriptor in C which remains to be described is used for function types. Consider the following source line defining a global function pointer.

4  int (*g_pf)();

It generates the following code. Since the variable is not initialized, the code is located in the common area at the end of the file.

    <32> N_GSYM - global variable
    .stabs "name:sym_desc(global)type_def(24)=ptr_to(25)=
134 .stabs "g_pf:G24=*25=f1",32,0,0,0
135     .common _g_pf,4,"bss"

Since the variable is global, the stab type is N_GSYM and the symbol descriptor is G. The variable defines a new type, 24, which is a pointer to another new type, 25, which is defined as a function returning int.

Go to the first, previous, next, last section, table of contents.