Skip to content

Commit

Permalink
WIP asm: add Metadata to asm.Instruction
Browse files Browse the repository at this point in the history
Added a new optional asm.Metadata struct to asm.Instruction which can be
used to store information about the origin of a instruction.
  • Loading branch information
Dylan Reimerink committed Feb 10, 2022
1 parent 7816b93 commit 59fbf81
Show file tree
Hide file tree
Showing 4 changed files with 92 additions and 11 deletions.
17 changes: 17 additions & 0 deletions asm/instruction.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ type Instruction struct {

// Symbol denotes an instruction at the start of a function body.
Symbol string

// Metadata contains optional metadata about this instruction like which file and line generated it.
Metadata *Metadata
}

// Sym creates a symbol.
Expand Down Expand Up @@ -493,6 +496,12 @@ func (insns Instructions) Format(f fmt.State, c rune) {
if iter.Ins.Symbol != "" {
fmt.Fprintf(f, "%s%s:\n", symIndent, iter.Ins.Symbol)
}
if iter.Ins.Metadata != nil {
line := strings.TrimSpace(iter.Ins.Metadata.Line)
if line != "" {
fmt.Fprintf(f, "%s%*s; %s\n", indent, offsetWidth, " ", line)
}
}
fmt.Fprintf(f, "%s%*d: %v\n", indent, offsetWidth, iter.Offset, iter.Ins)
}
}
Expand Down Expand Up @@ -562,6 +571,14 @@ func (iter *InstructionIterator) Next() bool {
return true
}

// Metadata holds metadata about the origin of a Instruction
type Metadata struct {
FileName string
Line string
LineNumber int
ColumnNumber int
}

type bpfRegisters uint8

func newBPFRegisters(dst, src Register, bo binary.ByteOrder) (bpfRegisters, error) {
Expand Down
57 changes: 46 additions & 11 deletions elf_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -343,15 +343,18 @@ func (ec *elfCode) loadProgramSections() (map[string]*ProgramSpec, error) {
// The resulting map is indexed by function name.
func (ec *elfCode) loadFunctions(section *elfSection) (map[string]asm.Instructions, error) {
var (
r = bufio.NewReader(section.Open())
funcs = make(map[string]asm.Instructions)
offset uint64
insns asm.Instructions
r = bufio.NewReader(section.Open())
funcs = make(map[string]asm.Instructions)
secOffset uint64
funcOffset uint64
insns asm.Instructions
lineInfos btf.LineInfos
)

for {
ins := asm.Instruction{
// Symbols denote the first instruction of a function body.
Symbol: section.symbols[offset].Name,
Symbol: section.symbols[secOffset].Name,
}

// Pull one instruction from the instruction stream.
Expand All @@ -369,20 +372,21 @@ func (ec *elfCode) loadFunctions(section *elfSection) (map[string]asm.Instructio
break
}
if err != nil {
return nil, fmt.Errorf("offset %d: %w", offset, err)
return nil, fmt.Errorf("offset %d: %w", secOffset, err)
}

// Decoded the first instruction of a function body but insns already
// holds a valid instruction stream. Store the result and flush insns.
if ins.Symbol != "" && insns.Name() != "" {
funcs[insns.Name()] = insns
insns = nil
funcOffset = 0
}

if rel, ok := section.relocations[offset]; ok {
if rel, ok := section.relocations[secOffset]; ok {
// A relocation was found for the current offset. Apply it to the insn.
if err = ec.relocateInstruction(&ins, rel); err != nil {
return nil, fmt.Errorf("offset %d: relocate instruction: %w", offset, err)
return nil, fmt.Errorf("offset %d: relocate instruction: %w", secOffset, err)
}
} else {
// Up to LLVM 9, calls to subprograms within the same ELF section are
Expand All @@ -393,19 +397,50 @@ func (ec *elfCode) loadFunctions(section *elfSection) (map[string]asm.Instructio
// When splitting sections into subprograms, the targets of these calls
// are no longer in scope, so they must be resolved here.
if ins.IsFunctionReference() && ins.Constant != -1 {
tgt := jumpTarget(offset, ins)
tgt := jumpTarget(secOffset, ins)
sym := section.symbols[tgt].Name
if sym == "" {
return nil, fmt.Errorf("offset %d: no jump target found at offset %d", offset, tgt)
return nil, fmt.Errorf("offset %d: no jump target found at offset %d", secOffset, tgt)
}

ins.Reference = sym
ins.Constant = -1
}
}

if ec.btf != nil {
name := ins.Symbol
if name == "" {
name = insns.Name()
}

lineInfos = ec.btf.GetFuncLineInfos(name)
}
for _, lineInfo := range lineInfos {
if uint64(lineInfo.InsnOff) == funcOffset {
fileName, err := lineInfo.FileName(ec.btf)
if err != nil {
continue
}

line, err := lineInfo.Line(ec.btf)
if err != nil {
continue
}

ins.Metadata = &asm.Metadata{
FileName: fileName,
Line: line,
LineNumber: int(lineInfo.LineNum()),
ColumnNumber: int(lineInfo.ColNum()),
}
break
}
}

insns = append(insns, ins)
offset += n
secOffset += n
funcOffset += n
}

return funcs, nil
Expand Down
9 changes: 9 additions & 0 deletions internal/btf/btf.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ func loadSpecFromELF(file *internal.SafeELFFile, variableOffsets map[variable]ui
return spec, nil
}

// GetFuncLineInfos returns the LineInfos for the given function name or nil if LineInfos can be found
func (spec *Spec) GetFuncLineInfos(funcName string) LineInfos {
if spec.lineInfos == nil {
return nil
}

return spec.lineInfos[funcName]
}

// splitExtInfos takes FuncInfos, LineInfos and CoreRelos indexed by section and
// transforms them to be indexed by function. Retrieves function names from
// the BTF spec.
Expand Down
20 changes: 20 additions & 0 deletions internal/btf/ext_info.go
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,26 @@ type LineInfo struct {
LineCol uint32
}

// FileName returns the file name as string
func (li LineInfo) FileName(btfSpec *Spec) (string, error) {
return btfSpec.strings.Lookup(li.FileNameOff)
}

// Line returns the line of source code as a string
func (li LineInfo) Line(btfSpec *Spec) (string, error) {
return btfSpec.strings.Lookup(li.LineOff)
}

// LineNum returns the line number within the FileName
func (li LineInfo) LineNum() uint32 {
return li.LineCol >> 10
}

// ColNumber returns the column number within the Line
func (li LineInfo) ColNum() uint32 {
return li.LineCol & 0x3ff
}

// Marshal writes the binary representation of the LineInfo to w.
// The instruction offset is converted from bytes to instructions.
func (li LineInfo) Marshal(w io.Writer, offset uint64) error {
Expand Down

0 comments on commit 59fbf81

Please sign in to comment.