This source file includes following definitions.
- addrput
 
- uleb128enc
 
- sleb128enc
 
- uleb128put
 
- sleb128put
 
- writeabbrev
 
- hashstr
 
- newattr
 
- getattr
 
- newdie
 
- mkindex
 
- walktypedef
 
- find
 
- find_or_diag
 
- adddwarfrel
 
- newrefattr
 
- putattr
 
- putattrs
 
- putdies
 
- putdie
 
- reverselist
 
- reversetree
 
- newmemberoffsetattr
 
- newabslocexprattr
 
- lookup_or_diag
 
- dotypedef
 
- defgotype
 
- defptrto
 
- copychildrenexcept
 
- copychildren
 
- substitutetype
 
- synthesizestringtypes
 
- synthesizeslicetypes
 
- mkinternaltypename
 
- synthesizemaptypes
 
- synthesizechantypes
 
- defdwsymb
 
- movetomodule
 
- finddebugruntimepath
 
- putpclcdelta
 
- newcfaoffsetattr
 
- mkvarname
 
- flushunit
 
- writelines
 
- putpccfadelta
 
- writeframes
 
- writeinfo
 
- ispubname
 
- ispubtype
 
- writepub
 
- writearanges
 
- writegdbscript
 
- align
 
- writedwarfreloc
 
- dwarfemitdebugsections
 
- dwarfaddshstrings
 
- dwarfaddelfsectionsyms
 
- dwarfaddelfrelocheader
 
- dwarfaddelfheaders
 
- dwarfaddmachoheaders
 
- dwarfaddpeheaders
 
