github.com/gagliardetto/golang-go@v0.0.0-20201020153340-53909ea70814/cmd/internal/obj/dwarf.go (about)

     1  // Copyright 2019 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // Writes dwarf information to object files.
     6  
     7  package obj
     8  
     9  import (
    10  	"github.com/gagliardetto/golang-go/cmd/internal/dwarf"
    11  	"github.com/gagliardetto/golang-go/cmd/internal/src"
    12  	"fmt"
    13  )
    14  
    15  // Generate a sequence of opcodes that is as short as possible.
    16  // See section 6.2.5
    17  const (
    18  	LINE_BASE   = -4
    19  	LINE_RANGE  = 10
    20  	PC_RANGE    = (255 - OPCODE_BASE) / LINE_RANGE
    21  	OPCODE_BASE = 11
    22  )
    23  
    24  // generateDebugLinesSymbol fills the debug lines symbol of a given function.
    25  //
    26  // It's worth noting that this function doesn't generate the full debug_lines
    27  // DWARF section, saving that for the linker. This function just generates the
    28  // state machine part of debug_lines. The full table is generated by the
    29  // linker.  Also, we use the file numbers from the full package (not just the
    30  // function in question) when generating the state machine. We do this so we
    31  // don't have to do a fixup on the indices when writing the full section.
    32  func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) {
    33  	dctxt := dwCtxt{ctxt}
    34  
    35  	// The Pcfile table is used to generate the debug_lines section, and the file
    36  	// indices for that data could differ from the files we write out for the
    37  	// debug_lines section. Here we generate a LUT between those two indices.
    38  	fileNums := make(map[int32]int64)
    39  	for i, filename := range s.Func.Pcln.File {
    40  		if symbolIndex := ctxt.PosTable.FileIndex(filename); symbolIndex >= 0 {
    41  			fileNums[int32(i)] = int64(symbolIndex) + 1
    42  		} else {
    43  			panic(fmt.Sprintf("First time we've seen filename: %q", filename))
    44  		}
    45  	}
    46  
    47  	// Set up the debug_lines state machine.
    48  	// NB: This state machine is reset to this state when we've finished
    49  	// generating the line table. See below.
    50  	// TODO: Once delve can support multiple DW_LNS_end_statements, we don't have
    51  	// to do this.
    52  	stmt := true
    53  	line := int64(1)
    54  	pc := s.Func.Text.Pc
    55  	name := ""
    56  	prologue, wrotePrologue := false, false
    57  	// Walk the progs, generating the DWARF table.
    58  	for p := s.Func.Text; p != nil; p = p.Link {
    59  		prologue = prologue || (p.Pos.Xlogue() == src.PosPrologueEnd)
    60  		// If we're not at a real instruction, keep looping!
    61  		if p.Pos.Line() == 0 || (p.Link != nil && p.Link.Pc == p.Pc) {
    62  			continue
    63  		}
    64  		newStmt := p.Pos.IsStmt() != src.PosNotStmt
    65  		newName, newLine := linkgetlineFromPos(ctxt, p.Pos)
    66  
    67  		// Output debug info.
    68  		wrote := false
    69  		if name != newName {
    70  			newFile := ctxt.PosTable.FileIndex(newName) + 1 // 1 indexing for the table.
    71  			dctxt.AddUint8(lines, dwarf.DW_LNS_set_file)
    72  			dwarf.Uleb128put(dctxt, lines, int64(newFile))
    73  			name = newName
    74  			wrote = true
    75  		}
    76  		if prologue && !wrotePrologue {
    77  			dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_set_prologue_end))
    78  			wrotePrologue = true
    79  			wrote = true
    80  		}
    81  		if stmt != newStmt {
    82  			dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_negate_stmt))
    83  			stmt = newStmt
    84  			wrote = true
    85  		}
    86  
    87  		if line != int64(newLine) || wrote {
    88  			pcdelta := p.Pc - pc
    89  			putpclcdelta(ctxt, dctxt, lines, uint64(pcdelta), int64(newLine)-line)
    90  			line, pc = int64(newLine), p.Pc
    91  		}
    92  	}
    93  
    94  	// Because these symbols will be concatenated together by the linker, we need
    95  	// to reset the state machine that controls the debug symbols. The fields in
    96  	// the state machine that need to be reset are:
    97  	//   file = 1
    98  	//   line = 1
    99  	//   column = 0
   100  	//   stmt = set in header, we assume true
   101  	//   basic_block = false
   102  	// Careful readers of the DWARF specification will note that we don't reset
   103  	// the address of the state machine -- but this will happen at the beginning
   104  	// of the NEXT block of opcodes.
   105  	dctxt.AddUint8(lines, dwarf.DW_LNS_set_file)
   106  	dwarf.Uleb128put(dctxt, lines, 1)
   107  	dctxt.AddUint8(lines, dwarf.DW_LNS_advance_line)
   108  	dwarf.Sleb128put(dctxt, lines, int64(1-line))
   109  	if !stmt {
   110  		dctxt.AddUint8(lines, dwarf.DW_LNS_negate_stmt)
   111  	}
   112  	dctxt.AddUint8(lines, dwarf.DW_LNS_copy)
   113  }
   114  
   115  func putpclcdelta(linkctxt *Link, dctxt dwCtxt, s *LSym, deltaPC uint64, deltaLC int64) {
   116  	// Choose a special opcode that minimizes the number of bytes needed to
   117  	// encode the remaining PC delta and LC delta.
   118  	var opcode int64
   119  	if deltaLC < LINE_BASE {
   120  		if deltaPC >= PC_RANGE {
   121  			opcode = OPCODE_BASE + (LINE_RANGE * PC_RANGE)
   122  		} else {
   123  			opcode = OPCODE_BASE + (LINE_RANGE * int64(deltaPC))
   124  		}
   125  	} else if deltaLC < LINE_BASE+LINE_RANGE {
   126  		if deltaPC >= PC_RANGE {
   127  			opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * PC_RANGE)
   128  			if opcode > 255 {
   129  				opcode -= LINE_RANGE
   130  			}
   131  		} else {
   132  			opcode = OPCODE_BASE + (deltaLC - LINE_BASE) + (LINE_RANGE * int64(deltaPC))
   133  		}
   134  	} else {
   135  		if deltaPC <= PC_RANGE {
   136  			opcode = OPCODE_BASE + (LINE_RANGE - 1) + (LINE_RANGE * int64(deltaPC))
   137  			if opcode > 255 {
   138  				opcode = 255
   139  			}
   140  		} else {
   141  			// Use opcode 249 (pc+=23, lc+=5) or 255 (pc+=24, lc+=1).
   142  			//
   143  			// Let x=deltaPC-PC_RANGE.  If we use opcode 255, x will be the remaining
   144  			// deltaPC that we need to encode separately before emitting 255.  If we
   145  			// use opcode 249, we will need to encode x+1.  If x+1 takes one more
   146  			// byte to encode than x, then we use opcode 255.
   147  			//
   148  			// In all other cases x and x+1 take the same number of bytes to encode,
   149  			// so we use opcode 249, which may save us a byte in encoding deltaLC,
   150  			// for similar reasons.
   151  			switch deltaPC - PC_RANGE {
   152  			// PC_RANGE is the largest deltaPC we can encode in one byte, using
   153  			// DW_LNS_const_add_pc.
   154  			//
   155  			// (1<<16)-1 is the largest deltaPC we can encode in three bytes, using
   156  			// DW_LNS_fixed_advance_pc.
   157  			//
   158  			// (1<<(7n))-1 is the largest deltaPC we can encode in n+1 bytes for
   159  			// n=1,3,4,5,..., using DW_LNS_advance_pc.
   160  			case PC_RANGE, (1 << 7) - 1, (1 << 16) - 1, (1 << 21) - 1, (1 << 28) - 1,
   161  				(1 << 35) - 1, (1 << 42) - 1, (1 << 49) - 1, (1 << 56) - 1, (1 << 63) - 1:
   162  				opcode = 255
   163  			default:
   164  				opcode = OPCODE_BASE + LINE_RANGE*PC_RANGE - 1 // 249
   165  			}
   166  		}
   167  	}
   168  	if opcode < OPCODE_BASE || opcode > 255 {
   169  		panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
   170  	}
   171  
   172  	// Subtract from deltaPC and deltaLC the amounts that the opcode will add.
   173  	deltaPC -= uint64((opcode - OPCODE_BASE) / LINE_RANGE)
   174  	deltaLC -= (opcode-OPCODE_BASE)%LINE_RANGE + LINE_BASE
   175  
   176  	// Encode deltaPC.
   177  	if deltaPC != 0 {
   178  		if deltaPC <= PC_RANGE {
   179  			// Adjust the opcode so that we can use the 1-byte DW_LNS_const_add_pc
   180  			// instruction.
   181  			opcode -= LINE_RANGE * int64(PC_RANGE-deltaPC)
   182  			if opcode < OPCODE_BASE {
   183  				panic(fmt.Sprintf("produced invalid special opcode %d", opcode))
   184  			}
   185  			dctxt.AddUint8(s, dwarf.DW_LNS_const_add_pc)
   186  		} else if (1<<14) <= deltaPC && deltaPC < (1<<16) {
   187  			dctxt.AddUint8(s, dwarf.DW_LNS_fixed_advance_pc)
   188  			dctxt.AddUint16(s, uint16(deltaPC))
   189  		} else {
   190  			dctxt.AddUint8(s, dwarf.DW_LNS_advance_pc)
   191  			dwarf.Uleb128put(dctxt, s, int64(deltaPC))
   192  		}
   193  	}
   194  
   195  	// Encode deltaLC.
   196  	if deltaLC != 0 {
   197  		dctxt.AddUint8(s, dwarf.DW_LNS_advance_line)
   198  		dwarf.Sleb128put(dctxt, s, deltaLC)
   199  	}
   200  
   201  	// Output the special opcode.
   202  	dctxt.AddUint8(s, uint8(opcode))
   203  }