Go on z/OS - Group home

Calling Native z/OS services

By Hyder Naqvi posted Mon September 25, 2023 12:43 PM


Overview of z/OS Callable Services:

z/OS callable services are native services for high-level-languages that enable MVS services by issuing CALLs. Utilizing callable services is a rewarding feature allowing a program additional abilities beyond what is available on other platforms. This blog will explore using callable services in Go and provide examples that you can easily start modifying and implementing in your operations. This blog was inspired by the zOS Callable Services for Java developer blog written by Jakob Balhar and the discussed sample code provided below borrows from his example. Furthermore this blog also owes gratitude to CW for the time he spent diving into assembly language and safely using unsafe pointers so you don’t have to, by making his solutions available in the ibmruntimes/go-recordio module.

Overview of the z/OS Mainframe Demonstrating z/OS Native Services and Go Accessable Technology [By Bill O'Farrell]

2. Implementing Callable Services in Go:

You will find that most of the work you have to do to get access to callable services in Go has already been done for you. You don’t have to dive into assembly, or figure out 31 or 24 bit addressing, but this blog will walk you through some of it and reference solutions and documentation along the way. Follow along as this blogs uses the ibmruntimes/go-recordio RACF example to provide a guided tour.

Step 1: Define the Callable Service Interface:

The following is working code used for demonstrating callable services. It starts with a standard interface to define the service to be invoked by your Go application.

R_usermap is a service used for identification of RACF ID. It requires a format for its parameters which is handled by the list member inside the PLIST struct. list is an array with 15 fields and corresponds with the format documentation found here.

Look over the sample code as it will be used to demonstrate callable services, and make note of the portions that have already been covered.

package main

import (


type PLIST struct {
	list          [15]uint32
	Workarea      [128]uint64
	SafRcAlet     int32
	Rc            int32
	RacfRcAlet    int32
	RacfRc        int32
	RacfRsnAlet   int32
	RacfRsnRc     int32
	FuncAlet      int32
	Func          int16
	OptionWord    int32
	RacfUseridLen byte
	RacfUserid    [8]byte
	CertLen       byte
	Cert          [4096]byte
	AppIdLen      int16
	AppId         [246]byte
	DistNameLen   int16
	DistName      [246]byte
	RegNameLen    int16
	RegName       [255]byte

func main() {
	// Set up PLIST
	siz := (int((reflect.TypeOf((*PLIST)(nil)).Elem()).Size()))
	plist31 := (*PLIST)(unsafe.Pointer(utils.Malloc31(siz)))
	plist31.list[0] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.Workarea)))
	plist31.list[1] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.SafRcAlet)))
	plist31.list[2] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.Rc)))
	plist31.list[3] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfRcAlet)))
	plist31.list[4] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfRc)))
	plist31.list[5] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfRsnAlet)))
	plist31.list[6] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfRsnRc)))
	plist31.list[7] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.FuncAlet)))
	plist31.list[8] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.Func)))
	plist31.list[9] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.OptionWord)))
	plist31.list[10] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RacfUseridLen)))
	plist31.list[11] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.CertLen)))
	plist31.list[12] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.AppIdLen)))
	plist31.list[13] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.DistNameLen)))
	plist31.list[14] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.RegNameLen)))
	plist31.list[14] |= uint32(0x80000000)

	mod := utils.LoadMod("IRRSIM00")
	if uintptr(unsafe.Pointer(mod)) != 0 {
		plist31.Func = 0x3
		copy(plist31.RacfUserid[:], "IBMUSER ")
		plist31.RacfUseridLen = 7 // length of userid
		RC := mod.Call(uintptr(unsafe.Pointer(plist31)))
		if RC == 0 {
			fmt.Printf("SafRC %d RacfRc %d Reason %d\n", plist31.Rc, plist31.RacfRc, plist31.RacfRsnRc)
		} else {
			fmt.Printf("Call rc=0x%x\n", RC)
	} else {
		fmt.Printf("Failed to load IRRSIM00\n")


Looking over plist31.list[] it’s interesting that it holds the pointer to the app ID length (AppIdLen) but not the app ID itself (AppId). This is because in the PLIST struct AppIdLen comes first followed by AppId physically in memory, and this is how the callable services look for the app ID - relative to the memory holding the app ID length. The same concept applies for CertLen and Cert

This is how reference is made to the location in memory to the lengths of the respective values.

plist31.list[11] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.CertLen)))
plist31.list[12] = uint32(0x7fffffff & uintptr(unsafe.Pointer(&plist31.AppIdLen)))

Recall how these values are placed in the PLIST struct relative to one another.

CertLen       byte
Cert          [4096]byte
AppIdLen      int16
AppId         [246]byte

The end of the plist31.list[] is marked by a leading 1 which it indicates the end of the array or 31 bit field.

plist31.list[14] |= uint32(0x80000000)

Step 2: Invoke the Callable Service

Now that all of the parameters are in order the LoadMod funciton is to load an AMODE-31 module IRRSIM00.

mod := utils.LoadMod("IRRSIM00")

LoadMod is ultimately a function that relies on assembly. It loads the named module and determines the Amode.

You will now need your user ID. As a filler, this examples uses the ID IBMUSER. There is a blank space at the end of the string out of the abundance of caution for 31 bit systems. However, the length of the userid is saved as 7, so we are passing enough information for the system to determine what the user ID is.

  copy(plist31.RacfUserid[:], "IBMUSER ")
  plist31.RacfUseridLen = 7 // length of userid

A callable services call is then made using mod.Call() and points to in the plist31 that was setup earlier. The call is also depends on assembly similar to LoadMod because it factors in the Amode that was determined by utilizeing Assembly.

RC := mod.Call(uintptr(unsafe.Pointer(plist31)))


Having covered the basics of Callable services and the tools to do this yourself, you now have a good idea of how to access Callable Services with Go. You also have a reference point from which to call other callable services using LoadMod and Call. Furthermore, a starting point to exploring how to handle 24 and 31 bit calls is also clear. With all that cleared, experimentation is highly encouraged and so is contribution. If you find in your experimentation that you have useful contributions, please feel free to create an issue on ibmruntimes/go-recordio so we can accommodate your pull request.