Type-safe linking and modular assembly language

Linking is a low-level task that is usually vaguely specified, if at all, by language definitions. However, the security of web browsers and other extensible systems depends crucially upon a set of checks that must be performed at link time. Building upon the simple, but elegant ideas of Cardelli, and module constructs from high-level languages, we present a formal model of typed object files and a set of inference rules that are sufficient to guarantee that type safety is preserved by the linking process.Whereas Cardelli's link calculus is built on top of the simply-typed lambda calculus, our object files are based upon typed assembly language so that we may model important low-level implementation issues. Furthermore, unlike Cardelli, we provide support for abstract types and higher-order type constructors-features critical for building extensible systems or modern programming languages such as ML.


Introduction
Linking separately compiled program units is an important task that is typically omitted from language definitions.In large part, this omission is due to low-level architecture and compiler dependencies that seem outside the realm of language design.However, extensible systems [LY97, WLAG93, BSP + 95, Nec97, Koz98, HCC + 98] are being built based upon the strong safety guarantees that language definitions provide.These systems use linking and loading as a fundamental part of their operation, so it is critical to precisely define the consistency checks a linker, be it static or dynamic, must perform.
For example, Java-extensible web browsers, such as Netscape's Navigator and Microsoft's Internet Explorer, rely on static type systems with link checks to enforce a wide class of important safety properties.Extensions (applets) are written in a high-level language such as Java, and then compiled by an untrusted compiler to a target language (Java Virtual Machine bytecode).The integrity of the browser does not depend on type soundness of Java, but rather on type soundness (among other properties) of the JVM.As the JVM supports dynamic linking and loading of applets, a critical component of the JVM definition is the description of well-formed compilation units and link compatibility.Unfortunately, this component is vaguely specified and has been a source of well publicised security holes [MF96,Sar97].
Recently, Cardelli proposed a calculus of compilation units for the simply-typed lambda-calculus and presented a set of rules for determining link compatibility [Car97].Cardelli's work specified high-level abstractions for modules and interfaces, and provided a set of inference rules for determining that program fragments, when compiled under certain typing assumptions, met a set of consistency requirements necessary to ensure that the resulting program was well-formed and hence would not "go wrong" when evaluated.
In previous work, we presented a Typed Assembly Language (TAL) [MWCG98] that was suitable for compiling high-level core languages, and it seemed natural to extend this work with the ideas of Cardelli to give a detailed treatment of type-safe linking.However, though Cardelli's calculus is an elegant formulation of some of the high-level issues involved in linking, we found that it abstracts from many important low-level details such as binding and alpha-variance of labels, and omits certain critical features, notably support for cyclic inter-object file references, user-defined type abstraction, and dynamic linking.Hence, the goal of this paper is to build upon Cardelli's work and provide a suitable treatment of these issues.In particular, we extend core TAL with a language of typed object files and formalise the concepts of linking and link compatibility.The goals of the design were to model important properties of conventional object files and linkers (e.g., Unix's ld or Win32's link), and provide a module structure that supports separate type-checking of object files and separate compilation of high-level language features such as the abstract types, signatures, structures, and functors of SML.
Our design for typed object files borrows heavily from the previous work on modules for high-level languages and hence there are relatively few important innovations.However, we believe this to be a virtue as it demonstrates that the programming language community has identified most of the critical issues for any module language, and it allows us to concentrate on those issues specific to object files.
We have implemented the resulting design as part of an on-going language-based security project.The implementation provides a set of tools for defining and typechecking a variant of IA32 (Intel 80x86) object files and interfaces.These tools, together with a compiler that maps a variant of type-safe C code to type-safe object files is available at http://www.cs.cornell.edu/home/walker/talnet/tal.html#talc.
We proceed as follows: In Section 2, we present the abstractions of conventional untyped object files and linkers (e.g., Unix's ld) and discuss the issues of link compatibility in this simplified setting.In Section 3, we briefly review the type system of TAL and introduce a simple module language MTAL0, which, in the spirit of Cardelli, provides support for separate compilation, separate type-checking, and a stronger notion of link compatibility.We extend MTAL0 in Section 4 with support for abstract types in the style of Clu or Modula-2, higher-order type constructors in the style of Objective Caml, and translucent types in the style of Harper and Lillibridge [HL94] and Leroy [Ler94].The resulting language, MTAL (pronounced metal), is sufficiently expressive that we may compile ML-style modules, including functors, to the target language.Finally, in Section 5 we discuss extending our model to include dynamic linking and dynamic loading.

Untyped Object Files and Linkers
We begin with a model of typical untyped object files and the process of linking.

Untyped Assembly Language
Figure 1 shows a simple, operational model an of untyped assembly language.We model execution as a one-step reduction relation between program states.In our model, the state of an assembly language program consists of three components: a heap representing memory, a register file, and an instruction sequence representing the program counter.A heap, H, is a finite mapping of labels (symbolic addresses), , to heap values, h.There are two types of heap values: code sequences, code I, where I is an instruction sequence, and tuples, v1, . . ., vn , where the vi are small values such as integers or labels.Instructions include typical RISC instructions (e.g., add rd, rs, v) and one special instruction, malloc r[n].This instruction allocates in the heap a new tuple with n uninitialised entries and places the new label in r.Small values are the word sized objects of the machine, that is, objects that fit in a register.We also consider registers to be small values to simplify the syntax of operands.1A register file is a finite map from registers, r, to small values.The reduction relation simulates the execution of one instruction, such as: Here, the initial program state includes a register file that maps r2 to the value 6, and the program counter points to an instruction sequence where the first instruction is intended to add 3 to the contents of register r2, placing the resulting value in r1.Hence, the reduction relation takes us to a program state with the same heap, an updated register file that maps r1 to the value 9, and an updated program counter that points to the next instruction in the sequence to be executed.

Object Files
Abstractly, an object file consists of three components: 1.A heap H.
2. An import set I: a set of labels not defined in the heap of the object file, but possibly referenced by terms in the heap.
3. An export set E: a subset of the labels defined in the heap.
While this description of object files is generic, as heaps could map labels to the terms of any language, in order to provide specific examples, we will use the untyped assembly language of the previous section.
We use [I ⇒ H : E] to denote an object file and give the well-formedness conditions with the following inference rule, where FL(e) denotes the set of free labels occurring in a term e: The set of labels that are defined in the heap but not in the export set are said to be local labels, as the scope of these labels is the object file only.Following standard convention for fixed-scope identifiers, we consider object files to be equivalent up to a systematic renaming (alpha-conversion) of local labels.The justification for this implicit alpha-conversion is that real object files represent local labels as relative offsets from the base address of the object file.This base address is adjusted during the linking and/or loading process to place object files in different address ranges and hence the local labels are implicitly adjusted, in a fashion similar to DeBruijin indices.In contrast, exported labels do not alpha-vary so that the linker may resolve cross references among object files.

Linking Untyped Object Files
Linking is the process of taking two (or more) object files and combining their heaps, import sets, and export sets in a suitable fashion to produce a new object file.However, even if the input object files are well-formed, the output may not be, hence, the notion of link compatibility.When two object files are link compatible, we may link them to produce a new object file as follows: Because of the condition dom(H1) ∩ dom(H2) = ∅ (technically a side condition), in applying the link rule, alphavariants of the object files must be chosen such that their local labels are disjoint.It follows from the definitions that if O1 link O2 O then O (i .e., the resulting object file is well-formed.)

Static Executables
The final operation the linker performs is to produce an executable.An executable is just a closed heap paired with an entry label defined in that heap, denoted (H, ).Thus, an executable is well-formed according to the following rule: Given a well-formed object file and a distinguished label from its export set, we may produce a well-formed executable only when the import set of the object file is empty: To load and run an executable, the operating system creates a new process with the heap as its initial memory image and jumps to the entry label passing in some parameters.2Hence, ignoring the parameters, an executable is mapped to an initial program state by taking the heap of the executable, an empty register file, and a single-instruction sequence that jumps to the code bound to the entry label of the executable: exec (H, ∅, jmp )

MTAL0
The goal of this paper is to formalise typed object files combining the development in Section 2 with Cardelli's high level, typed linking ideas [Car97].As a step towards this goal, this section defines MTAL0, a simple, typed object file calculus; the next section will extend MTAL0 to our full calculus by adding (higher order) type abstraction.While our module calculus is independent of the core calculus, MTAL0 is based on TAL [MWCG97, MCGW98] for concreteness.In the following sections, we briefly review TAL and the benefits of type safety, and then build a notion of typed object files on top of TAL.

TAL
TAL is essentially a typed version of the assembly language of Section 2.1.Its types include integers, tuples, and code types.TAL also includes some "typing" instructions that have no operational effect but make type checking easier (see [MWCG98] for details.)The allocation instruction malloc r[τ1, . . ., τn] includes the types of the entries of the tuple to be allocated.It assigns r the tuple type τ 0 1 , . . ., τ 0 n where the 0s indicate possibly uninitialised slots.Storing into the first slot would give r the type τ 1 1 , τ 0 2 , . . ., τ 0 n where 1 indicates a definitely initialised slot.A slot may be loaded only if it is definitely initialised.
A register file type Γ = {r1:τ1, . . ., rn:τn} states that ri contains a value of type τi and is used both to typecheck individual instructions and to assign a type to instruction sequences.In general, TAL code sequences have the form code[α1, . . ., αn]Γ.I where the code is polymorphic over the type variables α1, . . ., αn, and I is a typed instruction sequence whose first instruction expects the registers to have register file type Γ.A label mapped to this code sequence is assigned the type ∀[α1, . . ., αn]Γ.When the code is monomorphic, we abbreviate it as codeΓ.I and abbreviate its label's type as simply Γ.
An increment function might be written in TAL as: inc → code{r1:int, ra:{r1:int}}.addr1, r1, 1; jmp ra To call inc, the caller must place an integer in r1 and a return label in ra, the return label must accept an integer in r1.Thus, the label inc is assigned the type: {r1:int, ra:{r1:int}} Finally, TAL assigns to heaps heap-typings, which are finite maps from labels to types, { 1:τ1, . . ., n:τn}.The judgement Ψ1 H : Ψ2 asserts that H has heap-typing Ψ2 where free labels are typed by Ψ1.TAL heaps are unordered maps and a heap value may refer to its own label directly or indirectly.Thus, TAL's type checker uses the final heap-type during checking of the heap, Ψ H : Ψ, as done in Harper's system for mutable references [Har94].Note that this allows circular references such as { → 1, } and thus extends Cardelli's work, which considered only DAG-like heaps, to general graphs.

Type Safety
The main motivation for static typing is the property that a well typed program never performs an illegal operation.Consider a set of well formed and mutually link compatible object files that together with a label are a complete program.We desire that the link operation is type preserving, so the resulting object file will be well formed.Similarly, we desire that formation and creation of an initial program state are type preserving, so the initial program state resulting from the execution of the linked object file is well formed.TAL's type safety means that the execution starting in this well formed initial state will never perform an illegal operation.Thus if we define typed linking, program formation, and formation of an initial state to be type preserving, then MTAL0 will be type safe.
An extensible system writer may desire other guarantees from MTAL0.For example, if an extension is checked against a fixed import interface, the MTAL0 type system guarantees that the extension can access only the labels mentioned in that interface.The extensible system can use this fact to ensure that a security monitor interposed between the extension and the underlying system is not circumvented.An overview of the necessary guarantees and security properties of extensible systems is beyond the scope of this paper, but Leroy and Rouaix [LR98] provide a discussion of some of these issues.The intention is that an integer n is passed in register r1 to the entry label main.The main object file calls the other object file's fact label which computes and returns the factorial of its argument.The main object file then halts with n! in register r1.The keywords import and export are used to show the import and export interfaces respectively.In addition to the checks made in untyped object files, the well formedness condition for MTAL0 object files requires type checking: The heap has an actual type ΨA and is checked in the context ΨI ∪ ΨA as it may refer to imported labels or to itself.The heap must define labels different from the imports, that is, ΨA and ΨI must have disjoint domains.The heap must provide the exported labels at the types specified, ΨA ≤ ΨE. 3 A typed object file can be checked in isolation.While it contains type information about labels in other object files, it does not contain any term level information about those labels.Put another way, MTAL0 has a separate type checking property and thus MTAL0 supports separate compilation in the following fashion: If a source-level module can be type checked using only source-level interfaces for other modules, then it can be compiled to a typed object 3 ΨA ≤ ΨE means that ΨA is a subinterface of ΨE , formally, ∀ ∈ dom(ΨE ) : ΨA( ) = ΨE ( ).Note that in a subtype setting, this could be extended to ∀ ∈ dom(ΨE ) : ΨA( ) ≤ ΨE ( ).
file without needing the implementations of the other modules.

Linking
Crucial to typed link compatibility is interface compatibility, Ψ1 ∼ Ψ2.In particular, if two interfaces mention the same label then they must give it compatible types:4 Given interface compatibility, link compatibility is easily defined: The two object files have compatible imports and exports, and the exports must (as before) be disjoint.The link operation is defined in the same way as the untyped link operation but uses typed object files and typed judgements.Again, if O1 link O2 O then O.This theorem is much stronger than in the untyped case as it asserts that no type errors are introduced by a linking operation.
MTAL0 has a separate link-checking property.That is, link compatibility is defined entirely in terms of the imported and exported interfaces of the two modules and is independent of the modules' heaps.A type safe linker will load each object file and type check it separately, then perform the linking, doing checks that involve only the interface information; the code need not be rechecked.

Programs and Execution
A program is a closed TAL heap and a label.The heap must be well formed and the label must have an appropriate type: where τe is the type the entry convention gives the entry label.The factorial example's intended entry convention has τe = ∀[ ]{r1 : int}.The entry convention is an important low-level detail of how programs get executed, which we can formally specify as a MTAL0 type.
As before, we can check when an object file is complete: However, programmers and language designers want to reason about when a collection of object files together forms a complete program.That is, they want a set of checks to ensure that when those object files are linked the result will be a complete program according to the judgement above.Informally, each object file's imports must be contained within the exports of the other object files and the entry label must be exported by one of the object files with type τe.We formalise these checks as a judgement and prove a correctness theorem in appendix A.  The production of an executable and the process of execution is the same as in the untyped calculus.However, the consistency checks are sound: the formation of an executable implies the executable is well formed, and the formation of an initial state implies the initial state is well formed.(TAL program state well formedness and reduction are described in detail in [MWCG97].)

MTAL
MTAL0 is a typed low-level calculus with a formalised notion of link compatibility.It extends the work of Cardelli making important low level concerns explicit, and it very closely models the tasks of real linkers.However, MTAL0 does not address other shortcomings of Cardelli's calculus.We will progressively add constructs to MTAL0 in the following sections to obtain our full calculus MTAL.A complete description of MTAL, including its syntax and static semantics, appears in appendix A and forms the basis of our implementation.

Abstract Types
MTAL0 provides many type safety guarantees but does not provide type abstraction guarantees.5Consider a security monitor for file access that exports an operation open that takes a string and returns a file handle.Suppose further that the file handle pairs the extension's access rights with an operating system file handle, each represented as an integer.In a system without type abstraction, the implementation must expose the representation of the file handle giving open the type string → int, int .Because clients see this type, not a type like string → file, they can ignore the abstraction and use integer operations to directly modify the access rights.Following high-level module designs which address this issue, we add to MTAL the ability to declare abstract types in interfaces and use them in the types given to labels.
A MTAL interface, Int, is a pair (Φ, Ψ) consisting of a type part Φ and a value part Ψ.The type part, also called a type-heap kinding, is a finite map from type-labels to kinds. 6Object files are still a triple [IntI ⇒ (TH , VH ) : IntE] consisting of an import and export interface, but there are now two heaps: one for types and one for values.Type heaps are finite mappings from labels to types.Program states are also extended to include a type heap.
The file example is shown in Figure 3.It exports an abstract type file which is used in the types of the values that it exports.The concrete type of file is a pair of integers, and the example sketches the relevant details of the implementation of the operations.
Definitions of typed object files, link compatibility, linking, executable formation, and execution similar to that in Sections 2 and 3 can be repeated for MTAL; we mention just the highlights.
Just as value heaps can contain cyclic references we also allow cyclic type heap references, introducing the possibility of recursive types.Following standard type theory, in a type heap TH a type label is isomorphic to TH ( ).There are two ways to reflect this isomorphism in the type system.The first way implicitly treats and TH ( ) as equal types.This makes a decision procedure for type equality considerably more complex.We choose the second way and introduce explicit roll and unroll operations that witness the isomorphism ∼ = TH ( ).Roll coerces an object from a concrete type to an abstract type; Unroll does the opposite.
The value heap of an object file is checked using its type heap.Thus, the roll (•) can be used if and only if the type heap defines .An object file implementing will have a definition for in its type heap.A client of the abstract type , however, will import , and since the import interface is disjoint from the type heap, the latter will not contain a definition for .Thus, neither roll nor unroll can be applied in the client code.That is, the client cannot create or directly manipulate members of and thus really is abstract.Consequently, the roll and unroll operations are used not only to mediate recursive types, but also to provide explicit coercions to and from abstract types.
In this respect, our treatment of label types is similar to the "generative" datatypes of SML.Unlike SML, however, our abstract type labels have global scope.This simplifies link consistency and provides a means to split mutually-recursive type definitions across compilation units as with Mixins [DS98,FF98].The price paid, however, is that programmers or compilers must ensure that two compilation units that are to be linked together do not define the same type label.
Our implementation includes two extensions omitted from MTAL.In our implementation of interfaces, a type label may be declared abstract, given a definition, or given a bound.When given a definition, a type label is like a translucent type, as in Harper and Lillibridge [HL94] and Leroy [Ler94].This definition is included along with the type heap of an object file during the type checking of the object file's value heap.When given a bound, a type label is like a partially abstract type.The typing rules allow a bounded type label to be unrolled to its bound but do not allow a roll operation on that type label.This approach is based upon standard type theory on singleton kinds 7 and power kinds [Car88,Car91] respectively.However, as we only support globally-scoped type labels, the setting is greatly simplified because we do not need both internal and external names for types as in Harper and Lillibridge.Again, the price paid is that programmers or compilers must manage the flat name space.
In summary, MTAL chooses to treat type labels as globally scoped identifiers.This simplifies the treatment of separately-compiled recursive types, generative abstract types, and translucent types but at the price of a flat name space.Since traditional linkers only provide a flat name space for value labels, we felt that the symmetry at the type-level, together with the simplification of these language features justified the cost.A promising approach to alleviate the software engineering problems of a flat name space is to provide a means to restrict the scope of a type-or value-label during the linking process, just as the restriction operator of the pi-calculus limits the scope of a channel [Mil91].Operationally, a restriction operator could be implemented by a tool that systematically turned global labels into local labels (i .e., an explicit alpha-conversion.)

Abstract Type Constructors
Good modular programming requires more than just abstract types.For example, there is a large class of container abstractions whose types are parameterised by the types of the objects they contain.For instance, a stack datatype exports an abstract type constructor taking one argument (the type of the elements to be placed in the stack.)To handle such constructors, MTAL's types are extended to a type constructor language and its kinds are extended to include functions and products, resulting in a three tiered system very similar to Fω [Gir71,Gir72].
Figure 4 shows how the stack abstraction might look as a MTAL interface.It declares an abstract type constructor stack$t which takes the element type and returns the type of the stacks.Each of the operations is polymorphic over the element type α and the stack arguments and results have type stack$t α (the application of the stack type constructor to α).An implementation of this interface will have to give a concrete type for stack$t, for example: To deal with this higher-order recursive type, the roll and Note that since we have essentially embedded Fω into our calculus, we can use phase splitting [HMM90,Sha98] to compile functor systems into MTAL.

Dynamic Linking
Modern operating systems and languages provide dynamic linking and dynamic loading.Dynamic linking allows the linker to produce "executables" that contain references to labels that will be resolved at the time the operating system loads the executable into a process's address space.Each executable contains a set of names for dynamically linked libraries, and for each name a set of labels it imports from that library.When the executable is loaded, the operating system searches for appropriate libraries and links them with the executable to form the initial process image.In our model, dynamically linked executables can be represented by normal object files.Indeed, the only difference between the dynamic and static linking in the model is that the final steps of linking and the formation of the "real" executable are delayed until load-time.
Dynamic loading involves linking object files or libraries into the process image during execution.A program might contain references to labels in these dynamically loaded object files.It must ensure that it loads an appropriate object file before using these references.However, it can delay loading until right before use, and if it does not use the references, it need not load the object file.With dynamic loading there is also the possibility of unloading, that is, removing a linked object file from the process image during execution, making references to that object file unusable.
We believe that our model should extend to incorporate dynamic loading.In fact, during the course of this work, we sketched several possible extensions, each resulting from different choices in resolving specific issues.But in contrast to our previous development, none of these extensions seemed to be "the right" model.In particular, dynamic loading introduces new failure modes and many interface choices.For example, we could make it the responsibility of an executable to explicitly load definitions for labels before they may be dereferenced.Failure can then be isolated to points where dynamic loading explicitly occurs.Alternatively, as in Java, we could support implicit loading upon reference to an undefined label.Failure in this model can potentially occur at any label dereference.
An important technical issue with dynamic loading is that we must extend our evaluation relation to support execution on program states with unresolved labels.Type or kind information for those labels must be maintained at run-time in order to ensure consistency when dynamic loading is performed.This begs the question of exactly how much type and interface information must be retained and whether it is under program control or operating system control.The presence of this information enables further possibilities, particularly introspection or reflection: the ability of a program to query what labels are defined and at what types.

Related Work and Implementation
Our work is closely related to Cardelli's [Car97] and builds on the type theory of high level modules including work by Leroy [Ler94] and Harper and Lillibridge [HL94].More recently, Flatt and Felleisen have proposed a new advanced module system [FF98].Their system includes a first class notion of modules called units.Units can import and export named types and values.The named types and values of one unit can be connected to the named types and values of other units.Units can be abstracted over and linking is a first class primitive.MTAL is similar but describes what operating systems provide at the low level, whereas Flatt and Felleisen concentrate on source level module systems.Dean has investigated the dynamic linking and loading aspect of Java; his work focuses on the class loader and how its operation interacts with static typing [Dea97].His work is a very abstract description of this interaction and does not describe actual linking and link compatibility.
Our work is also related to the security of extensible systems.We formalise the checks necessary for linking, but do not address the orthogonal security concerns such as determining what interfaces principals may link against or authenticating principals.Other systems, such as the SPIN project [SFPB96], have addressed these concerns and their ideas could be combined with MTAL's.
We have implemented the ideas presented in this paper in our compiler as part of a language called Talx86.Talx86, based on the instruction set of IA32 (Intel's 80x86 architecture), scales the ideas of TAL up to handle real languages, and uses the ideas of MTAL for its module language.As well as TAL's tuples and code, Talx86 includes sums, arrays, recursive types, exceptions, abstract types, translucent types, subtyping and bounded type labels.We have a type checker for Talx86 object files, a link compatibility and program completeness checker, and two front ends for toy languages meant to demonstrate the viability of Talx86 as a target language.We are also working on a front end for KML [Cra98], a variant of ML with higher order modules, full polymorphism, and subtyping among other features.

Conclusion
This paper presents MTAL a calculus that formalises a low level notion of linking similar to the linker tools of modern operating systems.MTAL extends the earlier work of Cardelli [Car97], providing a better explanation of intermodule references, handling cyclic dependencies between modules, supporting abstract type constructors and the compilation of phase-splitable functor systems, and modeling dynamic linking and loading.MTAL unifies previous work on typed assembly languages [MWCG98, MWCG97,MCGW98] with previous work on the type theory of modules [Ler94,HL94,FF98].
Our account is a straightforward combination of the previous work of Cardelli and on typed assembly languages.The contribution of our paper is a complete account of typesafe linking for a realistic, low-level language.

A MTAL
This appendix gives a full technical description of our calculus MTAL as well as statements of important theorems.The calculus is fairly independent of the core language and does not have to be used at the assembly language level.The presentation style follows closely that of previous TAL papers [MWCG97].
The syntax for MTAL appears in Figure 5.The core language constructs fit in the ellipses of c, h, w, and I.The α in λα:κ.cbinds α in c.Type constructor heap typings, value heap typings, register file typings, type constructor heaps, value heaps, and register files are considered unordered finite maps.The notation dom(M ) is the domain of the finite mapping M .All constructs are equivalent up to alpha conversion and reordering of unordered maps.The capture avoiding substitution of z for y in x is denoted x[y := z].
The judgements for the static semantics are summarised in Figure 6, and the typing rules appear in Figure 7.The rules for type constructor kinding are standard except for the rule for type labels: The rules for type constructor equality are the usual reflexive, transitive, and congruence rules, and rules for the core calculus.The rules for heap values, word values (except for roll and unroll), and instructions are given by the core calculus.The rules for the judgements for type constructor and value heap typings are similar as they are both similar to record types.A heap typing is well formed when the labels defined are disjoint, and kinds/types are well formed.Two heap typings are equal when they define the same set of labels, and, map a given label to equal kinds/types.A heap typing is a heap subtype of another heap typing when it defines at least the labels of the later, the common labels are mapped to equal kinds/types, and the extra labels are mapped to well formed kinds/types.Two heap typings are disjoint when they define disjoint sets of labels.Two heap typings are compatible when they are both well formed, and labels common to both are mapped to equal kinds/types.Interface well formedness, equality, subinterfacing, disjointness, and compatibility are defined pairwise in terms of the same judgements on type constructor heap and value heap typings.
The rules for roll and unroll are the standard ones except they are in terms of type labels rather than recursive types.The rules for type constructor heaps, value heaps, and register files are all similar to the rule for records.The labels must be distinct and the type maps the same labels to the types that correspond to the items.
The interesting typing rules are those for object files, O, executables, E executable, and program states, P state.An object file is well formed if there is an actual interface for the heaps that is disjoint from the import interface and a subinterface of the export interface.The heaps must have types matching the actual interface, but are checked in a context that includes the imports.The value heap is checked in a context that also includes the type constructor heap, as the latter defines abstract types the object file implements.The rules for executables and program states are similar.They require an interface that describes their heaps but this time their are no imports.In the case of executables the entry label must satisfy the entry convention.In the case of program states the register file must also have a type and the instruction sequence must be well formed.
The linking and execution judgements are in Figure 8.The linking and execution operations are specified as type directed translations and include the conditions that specify when a linking operation is valid.M − L is the mapping M with entries for labels in the set L removed The first operation is the linking of two object files: O1 link O2 O. Linking is governed by link compatibility.Two object files are link compatible when their import and export interface pairs are link compatible.The later holds when the imports of one are compatible with the imports of the other and vice versa, the imports of both are compatible, and the exports of both are disjoint.The actual rule for linking looks daunting but is actually straightforward.It requires two well formed and link compatible input object files.The output object file combines the heaps and exports of the input object files and imports what the input object files imported but did not export.Note that the side condition forces the choice of alpha variants of the source heaps that have disjoint domains.This corresponds to the linker relocating source object files.Linking two object files results in a well formed object file.In other words, the operation is type correct: The second operation is the formation of an executable from an object file and an entry label: (O, ) prg E. This operation requires the object file and entry label to be complete, that is, the object file to import nothing and the entry label to exist in the object file's exports at the type required by the entry convention (in this formulation ∀[ ]{}).Executable formation is type correct: The last operation is the execution of an executable: