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