forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			291 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
			
		
		
	
	
			291 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			ReStructuredText
		
	
	
	
| =============
 | |
| Type Metadata
 | |
| =============
 | |
| 
 | |
| Type metadata is a mechanism that allows IR modules to co-operatively build
 | |
| pointer sets corresponding to addresses within a given set of globals. LLVM's
 | |
| `control flow integrity`_ implementation uses this metadata to efficiently
 | |
| check (at each call site) that a given address corresponds to either a
 | |
| valid vtable or function pointer for a given class or function type, and its
 | |
| whole-program devirtualization pass uses the metadata to identify potential
 | |
| callees for a given virtual call.
 | |
| 
 | |
| To use the mechanism, a client creates metadata nodes with two elements:
 | |
| 
 | |
| 1. a byte offset into the global (generally zero for functions)
 | |
| 2. a metadata object representing an identifier for the type
 | |
| 
 | |
| These metadata nodes are associated with globals by using global object
 | |
| metadata attachments with the ``!type`` metadata kind.
 | |
| 
 | |
| Each type identifier must exclusively identify either global variables
 | |
| or functions.
 | |
| 
 | |
| .. admonition:: Limitation
 | |
| 
 | |
|   The current implementation only supports attaching metadata to functions on
 | |
|   the x86-32 and x86-64 architectures.
 | |
| 
 | |
| An intrinsic, :ref:`llvm.type.test <type.test>`, is used to test whether a
 | |
| given pointer is associated with a type identifier.
 | |
| 
 | |
| .. _control flow integrity: http://clang.llvm.org/docs/ControlFlowIntegrity.html
 | |
| 
 | |
| Representing Type Information using Type Metadata
 | |
| =================================================
 | |
| 
 | |
| This section describes how Clang represents C++ type information associated with
 | |
| virtual tables using type metadata.
 | |
| 
 | |
| Consider the following inheritance hierarchy:
 | |
| 
 | |
| .. code-block:: c++
 | |
| 
 | |
|   struct A {
 | |
|     virtual void f();
 | |
|   };
 | |
| 
 | |
|   struct B : A {
 | |
|     virtual void f();
 | |
|     virtual void g();
 | |
|   };
 | |
| 
 | |
|   struct C {
 | |
|     virtual void h();
 | |
|   };
 | |
| 
 | |
|   struct D : A, C {
 | |
|     virtual void f();
 | |
|     virtual void h();
 | |
|   };
 | |
| 
 | |
| The virtual table objects for A, B, C and D look like this (under the Itanium ABI):
 | |
| 
 | |
| .. csv-table:: Virtual Table Layout for A, B, C, D
 | |
|   :header: Class, 0, 1, 2, 3, 4, 5, 6
 | |
| 
 | |
|   A, A::offset-to-top, &A::rtti, &A::f
 | |
|   B, B::offset-to-top, &B::rtti, &B::f, &B::g
 | |
|   C, C::offset-to-top, &C::rtti, &C::h
 | |
|   D, D::offset-to-top, &D::rtti, &D::f, &D::h, D::offset-to-top, &D::rtti, thunk for &D::h
 | |
| 
 | |
| When an object of type A is constructed, the address of ``&A::f`` in A's
 | |
| virtual table object is stored in the object's vtable pointer.  In ABI parlance
 | |
| this address is known as an `address point`_. Similarly, when an object of type
 | |
| B is constructed, the address of ``&B::f`` is stored in the vtable pointer. In
 | |
| this way, the vtable in B's virtual table object is compatible with A's vtable.
 | |
| 
 | |
| D is a little more complicated, due to the use of multiple inheritance. Its
 | |
| virtual table object contains two vtables, one compatible with A's vtable and
 | |
| the other compatible with C's vtable. Objects of type D contain two virtual
 | |
| pointers, one belonging to the A subobject and containing the address of
 | |
| the vtable compatible with A's vtable, and the other belonging to the C
 | |
| subobject and containing the address of the vtable compatible with C's vtable.
 | |
| 
 | |
| The full set of compatibility information for the above class hierarchy is
 | |
| shown below. The following table shows the name of a class, the offset of an
 | |
| address point within that class's vtable and the name of one of the classes
 | |
| with which that address point is compatible.
 | |
| 
 | |
| .. csv-table:: Type Offsets for A, B, C, D
 | |
|   :header: VTable for, Offset, Compatible Class
 | |
| 
 | |
|   A, 16, A
 | |
|   B, 16, A
 | |
|    ,   , B
 | |
|   C, 16, C
 | |
|   D, 16, A
 | |
|    ,   , D
 | |
|    , 48, C
 | |
| 
 | |
| The next step is to encode this compatibility information into the IR. The way
 | |
| this is done is to create type metadata named after each of the compatible
 | |
| classes, with which we associate each of the compatible address points in
 | |
| each vtable. For example, these type metadata entries encode the compatibility
 | |
| information for the above hierarchy:
 | |
| 
 | |
| ::
 | |
| 
 | |
|   @_ZTV1A = constant [...], !type !0
 | |
|   @_ZTV1B = constant [...], !type !0, !type !1
 | |
|   @_ZTV1C = constant [...], !type !2
 | |
|   @_ZTV1D = constant [...], !type !0, !type !3, !type !4
 | |
| 
 | |
|   !0 = !{i64 16, !"_ZTS1A"}
 | |
|   !1 = !{i64 16, !"_ZTS1B"}
 | |
|   !2 = !{i64 16, !"_ZTS1C"}
 | |
|   !3 = !{i64 16, !"_ZTS1D"}
 | |
|   !4 = !{i64 48, !"_ZTS1C"}
 | |
| 
 | |
| With this type metadata, we can now use the ``llvm.type.test`` intrinsic to
 | |
| test whether a given pointer is compatible with a type identifier. Working
 | |
| backwards, if ``llvm.type.test`` returns true for a particular pointer,
 | |
| we can also statically determine the identities of the virtual functions
 | |
| that a particular virtual call may call. For example, if a program assumes
 | |
| a pointer to be a member of ``!"_ZST1A"``, we know that the address can
 | |
| be only be one of ``_ZTV1A+16``, ``_ZTV1B+16`` or ``_ZTV1D+16`` (i.e. the
 | |
| address points of the vtables of A, B and D respectively). If we then load
 | |
| an address from that pointer, we know that the address can only be one of
 | |
| ``&A::f``, ``&B::f`` or ``&D::f``.
 | |
| 
 | |
| .. _address point: https://itanium-cxx-abi.github.io/cxx-abi/abi.html#vtable-general
 | |
| 
 | |
| Testing Addresses For Type Membership
 | |
| =====================================
 | |
| 
 | |
| If a program tests an address using ``llvm.type.test``, this will cause
 | |
| a link-time optimization pass, ``LowerTypeTests``, to replace calls to this
 | |
| intrinsic with efficient code to perform type member tests. At a high level,
 | |
| the pass will lay out referenced globals in a consecutive memory region in
 | |
| the object file, construct bit vectors that map onto that memory region,
 | |
| and generate code at each of the ``llvm.type.test`` call sites to test
 | |
| pointers against those bit vectors. Because of the layout manipulation, the
 | |
| globals' definitions must be available at LTO time. For more information,
 | |
| see the `control flow integrity design document`_.
 | |
| 
 | |
| A type identifier that identifies functions is transformed into a jump table,
 | |
| which is a block of code consisting of one branch instruction for each
 | |
| of the functions associated with the type identifier that branches to the
 | |
| target function. The pass will redirect any taken function addresses to the
 | |
| corresponding jump table entry. In the object file's symbol table, the jump
 | |
| table entries take the identities of the original functions, so that addresses
 | |
| taken outside the module will pass any verification done inside the module.
 | |
| 
 | |
| Jump tables may call external functions, so their definitions need not
 | |
| be available at LTO time. Note that if an externally defined function is
 | |
| associated with a type identifier, there is no guarantee that its identity
 | |
| within the module will be the same as its identity outside of the module,
 | |
| as the former will be the jump table entry if a jump table is necessary.
 | |
| 
 | |
| The `GlobalLayoutBuilder`_ class is responsible for laying out the globals
 | |
| efficiently to minimize the sizes of the underlying bitsets.
 | |
| 
 | |
| .. _control flow integrity design document: http://clang.llvm.org/docs/ControlFlowIntegrityDesign.html
 | |
| 
 | |
| :Example:
 | |
| 
 | |
| ::
 | |
| 
 | |
|     target datalayout = "e-p:32:32"
 | |
| 
 | |
|     @a = internal global i32 0, !type !0
 | |
|     @b = internal global i32 0, !type !0, !type !1
 | |
|     @c = internal global i32 0, !type !1
 | |
|     @d = internal global [2 x i32] [i32 0, i32 0], !type !2
 | |
| 
 | |
|     define void @e() !type !3 {
 | |
|       ret void
 | |
|     }
 | |
| 
 | |
|     define void @f() {
 | |
|       ret void
 | |
|     }
 | |
| 
 | |
|     declare void @g() !type !3
 | |
| 
 | |
|     !0 = !{i32 0, !"typeid1"}
 | |
|     !1 = !{i32 0, !"typeid2"}
 | |
|     !2 = !{i32 4, !"typeid2"}
 | |
|     !3 = !{i32 0, !"typeid3"}
 | |
| 
 | |
|     declare i1 @llvm.type.test(i8* %ptr, metadata %typeid) nounwind readnone
 | |
| 
 | |
|     define i1 @foo(i32* %p) {
 | |
|       %pi8 = bitcast i32* %p to i8*
 | |
|       %x = call i1 @llvm.type.test(i8* %pi8, metadata !"typeid1")
 | |
|       ret i1 %x
 | |
|     }
 | |
| 
 | |
|     define i1 @bar(i32* %p) {
 | |
|       %pi8 = bitcast i32* %p to i8*
 | |
|       %x = call i1 @llvm.type.test(i8* %pi8, metadata !"typeid2")
 | |
|       ret i1 %x
 | |
|     }
 | |
| 
 | |
|     define i1 @baz(void ()* %p) {
 | |
|       %pi8 = bitcast void ()* %p to i8*
 | |
|       %x = call i1 @llvm.type.test(i8* %pi8, metadata !"typeid3")
 | |
|       ret i1 %x
 | |
|     }
 | |
| 
 | |
|     define void @main() {
 | |
|       %a1 = call i1 @foo(i32* @a) ; returns 1
 | |
|       %b1 = call i1 @foo(i32* @b) ; returns 1
 | |
|       %c1 = call i1 @foo(i32* @c) ; returns 0
 | |
|       %a2 = call i1 @bar(i32* @a) ; returns 0
 | |
|       %b2 = call i1 @bar(i32* @b) ; returns 1
 | |
|       %c2 = call i1 @bar(i32* @c) ; returns 1
 | |
|       %d02 = call i1 @bar(i32* getelementptr ([2 x i32]* @d, i32 0, i32 0)) ; returns 0
 | |
|       %d12 = call i1 @bar(i32* getelementptr ([2 x i32]* @d, i32 0, i32 1)) ; returns 1
 | |
|       %e = call i1 @baz(void ()* @e) ; returns 1
 | |
|       %f = call i1 @baz(void ()* @f) ; returns 0
 | |
|       %g = call i1 @baz(void ()* @g) ; returns 1
 | |
|       ret void
 | |
|     }
 | |
| 
 | |
| .. _GlobalLayoutBuilder: https://github.com/llvm/llvm-project/blob/master/llvm/include/llvm/Transforms/IPO/LowerTypeTests.h
 | |
| 
 | |
| ``!vcall_visibility`` Metadata
 | |
| ==============================
 | |
| 
 | |
| In order to allow removing unused function pointers from vtables, we need to
 | |
| know whether every virtual call which could use it is known to the compiler, or
 | |
| whether another translation unit could introduce more calls through the vtable.
 | |
| This is not the same as the linkage of the vtable, because call sites could be
 | |
| using a pointer of a more widely-visible base class. For example, consider this
 | |
| code:
 | |
| 
 | |
| .. code-block:: c++
 | |
| 
 | |
|   __attribute__((visibility("default")))
 | |
|   struct A {
 | |
|     virtual void f();
 | |
|   };
 | |
| 
 | |
|   __attribute__((visibility("hidden")))
 | |
|   struct B : A {
 | |
|     virtual void f();
 | |
|   };
 | |
| 
 | |
| With LTO, we know that all code which can see the declaration of ``B`` is
 | |
| visible to us. However, a pointer to a ``B`` could be cast to ``A*`` and passed
 | |
| to another linkage unit, which could then call ``f`` on it. This call would
 | |
| load from the vtable for ``B`` (using the object pointer), and then call
 | |
| ``B::f``. This means we can't remove the function pointer from ``B``'s vtable,
 | |
| or the implementation of ``B::f``. However, if we can see all code which knows
 | |
| about any dynamic base class (which would be the case if ``B`` only inherited
 | |
| from classes with hidden visibility), then this optimisation would be valid.
 | |
| 
 | |
| This concept is represented in IR by the ``!vcall_visibility`` metadata
 | |
| attached to vtable objects, with the following values:
 | |
| 
 | |
| .. list-table::
 | |
|    :header-rows: 1
 | |
|    :widths: 10 90
 | |
| 
 | |
|    * - Value
 | |
|      - Behavior
 | |
| 
 | |
|    * - 0 (or omitted)
 | |
|      - **Public**
 | |
|            Virtual function calls using this vtable could be made from external
 | |
|            code.
 | |
| 
 | |
|    * - 1
 | |
|      - **Linkage Unit**
 | |
|            All virtual function calls which might use this vtable are in the
 | |
|            current LTO unit, meaning they will be in the current module once
 | |
|            LTO linking has been performed.
 | |
| 
 | |
|    * - 2
 | |
|      - **Translation Unit**
 | |
|            All virtual function calls which might use this vtable are in the
 | |
|            current module.
 | |
| 
 | |
| In addition, all function pointer loads from a vtable marked with the
 | |
| ``!vcall_visibility`` metadata (with a non-zero value) must be done using the
 | |
| :ref:`llvm.type.checked.load <type.checked.load>` intrinsic, so that virtual
 | |
| calls sites can be correlated with the vtables which they might load from.
 | |
| Other parts of the vtable (RTTI, offset-to-top, ...) can still be accessed with
 | |
| normal loads.
 |