The present invention relates to techniques and systems for executing a program compiled for a source architecture on a machine having a different target architecture.
Executable applications (also sometimes called programs) are compiled from source code. A limitation of computer systems is that a given executable application can only run under the operating system and machine instruction set for which it was compiled or assembled. This is true because executable programs target a specific instruction set (i.e. instructions that the system recognizes and can execute), with a known set of registers, and the program's ability to carry out input/output operations is typically with reference to the available calls to a known operating system. For example, as illustrated in
Hardware instructions that are natively supported by a particular computer or processor constitute part of the architecture of that computer or processor. Processors or computers of one platform may have a different architecture, and thus allow the native execution of different instructions than processors or computers on another platform. In the mainframe environment, a relocatable load module typically refers to all or part of an executable application, that is compiled to run mainframe computing environment. A compiler, such as the Cobol compiler, translates a source code program made up of one or more source code files into object code including one or more machine language program files. These object code files, in some cases together with additional object files, can be linked and assembled into an executable program.
A load module refers to all or part of an executable computer program, typically prepared for loading into the main memory of a legacy computer platform such as a S/370, S/390, or z/OS mainframe. Typically, a load module comprises object code that was compiled for a legacy platform, may include assembler code targeting the same legacy program, that has been linked by a linkage editor for loading and execution on the legacy platform. Because the object code and/or assembler code in the load module were compiled for the target legacy platform, a legacy load module typically cannot run on a different platform.
One approach to enabling a legacy load module to run on a different platform is emulation. For example, an emulation platform, which may include non- a non-legacy operating system, non-legacy hardware, or both, may be constructed to emulate the legacy operating system and hardware, providing a virtual container that presents the appearance of the legacy environment to the application. A disadvantage of emulation is that it often reduces system performance, and typically does not provide support for legacy memory protection schemes. However, improved system performance can be achieved through the implementation of optimized native application program interfaces (APIs) to replace the emulation of operating system function calls. Protection key memory management can be implemented through the memory management system described in, “Protection Key Management and Prefixing in Virtual Address Space Legacy Emulation System.” WO2017/103,651, incorporated by reference in its entirety herein.
In another approach to enable an application to run on different hardware platform, a compiler or cross compiler may be used to recompile the program so that it is expressed using the native hardware instructions of the new, target hardware platform. However, in many situations it is undesirable to recompile the source code. Recompiling may result in errors, changes in system performance, or changes in system behavior. Resolving these issues may require changes to the original source code, which fragments the code base and increases management complexity. Additionally, the source code for a particular application may not always be available, placing further restrictions on the ability to operate a given program on a different platform.
A decompiler is a program that reverses the process of a compiler which translates a computer program written in a high-level, typically human-readable language into a machine language program. In other words, a decompiler takes as input a program written in machine language, and translates that program into an equivalent program written in a higher-level language. A decompiler can be used to create a source code file that may be edited by a programmer and subsequently recompiled, or cross compiled for execution on a platform having a different machine architecture. However, decompilation does not produce source code that is identical to the original source code from which the machine language code or object code was originally generated. In particular, where optimizing compilers have been used to improve executable performance, information is frequently lost which cannot be fully recovered using a decompiler. Additionally, decompiling object code is complicated because it is difficult to separate computer code from data. Nevertheless, decompilation has found applications in algorithm extraction and analysis, in malware detection, and, to a limited degree, for source code recovery for purposes of modifying or translating object code from one environment to another.
A disassembler receives as input an executable program and converts that program into a machine independent assembly code representation. Assembly language typically has a one-to-one correspondence between assembly instructions and underlying machine instructions. A disassembled program can be reassembled by an assembler into an executable program.
In the case of interdependent software programs, translating code from one computer architecture to another typically introduces changes that break interfaces between previously interoperable programs. For example, in the case of call back programs that pass as parameters the addresses of routines or functions to allow a receiving program to invoke a routine in a calling program, translation through decompilation and recompilation to a different target architecture will typically change the addresses, and may change the size of address operands, so as to disrupt the ability of the receiving program to invoke the remote routine. Conversion of data between EBCDIC format typically used in legacy mainframe systems, and ASCII format used with newer platforms A load module compiler that can receive as input, a compiled legacy load module such as a Cobol load module compiled for a System 390 mainframe, and that generate as output an executable program that runs on a 64 bit x86 platform while continuing to make external references accessible has been described in “Load Module Compiler”, WO2017/153815, which is incorporated by reference herein in its entirety. The load module compiler enables the selective decompliation and recompilation of executable load modules to a new, target native environment, including the provision of mechanism to correctly handle addressing and data formatting changes. However, computer programs that have been migrated to the new computing environment using the load module compiler may not exploit programming features offered by the new programing environment.
In one example of a load module compiler, a load module decompiler is provided that is operable to receive as input, a relocatable load module previously compiled to run on a 32-bit mainframe computer, and to generate as output, an executable program compiled to run on a 64-bit computer. By selecting input executable programs that are not self-modifying and whose data and instructions can be separated because the operation of the compiler and its settings are known to separate data and instructions, it is possible to design a decompiler that will translate the input executable, program into an intermediate code representation that may subsequently be compiled to run on a processor supporting a different instruction sets. Cobol load modules compiled using a Cobol compiler are an example of such programs. To preserve interoperability with other programs that externally reference addresses of the original program, a load module compiler generates an index of addresses that may potentially be referenced from outside the program, and generates as output object code that incorporates 32-bit index locations in lieu of 64-bit addresses for those addresses. In operation, the runtime environment provides an exception handler to translate 32-bit index locations into the correct 64-bit memory addresses for the indexed locations. In one embodiment, the intermediate code representation generated by decompiling the load module is a file or set of files structured such that each instruction of the Cobol load module is represented by a macro in accordance with the rules of the C programming language. The intermediate code representation of the load module is then received by a load module compiler, executing in a legacy application environment (LAE) operating on an x86 platform that supports a 64-bit instruction set. A C compiler is invoked by the load module compiler to translate C macro instructions into 64-bit X86 instructions. The load module compiler incorporates the index into the output machine language program so that the external references to memory locations of the executable output program may be resolved. The load module compiler may also invoke a library of operating system functions to translate operating system function calls into native X86 instructions. The load module compiler converts the intermediate code representation into an X86 machine language program that is assembled into an executable file.
A load module compiler can be constructed to implement different instruction format, and to develop a different intermediate code representation before compiling the intermediate expression for the new target platform. Although an x86 target platform is described above, a different hardware architecture, such as ARM or other architecture may be used. Similarly, though a translation to a 64-bit address space is described above, other address spaces could be used. The decompiler of the load module compiler may be written to operate on the load module in multiple passes, or in a single pass. The load module may invoke known Binder APIs to obtain information about the Cobol load module used to decompile the module. Alternatively, information about the Cobol load module used to decompile it may be obtained from the load module itself. Information such as the exit lists, return codes, CSECT information, relocation dictionary entries, or external symbol directory entries may be otherwise be obtained to enable the decompilation of a load module. The load module compiler may include sets of scripted macros to process the intermediate code representation and index by invoking the compiler and library to generate x86 code. Alternatively, a compiled program may perform equivalent functions.
Java is an object-oriented programming language that was developed to facilitate the creation of programs that are targeted to run in a portable runtime environment called the JVM (JVM). While JVMs are written for a variety of different underlying hardware and operating system platforms, the intent of the JVM is to provide a portable runtime so that a Java program written for one runtime environment will operate in another. Due in part to the fact that it has been licensed under public licenses, adoption of Java has been widespread. Programs written in the java programming language for the JVM are compiled into an intermediate representation called Java bytecode, rather than to a native hardware-specific instruction set.
The JVM provides memory management features including the provision of a shared memory heap that is available to all threads running inside the JVM, and a private memory stack, assigned to an individual thread running in the JVM, whose memory is not accessible to programs running outside that thread. The JVM automates memory management processes, in part to simplify programming by removing responsibility for memory management from the Java application programmer. Programs running within a thread inside the JVM can create objects, and rely on the JVM's implicit memory allocation and deallocation processes to create space for such objects inside the stack. The allocation of per-thread stack space within the JVM is typically controlled by operating system settings in a default mode, but may be adjusted by dynamic settings that are set when the JVM is initialized.
The JVM includes a security manager object that may be adapted to defines a security policy for applications running in the JVM. By default, the security policy of the JVM is permissive, but policies may be implemented to identify certain actions to be unsafe, and, in the event of any actions disallowed by the security policy, cause the JVM to throw a SecurityException.
The Java native interface is a programming framework designed to allow programs written in the Java programming language and operating on a JVM to call, and to be called by programs running outside the JVM, that have been compiled to run natively on the host hardware and operating system.
When a Java program calls a native method, the JNI provides the native method with the JNIEnv pointer, and a reference to the calling object or class, and any arguments or parameters required by the callee method. The JNIEnv pointer (210) includes a reference to a function table pointing to the available functions of the JNI interface, and is illustrated in
Typically, the JVM is instantiated and java programs initiated by passing a classpath, class name, and optionally a byte string comprising input parameters to the JVM over the command line, which passes the byte string to the main( ) subroutine of the class.
Application developers desiring to introduce the flexibility offered by Java programming to legacy Cobol systems can rewrite code using object oriented Cobol, a modified version of the Cobol programming language that introduces the ability to define classes, encapsulated objects, methods for classes and objects, inheritance, polymorphism and method overrides, and the ability to create and manage objects and to invoke methods on objects. Through the creation of COBOL proxy objects in the Object Cobol runtime environment, an Object Cobol Program can communicate with a Java Object. Also, objects in Object Cobol that have been compiled so as to inherit java interface classes, can be encapsulated in Java wrapper classes that enable them to be called from Java objects. However, migrating a legacy Cobol application to object oriented Cobol requires rewriting the code to take advantage of the new features, and recompiling the application.
Another approach to introducing Java into a legacy environment is to introduce a server and JVM inside a CICS region, and to allow CICS programs to communicate with Java programs inside the CICS region. A disadvantage of this approach is that it requires rewriting and recompiling the CICS programs to make them aware of the JVM. An additional issue with this approach is that the CICS environment does not permit the export of processes that are not deemed thread safe to JAVA, which further limits the ability to selectively replace older CICS programs with JAVA programs.
It would be desirable to provide an environment that permitted the selective replacement of portions of a legacy application, such as a legacy Cobol application, by replacing individual load modules, or individual executable programs within load modules, with Java programs that could run in a virtual machine, without requiring modification of other executable programs or load modules, either by re-writing or recompiling the legacy code. Such a system would significantly reduce the business risk associated with migrating an application to a new environment, since portions of the original code would be left intact, and would greatly facilitate the introduction of new features and capabilities that can readily be implemented in a newer platform, into a legacy application. In the case of CICS, it would also be advantageous to enable the replacement of older programs that were not written to be thread safe, into the JVM.
The present invention, according to one embodiment, provides a computer implemented method for executing an application comprising a first load module that calls a second load module for a source architecture on a machine having a target architecture, the method comprising:
loading by a loader, a first load module comprising object code that calls a second load module in a LAE;
detecting by the loader that at least one CSECT in the second load module has an associated replacement Java program;
inserting into memory of the LAE by the loader, a CSECT comprising an ENC instruction corresponding to the replacement Java program;
identifying a signature type indicative of the number and structure of input and output parameters of said replacement Java program, and inserting a signature type identifier in said memory area for each said replacement Java program;
provisioning a JVM;
executing, in said LAE, a native program associated with said ENC instruction, wherein said native program: (a) attaches its thread to the JVM; (b) adds a classpath of said replacement Java program and method name to the memory of the JVM; (c) loads a replacement Java program class corresponding to the replacement Java program using the classpath in the JVM; (d) constructs a signature comprising the input parameters structured according to said signature type and memory allocation appropriate to the output parameters of said signature type, in the memory of the JVM; (e) invokes a method of said replacement Java program class corresponding to said method name; and (d) detaches itself from said JVM.
According to further embodiments, which may be combined with the above method and with one another or any other elements of this specification, unless clearly mutually exclusive:
said detecting comprises determining that said second load module has been replaced by inspecting metadata of said second load module;
said detecting comprises performing by said loader, a table lookup to determine whether an individual C SECT within said second load module has been replaced;
said at least one replacement Java program comprises two or more Java programs, and wherein said loader writes a default entry point into said memory of the LAE;
said loader inserts said classpath, and a class name into the memory of the LAE;
said loader inserts said method name into the memory of the LAE;
said native program loads an interface class into the heap of said JVM, wherein private methods of said interface class implement a native interface to said LAE and wherein public methods of the interface class allow access to resources of the LAE by invoking one or more of said private methods;
said replacement Java program class invokes said first load module using a public method of said interface class, which invokes one or more of its private methods to access resources of the LAE;
said replacement Java program class invokes a third load module in the LAE by using a public method of said interface class, which invokes one or more of its private methods to access resources of the LAE;
said native program invokes a method of the interface class to overwrite one or more memory areas of the JVM;
said private methods of said interface class include functions to write to an LAE log, initiate program calls to programs executing in the LAE obtain or release storage, open or close files, set or clear error settings, put or get strings, bytes, or other data to or from files in the LAE data types, converting from zoned to packed, packet to zoned, integer to zoned, integer to packed, packed to integer, packed to zoned, ASCII to EBCDIC, EBCDIC to ASCII data formats;
said provisioning further comprises setting a lock on the JVM start instruction, determining by said native program that the JVM is not executing, initiating an LAE resource manager, and, subsequent to loading said interface class, releasing the lock;
provisioning the JVM comprises performing a first attempt at executing, determining that a memory error has occurred, then performing a second attempt at executing;
performing the first attempt at executing comprises using a first memory protection key and performing a second attempt at executing comprises using a second memory protection key;
performing the first attempt at executing comprises using a first address and performing a second attempt at executing comprises using a second address;
upon completion of the executing step, detecting an uncaught exception and terminating without providing an output of the executing step to the LAE;
said target architecture is the same architecture as said source architecture;
said target architecture differs from said source architecture;
the method further comprising configuring a security manager to prohibit halt and exit operations and to prohibit the loading of classes capable of interfacing with the LAE;
said step of configuring a security manager is performed by a method of said interface class;
the LAE comprises a binary interface and wherein, upon completion of execution of the invoked method of said replacement Java class to produce an output, the binary interface invokes a method of an SDM class to format the output according the signature type and returns the output to the LAE;
the native program accesses memory controlled by a protection key established by the LAE.
The present invention, according to a second embodiment, usable independently or in combination with any of the above methods, particularly as a way or provisioning a JVM, provides a computerized method of configuring a JVM to respond to a calling program in a LAE comprising;
setting a lock on the JVM start instruction;
determining by the calling program that the JVM is not executing;
instantiating the JVM;
loading a first class into the heap of said JVM;
wherein private methods of said first class implement a native interface to said LAE and wherein public methods of the first class allow access to resources of the LAE by invoking one or more of said private methods;
activating a security manager configured to prevent the loading of any additional classes that include a native interface to the LAE;
releasing the lock;
attaching a thread operating in said LAE to said JVM;
loading a second class and
invoking a method of said second class by said thread;
wherein said method of said second class provides a memory key and parameters to a public method of said first class to invoke a legacy program in the legacy program environment.
According to further embodiments, which may be combined with the above method and with one another or any other elements of this specification, unless clearly mutually exclusive:
the method further comprising redirecting a standard output of the JVM to a log in the LAE;
the method further comprising setting implicit diagnostic and tracing options of the JVM based on settings associated with the LAE;
the method further comprising setting dynamic settings for the JVM;
the method further comprising initiating a resource manager in the LAE, the resource manage operable to restrict the number of JVMs able to respond to the calling program in the LAE.
According to a third embodiment, usable independently or in combination with any of the above methods, the invention a method for establishing a secure interface between a two runtime environments running on a common operating system, the method comprising:
initiating execution in a first thread in a first runtime environment, of a first program comprising instructions for loading and executing a second program in a thread within a second runtime system;
storing in memory allocated to said second runtime environment, a first class comprising a library of native interface routines for communicating with said first runtime environment; said library contained within a set of private methods of said first class, wherein said first class further comprises a set of public methods for providing access to resources of the first runtime environment;
activating in said second runtime environment, a security manager routine configured to prohibit loading of further code capable of accessing said first runtime environment without invoking said first class;
associating said first thread with said second runtime environment;
loading by said first program, a second program in said second runtime environment;
accessing by said second program, resources in said first runtime environment, wherein said accessing comprises invoking a public method of said first class.
According to further embodiments, which may be combined with the above method and with one another or any other elements of this specification, unless clearly mutually exclusive:
said first runtime environment is a LAE;
said first runtime environment is a LAE adapted to support the execution of load modules comprising object code compiled for execution in a first instruction architecture on a platform of a second instruction architecture;
the second runtime system is a JVM;
said associating comprises invoking an AttachCurrentThread( ) routine by said first program.
According to a fourth embodiment, the invention further provides a system comprising hardware, an operating system, and a LAE, the system operable to execute a first program compiled for a different hardware, operating system, or environment and to perform any of the above methods.
For a more complete understanding of embodiments of the present invention and its features and advantages, reference is now made to the following description, taken in conjunction with the accompanying drawings, in which:
The present invention, in at least one embodiment, provides a binary interface to enable legacy load modules, operating in a legacy environment, to interoperate with methods of Java classes, operating in a JVM, without requiring the rewriting or recompilation of the legacy source code and legacy load modules. In one embodiment, one or more programs, which may constitute one or more entire load modules or one or more individual program object files within a relocatable load module, are replaced with one or more Java methods, without modification of others of the executable program files or load modules that comprise the application. The LAE recognizes the replacement of the one or more executable programs with a corresponding Java method, and constructs a load module that invokes one or more ENC instructions, to invoke the Java method running in a JVM. “ENC” or “execute native command” instructions are instructions of the target platform that, rather than run in emulation, cause the runtime environment to execute a native API to replace the original instruction, call, subroutine, or program.
LAE module (150) that operates as a container for the execution of legacy workloads on a different target platform, aspects of the inventive technique for enabling the selective replacement of legacy programs with Java could also be applied to the legacy environment itself, such as with the execution of program in a LAE running on a legacy platform.
One option for operating a load module compiled to operate in a legacy hardware and operating system environment in a different target environment is an emulation system, as depicted in
An important function of the Memory Management Unit (MMU) is to prevent a process or task from executing instructions in memory that has not been allocated to that process or task. An attempt to execute instructions in memory that has not been allocated results in a hardware fault, which is intercepted by the operating system, often called a segmentation fault (SEGV), which causes generally termination of the process.
In an embodiment implemented on an x86 machine running an instance of the Linux operating system, the MMU of the processor responds to an attempt to access instructions at these lowest addresses that have not been allocated to the user space of the program and causes the Linux operating system to generate a SEGV signal that invokes an exception handler. In one embodiment, the exception handler is configured to access the index of 32-bit addresses, and to translate the 32-bit address into the corresponding 64-bit address used by the executable program. The exception handler may also be configured to perform additional verifications, such as to support protection key management of memory addresses. One may use the exception handler and prefixing schemes described in PCT application PCT/IB2015/059646 titled “Protection Key Management and Prefixing in Virtual Address Space Application.”
In one embodiment, the legacy hardware environment module (330), native
APIs (340), LAE (350), binary JVM interface (355), and JVM (370), are deployed in a Docker container in a cloud-based deployment. The cloud-based deployment may allow for the provision of computer resources, such as server capacity and network storage, required by the system. Preferably, the native APIs (340) are implemented by compiling programs with switchable settings designed to optimize performance depending on whether they are compiled to execute on an x86 platform or ARM platform. In another embodiment, the stack is deployed on an x86 server hardware (310) and running under Linux as the host OS (320).
In the event that that the loader detects at step (410) that the load module is not a conventional load module, the loader checks the program metadata to indicate the replacement type. If the metadata indicates a Java replacement (450), then, in accordance with one embodiment, the loader constructs a new load module (455) within the LAE (350) that is configured to execute one or more ENC instructions. In one embodiment, each ENC instruction includes a parameter indicative of the legacy program that it has replaced, and a separate ENC instruction is used for each such program. In another embodiment, an array of parameters indicates replacement of programs. In constructing the load module, the loader first examines the program metadata, determines whether 24-bit, 31-bit, or 64-bit addressing applies, and establishes appropriate storage to insert the new load module. Then, at step (460), the loader inserts the classpath, class name, method name, and any optional JVM parameters associated with the load module in memory. In the case that multiple ENC instructions are used, a default entry point is set for the first class so that, in the event that the caller does not specify which ENC instruction should be invoked first, the correct sequence of instructions will be followed. Preferably, the classpath is provided as a uniform resource identifier (URI) which enables the class to reside at any address that is accessible to the JVM. Optionally, where a dedicated ENC instruction is assigned to a single program replacement, the classpath, class name, method, and optional JVM parameters will be known to the native API invoked by the ENC instruction, and need not be inserted at step (460) as part of the process of loading the replacement load module. In another embodiment, which is particularly suited to small programs that are frequently called, the replacement Java class is inserted inside the load module, and the classpath parameter indicates the internal location of the class.
Because a Java program that has replaced a legacy program, such as an executable Cobol program in the legacy load module, must be able to receive the parameters that would have been processed by the replaced Cobol program, and must return output in the format that is expected by the calling program, the inventive system examines the structure of the parameters to be passed from and returned to the calling program, and associates the parameters with a signature type. In one embodiment, and in the case of idiosyncratic structures of input and output parameters, the signature type is determined at the time that the replacement program is written. In another embodiment, the structure of the input and output parameters may vary with the caller program or may vary if the caller is an assembler, PL/1, Cobol, Java, or other program, and different signature types may be inserted in accordance with the caller. In another embodiment, rather than employ different signature types in the case of different caller programs or program environments, a different ENC instruction may be employed to handle the need for different input/output data formatting. Whichever the method of identifying the signature type, the loader inserts a signature type identifier into the memory associated with the load module at step (465). In the event that the replacement Java program had a dedicated ENC instruction, the identification of and insertion of the signature may be omitted at step (465) if they are provided as part of the native API invoked by the dedicated ENC instruction. At step (470), if there are more ENC instructions, the loader returns to step (460) for the subsequent class. The loader next passes the parameters of the Java replacement load module at step (430) as shown.
In accordance with one embodiment, the signatures are implemented as enumerated data types that correspond to typical patterns of input and output parameters for legacy applications. A common method for invoking a Java program from outside a virtual machine using the Java Native Interface (JNI) is to provide a single text string to communicate the byte-code input. Legacy program calls that take a single register argument (e.g. register 1) or return a single register value (e.g. register 15) have corresponding signatures. Signatures that presents a byte array of 16 bytes, or of 16 register settings are also common. In the case of CICS program calls, signatures that provide for multiple strings are typical.
In the event that a batch program is replaced, signatures ASV, ASI, SV, and SI will typically be used. The ASV signature is used where the input parameters are passed as a set of separate strings. The ASI signature is used where the input parameters are passed as a set of separate strings, and the return value is expected in register 15 of the legacy hardware for which the load module was originally created. The SV signature is used where the input parameter is passed as a single string. The SI parameter is used where the input parameter is passed as a single string, and the return value is expected in register 15 of the legacy hardware for which the load module was originally created. The ABV signature is used where the input parameter is passed as a byte array. The ABI signature is used where the input parameters are passed as a byte array and the return value is expected in register 15 of the legacy hardware for which the load module was originally created. The AIV signature is used where the input parameters are passed as an array of 16 integers containing the contents of legacy system registers R0 . . . R15 on entry. The All signature is used where the input parameters are passed as an array of 16 integers containing the contents of legacy system registers R0 . . . R15 on entry, and the return value is expected in register 15 of the legacy hardware for which the load module was originally created. The IV signature is used where the input parameter contains the contents of legacy system register R1. The II signature is used where the input parameter contains the contents of legacy system register R1, and the return value is expected in register 15 of the legacy hardware for which the load module was originally created. The PIV signature is used where the input parameters comprise an integer array containing a standard parameter list on entry, for which the last parameter is high order bit terminated. The PII signature is used where the input parameters contain a standard parameter list on entry, the last parameter is high order bit terminated, and the return value is expected in register 15 of the legacy hardware for which the load module was originally created.
Traditionally, when a Cobol application, such as one written in Object Cobol, seeks to invoke a class using the JVM, it does so by instantiating the JVM, and providing the name of the class and a string argument through the command line interface. This arrangement is inflexible because it requires the COBOL application to be rewritten in Object Cobol, and because invoking a class using the public static method void main(String[ ]) limits invocation to the main( ) function of the specified class.
As shown in
In accordance with a preferred embodiment, the LAE SDM class loads the JNI interface, which is preferably implemented by the LAE SDM class as a set of private methods. As such, and in light of the operation of the security manager preventing any other process from separately loading the JNI, access to external programs generally, and to the memory and programs running in the LAE (350) is limited to the LAE SDM class itself. In this way, the LAE SDM class functions as an interface class, by limiting the methods available to Java programs running in the JVM to access resources of the SDM. In order to enable the execution of replacement programs, or to access memory, programs, or other resources of the LAE, the LAE SDM preferably implements public methods or function calls. Examples of such public function calls include calls to open, close, read, or write legacy data sets, to read or write logs, to execute CICS, IMS, Cobol, or PL/1 function, to attach additional threads, read legacy register contents, perform type conversions, activate or deactivate tracing of the legacy environment, manipulate the legacy environment, obtain legacy date/time data, perform legacy wait or post operations, or to obtain legacy system job ID data.
The LAE SDM class redirects the JVM's standard system out and system error calls to the SDM log, or to another SDM data set. The LAE SDM class also instantiates the Java security manager. By default, the JVM security manager is permissive. Before releasing control of the JVM upon completion of initialization, the LAE SDM class configures the security manager to prevent processes from calling halts or exits, and may be configured to restrict access to external functions, legacy data sets, and the ability to load other java classes. The security manager prevents the loading of other classes that could access the LAE (350) other than through the SDM's own methods.
The implementation of the JNI described above differs considerably from a standard JNI because its support of numerous legacy system functions, its methods of converting data formats, and its ability to interact with external memory controlled using protection keys, and to properly respond to legacy system errors in a manner that permits Java program running in the JVM to gracefully react to such errors is not found in a standard JNI.
In one embodiment, the JNI includes functions to write to the LAE log, initiate program calls to programs executing in the LAE obtain or release storage, open or close files, set or clear error settings, put or get strings, bytes, or other data to or from files in the LAE data types such as converting from zoned to packed, packet to zoned, integer to zoned, integer to packed, packed to integer, packed to zoned, . . . recite the functions of the JNI. Exemplary functions included in the JNI include:
private static native void lz_log(int level, String message);
private static native int lz_wto(String message);
private static native int lz_mlwto(String[ ] messages);
private static native int LLE_pgmcall(String epname, int parm, int flags, int dcb, int lang, int type);
private static native int LLE_syncall(int epa, int parm, int flags, int dcb, int lang, int type);
private static native int LLE_storage_obtain(int size, int subpool, int flags);
private static native int LLE_storage_release(int addr, int size, int subpool, int flags);
private static native long lzfopen(String[ ] args, int lrecl);
private static native int lzfclose(long file);
private static native int lzfeof(long file);
private static native int lzferror(long file);
private static native void lzclearerr(long file);
private static native int lzfputs(String str, long file);
private static native int lzputs(String str);
private static native String lzfgets(int len, long file);
private static native String lzgets(int len);
private static native byte[ ] lzfread(int len, long file);
private static native int lzfwrite(byte[ ] v, int len, long file);
private static native byte[ ] convert_zoned_to_packed(int packed_len, byte[ ] zoned, int zoned_len);
private static native byte[ ] convert_packed_to_zoned(int zoned_len, byte[ ] packed, int packed_len);
private static native byte[ ] convert_uint64_to_zoned(int zoned_len, long uint64);
private static native byte[ ] convert_int64_to_zoned(int zoned_len, long int64);
private static native byte[ ] convert_uint64_to_packed(int packed_len, long uint64);
private static native byte[ ] convert_int64_to_packed(int packed_len, long int64);
private static native long convert_zoned_to_uint64(byte[ ] zoned, int len);
private static native long convert_zoned_to_int64(byte[ ] zoned, int len);
private static native long convert_packed_to_uint64(byte[ ] packed, int len);
private static native long convert_packed_to_int64(byte[ ] packed, int len);
private static native int LAE_storage_obtain(int size, int subpool, int flags);
private static native int LAE_storage_release(int addr, int size, int subpool, int flags);
private static native int LAE_rfetch(int r);
private static native void LAE_rstore(int r, int v);
private static native byte LAE_vfetchb(int a);
private static native void LAE_vstoreb(byte v, int a);
private static native short LAE_vfetch2(int a);
private static native void LAE_vstore2(short v, int a);
private static native int LAE_vfetch4(int a);
private static native void LAE_vstore4(int v, int a);
private static native byte[ ] LAE_vfetchc(int a, int l);
private static native void LAE_vstorec(byte[ ] v, int a, int l);
In accordance with one embodiment, functions that perform input or output such as lzfopen, lzfclose, the various string functions, read, write, fetch, and store functions take as arguments the data definition name, string mode, and legacy record format, in addition to data arguments. The string mode setting indicates whether the data is converted from EBCDIC to ASCII format, ASCII to EBCDIC format, and whether to convert between little endian and big endian format, or to leave the data in the format presented by the calling program. Alternatively, separate functions to perform such data format conversions may be separately implemented.
Embodiments of the JNI in accordance with the inventive system may include support for commands such as /TEST MFS, /TES MFS, /TEST MFS NODE LTEM, /FORMAT, /DISPLAY, /START TRAN, /STA PROG, /STA DB, /STOP TRAN, /STO PROG, /STO DB, /DBRECOVERY, /DBR, /RESTART, /END in to enable the invocation of IMS operator commands. The JNI may database calls, such as CLSE, DEQ, GU, GHU, GUR, GN, GHN, GNP, GHNP, DLET, REPL, ISRT, FLD, POS, message calls, such as AUTH, GU, GN, ISRT, CHNG, CMD, GCMD, PURG, SETO, and service calls, such as PCB, CHKP, INIT, INQY, ICAL, GMSG, GSCD, ICMD, RCMD, JAVA, LOG. APSB, DPSB, ROLB, ROLL, ROLS, SETS, SETU, SNAP, STAT, SYNC, TERM, and XRST. Similarly, the JNI may include CICS command interfaces, including, for example, Abend support, APPC basic and mapped conversation calls, authentication, batch data interchange, BMS messaging, BIF, CICS business transaction services, channel commands, console support, diagnostic services (e.g. DUMP TRANSACTION, ENTER TRACENUM), Document services, Environmental Services (e.g. ADDRESS, ADDRESS SET, ASSIGN), Event processing, Exception support, File control services, Interval control services, Journaling, Monitoring, Named counter server commands, Program control commands (e.g. INVOKE APPLICATION, LINK, LOAD, RELEASE, RETURN, XCTL), Scheduling services, Security Services, Storage control (e.g. GETMAIN, FREEMAIN), SYNCPOINT and SYNCPOINT ROLLBACK, Task control services, Terminal control, Transient data, CICS Web support commands, and other CICS command interfaces. Similarly, the JNI may include commands corresponding to VSAM macro instructions, including”
int LAE_vsam_get(VADR rpladdr, REGS *regs);
int LAE_vsam_put(VADR rpladdr, REGS *regs);
int LAE_vsam_point(VADR rpladdr, REGS *regs);
int LAE_vsam_endreq(VADR rpladdr, REGS *regs);
int LAE_vsam_erase(VADR rpladdr, REGS *regs);
int LAE_vsam_idalkadd(VADR rpladdr, REGS *regs);
If the JVM is running, then the thread executing the native API of step (405) attaches itself to the JVM using the AttachCurrentThread( ) call at step (655). Using the AttachCurrentThread( ) call, a native thread can attach itself to the virtual machine and obtain a JNI interface pointer. Once attached, the native thread operates like a regular Java thread running within the native method, and remains attached until it calls DetachCurrentThread( ) to detach itself. Once attached, the native API thread receives the classpath, class name, method, signature type, and parameter list at step (657), and, it adds the classpath to the thread stack (660), and loads the class in step (665). Next, at step (670), the binary interface thread invokes a method of the SDM class to construct the appropriate signature in the thread stack, inserting the received parameters into the objects as required to enable the Java replacement method to access them as the legacy program would have done. Next, at step (675), the binary interface routine invokes the method specified in the input parameter list at step (605).
When the method completes execution, control returns to the native API program, which uses the signature to structure the output in the form required by the calling program.
The method need not be the main( ) routine of the class that implements the Java replacement program, because it is invoked by the LAE routine associated with the thread, rather than invoked from outside the JVM. The method operates on the signature that was constructed in step (670), which contains the parameters passed by the calling program. As discussed above, the calling program may be a load module constructed to run in a legacy platform, a legacy load module that has been adapted to run in the LAE (350), but has not been compiled or otherwise made aware that the callee program has been replaced with a Java program. The calling program may also be another Java program running in the JVM. Upon completion of execution of the method, control returns to the LAE native API, which uses the signature created in step 670 to format the output for the return. (670).
The Java programs running inside the JVM communicate with the LAE (350) by invoking methods in the SDM class. These communications may involve memory accesses, which are controlled by the protection key mechanism described in WO2017/103,651, which may produce memory exceptions when the wrong storage key is used, or other memory access error occurs. Similarly, the communications may involve accessing a legacy program, which may produce any of a myriad of exceptions that may arise due to program loading or execution. The binary JVM interface communicates exceptions arise in the LAE (350) to the Java program, as they may be handled by the Java program as part of its ordinary operation. Upon completion of execution by the Java program, control returns to the LAE thread, which uses the signature to structure output in the manner required by the calling program. Then, at step (677), the LAE detaches the thread from the JVM. In the case of exceptions not caught by the Java program, the exceptions may be thrown by the JVM's exception handler at step (679) producing an ABEND. OC4 memory exceptions are translated into Java illegal access exceptions. OC7 packed decimal error exceptions are translated to Java number format exceptions. Divide by zero exceptions are translated to Java arithmetic exceptions. Generic exceptions, such as would arise if a thread cannot be started, are translated into illegal state exceptions. Upon completion of execution of the called method, the binary interface invokes a method of the SDM class at step (680) to format output in accordance with the signature type, including making any required ASCII to EBCDIC and endianness conversions and, the SDM invokes private methods implementing the JNI, to return output to the calling program in the LAE.
In the case of a CICS application, the LAE (350) schedules both thread safe and non-thread safe jobs for execution. This is because, in legacy CICS, all applications ran in the same thread and were inherently serialized. When CICS began to allow multi-threaded operations, some programs were written to be thread safe, while others were not. Traditionally, CICS cannot permit a Java call to a process that is not thread safe. In the case of CICS programs that are not thread safe, the LAE (350), rather than the CICS application, sets a lock to ensure that two CICS applications that are not thread safe do not cause illegal resource contention or deadlocks. Because the scheduling of the CICS applications is controlled within the LAE (350), a CICS load module, or CICS program within a load module that is not thread safe may be replaced by a Java program, without risk that the operation of the replacement Java program inside the JVM will produce erroneous behavior.
As shown in
Though not shown, a load module may be replaced my more than one Python, Rexx, or C-Shell or other script. In that case, the system will create one load module that includes multiple ENC instructions, and will iterate through the process of inserting the corresponding parameters in memory assigned to the execution of the corresponding script. As in the Java example, the system will either assign a default entry point of sequence the ENC instructions to establish an implicit default entry point for the replacement programs. In some embodiments, the same ENC instruction may be used to implement multiple Python, Rexx, or C-shell/tsch scripts, and provided an identifier as an argument to associate the corresponding script with the instruction. If the loader exhausts the available options, and does not find a corresponding replacement program for the load module, the loader returns and exception.
The computer readable instructions for implementing the LAE (350), the binary JVM interface (JVM), the legacy hardware environment module (330), the native APIs (340), the Java classes (380), the JVM (370), or other aspects of the present invention, may be loaded from a hard disk drive, flexible disk, flash memory, optical disk, tape library, or other non-volatile storage medium, or from multiple such storage media. The hardware platform (310) or other hardware platform may comprise an x86, ARM, or other platform, and the host OS (320) may comprise Linux, Unix, Windows, or other operating system. The system may be deployed on a single computer, a network of computers, a networked cluster of computers, in a cloud deployment, or in a microservices deployment involving Docker or other container technology.
Instructions for carrying out the inventive system and methods described herein may be computer assembler instructions, instructions for a specific hardware architecture, machine instructions, microcode, firmware, and source or object code. Processes described in diagrams and flowcharts of this patent may be implemented by computer program instructions executing on computer hardware, which may be a general purpose computer, special purpose computer, or other data processing machine adapted to execute such instructions. The blocks or steps depicted in the diagrams may be implemented by program segments, functions, or modules. Program modules may include routines, program code, logic, data structures, objects, components that perform tasks when executing on various platforms.
The Java classes and objects discussed above may be implemented in other arrangements of classes, and may incorporate sub-classes and sub-sub-classes to inherit or override properties to achieve the behavior described herein. The discussion of Java objects in the JVM is understood to refer to computer program instructions and data, and references to the public or private methods of such objects or classes are understood to refer to portions of computer instructions in an object that execute in a computer platform, such as the JVM running on a computer platform. Other programming languages and virtual machine environments could be used with the inventive systems and methods described herein.
Elements may be installed on different platforms. which may be the exemplary x86 platform on which the systems described herein are installed. Alternatively the various parts of the system may distributed on multiple platforms within the spirit of the invention described herein. Where more than one platform is used to install and operate the different parts of the inventive system, the different platforms are coupled by way of a computer network, which may include wired, optical, or wireless local area networks, wide area networks, satellite or other telecommunications networks. In operation, the system may be loaded from a non-volatile storage medium such as a disk, tape, hard drive, flash memory drive, or other non-volatile storage medium, such that portions or all of the components of the system are simultaneously resident in the volatile memory of the one or more computers that make up the system.
Many examples are provided herein. These examples may be modified without departing from the spirit of the present invention. The examples and embodiments described herein are offered as examples, and other components, routines, or modules may also be used.
Filing Document | Filing Date | Country | Kind |
---|---|---|---|
PCT/IB2019/059149 | 10/24/2019 | WO | 00 |
Number | Date | Country | |
---|---|---|---|
62755230 | Nov 2018 | US |