github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/mmap/vmmap/vmmap_fallback_darwin.go (about)

     1  //go:build darwin && !cgo
     2  // +build darwin,!cgo
     3  
     4  package vmmap
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"github.com/eh-steve/goloader/mmap/mapping"
    10  	"os"
    11  	"os/exec"
    12  	"strconv"
    13  )
    14  
    15  func Vmmap() ([]mapping.Mapping, error) {
    16  	// This is horrible
    17  	pid := os.Getpid()
    18  	cmd := exec.Command("vmmap", "-pages", "-interleaved", fmt.Sprintf("%d", pid))
    19  	output, err := cmd.CombinedOutput()
    20  
    21  	if err != nil {
    22  		return nil, fmt.Errorf("could not run 'vmmap -v %d': %w", pid, err)
    23  	}
    24  
    25  	sections := bytes.Split(output, []byte("REGION DETAIL\n"))
    26  	lines := bytes.Split(sections[0], []byte("\n"))
    27  
    28  	columnHeaders := append(lines[len(lines)-1], []byte("REGION DETAIL")...)
    29  	if len(sections) != 2 {
    30  		return nil, fmt.Errorf("failed to parse vmmap output: expected REGION_DETAIL to be column header, got %d", len(sections))
    31  	}
    32  	sections = bytes.Split(sections[1], []byte("==== Legend\n"))
    33  	if len(sections) != 2 {
    34  		return nil, fmt.Errorf("failed to parse vmmap output: expected REGION_DETAIL to be column header, got")
    35  	}
    36  
    37  	// The vmmap address start/end output is centered around a common hyphen placement - we need to find the index of that hyphen, then split around it
    38  	hyphenIndex := bytes.Index(columnHeaders, []byte("START - END")) + len("START ")
    39  	entries := bytes.Split(sections[0], []byte("\n"))
    40  	var mappings = make([]mapping.Mapping, 0, len(entries))
    41  	for rowNumber, entry := range entries {
    42  		if len(entry) <= hyphenIndex {
    43  			continue
    44  		}
    45  		var i int
    46  		for i = hyphenIndex; i > 0; i-- {
    47  			if entry[i] == ' ' {
    48  				i++
    49  				break
    50  			}
    51  		}
    52  		startStr := string(entry[i:hyphenIndex])
    53  		for i = hyphenIndex + 1; i < hyphenIndex+18; i++ {
    54  			if entry[i] == ' ' {
    55  				break
    56  			}
    57  		}
    58  		endStr := string(entry[hyphenIndex+1 : i])
    59  		start, err := strconv.ParseUint(startStr, 16, 64)
    60  		if err != nil {
    61  			return nil, fmt.Errorf("failed to parse start hex uint64 from '%s' on row %d of vmmap output. Full output:\n%s", startStr, rowNumber, output)
    62  		}
    63  		end, err := strconv.ParseUint(endStr, 16, 64)
    64  		if err != nil {
    65  			return nil, fmt.Errorf("failed to parse end hex uint64 from '%s' on row %d of vmmap output. Full output:\n%s", endStr, rowNumber, output)
    66  		}
    67  		m := mapping.Mapping{
    68  			StartAddr: uintptr(start),
    69  			EndAddr:   uintptr(end),
    70  		}
    71  		mappings = append(mappings, m)
    72  	}
    73  
    74  	return mappings, nil
    75  }