Skip to content

cmd/compile: bad pcln associations #21098

@aarzilli

Description

@aarzilli

Please answer these questions before submitting your issue. Thanks!

What version of Go are you using (go version)?

go version go1.9beta2 linux/amd64

What did you do?

Compile this function with -gcflags '-N -l' (context http://github.com/aarzilli/diexplorer)

func openPE(path string) (*dwarf.Data, uint64, []byte) {
	file, _ := pe.Open(path)
	if file == nil {
		return nil, 0, nil
	}
	fmt.Fprintf(os.Stderr, "Found PE executable\n")
	dwarf, err := file.DWARF()
	must(err)

	var imageBase uint64
	switch oh := file.OptionalHeader.(type) {
	case *pe.OptionalHeader32:
		imageBase = uint64(oh.ImageBase)
	case *pe.OptionalHeader64:
		imageBase = oh.ImageBase
	default:
		panic(fmt.Errorf("pe file format not recognized"))
	}
	sect := file.Section(".text")
	if sect == nil {
		panic(fmt.Errorf("text section not found"))
	}
	textStart := imageBase + uint64(sect.VirtualAddress)
	textData, err := sect.Data()
	must(err)
	return dwarf, textStart, textData
}

disassembles to https://play.golang.org/p/Wn2J8N43ws. The problem is the LEA at 0x715c55 it gets assigned to main.go:64 (the return line) when it should be assigned to main.go:59 (the panic line after if sect == nil) you can see that the program jumps there from line 58 and all instructions after it are for line 59. I believe the problem is that the LEA doesn't have a position associated and it gets an automatic one assigned but since it's the first instruction emitted for its block the position is wrong.

The function main.disassemble (from the same repository, I'm not copying it here because it's too long) seems to have the opposite problem (disassembly of that function here: https://play.golang.org/p/XELhHHYCkW) the LEA at 0x714334 gets assigned to disass.go:230 but it should be assigned to disass.go:161 like all the instructions surrounding it.

This two problems aren't common enough that I can find a minimal example to reproduce them but they are common enough that I encounter them in the wild while using delve.

I made a program (http://github.com/aarzilli/badnext) to find such problems with pcln automatically, it works by comparing the CFG derived from parsing the source code with the CFG derived from the disassembly and reporting discrepancies. Usage badnext [-v] check <regex> <executable> checks all functions matching in (doesn't work on anonymous functions).

Sometimes the discrepancies it finds are justifiable but it does find legitimate problems.

The final problem I encounter with the pcln table is that with any code that looks like this:

if condition {
    body
}

the if's body is compiled into something that ends with a JMP, and that JMP is assigned the same position as the if's header, it would be better if the JMP had the same position as the last instruction of the body or the same position as the closing brace. This isn't as big a problem as the problem with LEAs above because it's regular enough that you get used to it quickly, but it would be nice if it was fixed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DebuggingFrozenDueToAgeNeedsFixThe path to resolution is known, but the work has not been done.

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions