github.com/eh-steve/goloader@v0.0.0-20240111193454-90ff3cfdae39/mmap/manager_unix.go (about) 1 //go:build freebsd || linux || netbsd 2 // +build freebsd linux netbsd 3 4 package mmap 5 6 import ( 7 "bytes" 8 "fmt" 9 "github.com/eh-steve/goloader/mmap/mapping" 10 "os" 11 "strconv" 12 "strings" 13 ) 14 15 // Based on format of /proc/[pid]/maps from https://man7.org/linux/man-pages/man5/proc.5.html 16 // Only tested on linux, but netbsd/freebsd should be the same. 17 // TODO: OpenBSD needs to use procmap https://man.openbsd.org/procmap.1 18 // TODO: Solaris needs to use /proc/[pid]/map with a different format 19 // TODO: Dragonfly needs /proc/curproc/map with a different format 20 func getCurrentProcMaps() ([]mapping.Mapping, error) { 21 mapsData, err := os.ReadFile("/proc/self/maps") 22 if err != nil { 23 return nil, fmt.Errorf("could not read '/proc/self/maps': %w", err) 24 } 25 lines := bytes.Split(mapsData, []byte("\n")) 26 var mappings []mapping.Mapping 27 for i, line := range lines { 28 var mapping mapping.Mapping 29 mmapFields := strings.Fields(string(line)) 30 if len(mmapFields) == 0 { 31 continue 32 } 33 if len(mmapFields) < 4 { 34 return nil, fmt.Errorf("got fewer than 4 fields on line %d of /proc/self/maps: %s", i, line) 35 } 36 addrRange := strings.Split(mmapFields[0], "-") 37 if len(addrRange) != 2 { 38 return nil, fmt.Errorf("got %d fields for address range on line %d (expected 2): %s", len(addrRange), i, line) 39 } 40 startAddr, err := strconv.ParseUint(addrRange[0], 16, 64) 41 if err != nil { 42 return nil, fmt.Errorf("failed to parse start address (%s) on line %d as uint64 (line: %s): %w", addrRange[0], i, line, err) 43 } 44 endAddr, err := strconv.ParseUint(addrRange[1], 16, 64) 45 if err != nil { 46 return nil, fmt.Errorf("failed to parse end address (%s) on line %d as uint64 (line: %s): %w", addrRange[1], i, line, err) 47 } 48 mapping.StartAddr = uintptr(startAddr) 49 mapping.EndAddr = uintptr(endAddr) 50 perms := mmapFields[1] 51 for _, char := range perms { 52 switch char { 53 case 'r': 54 mapping.ReadPerm = true 55 case 'w': 56 mapping.WritePerm = true 57 case 'x': 58 mapping.ExecutePerm = true 59 case 's': 60 mapping.SharedPerm = true 61 case 'p': 62 mapping.PrivatePerm = true 63 case '-': 64 default: 65 return nil, fmt.Errorf("got an unexpected permission bit '%s' in perms '%s'", string(char), perms) 66 } 67 } 68 offset, err := strconv.ParseUint(mmapFields[2], 16, 64) 69 if err != nil { 70 return nil, fmt.Errorf("failed to parse file offset (%s) on line %d as uint64 (line: %s): %w", mmapFields[2], i, line, err) 71 } 72 mapping.Offset = uintptr(offset) 73 mapping.Dev = mmapFields[3] 74 inode, err := strconv.ParseUint(mmapFields[4], 16, 64) 75 if err != nil { 76 return nil, fmt.Errorf("failed to parse inode (%s) on line %d as uint64 (line: %s): %w", mmapFields[4], i, line, err) 77 } 78 mapping.Inode = inode 79 if len(mmapFields) > 5 { 80 mapping.PathName = mmapFields[5] 81 } 82 mappings = append(mappings, mapping) 83 } 84 85 return mappings, nil 86 }