github.com/tinygo-org/tinygo@v0.31.3-0.20240404173401-90b0bf646c27/src/runtime/os_darwin.go (about) 1 //go:build darwin 2 3 package runtime 4 5 import "unsafe" 6 7 import "C" // dummy import so that os_darwin.c works 8 9 const GOOS = "darwin" 10 11 const ( 12 // See https://github.com/golang/go/blob/master/src/syscall/zerrors_darwin_amd64.go 13 flag_PROT_READ = 0x1 14 flag_PROT_WRITE = 0x2 15 flag_MAP_PRIVATE = 0x2 16 flag_MAP_ANONYMOUS = 0x1000 // MAP_ANON 17 ) 18 19 // Source: https://opensource.apple.com/source/Libc/Libc-1439.100.3/include/time.h.auto.html 20 const ( 21 clock_REALTIME = 0 22 clock_MONOTONIC_RAW = 4 23 ) 24 25 // https://opensource.apple.com/source/xnu/xnu-7195.141.2/EXTERNAL_HEADERS/mach-o/loader.h.auto.html 26 type machHeader struct { 27 magic uint32 28 cputype uint32 29 cpusubtype uint32 30 filetype uint32 31 ncmds uint32 32 sizeofcmds uint32 33 flags uint32 34 reserved uint32 35 } 36 37 // Struct for the LC_SEGMENT_64 load command. 38 type segmentLoadCommand struct { 39 cmd uint32 // LC_SEGMENT_64 40 cmdsize uint32 41 segname [16]byte 42 vmaddr uintptr 43 vmsize uintptr 44 fileoff uintptr 45 filesize uintptr 46 maxprot uint32 47 initprot uint32 48 nsects uint32 49 flags uint32 50 } 51 52 // MachO header of the currently running process. 53 // 54 //go:extern _mh_execute_header 55 var libc_mh_execute_header machHeader 56 57 // Find global variables in .data/.bss sections. 58 // The MachO linker doesn't seem to provide symbols for the start and end of the 59 // data section. There is get_etext, get_edata, and get_end, but these are 60 // undocumented and don't work with ASLR (which is enabled by default). 61 // Therefore, read the MachO header directly. 62 func findGlobals(found func(start, end uintptr)) { 63 // Here is a useful blog post to understand the MachO file format: 64 // https://h3adsh0tzz.com/2020/01/macho-file-format/ 65 66 const ( 67 MH_MAGIC_64 = 0xfeedfacf 68 LC_SEGMENT_64 = 0x19 69 VM_PROT_WRITE = 0x02 70 ) 71 72 // Sanity check that we're actually looking at a MachO header. 73 if gcAsserts && libc_mh_execute_header.magic != MH_MAGIC_64 { 74 runtimePanic("gc: unexpected MachO header") 75 } 76 77 // Iterate through the load commands. 78 // Because we're only interested in LC_SEGMENT_64 load commands, cast the 79 // pointer to that struct in advance. 80 var offset uintptr 81 var hasOffset bool 82 cmd := (*segmentLoadCommand)(unsafe.Pointer(uintptr(unsafe.Pointer(&libc_mh_execute_header)) + unsafe.Sizeof(machHeader{}))) 83 for i := libc_mh_execute_header.ncmds; i != 0; i-- { 84 if cmd.cmd == LC_SEGMENT_64 { 85 if cmd.fileoff == 0 && cmd.nsects != 0 { 86 // Detect ASLR offset by checking fileoff and nsects. This 87 // locates the __TEXT segment. This matches getsectiondata: 88 // https://opensource.apple.com/source/cctools/cctools-973.0.1/libmacho/getsecbyname.c.auto.html 89 offset = uintptr(unsafe.Pointer(&libc_mh_execute_header)) - cmd.vmaddr 90 hasOffset = true 91 } 92 if cmd.maxprot&VM_PROT_WRITE != 0 { 93 // Found a writable segment, which may contain Go globals. 94 if gcAsserts && !hasOffset { 95 // No ASLR offset detected. Did the __TEXT segment come 96 // after the __DATA segment? 97 // Note that when ASLR is disabled (for example, when 98 // running inside lldb), the offset is zero. That's why we 99 // need a separate hasOffset for this assert. 100 runtimePanic("gc: did not detect ASLR offset") 101 } 102 // Scan this segment for GC roots. 103 // This could be improved by only reading the memory areas 104 // covered by sections. That would reduce the amount of memory 105 // scanned a little bit (up to a single VM page). 106 found(offset+cmd.vmaddr, offset+cmd.vmaddr+cmd.vmsize) 107 } 108 } 109 110 // Move on to the next load command (wich may or may not be a 111 // LC_SEGMENT_64). 112 cmd = (*segmentLoadCommand)(unsafe.Add(unsafe.Pointer(cmd), cmd.cmdsize)) 113 } 114 } 115 116 func hardwareRand() (n uint64, ok bool) { 117 n |= uint64(libc_arc4random()) 118 n |= uint64(libc_arc4random()) << 32 119 return n, true 120 } 121 122 // uint32_t arc4random(void); 123 // 124 //export arc4random 125 func libc_arc4random() uint32