#include        "l.h"
#include        "lib.h"
#include        "../ld/dwarf.h"
#include        "../ld/dwarf_defs.h"
#include        "../ld/elf.h"
#include        "../ld/macho.h"
#include        "../ld/pe.h"
#include        "../../pkg/runtime/typekind.h"
static vlong abbrevo;
static vlong abbrevsize;
static LSym*  abbrevsym;
static vlong abbrevsympos;
static vlong lineo;
static vlong linesize;
static LSym*  linesym;
static vlong linesympos;
static vlong infoo;     
static vlong infosize;
static LSym*  infosym;
static vlong infosympos;
static vlong frameo;
static vlong framesize;
static LSym*  framesym;
static vlong framesympos;
static vlong pubnameso;
static vlong pubnamessize;
static vlong pubtypeso;
static vlong pubtypessize;
static vlong arangeso;
static vlong arangessize;
static vlong gdbscripto;
static vlong gdbscriptsize;
static LSym *infosec;
static vlong inforeloco;
static vlong inforelocsize;
static LSym *arangessec;
static vlong arangesreloco;
static vlong arangesrelocsize;
static LSym *linesec;
static vlong linereloco;
static vlong linerelocsize;
static LSym *framesec;
static vlong framereloco;
static vlong framerelocsize;
static char  gdbscript[1024];
static void
addrput(vlong addr)
{
        switch(PtrSize) {
        case 4:
                LPUT(addr);
                break;
        case 8:
                VPUT(addr);
                break;
        }
}
static int
uleb128enc(uvlong v, char* dst)
{
        uint8 c, len;
        len = 0;
        do {
                c = v & 0x7f;
                v >>= 7;
                if (v)
                        c |= 0x80;
                if (dst)
                        *dst++ = c;
                len++;
        } while (c & 0x80);
        return len;
};
static int
sleb128enc(vlong v, char *dst)
{
        uint8 c, s, len;
        len = 0;
        do {
                c = v & 0x7f;
                s = v & 0x40;
                v >>= 7;
                if ((v != -1 || !s) && (v != 0 || s))
                        c |= 0x80;
                if (dst)
                        *dst++ = c;
                len++;
        } while(c & 0x80);
        return len;
}
static void
uleb128put(vlong v)
{
        char buf[10];
        strnput(buf, uleb128enc(v, buf));
}
static void
sleb128put(vlong v)
{
        char buf[10];
        strnput(buf, sleb128enc(v, buf));
}
typedef struct DWAttrForm DWAttrForm;
struct DWAttrForm {
        uint8 attr;
        uint8 form;
};
enum
{
        DW_ABRV_NULL,
        DW_ABRV_COMPUNIT,
        DW_ABRV_FUNCTION,
        DW_ABRV_VARIABLE,
        DW_ABRV_AUTO,
        DW_ABRV_PARAM,
        DW_ABRV_STRUCTFIELD,
        DW_ABRV_FUNCTYPEPARAM,
        DW_ABRV_DOTDOTDOT,
        DW_ABRV_ARRAYRANGE,
        DW_ABRV_NULLTYPE,
        DW_ABRV_BASETYPE,
        DW_ABRV_ARRAYTYPE,
        DW_ABRV_CHANTYPE,
        DW_ABRV_FUNCTYPE,
        DW_ABRV_IFACETYPE,
        DW_ABRV_MAPTYPE,
        DW_ABRV_PTRTYPE,
        DW_ABRV_BARE_PTRTYPE, 
        DW_ABRV_SLICETYPE,
        DW_ABRV_STRINGTYPE,
        DW_ABRV_STRUCTTYPE,
        DW_ABRV_TYPEDECL,
        DW_NABRV
};
typedef struct DWAbbrev DWAbbrev;
static struct DWAbbrev {
        uint8 tag;
        uint8 children;
        DWAttrForm attr[30];
} abbrevs[DW_NABRV] = {
        
        { 0 },
        
        {
                DW_TAG_compile_unit, DW_CHILDREN_yes,
                DW_AT_name,      DW_FORM_string,
                DW_AT_language,  DW_FORM_data1,
                DW_AT_low_pc,    DW_FORM_addr,
                DW_AT_high_pc,   DW_FORM_addr,
                DW_AT_stmt_list, DW_FORM_data4,
                0, 0
        },
        
        {
                DW_TAG_subprogram, DW_CHILDREN_yes,
                DW_AT_name,      DW_FORM_string,
                DW_AT_low_pc,    DW_FORM_addr,
                DW_AT_high_pc,   DW_FORM_addr,
                DW_AT_external,  DW_FORM_flag,
                0, 0
        },
        
        {
                DW_TAG_variable, DW_CHILDREN_no,
                DW_AT_name,      DW_FORM_string,
                DW_AT_location,  DW_FORM_block1,
                DW_AT_type,      DW_FORM_ref_addr,
                DW_AT_external,  DW_FORM_flag,
                0, 0
        },
        
        {
                DW_TAG_variable, DW_CHILDREN_no,
                DW_AT_name,      DW_FORM_string,
                DW_AT_location,  DW_FORM_block1,
                DW_AT_type,      DW_FORM_ref_addr,
                0, 0
        },
        
        {
                DW_TAG_formal_parameter, DW_CHILDREN_no,
                DW_AT_name,      DW_FORM_string,
                DW_AT_location,  DW_FORM_block1,
                DW_AT_type,      DW_FORM_ref_addr,
                0, 0
        },
        
        {
                DW_TAG_member,  DW_CHILDREN_no,
                DW_AT_name,     DW_FORM_string,
                DW_AT_data_member_location, DW_FORM_block1,
                DW_AT_type,      DW_FORM_ref_addr,
                0, 0
        },
        
        {
                DW_TAG_formal_parameter, DW_CHILDREN_no,
                
                DW_AT_type,      DW_FORM_ref_addr,
                0, 0
        },
        
        {
                DW_TAG_unspecified_parameters, DW_CHILDREN_no,
                0, 0
        },
        
        {
                DW_TAG_subrange_type, DW_CHILDREN_no,
                
                DW_AT_type,      DW_FORM_ref_addr,
                DW_AT_upper_bound, DW_FORM_data1,
                0, 0
        },
        
        
        {
                DW_TAG_unspecified_type, DW_CHILDREN_no,
                DW_AT_name,     DW_FORM_string,
                0, 0
        },
        
        {
                DW_TAG_base_type, DW_CHILDREN_no,
                DW_AT_name,      DW_FORM_string,
                DW_AT_encoding,  DW_FORM_data1,
                DW_AT_byte_size, DW_FORM_data1,
                0, 0
        },
        
        
        {
                DW_TAG_array_type, DW_CHILDREN_yes,
                DW_AT_name,     DW_FORM_string,
                DW_AT_type,     DW_FORM_ref_addr,
                DW_AT_byte_size, DW_FORM_udata,
                0, 0
        },
        
        {
                DW_TAG_typedef, DW_CHILDREN_no,
                DW_AT_name,     DW_FORM_string,
                DW_AT_type,     DW_FORM_ref_addr,
                0, 0
        },
        
        {
                DW_TAG_subroutine_type, DW_CHILDREN_yes,
                DW_AT_name,     DW_FORM_string,
                0, 0
        },
        
        {
                DW_TAG_typedef, DW_CHILDREN_yes,
                DW_AT_name,      DW_FORM_string,
                DW_AT_type,     DW_FORM_ref_addr,
                0, 0
        },
        
        {
                DW_TAG_typedef, DW_CHILDREN_no,
                DW_AT_name,     DW_FORM_string,
                DW_AT_type,     DW_FORM_ref_addr,
                0, 0
        },
        
        {
                DW_TAG_pointer_type, DW_CHILDREN_no,
                DW_AT_name,     DW_FORM_string,
                DW_AT_type,     DW_FORM_ref_addr,
                0, 0
        },
        
        {
                DW_TAG_pointer_type, DW_CHILDREN_no,
                DW_AT_name,     DW_FORM_string,
                0, 0
        },
        
        {
                DW_TAG_structure_type, DW_CHILDREN_yes,
                DW_AT_name,     DW_FORM_string,
                DW_AT_byte_size, DW_FORM_udata,
                0, 0
        },
        
        {
                DW_TAG_structure_type, DW_CHILDREN_yes,
                DW_AT_name,     DW_FORM_string,
                DW_AT_byte_size, DW_FORM_udata,
                0, 0
        },
        
        {
                DW_TAG_structure_type, DW_CHILDREN_yes,
                DW_AT_name,     DW_FORM_string,
                DW_AT_byte_size, DW_FORM_udata,
                0, 0
        },
        
        {
                DW_TAG_typedef, DW_CHILDREN_no,
                DW_AT_name,     DW_FORM_string,
                DW_AT_type,     DW_FORM_ref_addr,
                0, 0
        },
};
static void
writeabbrev(void)
{
        int i, n;
        abbrevo = cpos();
        for (i = 1; i < DW_NABRV; i++) {
                
                uleb128put(i);
                uleb128put(abbrevs[i].tag);
                cput(abbrevs[i].children);
                
                
                n = strlen((char*)abbrevs[i].attr) / 2;
                strnput((char*)abbrevs[i].attr,
                        (n+1) * sizeof(DWAttrForm));
        }
        cput(0);
        abbrevsize = cpos() - abbrevo;
}
enum
{
        HASHSIZE = 107
};
static uint32
hashstr(char* s)
{
        uint32 h;
        h = 0;
        while (*s)
                h = h+h+h + *s++;
        return h % HASHSIZE;
}
typedef struct DWAttr DWAttr;
struct DWAttr {
        DWAttr *link;
        uint8 atr;  
        uint8 cls;  
        vlong value;
        char *data;
};
typedef struct DWDie DWDie;
struct DWDie {
        int abbrev;
        DWDie *link;
        DWDie *child;
        DWAttr *attr;
        
        
        vlong offs;
        DWDie **hash;  
        DWDie *hlink;  
};
static DWDie dwroot;
static DWDie dwtypes;
static DWDie dwglobals;
static DWAttr*
newattr(DWDie *die, uint8 attr, int cls, vlong value, char *data)
{
        DWAttr *a;
        a = mal(sizeof *a);
        a->link = die->attr;
        die->attr = a;
        a->atr = attr;
        a->cls = cls;
        a->value = value;
        a->data = data;
        return a;
}
static DWAttr*
getattr(DWDie *die, uint8 attr)
{
        DWAttr *a, *b;
        if (die->attr->atr == attr)
                return die->attr;
        a = die->attr;
        b = a->link;
        while (b != nil) {
                if (b->atr == attr) {
                        a->link = b->link;
                        b->link = die->attr;
                        die->attr = b;
                        return b;
                }
                a = b;
                b = b->link;
        }
        return nil;
}
static DWDie*
newdie(DWDie *parent, int abbrev, char *name)
{
        DWDie *die;
        int h;
        die = mal(sizeof *die);
        die->abbrev = abbrev;
        die->link = parent->child;
        parent->child = die;
        newattr(die, DW_AT_name, DW_CLS_STRING, strlen(name), name);
        if (parent->hash) {
                h = hashstr(name);
                die->hlink = parent->hash[h];
                parent->hash[h] = die;
        }
        return die;
}
static void
mkindex(DWDie *die)
{
        die->hash = mal(HASHSIZE * sizeof(DWDie*));
}
static DWDie*
walktypedef(DWDie *die)
{
        DWAttr *attr;
        
        if (die->abbrev == DW_ABRV_TYPEDECL) {
                for (attr = die->attr; attr; attr = attr->link) {
                        if (attr->atr == DW_AT_type && attr->cls == DW_CLS_REFERENCE && attr->data != nil) {
                                return (DWDie*)attr->data;
                        }
                }
        }
        return die;
}
static DWDie*
find(DWDie *die, char* name)
{
        DWDie *a, *b, *die2;
        int h;
top:
        if (die->hash == nil) {
                for (a = die->child; a != nil; a = a->link)
                        if (strcmp(name, getattr(a, DW_AT_name)->data) == 0)
                                return a;
                goto notfound;
        }
        h = hashstr(name);
        a = die->hash[h];
        if (a == nil)
                goto notfound;
        if (strcmp(name, getattr(a, DW_AT_name)->data) == 0)
                return a;
        
        b = a->hlink;
        while (b != nil) {
                if (strcmp(name, getattr(b, DW_AT_name)->data) == 0) {
                        a->hlink = b->hlink;
                        b->hlink = die->hash[h];
                        die->hash[h] = b;
                        return b;
                }
                a = b;
                b = b->hlink;
        }
notfound:
        die2 = walktypedef(die);
        if(die2 != die) {
                die = die2;
                goto top;
        }
        return nil;
}
static DWDie*
find_or_diag(DWDie *die, char* name)
{
        DWDie *r;
        r = find(die, name);
        if (r == nil) {
                diag("dwarf find: %s %p has no %s", getattr(die, DW_AT_name)->data, die, name);
                errorexit();
        }
        return r;
}
static void
adddwarfrel(LSym* sec, LSym* sym, vlong offsetbase, int siz, vlong addend)
{
        Reloc *r;
        r = addrel(sec);
        r->sym = sym;
        r->xsym = sym;
        r->off = cpos() - offsetbase;
        r->siz = siz;
        r->type = R_ADDR;
        r->add = addend;
        r->xadd = addend;
        if(iself && thechar == '6')
                addend = 0;
        switch(siz) {
        case 4:
                LPUT(addend);
                break;
        case 8:
                VPUT(addend);
                break;
        default:
                diag("bad size in adddwarfrel");
                break;
        }
}
static DWAttr*
newrefattr(DWDie *die, uint8 attr, DWDie* ref)
{
        if (ref == nil)
                return nil;
        return newattr(die, attr, DW_CLS_REFERENCE, 0, (char*)ref);
}
static int fwdcount;
static void
putattr(int abbrev, int form, int cls, vlong value, char *data)
{
        vlong off;
        switch(form) {
        case DW_FORM_addr:      
                if(linkmode == LinkExternal) {
                        value -= ((LSym*)data)->value;
                        adddwarfrel(infosec, (LSym*)data, infoo, PtrSize, value);
                        break;
                }
                addrput(value);
                break;
        case DW_FORM_block1:    
                if(cls == DW_CLS_ADDRESS) {
                        cput(1+PtrSize);
                        cput(DW_OP_addr);
                        if(linkmode == LinkExternal) {
                                value -= ((LSym*)data)->value;
                                adddwarfrel(infosec, (LSym*)data, infoo, PtrSize, value);
                                break;
                        }
                        addrput(value);
                        break;
                }
                value &= 0xff;
                cput(value);
                while(value--)
                        cput(*data++);
                break;
        case DW_FORM_block2:    
                value &= 0xffff;
                WPUT(value);
                while(value--)
                        cput(*data++);
                break;
        case DW_FORM_block4:    
                value &= 0xffffffff;
                LPUT(value);
                while(value--)
                        cput(*data++);
                break;
        case DW_FORM_block:     
                uleb128put(value);
                while(value--)
                        cput(*data++);
                break;
        case DW_FORM_data1:     
                cput(value);
                break;
        case DW_FORM_data2:     
                WPUT(value);
                break;
        case DW_FORM_data4:     
                if(linkmode == LinkExternal && cls == DW_CLS_PTR) {
                        adddwarfrel(infosec, linesym, infoo, 4, value);
                        break;
                }
                LPUT(value);
                break;
        case DW_FORM_data8:     
                VPUT(value);
                break;
        case DW_FORM_sdata:     
                sleb128put(value);
                break;
        case DW_FORM_udata:     
                uleb128put(value);
                break;
        case DW_FORM_string:    
                strnput(data, value+1);
                break;
        case DW_FORM_flag:      
                cput(value?1:0);
                break;
        case DW_FORM_ref_addr:  
                
                
                
                
                if (data == nil) {
                        diag("dwarf: null reference in %d", abbrev);
                        if(PtrSize == 8)
                                VPUT(0); 
                        else
                                LPUT(0); 
                } else {
                        off = ((DWDie*)data)->offs;
                        if (off == 0)
                                fwdcount++;
                        if(linkmode == LinkExternal) {
                                adddwarfrel(infosec, infosym, infoo, PtrSize, off);
                                break;
                        }
                        addrput(off);
                }
                break;
        case DW_FORM_ref1:      
        case DW_FORM_ref2:      
        case DW_FORM_ref4:      
        case DW_FORM_ref8:      
        case DW_FORM_ref_udata: 
        case DW_FORM_strp:      
        case DW_FORM_indirect:  
        default:
                diag("dwarf: unsupported attribute form %d / class %d", form, cls);
                errorexit();
        }
}
static void
putattrs(int abbrev, DWAttr* attr)
{
        DWAttr *attrs[DW_AT_recursive + 1];
        DWAttrForm* af;
        memset(attrs, 0, sizeof attrs);
        for( ; attr; attr = attr->link)
                if (attr->atr < nelem(attrs))
                        attrs[attr->atr] = attr;
        for(af = abbrevs[abbrev].attr; af->attr; af++)
                if (attrs[af->attr])
                        putattr(abbrev, af->form,
                                attrs[af->attr]->cls,
                                attrs[af->attr]->value,
                                attrs[af->attr]->data);
                else
                        putattr(abbrev, af->form, 0, 0, nil);
}
static void putdie(DWDie* die);
static void
putdies(DWDie* die)
{
        for(; die; die = die->link)
                putdie(die);
}
static void
putdie(DWDie* die)
{
        die->offs = cpos() - infoo;
        uleb128put(die->abbrev);
        putattrs(die->abbrev, die->attr);
        if (abbrevs[die->abbrev].children) {
                putdies(die->child);
                cput(0);
        }
}
static void
reverselist(DWDie** list)
{
        DWDie *curr, *prev;
        curr = *list;
        prev = nil;
        while(curr != nil) {
                DWDie* next = curr->link;
                curr->link = prev;
                prev = curr;
                curr = next;
        }
        *list = prev;
}
static void
reversetree(DWDie** list)
{
         DWDie *die;
         reverselist(list);
         for (die = *list; die != nil; die = die->link)
                 if (abbrevs[die->abbrev].children)
                         reversetree(&die->child);
}
static void
newmemberoffsetattr(DWDie *die, int32 offs)
{
        char block[10];
        int i;
        i = 0;
        if (offs != 0) {
                block[i++] = DW_OP_consts;
                i += sleb128enc(offs, block+i);
                block[i++] = DW_OP_plus;
        }
        newattr(die, DW_AT_data_member_location, DW_CLS_BLOCK, i, mal(i));
        memmove(die->attr->data, block, i);
}
static void
newabslocexprattr(DWDie *die, vlong addr, LSym *sym)
{
        newattr(die, DW_AT_location, DW_CLS_ADDRESS, addr, (char*)sym);
}
enum {
        DW_AT_internal_elem_type = 250,  
        DW_AT_internal_key_type = 251,   
        DW_AT_internal_val_type = 252,   
        DW_AT_internal_location = 253,   
};
static DWDie* defptrto(DWDie *dwtype);  
static LSym*
lookup_or_diag(char *n)
{
        LSym *s;
        s = linkrlookup(ctxt, n, 0);
        if (s == nil || s->size == 0) {
                diag("dwarf: missing type: %s", n);
                errorexit();
        }
        return s;
}
static void
dotypedef(DWDie *parent, char *name, DWDie *def)
{
        DWDie *die;
        
        if(strncmp(name, "map[", 4) == 0)
                return;
        if(strncmp(name, "struct {", 8) == 0)
                return;
        if(strncmp(name, "chan ", 5) == 0)
                return;
        if(*name == '[' || *name == '*')
                return;
        if(def == nil)
                diag("dwarf: bad def in dotypedef");
        
        
        
        
        die = newdie(parent, DW_ABRV_TYPEDECL, name);
        newrefattr(die, DW_AT_type, def);
}
static DWDie*
defgotype(LSym *gotype)
{
        DWDie *die, *fld;
        LSym *s;
        char *name, *f;
        uint8 kind;
        vlong bytesize;
        int i, nfields;
        if (gotype == nil)
                return find_or_diag(&dwtypes, "<unspecified>");
        if (strncmp("type.", gotype->name, 5) != 0) {
                diag("dwarf: type name doesn't start with \".type\": %s", gotype->name);
                return find_or_diag(&dwtypes, "<unspecified>");
        }
        name = gotype->name + 5;  
        die = find(&dwtypes, name);
        if (die != nil)
                return die;
        if (0 && debug['v'] > 2)
                print("new type: %Y\n", gotype);
        kind = decodetype_kind(gotype);
        bytesize = decodetype_size(gotype);
        switch (kind) {
        case KindBool:
                die = newdie(&dwtypes, DW_ABRV_BASETYPE, name);
                newattr(die, DW_AT_encoding,  DW_CLS_CONSTANT, DW_ATE_boolean, 0);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                break;
        case KindInt:
        case KindInt8:
        case KindInt16:
        case KindInt32:
        case KindInt64:
                die = newdie(&dwtypes, DW_ABRV_BASETYPE, name);
                newattr(die, DW_AT_encoding,  DW_CLS_CONSTANT, DW_ATE_signed, 0);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                break;
        case KindUint:
        case KindUint8:
        case KindUint16:
        case KindUint32:
        case KindUint64:
        case KindUintptr:
                die = newdie(&dwtypes, DW_ABRV_BASETYPE, name);
                newattr(die, DW_AT_encoding,  DW_CLS_CONSTANT, DW_ATE_unsigned, 0);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                break;
        case KindFloat32:
        case KindFloat64:
                die = newdie(&dwtypes, DW_ABRV_BASETYPE, name);
                newattr(die, DW_AT_encoding,  DW_CLS_CONSTANT, DW_ATE_float, 0);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                break;
        case KindComplex64:
        case KindComplex128:
                die = newdie(&dwtypes, DW_ABRV_BASETYPE, name);
                newattr(die, DW_AT_encoding,  DW_CLS_CONSTANT, DW_ATE_complex_float, 0);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                break;
        case KindArray:
                die = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, name);
                dotypedef(&dwtypes, name, die);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                s = decodetype_arrayelem(gotype);
                newrefattr(die, DW_AT_type, defgotype(s));
                fld = newdie(die, DW_ABRV_ARRAYRANGE, "range");
                newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, decodetype_arraylen(gotype), 0);
                newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr"));
                break;
        case KindChan:
                die = newdie(&dwtypes, DW_ABRV_CHANTYPE, name);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                s = decodetype_chanelem(gotype);
                newrefattr(die, DW_AT_internal_elem_type, defgotype(s));
                break;
        case KindFunc:
                die = newdie(&dwtypes, DW_ABRV_FUNCTYPE, name);
                dotypedef(&dwtypes, name, die);
                newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "void"));
                nfields = decodetype_funcincount(gotype);
                for (i = 0; i < nfields; i++) {
                        s = decodetype_funcintype(gotype, i);
                        fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5);
                        newrefattr(fld, DW_AT_type, defgotype(s));
                }
                if (decodetype_funcdotdotdot(gotype))
                        newdie(die, DW_ABRV_DOTDOTDOT, "...");
                nfields = decodetype_funcoutcount(gotype);
                for (i = 0; i < nfields; i++) {
                        s = decodetype_funcouttype(gotype, i);
                        fld = newdie(die, DW_ABRV_FUNCTYPEPARAM, s->name+5);
                        newrefattr(fld, DW_AT_type, defptrto(defgotype(s)));
                }
                break;
        case KindInterface:
                die = newdie(&dwtypes, DW_ABRV_IFACETYPE, name);
                dotypedef(&dwtypes, name, die);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                nfields = decodetype_ifacemethodcount(gotype);
                if (nfields == 0)
                        s = lookup_or_diag("type.runtime.eface");
                else
                        s = lookup_or_diag("type.runtime.iface");
                newrefattr(die, DW_AT_type, defgotype(s));
                break;
        case KindMap:
                die = newdie(&dwtypes, DW_ABRV_MAPTYPE, name);
                s = decodetype_mapkey(gotype);
                newrefattr(die, DW_AT_internal_key_type, defgotype(s));
                s = decodetype_mapvalue(gotype);
                newrefattr(die, DW_AT_internal_val_type, defgotype(s));
                break;
        case KindPtr:
                die = newdie(&dwtypes, DW_ABRV_PTRTYPE, name);
                dotypedef(&dwtypes, name, die);
                s = decodetype_ptrelem(gotype);
                newrefattr(die, DW_AT_type, defgotype(s));
                break;
        case KindSlice:
                die = newdie(&dwtypes, DW_ABRV_SLICETYPE, name);
                dotypedef(&dwtypes, name, die);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                s = decodetype_arrayelem(gotype);
                newrefattr(die, DW_AT_internal_elem_type, defgotype(s));
                break;
        case KindString:
                die = newdie(&dwtypes, DW_ABRV_STRINGTYPE, name);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                break;
        case KindStruct:
                die = newdie(&dwtypes, DW_ABRV_STRUCTTYPE, name);
                dotypedef(&dwtypes, name, die);
                newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, bytesize, 0);
                nfields = decodetype_structfieldcount(gotype);
                for (i = 0; i < nfields; i++) {
                        f = decodetype_structfieldname(gotype, i);
                        s = decodetype_structfieldtype(gotype, i);
                        if (f == nil)
                                f = s->name + 5;         
                        fld = newdie(die, DW_ABRV_STRUCTFIELD, f);
                        newrefattr(fld, DW_AT_type, defgotype(s));
                        newmemberoffsetattr(fld, decodetype_structfieldoffs(gotype, i));
                }
                break;
        case KindUnsafePointer:
                die = newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, name);
                break;
        default:
                diag("dwarf: definition of unknown kind %d: %s", kind, gotype->name);
                die = newdie(&dwtypes, DW_ABRV_TYPEDECL, name);
                newrefattr(die, DW_AT_type, find_or_diag(&dwtypes, "<unspecified>"));
         }
        return die;
}
static DWDie*
defptrto(DWDie *dwtype)
{
        char ptrname[1024];
        DWDie *die;
        snprint(ptrname, sizeof ptrname, "*%s", getattr(dwtype, DW_AT_name)->data);
        die = find(&dwtypes, ptrname);
        if (die == nil) {
                die = newdie(&dwtypes, DW_ABRV_PTRTYPE,
                             strcpy(mal(strlen(ptrname)+1), ptrname));
                newrefattr(die, DW_AT_type, dwtype);
        }
        return die;
}
static void
copychildrenexcept(DWDie *dst, DWDie *src, DWDie *except)
{
        DWDie *c;
        DWAttr *a;
        for (src = src->child; src != nil; src = src->link) {
                if(src == except)
                        continue;
                c = newdie(dst, src->abbrev, getattr(src, DW_AT_name)->data);
                for (a = src->attr; a != nil; a = a->link)
                        newattr(c, a->atr, a->cls, a->value, a->data);
                copychildrenexcept(c, src, nil);
        }
        reverselist(&dst->child);
}
static void
copychildren(DWDie *dst, DWDie *src)
{
        copychildrenexcept(dst, src, nil);
}
static void
substitutetype(DWDie *structdie, char *field, DWDie* dwtype)
{
        DWDie *child;
        DWAttr *a;
        child = find_or_diag(structdie, field);
        if (child == nil)
                return;
        a = getattr(child, DW_AT_type);
        if (a != nil)
                a->data = (char*) dwtype;
        else
                newrefattr(child, DW_AT_type, dwtype);
}
static void
synthesizestringtypes(DWDie* die)
{
        DWDie *prototype;
        prototype = walktypedef(defgotype(lookup_or_diag("type.runtime._string")));
        if (prototype == nil)
                return;
        for (; die != nil; die = die->link) {
                if (die->abbrev != DW_ABRV_STRINGTYPE)
                        continue;
                copychildren(die, prototype);
        }
}
static void
synthesizeslicetypes(DWDie *die)
{
        DWDie *prototype, *elem;
        prototype = walktypedef(defgotype(lookup_or_diag("type.runtime.slice")));
        if (prototype == nil)
                return;
        for (; die != nil; die = die->link) {
                if (die->abbrev != DW_ABRV_SLICETYPE)
                        continue;
                copychildren(die, prototype);
                elem = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data;
                substitutetype(die, "array", defptrto(elem));
        }
}
static char*
mkinternaltypename(char *base, char *arg1, char *arg2)
{
        char buf[1024];
        char *n;
        if (arg2 == nil)
                snprint(buf, sizeof buf, "%s<%s>", base, arg1);
        else
                snprint(buf, sizeof buf, "%s<%s,%s>", base, arg1, arg2);
        n = mal(strlen(buf) + 1);
        memmove(n, buf, strlen(buf));
        return n;
}
enum {
        MaxKeySize = 128,
        MaxValSize = 128,
        BucketSize = 8,
};
static void
synthesizemaptypes(DWDie *die)
{
        DWDie *hash, *bucket, *dwh, *dwhk, *dwhv, *dwhb, *keytype, *valtype, *fld;
        int indirect_key, indirect_val;
        int keysize, valsize;
        DWAttr *a;
        hash            = walktypedef(defgotype(lookup_or_diag("type.runtime.hmap")));
        bucket          = walktypedef(defgotype(lookup_or_diag("type.runtime.bucket")));
        if (hash == nil)
                return;
        for (; die != nil; die = die->link) {
                if (die->abbrev != DW_ABRV_MAPTYPE)
                        continue;
                keytype = walktypedef((DWDie*) getattr(die, DW_AT_internal_key_type)->data);
                valtype = walktypedef((DWDie*) getattr(die, DW_AT_internal_val_type)->data);
                
                a = getattr(keytype, DW_AT_byte_size);
                keysize = a ? a->value : PtrSize;  
                a = getattr(valtype, DW_AT_byte_size);
                valsize = a ? a->value : PtrSize;
                indirect_key = 0;
                indirect_val = 0;
                if(keysize > MaxKeySize) {
                        keysize = PtrSize;
                        indirect_key = 1;
                }
                if(valsize > MaxValSize) {
                        valsize = PtrSize;
                        indirect_val = 1;
                }
                
                dwhk = newdie(&dwtypes, DW_ABRV_ARRAYTYPE,
                              mkinternaltypename("[]key",
                                                 getattr(keytype, DW_AT_name)->data, nil));
                newattr(dwhk, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize * keysize, 0);
                newrefattr(dwhk, DW_AT_type, indirect_key ? defptrto(keytype) : keytype);
                fld = newdie(dwhk, DW_ABRV_ARRAYRANGE, "size");
                newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, BucketSize, 0);
                newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr"));
                
                
                dwhv = newdie(&dwtypes, DW_ABRV_ARRAYTYPE, 
                              mkinternaltypename("[]val",
                                                 getattr(valtype, DW_AT_name)->data, nil));
                newattr(dwhv, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize * valsize, 0);
                newrefattr(dwhv, DW_AT_type, indirect_val ? defptrto(valtype) : valtype);
                fld = newdie(dwhv, DW_ABRV_ARRAYRANGE, "size");
                newattr(fld, DW_AT_upper_bound, DW_CLS_CONSTANT, BucketSize, 0);
                newrefattr(fld, DW_AT_type, find_or_diag(&dwtypes, "uintptr"));
                
                dwhb = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
                              mkinternaltypename("bucket",
                                                 getattr(keytype, DW_AT_name)->data,
                                                 getattr(valtype, DW_AT_name)->data));
                
                
                copychildrenexcept(dwhb, bucket, find(bucket, "data"));
                
                fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "keys");
                newrefattr(fld, DW_AT_type, dwhk);
                newmemberoffsetattr(fld, BucketSize + PtrSize);
                fld = newdie(dwhb, DW_ABRV_STRUCTFIELD, "values");
                newrefattr(fld, DW_AT_type, dwhv);
                newmemberoffsetattr(fld, BucketSize + PtrSize + BucketSize * keysize);
                newattr(dwhb, DW_AT_byte_size, DW_CLS_CONSTANT, BucketSize + PtrSize + BucketSize * keysize + BucketSize * valsize, 0);
                substitutetype(dwhb, "overflow", defptrto(dwhb));
                
                dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
                        mkinternaltypename("hash",
                                getattr(keytype, DW_AT_name)->data,
                                getattr(valtype, DW_AT_name)->data));
                copychildren(dwh, hash);
                substitutetype(dwh, "buckets", defptrto(dwhb));
                substitutetype(dwh, "oldbuckets", defptrto(dwhb));
                newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT,
                        getattr(hash, DW_AT_byte_size)->value, nil);
                
                newrefattr(die, DW_AT_type, defptrto(dwh));
        }
}
static void
synthesizechantypes(DWDie *die)
{
        DWDie *sudog, *waitq, *hchan,
                *dws, *dww, *dwh, *elemtype;
        DWAttr *a;
        int elemsize, sudogsize;
        sudog = walktypedef(defgotype(lookup_or_diag("type.runtime.sudog")));
        waitq = walktypedef(defgotype(lookup_or_diag("type.runtime.waitq")));
        hchan = walktypedef(defgotype(lookup_or_diag("type.runtime.hchan")));
        if (sudog == nil || waitq == nil || hchan == nil)
                return;
        sudogsize = getattr(sudog, DW_AT_byte_size)->value;
        for (; die != nil; die = die->link) {
                if (die->abbrev != DW_ABRV_CHANTYPE)
                        continue;
                elemtype = (DWDie*) getattr(die, DW_AT_internal_elem_type)->data;
                a = getattr(elemtype, DW_AT_byte_size);
                elemsize = a ? a->value : PtrSize;
                
                dws = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
                        mkinternaltypename("sudog",
                                getattr(elemtype, DW_AT_name)->data, nil));
                copychildren(dws, sudog);
                substitutetype(dws, "elem", elemtype);
                newattr(dws, DW_AT_byte_size, DW_CLS_CONSTANT,
                        sudogsize + (elemsize > 8 ? elemsize - 8 : 0), nil);
                
                dww = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
                        mkinternaltypename("waitq", getattr(elemtype, DW_AT_name)->data, nil));
                copychildren(dww, waitq);
                substitutetype(dww, "first", defptrto(dws));
                substitutetype(dww, "last",  defptrto(dws));
                newattr(dww, DW_AT_byte_size, DW_CLS_CONSTANT,
                        getattr(waitq, DW_AT_byte_size)->value, nil);
                
                dwh = newdie(&dwtypes, DW_ABRV_STRUCTTYPE,
                        mkinternaltypename("hchan", getattr(elemtype, DW_AT_name)->data, nil));
                copychildren(dwh, hchan);
                substitutetype(dwh, "recvq", dww);
                substitutetype(dwh, "sendq", dww);
                newattr(dwh, DW_AT_byte_size, DW_CLS_CONSTANT,
                        getattr(hchan, DW_AT_byte_size)->value, nil);
                newrefattr(die, DW_AT_type, defptrto(dwh));
        }
}
static void
defdwsymb(LSym* sym, char *s, int t, vlong v, vlong size, int ver, LSym *gotype)
{
        DWDie *dv, *dt;
        USED(size);
        if (strncmp(s, "go.string.", 10) == 0)
                return;
        if (strncmp(s, "type.", 5) == 0 && strcmp(s, "type.*") != 0 && strncmp(s, "type..", 6) != 0) {
                defgotype(sym);
                return;
        }
        dv = nil;
        switch (t) {
        default:
                return;
        case 'd':
        case 'b':
        case 'D':
        case 'B':
                dv = newdie(&dwglobals, DW_ABRV_VARIABLE, s);
                newabslocexprattr(dv, v, sym);
                if (ver == 0)
                        newattr(dv, DW_AT_external, DW_CLS_FLAG, 1, 0);
                
        case 'a':
        case 'p':
                dt = defgotype(gotype);
        }
        if (dv != nil)
                newrefattr(dv, DW_AT_type, dt);
}
static void
movetomodule(DWDie *parent)
{
        DWDie *die;
        die = dwroot.child->child;
        while(die->link != nil)
                die = die->link;
        die->link = parent->child;
}
static void
finddebugruntimepath(LSym *s)
{
        int i;
        char *p;
        LSym *f;
        
        if(gdbscript[0] != '\0')
                return;
        for(i=0; i<s->pcln->nfile; i++) {
                f = s->pcln->file[i];
                if((p = strstr(f->name, "runtime/string.goc")) != nil) {
                        *p = '\0';
                        snprint(gdbscript, sizeof gdbscript, "%sruntime/runtime-gdb.py", f->name);
                        *p = 'r';
                        break;
                }
        }
}
enum {
        LINE_BASE = -1,
        LINE_RANGE = 4,
        OPCODE_BASE = 10
};
static void
putpclcdelta(vlong delta_pc, vlong delta_lc)
{
        if (LINE_BASE <= delta_lc && delta_lc < LINE_BASE+LINE_RANGE) {
                vlong opcode = OPCODE_BASE + (delta_lc - LINE_BASE) + (LINE_RANGE * delta_pc);
                if (OPCODE_BASE <= opcode && opcode < 256) {
                        cput(opcode);
                        return;
                }
        }
        if (delta_pc) {
                cput(DW_LNS_advance_pc);
                sleb128put(delta_pc);
        }
        cput(DW_LNS_advance_line);
        sleb128put(delta_lc);
        cput(DW_LNS_copy);
}
static void
newcfaoffsetattr(DWDie *die, int32 offs)
{
        char block[10];
        int i;
        i = 0;
        block[i++] = DW_OP_call_frame_cfa;
        if (offs != 0) {
                block[i++] = DW_OP_consts;
                i += sleb128enc(offs, block+i);
                block[i++] = DW_OP_plus;
        }
        newattr(die, DW_AT_location, DW_CLS_BLOCK, i, mal(i));
        memmove(die->attr->data, block, i);
}
static char*
mkvarname(char* name, int da)
{
        char buf[1024];
        char *n;
        snprint(buf, sizeof buf, "%s#%d", name, da);
        n = mal(strlen(buf) + 1);
        memmove(n, buf, strlen(buf));
        return n;
}
static void
flushunit(DWDie *dwinfo, vlong pc, LSym *pcsym, vlong unitstart, int32 header_length)
{
        vlong here;
        if (dwinfo != nil && pc != 0) {
                newattr(dwinfo, DW_AT_high_pc, DW_CLS_ADDRESS, pc+1, (char*)pcsym);
        }
        if (unitstart >= 0) {
                cput(0);  
                uleb128put(1);
                cput(DW_LNE_end_sequence);
                here = cpos();
                cseek(unitstart);
                LPUT(here - unitstart - sizeof(int32));  
                WPUT(2);  
                LPUT(header_length); 
                cseek(here);
        }
}
static void
writelines(void)
{
        LSym *s, *epcs;
        Auto *a;
        vlong unitstart, headerend, offs;
        vlong pc, epc;
        int i, lang, da, dt, line, file;
        DWDie *dwinfo, *dwfunc, *dwvar, **dws;
        DWDie *varhash[HASHSIZE];
        char *n, *nn;
        Pciter pcfile, pcline;
        LSym **files, *f;
        if(linesec == S)
                linesec = linklookup(ctxt, ".dwarfline", 0);
        linesec->nr = 0;
        unitstart = -1;
        headerend = -1;
        epc = 0;
        epcs = S;
        lineo = cpos();
        dwinfo = nil;
        
        flushunit(dwinfo, epc, epcs, unitstart, headerend - unitstart - 10);
        unitstart = cpos();
        
        lang = DW_LANG_Go;
        
        s = ctxt->textp;
        dwinfo = newdie(&dwroot, DW_ABRV_COMPUNIT, estrdup("go"));
        newattr(dwinfo, DW_AT_language, DW_CLS_CONSTANT,lang, 0);
        newattr(dwinfo, DW_AT_stmt_list, DW_CLS_PTR, unitstart - lineo, 0);
        newattr(dwinfo, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, (char*)s);
        
        
        LPUT(0);   
        WPUT(2);   
        LPUT(0);   
        
        cput(1);   
        cput(1);   
        cput(LINE_BASE);     
        cput(LINE_RANGE);    
        cput(OPCODE_BASE);   
        cput(0);   
        cput(1);   
        cput(1);   
        cput(1);   
        cput(1);   
        cput(0);   
        cput(0);   
        cput(0);   
        cput(1);   
        cput(0);   
        files = emallocz(ctxt->nhistfile*sizeof files[0]);
        for(f = ctxt->filesyms; f != nil; f = f->next)
                files[f->value-1] = f;
        for(i=0; i<ctxt->nhistfile; i++) {
                strnput(files[i]->name, strlen(files[i]->name) + 4);
                
        }
        cput(0);   
        headerend = cpos();
        cput(0);  
        uleb128put(1 + PtrSize);
        cput(DW_LNE_set_address);
        pc = s->value;
        line = 1;
        file = 1;
        if(linkmode == LinkExternal)
                adddwarfrel(linesec, s, lineo, PtrSize, 0);
        else
                addrput(pc);
        for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next) {
                s = ctxt->cursym;
                dwfunc = newdie(dwinfo, DW_ABRV_FUNCTION, s->name);
                newattr(dwfunc, DW_AT_low_pc, DW_CLS_ADDRESS, s->value, (char*)s);
                epc = s->value + s->size;
                epcs = s;
                newattr(dwfunc, DW_AT_high_pc, DW_CLS_ADDRESS, epc, (char*)s);
                if (s->version == 0)
                        newattr(dwfunc, DW_AT_external, DW_CLS_FLAG, 1, 0);
                if(s->pcln == nil)
                        continue;
                finddebugruntimepath(s);
                pciterinit(ctxt, &pcfile, &s->pcln->pcfile);
                pciterinit(ctxt, &pcline, &s->pcln->pcline);
                epc = pc;
                while(!pcfile.done && !pcline.done) {
                        if(epc - s->value >= pcfile.nextpc) {
                                pciternext(&pcfile);
                                continue;
                        }
                        if(epc - s->value >= pcline.nextpc) {
                                pciternext(&pcline);
                                continue;
                        }
                        if(file != pcfile.value) {
                                cput(DW_LNS_set_file);
                                uleb128put(pcfile.value);
                                file = pcfile.value;
                        }
                        putpclcdelta(s->value + pcline.pc - pc, pcline.value - line);
                        pc = s->value + pcline.pc;
                        line = pcline.value;
                        if(pcfile.nextpc < pcline.nextpc)
                                epc = pcfile.nextpc;
                        else
                                epc = pcline.nextpc;
                        epc += s->value;
                }
                da = 0;
                dwfunc->hash = varhash;  
                memset(varhash, 0, sizeof varhash);
                for(a = s->autom; a; a = a->link) {
                        switch (a->type) {
                        case A_AUTO:
                                dt = DW_ABRV_AUTO;
                                offs = a->aoffset - PtrSize;
                                break;
                        case A_PARAM:
                                dt = DW_ABRV_PARAM;
                                offs = a->aoffset;
                                break;
                        default:
                                continue;
                        }
                        if (strstr(a->asym->name, ".autotmp_"))
                                continue;
                        if (find(dwfunc, a->asym->name) != nil)
                                n = mkvarname(a->asym->name, da);
                        else
                                n = a->asym->name;
                        
                        nn = strrchr(n, '.');
                        if (nn)
                                n = nn + 1;
                        dwvar = newdie(dwfunc, dt, n);
                        newcfaoffsetattr(dwvar, offs);
                        newrefattr(dwvar, DW_AT_type, defgotype(a->gotype));
                        
                        newattr(dwvar, DW_AT_internal_location, DW_CLS_CONSTANT, offs, nil);
                        dwfunc->child = dwvar->link;  
                        for (dws = &dwfunc->child; *dws != nil; dws = &(*dws)->link)
                                if (offs > getattr(*dws, DW_AT_internal_location)->value)
                                        break;
                        dwvar->link = *dws;
                        *dws = dwvar;
                        da++;
                }
                dwfunc->hash = nil;
        }
        flushunit(dwinfo, epc, epcs, unitstart, headerend - unitstart - 10);
        linesize = cpos() - lineo;
}
enum
{
        CIERESERVE = 16,
        DATAALIGNMENTFACTOR = -4,       
        FAKERETURNCOLUMN = 16           
};
static void
putpccfadelta(vlong deltapc, vlong cfa)
{
        if (deltapc < 0x40) {
                cput(DW_CFA_advance_loc + deltapc);
        } else if (deltapc < 0x100) {
                cput(DW_CFA_advance_loc1);
                cput(deltapc);
        } else if (deltapc < 0x10000) {
                cput(DW_CFA_advance_loc2);
                WPUT(deltapc);
        } else {
                cput(DW_CFA_advance_loc4);
                LPUT(deltapc);
        }
        cput(DW_CFA_def_cfa_offset_sf);
        sleb128put(cfa / DATAALIGNMENTFACTOR);
}
static void
writeframes(void)
{
        LSym *s;
        vlong fdeo, fdesize, pad;
        Pciter pcsp;
        if(framesec == S)
                framesec = linklookup(ctxt, ".dwarfframe", 0);
        framesec->nr = 0;
        frameo = cpos();
        
        LPUT(CIERESERVE);       
        LPUT(0xffffffff);       
        cput(3);                
        cput(0);                
        uleb128put(1);          
        sleb128put(DATAALIGNMENTFACTOR); 
        uleb128put(FAKERETURNCOLUMN);   
        cput(DW_CFA_def_cfa);
        uleb128put(DWARFREGSP); 
        uleb128put(PtrSize);    
        cput(DW_CFA_offset + FAKERETURNCOLUMN);  
        uleb128put(-PtrSize / DATAALIGNMENTFACTOR);  
        
        pad = CIERESERVE + frameo + 4 - cpos();
        if (pad < 0) {
                diag("dwarf: CIERESERVE too small by %lld bytes.", -pad);
                errorexit();
        }
        strnput("", pad);
        for(ctxt->cursym = ctxt->textp; ctxt->cursym != nil; ctxt->cursym = ctxt->cursym->next) {
                s = ctxt->cursym;
                if(s->pcln == nil)
                        continue;
                fdeo = cpos();
                
                LPUT(0);        
                LPUT(0);        
                addrput(0);     
                addrput(0);     
                for(pciterinit(ctxt, &pcsp, &s->pcln->pcsp); !pcsp.done; pciternext(&pcsp))
                        putpccfadelta(pcsp.nextpc - pcsp.pc, PtrSize + pcsp.value);
                fdesize = cpos() - fdeo - 4;    
                pad = rnd(fdesize, PtrSize) - fdesize;
                strnput("", pad);
                fdesize += pad;
                
                cseek(fdeo);
                LPUT(fdesize);
                if(linkmode == LinkExternal) {
                        adddwarfrel(framesec, framesym, frameo, 4, 0);
                        adddwarfrel(framesec, s, frameo, PtrSize, 0);
                }
                else {
                        LPUT(0);
                        addrput(s->value);
                }
                addrput(s->size);
                cseek(fdeo + 4 + fdesize);
        }
        cflush();
        framesize = cpos() - frameo;
}
enum
{
        COMPUNITHEADERSIZE = 4+2+4+1
};
static void
writeinfo(void)
{
        DWDie *compunit;
        vlong unitstart, here;
        fwdcount = 0;
        if (infosec == S)
                infosec = linklookup(ctxt, ".dwarfinfo", 0);
        infosec->nr = 0;
        if(arangessec == S)
                arangessec = linklookup(ctxt, ".dwarfaranges", 0);
        arangessec->nr = 0;
        for (compunit = dwroot.child; compunit; compunit = compunit->link) {
                unitstart = cpos();
                
                
                
                LPUT(0);        
                WPUT(2);        
                
                if(linkmode == LinkExternal)
                        adddwarfrel(infosec, abbrevsym, infoo, 4, 0);
                else
                        LPUT(0);
                cput(PtrSize);  
                putdie(compunit);
                here = cpos();
                cseek(unitstart);
                LPUT(here - unitstart - 4);     
                cseek(here);
        }
        cflush();
}
static int
ispubname(DWDie *die)
{
        DWAttr *a;
        switch(die->abbrev) {
        case DW_ABRV_FUNCTION:
        case DW_ABRV_VARIABLE:
                a = getattr(die, DW_AT_external);
                return a && a->value;
        }
        return 0;
}
static int
ispubtype(DWDie *die)
{
        return die->abbrev >= DW_ABRV_NULLTYPE;
}
static vlong
writepub(int (*ispub)(DWDie*))
{
        DWDie *compunit, *die;
        DWAttr *dwa;
        vlong unitstart, unitend, sectionstart, here;
        sectionstart = cpos();
        for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) {
                unitstart = compunit->offs - COMPUNITHEADERSIZE;
                if (compunit->link != nil)
                        unitend = compunit->link->offs - COMPUNITHEADERSIZE;
                else
                        unitend = infoo + infosize;
                
                LPUT(0);                        
                WPUT(2);                        
                LPUT(unitstart);                
                LPUT(unitend - unitstart);      
                for (die = compunit->child; die != nil; die = die->link) {
                        if (!ispub(die)) continue;
                        LPUT(die->offs - unitstart);
                        dwa = getattr(die, DW_AT_name);
                        strnput(dwa->data, dwa->value + 1);
                }
                LPUT(0);
                here = cpos();
                cseek(sectionstart);
                LPUT(here - sectionstart - 4);  
                cseek(here);
        }
        return sectionstart;
}
static vlong
writearanges(void)
{
        DWDie *compunit;
        DWAttr *b, *e;
        int headersize;
        vlong sectionstart;
        vlong value;
        sectionstart = cpos();
        headersize = rnd(4+2+4+1+1, PtrSize);  
        for (compunit = dwroot.child; compunit != nil; compunit = compunit->link) {
                b = getattr(compunit,  DW_AT_low_pc);
                if (b == nil)
                        continue;
                e = getattr(compunit,  DW_AT_high_pc);
                if (e == nil)
                        continue;
                
                LPUT(headersize + 4*PtrSize - 4);       
                WPUT(2);        
                value = compunit->offs - COMPUNITHEADERSIZE;    
                if(linkmode == LinkExternal)
                        adddwarfrel(arangessec, infosym, sectionstart, 4, value);
                else
                        LPUT(value);
                cput(PtrSize);  
                cput(0);        
                strnput("", headersize - (4+2+4+1+1));  
                if(linkmode == LinkExternal)
                        adddwarfrel(arangessec, (LSym*)b->data, sectionstart, PtrSize, b->value-((LSym*)b->data)->value);
                else
                        addrput(b->value);
                addrput(e->value - b->value);
                addrput(0);
                addrput(0);
        }
        cflush();
        return sectionstart;
}
static vlong
writegdbscript(void)
{
        vlong sectionstart;
        sectionstart = cpos();
        if (gdbscript[0]) {
                cput(1);  
                strnput(gdbscript, strlen(gdbscript)+1);
                cflush();
        }
        return sectionstart;
}
static void
align(vlong size)
{
        if(HEADTYPE == Hwindows) 
                strnput("", rnd(size, PEFILEALIGN) - size);
}
static vlong
writedwarfreloc(LSym* s)
{
        int i;
        vlong start;
        Reloc *r;
        
        start = cpos();
        for(r = s->r; r < s->r+s->nr; r++) {
                if(iself)
                        i = elfreloc1(r, r->off);
                else if(HEADTYPE == Hdarwin)
                        i = machoreloc1(r, r->off);
                else
                        i = -1;
                if(i < 0)
                        diag("unsupported obj reloc %d/%d to %s", r->type, r->siz, r->sym->name);
        }
        return start;
}
void
dwarfemitdebugsections(void)
{
        vlong infoe;
        DWDie* die;
        if(debug['w'])  
                return;
        if(linkmode == LinkExternal && !iself)
                return;
        
        newattr(&dwtypes, DW_AT_name, DW_CLS_STRING, strlen("dwtypes"), "dwtypes");
        mkindex(&dwroot);
        mkindex(&dwtypes);
        mkindex(&dwglobals);
        
        newdie(&dwtypes, DW_ABRV_NULLTYPE, "<unspecified>");
        newdie(&dwtypes, DW_ABRV_NULLTYPE, "void");
        newdie(&dwtypes, DW_ABRV_BARE_PTRTYPE, "unsafe.Pointer");
        die = newdie(&dwtypes, DW_ABRV_BASETYPE, "uintptr");  
        newattr(die, DW_AT_encoding,  DW_CLS_CONSTANT, DW_ATE_unsigned, 0);
        newattr(die, DW_AT_byte_size, DW_CLS_CONSTANT, PtrSize, 0);
        
        defgotype(lookup_or_diag("type.runtime.rtype"));
        defgotype(lookup_or_diag("type.runtime.interfaceType"));
        defgotype(lookup_or_diag("type.runtime.itab"));
        genasmsym(defdwsymb);
        writeabbrev();
        align(abbrevsize);
        writelines();
        align(linesize);
        writeframes();
        align(framesize);
        synthesizestringtypes(dwtypes.child);
        synthesizeslicetypes(dwtypes.child);
        synthesizemaptypes(dwtypes.child);
        synthesizechantypes(dwtypes.child);
        reversetree(&dwroot.child);
        reversetree(&dwtypes.child);
        reversetree(&dwglobals.child);
        movetomodule(&dwtypes);
        movetomodule(&dwglobals);
        infoo = cpos();
        writeinfo();
        infoe = cpos();
        pubnameso = infoe;
        pubtypeso = infoe;
        arangeso = infoe;
        gdbscripto = infoe;
        if (fwdcount > 0) {
                if (debug['v'])
                        Bprint(&bso, "%5.2f dwarf pass 2.\n", cputime());
                cseek(infoo);
                writeinfo();
                if (fwdcount > 0) {
                        diag("dwarf: unresolved references after first dwarf info pass");
                        errorexit();
                }
                if (infoe != cpos()) {
                        diag("dwarf: inconsistent second dwarf info pass");
                        errorexit();
                }
        }
        infosize = infoe - infoo;
        align(infosize);
        pubnameso  = writepub(ispubname);
        pubnamessize  = cpos() - pubnameso;
        align(pubnamessize);
        pubtypeso  = writepub(ispubtype);
        pubtypessize  = cpos() - pubtypeso;
        align(pubtypessize);
        arangeso   = writearanges();
        arangessize   = cpos() - arangeso;
        align(arangessize);
        gdbscripto = writegdbscript();
        gdbscriptsize = cpos() - gdbscripto;
        align(gdbscriptsize);
        while(cpos()&7)
                cput(0);
        inforeloco = writedwarfreloc(infosec);
        inforelocsize = cpos() - inforeloco;
        align(inforelocsize);
        arangesreloco = writedwarfreloc(arangessec);
        arangesrelocsize = cpos() - arangesreloco;
        align(arangesrelocsize);
        linereloco = writedwarfreloc(linesec);
        linerelocsize = cpos() - linereloco;
        align(linerelocsize);
        framereloco = writedwarfreloc(framesec);
        framerelocsize = cpos() - framereloco;
        align(framerelocsize);
}
enum
{
        ElfStrDebugAbbrev,
        ElfStrDebugAranges,
        ElfStrDebugFrame,
        ElfStrDebugInfo,
        ElfStrDebugLine,
        ElfStrDebugLoc,
        ElfStrDebugMacinfo,
        ElfStrDebugPubNames,
        ElfStrDebugPubTypes,
        ElfStrDebugRanges,
        ElfStrDebugStr,
        ElfStrGDBScripts,
        ElfStrRelDebugInfo,
        ElfStrRelDebugAranges,
        ElfStrRelDebugLine,
        ElfStrRelDebugFrame,
        NElfStrDbg
};
vlong elfstrdbg[NElfStrDbg];
void
dwarfaddshstrings(LSym *shstrtab)
{
        if(debug['w'])  
                return;
        elfstrdbg[ElfStrDebugAbbrev]   = addstring(shstrtab, ".debug_abbrev");
        elfstrdbg[ElfStrDebugAranges]  = addstring(shstrtab, ".debug_aranges");
        elfstrdbg[ElfStrDebugFrame]    = addstring(shstrtab, ".debug_frame");
        elfstrdbg[ElfStrDebugInfo]     = addstring(shstrtab, ".debug_info");
        elfstrdbg[ElfStrDebugLine]     = addstring(shstrtab, ".debug_line");
        elfstrdbg[ElfStrDebugLoc]      = addstring(shstrtab, ".debug_loc");
        elfstrdbg[ElfStrDebugMacinfo]  = addstring(shstrtab, ".debug_macinfo");
        elfstrdbg[ElfStrDebugPubNames] = addstring(shstrtab, ".debug_pubnames");
        elfstrdbg[ElfStrDebugPubTypes] = addstring(shstrtab, ".debug_pubtypes");
        elfstrdbg[ElfStrDebugRanges]   = addstring(shstrtab, ".debug_ranges");
        elfstrdbg[ElfStrDebugStr]      = addstring(shstrtab, ".debug_str");
        elfstrdbg[ElfStrGDBScripts]    = addstring(shstrtab, ".debug_gdb_scripts");
        if(linkmode == LinkExternal) {
                if(thechar == '6') {
                        elfstrdbg[ElfStrRelDebugInfo] = addstring(shstrtab, ".rela.debug_info");
                        elfstrdbg[ElfStrRelDebugAranges] = addstring(shstrtab, ".rela.debug_aranges");
                        elfstrdbg[ElfStrRelDebugLine] = addstring(shstrtab, ".rela.debug_line");
                        elfstrdbg[ElfStrRelDebugFrame] = addstring(shstrtab, ".rela.debug_frame");
                } else {
                        elfstrdbg[ElfStrRelDebugInfo] = addstring(shstrtab, ".rel.debug_info");
                        elfstrdbg[ElfStrRelDebugAranges] = addstring(shstrtab, ".rel.debug_aranges");
                        elfstrdbg[ElfStrRelDebugLine] = addstring(shstrtab, ".rel.debug_line");
                        elfstrdbg[ElfStrRelDebugFrame] = addstring(shstrtab, ".rel.debug_frame");
                }
                infosym = linklookup(ctxt, ".debug_info", 0);
                infosym->hide = 1;
                abbrevsym = linklookup(ctxt, ".debug_abbrev", 0);
                abbrevsym->hide = 1;
                linesym = linklookup(ctxt, ".debug_line", 0);
                linesym->hide = 1;
                framesym = linklookup(ctxt, ".debug_frame", 0);
                framesym->hide = 1;
        }
}
void
dwarfaddelfsectionsyms()
{
        if(infosym != nil) {
                infosympos = cpos();
                putelfsectionsym(infosym, 0);
        }
        if(abbrevsym != nil) {
                abbrevsympos = cpos();
                putelfsectionsym(abbrevsym, 0);
        }
        if(linesym != nil) {
                linesympos = cpos();
                putelfsectionsym(linesym, 0);
        }
        if(framesym != nil) {
                framesympos = cpos();
                putelfsectionsym(framesym, 0);
        }
}
static void
dwarfaddelfrelocheader(int elfstr, ElfShdr *shdata, vlong off, vlong size)
{
        ElfShdr *sh;
        sh = newElfShdr(elfstrdbg[elfstr]);
        if(thechar == '6') {
                sh->type = SHT_RELA;
        } else {
                sh->type = SHT_REL;
        }
        sh->entsize = PtrSize*(2+(sh->type==SHT_RELA));
        sh->link = elfshname(".symtab")->shnum;
        sh->info = shdata->shnum;
        sh->off = off;
        sh->size = size;
        sh->addralign = PtrSize;
        
}
void
dwarfaddelfheaders(void)
{
        ElfShdr *sh, *shinfo, *sharanges, *shline, *shframe;
        if(debug['w'])  
                return;
        sh = newElfShdr(elfstrdbg[ElfStrDebugAbbrev]);
        sh->type = SHT_PROGBITS;
        sh->off = abbrevo;
        sh->size = abbrevsize;
        sh->addralign = 1;
        if(abbrevsympos > 0)
                putelfsymshndx(abbrevsympos, sh->shnum);
        sh = newElfShdr(elfstrdbg[ElfStrDebugLine]);
        sh->type = SHT_PROGBITS;
        sh->off = lineo;
        sh->size = linesize;
        sh->addralign = 1;
        if(linesympos > 0)
                putelfsymshndx(linesympos, sh->shnum);
        shline = sh;
        sh = newElfShdr(elfstrdbg[ElfStrDebugFrame]);
        sh->type = SHT_PROGBITS;
        sh->off = frameo;
        sh->size = framesize;
        sh->addralign = 1;
        if(framesympos > 0)
                putelfsymshndx(framesympos, sh->shnum);
        shframe = sh;
        sh = newElfShdr(elfstrdbg[ElfStrDebugInfo]);
        sh->type = SHT_PROGBITS;
        sh->off = infoo;
        sh->size = infosize;
        sh->addralign = 1;
        if(infosympos > 0)
                putelfsymshndx(infosympos, sh->shnum);
        shinfo = sh;
        if (pubnamessize > 0) {
                sh = newElfShdr(elfstrdbg[ElfStrDebugPubNames]);
                sh->type = SHT_PROGBITS;
                sh->off = pubnameso;
                sh->size = pubnamessize;
                sh->addralign = 1;
        }
        if (pubtypessize > 0) {
                sh = newElfShdr(elfstrdbg[ElfStrDebugPubTypes]);
                sh->type = SHT_PROGBITS;
                sh->off = pubtypeso;
                sh->size = pubtypessize;
                sh->addralign = 1;
        }
        sharanges = nil;
        if (arangessize) {
                sh = newElfShdr(elfstrdbg[ElfStrDebugAranges]);
                sh->type = SHT_PROGBITS;
                sh->off = arangeso;
                sh->size = arangessize;
                sh->addralign = 1;
                sharanges = sh;
        }
        if (gdbscriptsize) {
                sh = newElfShdr(elfstrdbg[ElfStrGDBScripts]);
                sh->type = SHT_PROGBITS;
                sh->off = gdbscripto;
                sh->size = gdbscriptsize;
                sh->addralign = 1;
        }
        if(inforelocsize)
                dwarfaddelfrelocheader(ElfStrRelDebugInfo, shinfo, inforeloco, inforelocsize);
        if(arangesrelocsize)
                dwarfaddelfrelocheader(ElfStrRelDebugAranges, sharanges, arangesreloco, arangesrelocsize);
        if(linerelocsize)
                dwarfaddelfrelocheader(ElfStrRelDebugLine, shline, linereloco, linerelocsize);
        if(framerelocsize)
                dwarfaddelfrelocheader(ElfStrRelDebugFrame, shframe, framereloco, framerelocsize);
}
void
dwarfaddmachoheaders(void)
{
        MachoSect *msect;
        MachoSeg *ms;
        vlong fakestart;
        int nsect;
        if(debug['w'])  
                return;
        
        
        fakestart = abbrevo & ~0xfff;
        nsect = 4;
        if (pubnamessize  > 0)
                nsect++;
        if (pubtypessize  > 0)
                nsect++;
        if (arangessize   > 0)
                nsect++;
        if (gdbscriptsize > 0)
                nsect++;
        ms = newMachoSeg("__DWARF", nsect);
        ms->fileoffset = fakestart;
        ms->filesize = abbrevo-fakestart;
        ms->vaddr = ms->fileoffset + segdata.vaddr - segdata.fileoff;
        msect = newMachoSect(ms, "__debug_abbrev", "__DWARF");
        msect->off = abbrevo;
        msect->size = abbrevsize;
        msect->addr = msect->off + segdata.vaddr - segdata.fileoff;
        ms->filesize += msect->size;
        msect = newMachoSect(ms, "__debug_line", "__DWARF");
        msect->off = lineo;
        msect->size = linesize;
        msect->addr = msect->off + segdata.vaddr - segdata.fileoff;
        ms->filesize += msect->size;
        msect = newMachoSect(ms, "__debug_frame", "__DWARF");
        msect->off = frameo;
        msect->size = framesize;
        msect->addr = msect->off + segdata.vaddr - segdata.fileoff;
        ms->filesize += msect->size;
        msect = newMachoSect(ms, "__debug_info", "__DWARF");
        msect->off = infoo;
        msect->size = infosize;
        msect->addr = msect->off + segdata.vaddr - segdata.fileoff;
        ms->filesize += msect->size;
        if (pubnamessize > 0) {
                msect = newMachoSect(ms, "__debug_pubnames", "__DWARF");
                msect->off = pubnameso;
                msect->size = pubnamessize;
                msect->addr = msect->off + segdata.vaddr - segdata.fileoff;
                ms->filesize += msect->size;
        }
        if (pubtypessize > 0) {
                msect = newMachoSect(ms, "__debug_pubtypes", "__DWARF");
                msect->off = pubtypeso;
                msect->size = pubtypessize;
                msect->addr = msect->off + segdata.vaddr - segdata.fileoff;
                ms->filesize += msect->size;
        }
        if (arangessize > 0) {
                msect = newMachoSect(ms, "__debug_aranges", "__DWARF");
                msect->off = arangeso;
                msect->size = arangessize;
                msect->addr = msect->off + segdata.vaddr - segdata.fileoff;
                ms->filesize += msect->size;
        }
        
        if (gdbscriptsize > 0) {
                msect = newMachoSect(ms, "__debug_gdb_scripts", "__DWARF");
                msect->off = gdbscripto;
                msect->size = gdbscriptsize;
                msect->addr = msect->off + segdata.vaddr - segdata.fileoff;
                ms->filesize += msect->size;
        }
}
void
dwarfaddpeheaders(void)
{
        if(debug['w'])  
                return;
        newPEDWARFSection(".debug_abbrev", abbrevsize);
        newPEDWARFSection(".debug_line", linesize);
        newPEDWARFSection(".debug_frame", framesize);
        newPEDWARFSection(".debug_info", infosize);
        newPEDWARFSection(".debug_pubnames", pubnamessize);
        newPEDWARFSection(".debug_pubtypes", pubtypessize);
        newPEDWARFSection(".debug_aranges", arangessize);
        newPEDWARFSection(".debug_gdb_scripts", gdbscriptsize);
}