diff options
Diffstat (limited to 'clang')
-rw-r--r-- | clang/docs/CallGraphSection.rst | 251 | ||||
-rw-r--r-- | clang/include/clang/Basic/CodeGenOptions.def | 2 | ||||
-rw-r--r-- | clang/include/clang/Driver/Options.td | 4 | ||||
-rw-r--r-- | clang/lib/CodeGen/BackendUtil.cpp | 1 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGCall.cpp | 22 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGClass.cpp | 9 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExpr.cpp | 5 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExprCXX.cpp | 34 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGObjCMac.cpp | 8 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenModule.cpp | 34 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenModule.h | 4 | ||||
-rw-r--r-- | clang/lib/Driver/ToolChains/Clang.cpp | 4 | ||||
-rw-r--r-- | clang/test/CodeGen/call-graph-section-1.cpp | 110 | ||||
-rw-r--r-- | clang/test/CodeGen/call-graph-section-2.cpp | 95 | ||||
-rw-r--r-- | clang/test/CodeGen/call-graph-section-3.cpp | 52 | ||||
-rw-r--r-- | clang/test/CodeGen/call-graph-section.c | 85 | ||||
-rw-r--r-- | clang/test/Driver/call-graph-section.c | 5 |
17 files changed, 714 insertions, 11 deletions
diff --git a/clang/docs/CallGraphSection.rst b/clang/docs/CallGraphSection.rst new file mode 100644 index 0000000..d5db507 --- /dev/null +++ b/clang/docs/CallGraphSection.rst @@ -0,0 +1,251 @@ +================== +Call Graph Section +================== + +Introduction +============ + +With ``-fcall-graph-section``, the compiler will create a call graph section +in the object file. It will include type identifiers for indirect calls and +targets. This information can be used to map indirect calls to their receivers +with matching types. A complete and high-precision call graph can be +reconstructed by complementing this information with disassembly +(see ``llvm-objdump --call-graph-info``). + +Semantics +========= + +A coarse-grained, type-agnostic call graph may allow indirect calls to target +any function in the program. This approach ensures completeness since no +indirect call edge is missing. However, it is generally poor in precision +due to having unneeded edges. + +A call graph section provides type identifiers for indirect calls and targets. +This information can be used to restrict the receivers of an indirect target to +indirect calls with matching type. Consequently, the precision for indirect +call edges are improved while maintaining the completeness. + +The ``llvm-objdump`` utility provides a ``--call-graph-info`` option to extract +full call graph information by parsing the content of the call graph section +and disassembling the program for complementary information, e.g., direct +calls. + +Section layout +============== + +A call graph section consists of zero or more call graph entries. +Each entry contains information on a function and its indirect calls. + +An entry of a call graph section has the following layout in the binary: + ++---------------------+-----------------------------------------------------------------------+ +| Element | Content | ++=====================+=======================================================================+ +| FormatVersionNumber | Format version number. | ++---------------------+-----------------------------------------------------------------------+ +| FunctionEntryPc | Function entry address. | ++---------------------+-----------------------------------+-----------------------------------+ +| | A flag whether the function is an | - 0: not an indirect target | +| FunctionKind | indirect target, and if so, | - 1: indirect target, unknown id | +| | whether its type id is known. | - 2: indirect target, known id | ++---------------------+-----------------------------------+-----------------------------------+ +| FunctionTypeId | Type id for the indirect target. Present only when FunctionKind is 2. | ++---------------------+-----------------------------------------------------------------------+ +| CallSiteCount | Number of type id to indirect call site mappings that follow. | ++---------------------+-----------------------------------------------------------------------+ +| CallSiteList | List of type id and indirect call site pc pairs. | ++---------------------+-----------------------------------------------------------------------+ + +Each element in an entry (including each element of the contained lists and +pairs) occupies 64-bit space. + +The format version number is repeated per entry to support concatenation of +call graph sections with different format versions by the linker. + +As of now, the only supported format version is described above and has version +number 0. + +Type identifiers +================ + +The type for an indirect call or target is the function signature. +The mapping from a type to an identifier is an ABI detail. +In the current experimental implementation, an identifier of type T is +computed as follows: + + - Obtain the generalized mangled name for “typeinfo name for T”. + - Compute MD5 hash of the name as a string. + - Reinterpret the first 8 bytes of the hash as a little-endian 64-bit integer. + +To avoid mismatched pointer types, generalizations are applied. +Pointers in return and argument types are treated as equivalent as long as the +qualifiers for the type they point to match. +For example, ``char*``, ``char**``, and ``int*`` are considered equivalent +types. However, ``char*`` and ``const char*`` are considered separate types. + +Missing type identifiers +======================== + +For functions, two cases need to be considered. First, if the compiler cannot +deduce a type id for an indirect target, it will be listed as an indirect target +without a type id. Second, if an object without a call graph section gets +linked, the final call graph section will lack information on functions from +the object. For completeness, these functions need to be taken as receiver to +any indirect call regardless of their type id. +``llvm-objdump --call-graph-info`` lists these functions as indirect targets +with `UNKNOWN` type id. + +For indirect calls, current implementation guarantees a type id for each +compiled call. However, if an object without a call graph section gets linked, +no type id will be present for its indirect calls. For completeness, these calls +need to be taken to target any indirect target regardless of their type id. For +indirect calls, ``llvm-objdump --call-graph-info`` prints 1) a complete list of +indirect calls, 2) type id to indirect call mappings. The difference of these +lists allow to deduce the indirect calls with missing type ids. + +TODO: measure and report the ratio of missed type ids + +Performance +=========== + +A call graph section does not affect the executable code and does not occupy +memory during process execution. Therefore, there is no performance overhead. + +The scheme has not yet been optimized for binary size. + +TODO: measure and report the increase in the binary size + +Example +======= + +For example, consider the following C++ code: + +.. code-block:: cpp + + namespace { + // Not an indirect target + void foo() {} + } + + // Indirect target 1 + void bar() {} + + // Indirect target 2 + int baz(char a, float *b) { + return 0; + } + + // Indirect target 3 + int main() { + char a; + float b; + void (*fp_bar)() = bar; + int (*fp_baz1)(char, float*) = baz; + int (*fp_baz2)(char, float*) = baz; + + // Indirect call site 1 + fp_bar(); + + // Indirect call site 2 + fp_baz1(a, &b); + + // Indirect call site 3: shares the type id with indirect call site 2 + fp_baz2(a, &b); + + // Direct call sites + foo(); + bar(); + baz(a, &b); + + return 0; + } + +Following will compile it with a call graph section created in the binary: + +.. code-block:: bash + + $ clang -fcall-graph-section example.cpp + +During the construction of the call graph section, the type identifiers are +computed as follows: + ++---------------+-----------------------+----------------------------+----------------------------+ +| Function name | Generalized signature | Mangled name (itanium ABI) | Numeric type id (md5 hash) | ++===============+=======================+============================+============================+ +| bar | void () | _ZTSFvvE.generalized | f85c699bb8ef20a2 | ++---------------+-----------------------+----------------------------+----------------------------+ +| baz | int (char, void*) | _ZTSFicPvE.generalized | e3804d2a7f2b03fe | ++---------------+-----------------------+----------------------------+----------------------------+ +| main | int () | _ZTSFivE.generalized | a9494def81a01dc | ++---------------+-----------------------+----------------------------+----------------------------+ + +The call graph section will have the following content: + ++---------------+-----------------+--------------+----------------+---------------+--------------------------------------+ +| FormatVersion | FunctionEntryPc | FunctionKind | FunctionTypeId | CallSiteCount | CallSiteList | ++===============+=================+==============+================+===============+======================================+ +| 0 | EntryPc(foo) | 0 | (empty) | 0 | (empty) | ++---------------+-----------------+--------------+----------------+---------------+--------------------------------------+ +| 0 | EntryPc(bar) | 2 | TypeId(bar) | 0 | (empty) | ++---------------+-----------------+--------------+----------------+---------------+--------------------------------------+ +| 0 | EntryPc(baz) | 2 | TypeId(baz) | 0 | (empty) | ++---------------+-----------------+--------------+----------------+---------------+--------------------------------------+ +| 0 | EntryPc(main) | 2 | TypeId(main) | 3 | * TypeId(bar), CallSitePc(fp_bar()) | +| | | | | | * TypeId(baz), CallSitePc(fp_baz1()) | +| | | | | | * TypeId(baz), CallSitePc(fp_baz2()) | ++---------------+-----------------+--------------+----------------+---------------+--------------------------------------+ + + +The ``llvm-objdump`` utility can parse the call graph section and disassemble +the program to provide complete call graph information. This includes any +additional call sites from the binary: + +.. code-block:: bash + + $ llvm-objdump --call-graph-info a.out + + # Comments are not a part of the llvm-objdump's output but inserted for clarifications. + + a.out: file format elf64-x86-64 + # These warnings are due to the functions and the indirect calls coming from linked objects. + llvm-objdump: warning: 'a.out': callgraph section does not have type ids for 3 indirect calls + llvm-objdump: warning: 'a.out': callgraph section does not have information for 10 functions + + # Unknown targets are the 10 functions the warnings mention. + INDIRECT TARGET TYPES (TYPEID [FUNC_ADDR,]) + UNKNOWN 401000 401100 401234 401050 401090 4010d0 4011d0 401020 401060 401230 + a9494def81a01dc 401150 # main() + f85c699bb8ef20a2 401120 # bar() + e3804d2a7f2b03fe 401130 # baz() + + # Notice that the call sites share the same type id as target functions + INDIRECT CALL TYPES (TYPEID [CALL_SITE_ADDR,]) + f85c699bb8ef20a2 401181 # Indirect call site 1 (fp_bar()) + e3804d2a7f2b03fe 401191 4011a1 # Indirect call site 2 and 3 (fp_baz1() and fp_baz2()) + + INDIRECT CALL SITES (CALLER_ADDR [CALL_SITE_ADDR,]) + 401000 401012 # _init + 401150 401181 401191 4011a1 # main calls fp_bar(), fp_baz1(), fp_baz2() + 4011d0 401215 # __libc_csu_init + 401020 40104a # _start + + DIRECT CALL SITES (CALLER_ADDR [(CALL_SITE_ADDR, TARGET_ADDR),]) + 4010d0 4010e2 401060 # __do_global_dtors_aux + 401150 4011a6 401110 4011ab 401120 4011ba 401130 # main calls foo(), bar(), baz() + 4011d0 4011fd 401000 # __libc_csu_init + + FUNCTIONS (FUNC_ENTRY_ADDR, SYM_NAME) + 401000 _init + 401100 frame_dummy + 401234 _fini + 401050 _dl_relocate_static_pie + 401090 register_tm_clones + 4010d0 __do_global_dtors_aux + 401110 _ZN12_GLOBAL__N_13fooEv # (anonymous namespace)::foo() + 401150 main # main + 4011d0 __libc_csu_init + 401020 _start + 401060 deregister_tm_clones + 401120 _Z3barv # bar() + 401130 _Z3bazcPf # baz(char, float*) + 401230 __libc_csu_fini diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index 340b08dd..9a8d629 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -76,6 +76,8 @@ CODEGENOPT(EnableNoundefAttrs, 1, 0) ///< Enable emitting `noundef` attributes o CODEGENOPT(DebugPassManager, 1, 0) ///< Prints debug information for the new ///< pass manager. CODEGENOPT(DisableRedZone , 1, 0) ///< Set when -mno-red-zone is enabled. +CODEGENOPT(CallGraphSection, 1, 0) ///< Emit a call graph section into the + ///< object file. CODEGENOPT(EmitCallSiteInfo, 1, 0) ///< Emit call site info only in the case of ///< '-g' + 'O>0' level. CODEGENOPT(IndirectTlsSegRefs, 1, 0) ///< Set when -mno-tls-direct-seg-refs diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index c3e90a7..3023ae9 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -4081,6 +4081,10 @@ defm data_sections : BoolFOption<"data-sections", PosFlag<SetTrue, [], [ClangOption, CC1Option], "Place each data in its own section">, NegFlag<SetFalse>>; +defm call_graph_section : BoolFOption<"call-graph-section", + CodeGenOpts<"CallGraphSection">, DefaultFalse, + PosFlag<SetTrue, [], [CC1Option], "Emit a call graph section">, + NegFlag<SetFalse>>; defm stack_size_section : BoolFOption<"stack-size-section", CodeGenOpts<"StackSizeSection">, DefaultFalse, PosFlag<SetTrue, [], [ClangOption, CC1Option], diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index c8b2a93..cba4064 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -420,6 +420,7 @@ static bool initTargetOptions(DiagnosticsEngine &Diags, Options.StackUsageOutput = CodeGenOpts.StackUsageOutput; Options.EmitAddrsig = CodeGenOpts.Addrsig; Options.ForceDwarfFrameSection = CodeGenOpts.ForceDwarfFrameSection; + Options.EmitCallGraphSection = CodeGenOpts.CallGraphSection; Options.EmitCallSiteInfo = CodeGenOpts.EmitCallSiteInfo; Options.EnableAIXExtendedAltivecABI = LangOpts.EnableAIXExtendedAltivecABI; Options.XRayFunctionIndex = CodeGenOpts.XRayFunctionIndex; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp index f12765b..51133b0 100644 --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -5681,6 +5681,28 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo, AllocAlignAttrEmitter AllocAlignAttrEmitter(*this, TargetDecl, CallArgs); Attrs = AllocAlignAttrEmitter.TryEmitAsCallSiteAttribute(Attrs); + if (CGM.getCodeGenOpts().CallGraphSection) { + // FIXME: create operand bundle only for indirect calls, not for all + + assert((TargetDecl && TargetDecl->getFunctionType() || + Callee.getAbstractInfo().getCalleeFunctionProtoType()) && + "cannot find callsite type"); + + QualType CST; + if (TargetDecl && TargetDecl->getFunctionType()) + CST = QualType(TargetDecl->getFunctionType(), 0); + else if (const auto *FPT = + Callee.getAbstractInfo().getCalleeFunctionProtoType()) + CST = QualType(FPT, 0); + + if (!CST.isNull()) { + auto *TypeIdMD = CGM.CreateMetadataIdentifierGeneralized(CST); + auto *TypeIdMDVal = + llvm::MetadataAsValue::get(getLLVMContext(), TypeIdMD); + BundleList.emplace_back("type", TypeIdMDVal); + } + } + // Emit the actual call/invoke instruction. llvm::CallBase *CI; if (!InvokeDest) { diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 8c1c8ee..2c347bd 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -2247,7 +2247,14 @@ void CodeGenFunction::EmitCXXConstructorCall(const CXXConstructorDecl *D, const CGFunctionInfo &Info = CGM.getTypes().arrangeCXXConstructorCall( Args, D, Type, ExtraArgs.Prefix, ExtraArgs.Suffix, PassPrototypeArgs); CGCallee Callee = CGCallee::forDirect(CalleePtr, GlobalDecl(D, Type)); - EmitCall(Info, Callee, ReturnValueSlot(), Args, nullptr, false, Loc); + llvm::CallBase *CallOrInvoke = nullptr; + EmitCall(Info, Callee, ReturnValueSlot(), Args, &CallOrInvoke, false, Loc); + + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(D->getType(), CallOrInvoke); + // Generate vtable assumptions if we're constructing a complete object // with a vtable. We don't do this for base subobjects for two reasons: diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 5443235..467a7cf 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -5953,6 +5953,11 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, const CGCallee &OrigCallee } } + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(QualType(FnType, 0), CallOrInvoke); + return Call; } diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index a4fb673..4cbffd7 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -93,9 +93,17 @@ RValue CodeGenFunction::EmitCXXMemberOrOperatorCall( *this, MD, This, ImplicitParam, ImplicitParamTy, CE, Args, RtlArgs); auto &FnInfo = CGM.getTypes().arrangeCXXMethodCall( Args, FPT, CallInfo.ReqArgs, CallInfo.PrefixSize); - return EmitCall(FnInfo, Callee, ReturnValue, Args, nullptr, + llvm::CallBase *CallOrInvoke = nullptr; + auto Call = EmitCall(FnInfo, Callee, ReturnValue, Args, &CallOrInvoke, CE && CE == MustTailCall, CE ? CE->getExprLoc() : SourceLocation()); + + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(MD->getType(), CallOrInvoke); + + return Call; } RValue CodeGenFunction::EmitCXXDestructorCall( @@ -119,9 +127,17 @@ RValue CodeGenFunction::EmitCXXDestructorCall( CallArgList Args; commonEmitCXXMemberOrOperatorCall(*this, Dtor, This, ImplicitParam, ImplicitParamTy, CE, Args, nullptr); - return EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(Dtor), Callee, - ReturnValueSlot(), Args, nullptr, CE && CE == MustTailCall, + llvm::CallBase *CallOrInvoke = nullptr; + auto Call = EmitCall(CGM.getTypes().arrangeCXXStructorDeclaration(Dtor), Callee, + ReturnValueSlot(), Args, &CallOrInvoke, CE && CE == MustTailCall, CE ? CE->getExprLoc() : SourceLocation{}); + + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(DtorDecl->getType(), CallOrInvoke); + + return Call; } RValue CodeGenFunction::EmitCXXPseudoDestructorExpr( @@ -482,10 +498,18 @@ CodeGenFunction::EmitCXXMemberPointerCallExpr(const CXXMemberCallExpr *E, // And the rest of the call args EmitCallArgs(Args, FPT, E->arguments()); - return EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required, + llvm::CallBase *CallOrInvoke = nullptr; + auto Call = EmitCall(CGM.getTypes().arrangeCXXMethodCall(Args, FPT, required, /*PrefixSize=*/0), - Callee, ReturnValue, Args, nullptr, E == MustTailCall, + Callee, ReturnValue, Args, &CallOrInvoke, E == MustTailCall, E->getExprLoc()); + + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && CallOrInvoke && + CallOrInvoke->isIndirectCall()) + CGM.CreateFunctionTypeMetadataForIcall(QualType(FPT, 0), CallOrInvoke); + + return Call; } RValue diff --git a/clang/lib/CodeGen/CGObjCMac.cpp b/clang/lib/CodeGen/CGObjCMac.cpp index 8a599c1..291f5af 100644 --- a/clang/lib/CodeGen/CGObjCMac.cpp +++ b/clang/lib/CodeGen/CGObjCMac.cpp @@ -2220,6 +2220,14 @@ CGObjCCommonMac::EmitMessageSend(CodeGen::CodeGenFunction &CGF, RValue rvalue = CGF.EmitCall(MSI.CallInfo, Callee, Return, ActualArgs, &CallSite); + // Set type identifier metadata of indirect calls for call graph section. + if (CGM.getCodeGenOpts().CallGraphSection && Method && CallSite && + CallSite->isIndirectCall()) { + if (const auto *FnType = Method->getFunctionType()) { + CGM.CreateFunctionTypeMetadataForIcall(QualType(FnType, 0), CallSite); + } + } + // Mark the call as noreturn if the method is marked noreturn and the // receiver cannot be null. if (Method && Method->hasAttr<NoReturnAttr>() && !ReceiverCanBeNull) { diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 00b3bfc..e19bbee 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -2486,8 +2486,8 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D, // In the cross-dso CFI mode with canonical jump tables, we want !type // attributes on definitions only. - if (CodeGenOpts.SanitizeCfiCrossDso && - CodeGenOpts.SanitizeCfiCanonicalJumpTables) { + if ((CodeGenOpts.SanitizeCfiCrossDso && + CodeGenOpts.SanitizeCfiCanonicalJumpTables) || CodeGenOpts.CallGraphSection) { if (auto *FD = dyn_cast<FunctionDecl>(D)) { // Skip available_externally functions. They won't be codegen'ed in the // current module anyway. @@ -2677,7 +2677,17 @@ static void setLinkageForGV(llvm::GlobalValue *GV, const NamedDecl *ND) { void CodeGenModule::CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD, llvm::Function *F) { - // Only if we are checking indirect calls. + bool EmittedMDIdGeneralized = false; + if (CodeGenOpts.CallGraphSection && + (!F->hasLocalLinkage() || + F->getFunction().hasAddressTaken(nullptr, /* IgnoreCallbackUses */ true, + /* IgnoreAssumeLikeCalls */ true, + /* IgnoreLLVMUsed */ false))) { + F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType())); + EmittedMDIdGeneralized = true; + } + + // Add additional metadata only if we are checking indirect calls with CFI. if (!LangOpts.Sanitize.has(SanitizerKind::CFIICall)) return; @@ -2688,7 +2698,9 @@ void CodeGenModule::CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD, llvm::Metadata *MD = CreateMetadataIdentifierForType(FD->getType()); F->addTypeMetadata(0, MD); - F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType())); + // Add the generalized identifier if not added already. + if (!EmittedMDIdGeneralized) + F->addTypeMetadata(0, CreateMetadataIdentifierGeneralized(FD->getType())); // Emit a hash-based bit set entry for cross-DSO calls. if (CodeGenOpts.SanitizeCfiCrossDso) @@ -2696,6 +2708,17 @@ void CodeGenModule::CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD, F->addTypeMetadata(0, llvm::ConstantAsMetadata::get(CrossDsoTypeId)); } +void CodeGenModule::CreateFunctionTypeMetadataForIcall(const QualType &QT, + llvm::CallBase *CB) { + // Only if needed for call graph section and only for indirect calls. + if (!(CodeGenOpts.CallGraphSection && CB && CB->isIndirectCall())) + return; + + auto *MD = CreateMetadataIdentifierGeneralized(QT); + auto *MDN = llvm::MDNode::get(getLLVMContext(), MD); + CB->setMetadata(llvm::LLVMContext::MD_type, MDN); +} + void CodeGenModule::setKCFIType(const FunctionDecl *FD, llvm::Function *F) { llvm::LLVMContext &Ctx = F->getContext(); llvm::MDBuilder MDB(Ctx); @@ -2823,7 +2846,8 @@ void CodeGenModule::SetFunctionAttributes(GlobalDecl GD, llvm::Function *F, // are non-canonical then we need type metadata in order to produce the local // jump table. if (!CodeGenOpts.SanitizeCfiCrossDso || - !CodeGenOpts.SanitizeCfiCanonicalJumpTables) + !CodeGenOpts.SanitizeCfiCanonicalJumpTables || + CodeGenOpts.CallGraphSection) CreateFunctionTypeMetadataForIcall(FD, F); if (LangOpts.Sanitize.has(SanitizerKind::KCFI)) diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index 1cc4477..12ac8d0 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -1473,6 +1473,10 @@ public: void CreateFunctionTypeMetadataForIcall(const FunctionDecl *FD, llvm::Function *F); + /// Create and attach type metadata to the given call. + void CreateFunctionTypeMetadataForIcall(const QualType &QT, + llvm::CallBase *CB); + /// Set type metadata to the given function. void setKCFIType(const FunctionDecl *FD, llvm::Function *F); diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index b7ec7e0..7cc2d9a 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -6407,6 +6407,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, CmdArgs.push_back(A->getValue()); } + if (Args.hasFlag(options::OPT_fcall_graph_section, + options::OPT_fno_call_graph_section, false)) + CmdArgs.push_back("-fcall-graph-section"); + Args.addOptInFlag(CmdArgs, options::OPT_fstack_size_section, options::OPT_fno_stack_size_section); diff --git a/clang/test/CodeGen/call-graph-section-1.cpp b/clang/test/CodeGen/call-graph-section-1.cpp new file mode 100644 index 0000000..25397e9 --- /dev/null +++ b/clang/test/CodeGen/call-graph-section-1.cpp @@ -0,0 +1,110 @@ +// Tests that we assign appropriate identifiers to indirect calls and targets +// specifically for C++ class and instance methods. + +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fcall-graph-section -S \ +// RUN: -emit-llvm -o %t %s +// RUN: FileCheck --check-prefix=FT %s < %t +// RUN: FileCheck --check-prefix=CST %s < %t + +//////////////////////////////////////////////////////////////////////////////// +// Class definitions (check for indirect target metadata) + +class Cls1 { +public: + // FT-DAG: define {{.*}} ptr @_ZN4Cls18receiverEPcPf({{.*}} !type [[F_TCLS1RECEIVER:![0-9]+]] + static int *receiver(char *a, float *b) { return 0; } +}; + +class Cls2 { +public: + int *(*fp)(char *, float *); + + // FT-DAG: define {{.*}} i32 @_ZN4Cls22f1Ecfd({{.*}} !type [[F_TCLS2F1:![0-9]+]] + int f1(char a, float b, double c) { return 0; } + + // FT-DAG: define {{.*}} ptr @_ZN4Cls22f2EPcPfPd({{.*}} !type [[F_TCLS2F2:![0-9]+]] + int *f2(char *a, float *b, double *c) { return 0; } + + // FT-DAG: define {{.*}} void @_ZN4Cls22f3E4Cls1({{.*}} !type [[F_TCLS2F3F4:![0-9]+]] + void f3(Cls1 a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f4E4Cls1({{.*}} !type [[F_TCLS2F3F4]] + void f4(const Cls1 a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f5EP4Cls1({{.*}} !type [[F_TCLS2F5:![0-9]+]] + void f5(Cls1 *a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f6EPK4Cls1({{.*}} !type [[F_TCLS2F6:![0-9]+]] + void f6(const Cls1 *a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f7ER4Cls1({{.*}} !type [[F_TCLS2F7:![0-9]+]] + void f7(Cls1 &a) {} + + // FT-DAG: define {{.*}} void @_ZN4Cls22f8ERK4Cls1({{.*}} !type [[F_TCLS2F8:![0-9]+]] + void f8(const Cls1 &a) {} + + // FT-DAG: define {{.*}} void @_ZNK4Cls22f9Ev({{.*}} !type [[F_TCLS2F9:![0-9]+]] + void f9() const {} +}; + +// FT-DAG: [[F_TCLS1RECEIVER]] = !{i64 0, !"_ZTSFPvS_S_E.generalized"} +// FT-DAG: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"} +// FT-DAG: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFicfdE.generalized"} +// FT-DAG: [[F_TCLS2F3F4]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvPvE.generalized"} +// FT-DAG: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvPKvE.generalized"} +// FT-DAG: [[F_TCLS2F7]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F8]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F9]] = !{i64 0, !"_ZTSKFvvE.generalized"} + +//////////////////////////////////////////////////////////////////////////////// +// Callsites (check for indirect callsite operand bundles) + +// CST-LABEL: define {{.*}} @_Z3foov +void foo() { + Cls2 ObjCls2; + ObjCls2.fp = &Cls1::receiver; + + // CST: call noundef ptr %{{.*}} [ "type"(metadata !"_ZTSFPvS_S_E.generalized") ] + ObjCls2.fp(0, 0); + + auto fp_f1 = &Cls2::f1; + auto fp_f2 = &Cls2::f2; + auto fp_f3 = &Cls2::f3; + auto fp_f4 = &Cls2::f4; + auto fp_f5 = &Cls2::f5; + auto fp_f6 = &Cls2::f6; + auto fp_f7 = &Cls2::f7; + auto fp_f8 = &Cls2::f8; + auto fp_f9 = &Cls2::f9; + + Cls2 *ObjCls2Ptr = &ObjCls2; + Cls1 Cls1Param; + + // CST: call noundef i32 %{{.*}} [ "type"(metadata !"_ZTSFicfdE.generalized") ] + (ObjCls2Ptr->*fp_f1)(0, 0, 0); + + // CST: call noundef ptr %{{.*}} [ "type"(metadata !"_ZTSFPvS_S_S_E.generalized") ] + (ObjCls2Ptr->*fp_f2)(0, 0, 0); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFv4Cls1E.generalized") ] + (ObjCls2Ptr->*fp_f3)(Cls1Param); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFv4Cls1E.generalized") ] + (ObjCls2Ptr->*fp_f4)(Cls1Param); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvPvE.generalized") ] + (ObjCls2Ptr->*fp_f5)(&Cls1Param); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvPKvE.generalized") ] + (ObjCls2Ptr->*fp_f6)(&Cls1Param); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvR4Cls1E.generalized") ] + (ObjCls2Ptr->*fp_f7)(Cls1Param); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvRK4Cls1E.generalized") ] + (ObjCls2Ptr->*fp_f8)(Cls1Param); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSKFvvE.generalized") ] + (ObjCls2Ptr->*fp_f9)(); +} diff --git a/clang/test/CodeGen/call-graph-section-2.cpp b/clang/test/CodeGen/call-graph-section-2.cpp new file mode 100644 index 0000000..4187fae --- /dev/null +++ b/clang/test/CodeGen/call-graph-section-2.cpp @@ -0,0 +1,95 @@ +// Tests that we assign appropriate identifiers to indirect calls and targets +// specifically for C++ templates. + +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fcall-graph-section -S \ +// RUN: -emit-llvm -o %t %s +// RUN: FileCheck --check-prefix=FT %s < %t +// RUN: FileCheck --check-prefix=CST %s < %t +// RUN: FileCheck --check-prefix=CHECK %s < %t + +//////////////////////////////////////////////////////////////////////////////// +// Class definitions and template classes (check for indirect target metadata) + +class Cls1 {}; + +// Cls2 is instantiated with T=Cls1 in foo(). Following checks are for this +// instantiation. +template <class T> +class Cls2 { +public: + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f1Ev({{.*}} !type [[F_TCLS2F1:![0-9]+]] + void f1() {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f2ES0_({{.*}} !type [[F_TCLS2F2:![0-9]+]] + void f2(T a) {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f3EPS0_({{.*}} !type [[F_TCLS2F3:![0-9]+]] + void f3(T *a) {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f4EPKS0_({{.*}} !type [[F_TCLS2F4:![0-9]+]] + void f4(const T *a) {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f5ERS0_({{.*}} !type [[F_TCLS2F5:![0-9]+]] + void f5(T &a) {} + + // FT: define {{.*}} void @_ZN4Cls2I4Cls1E2f6ERKS0_({{.*}} !type [[F_TCLS2F6:![0-9]+]] + void f6(const T &a) {} + + // Mixed type function pointer member + T *(*fp)(T a, T *b, const T *c, T &d, const T &e); +}; + +// FT-DAG: [[F_TCLS2F1]] = !{i64 0, !"_ZTSFvvE.generalized"} +// FT-DAG: [[F_TCLS2F2]] = !{i64 0, !"_ZTSFv4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F3]] = !{i64 0, !"_ZTSFvPvE.generalized"} +// FT-DAG: [[F_TCLS2F4]] = !{i64 0, !"_ZTSFvPKvE.generalized"} +// FT-DAG: [[F_TCLS2F5]] = !{i64 0, !"_ZTSFvR4Cls1E.generalized"} +// FT-DAG: [[F_TCLS2F6]] = !{i64 0, !"_ZTSFvRK4Cls1E.generalized"} + +//////////////////////////////////////////////////////////////////////////////// +// Callsites (check for indirect callsite operand bundles) + +template <class T> +T *T_func(T a, T *b, const T *c, T &d, const T &e) { return b; } + +// CST-LABEL: define {{.*}} @_Z3foov +void foo() { + // Methods for Cls2<Cls1> is checked above within the template description. + Cls2<Cls1> Obj; + + // CHECK-DAG: define {{.*}} @_Z6T_funcI4Cls1EPT_S1_S2_PKS1_RS1_RS3_({{.*}} !type [[F_TFUNC_CLS1:![0-9]+]] + // CHECK-DAG: [[F_TFUNC_CLS1]] = !{i64 0, !"_ZTSFPv4Cls1S_PKvRS0_RKS0_E.generalized"} + Obj.fp = T_func<Cls1>; + Cls1 Cls1Obj; + + // CST: call noundef ptr %{{.*}} [ "type"(metadata !"_ZTSFPv4Cls1S_PKvRS0_RKS0_E.generalized") ] + Obj.fp(Cls1Obj, &Cls1Obj, &Cls1Obj, Cls1Obj, Cls1Obj); + + // Make indirect calls to Cls2's member methods + auto fp_f1 = &Cls2<Cls1>::f1; + auto fp_f2 = &Cls2<Cls1>::f2; + auto fp_f3 = &Cls2<Cls1>::f3; + auto fp_f4 = &Cls2<Cls1>::f4; + auto fp_f5 = &Cls2<Cls1>::f5; + auto fp_f6 = &Cls2<Cls1>::f6; + + auto *Obj2Ptr = &Obj; + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvvE.generalized") ] + (Obj2Ptr->*fp_f1)(); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFv4Cls1E.generalized") ] + (Obj2Ptr->*fp_f2)(Cls1Obj); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvPvE.generalized") ] + (Obj2Ptr->*fp_f3)(&Cls1Obj); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvPKvE.generalized") ] + (Obj2Ptr->*fp_f4)(&Cls1Obj); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvR4Cls1E.generalized") ] + (Obj2Ptr->*fp_f5)(Cls1Obj); + + // CST: call void %{{.*}} [ "type"(metadata !"_ZTSFvRK4Cls1E.generalized") ] + (Obj2Ptr->*fp_f6)(Cls1Obj); +} diff --git a/clang/test/CodeGen/call-graph-section-3.cpp b/clang/test/CodeGen/call-graph-section-3.cpp new file mode 100644 index 0000000..77bb110 --- /dev/null +++ b/clang/test/CodeGen/call-graph-section-3.cpp @@ -0,0 +1,52 @@ +// Tests that we assign appropriate identifiers to indirect calls and targets +// specifically for virtual methods. + +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fcall-graph-section -S \ +// RUN: -emit-llvm -o %t %s +// RUN: FileCheck --check-prefix=FT %s < %t +// RUN: FileCheck --check-prefix=CST %s < %t + +//////////////////////////////////////////////////////////////////////////////// +// Class definitions (check for indirect target metadata) + +class Base { +public: + // FT-DAG: define {{.*}} @_ZN4Base2vfEPc({{.*}} !type [[F_TVF:![0-9]+]] + virtual int vf(char *a) { return 0; }; +}; + +class Derived : public Base { +public: + // FT-DAG: define {{.*}} @_ZN7Derived2vfEPc({{.*}} !type [[F_TVF]] + int vf(char *a) override { return 1; }; +}; + +// FT-DAG: [[F_TVF]] = !{i64 0, !"_ZTSFiPvE.generalized"} + +//////////////////////////////////////////////////////////////////////////////// +// Callsites (check for indirect callsite operand bundles) + +// CST-LABEL: define {{.*}} @_Z3foov +void foo() { + auto B = Base(); + auto D = Derived(); + + Base *Bptr = &B; + Base *BptrToD = &D; + Derived *Dptr = &D; + + auto FpBaseVf = &Base::vf; + auto FpDerivedVf = &Derived::vf; + + // CST: call noundef i32 %{{.*}} [ "type"(metadata !"_ZTSFiPvE.generalized") ] + (Bptr->*FpBaseVf)(0); + + // CST: call noundef i32 %{{.*}} [ "type"(metadata !"_ZTSFiPvE.generalized") ] + (BptrToD->*FpBaseVf)(0); + + // CST: call noundef i32 %{{.*}} [ "type"(metadata !"_ZTSFiPvE.generalized") ] + (Dptr->*FpBaseVf)(0); + + // CST: call noundef i32 %{{.*}} [ "type"(metadata !"_ZTSFiPvE.generalized") ] + (Dptr->*FpDerivedVf)(0); +} diff --git a/clang/test/CodeGen/call-graph-section.c b/clang/test/CodeGen/call-graph-section.c new file mode 100644 index 0000000..ed1bdb8 --- /dev/null +++ b/clang/test/CodeGen/call-graph-section.c @@ -0,0 +1,85 @@ +// Tests that we assign appropriate identifiers to indirect calls and targets. + +// RUN: %clang_cc1 -triple x86_64-unknown-linux -fcall-graph-section -S \ +// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,ITANIUM %s + +// RUN: %clang_cc1 -triple x86_64-pc-windows-msvc -fcall-graph-section -S \ +// RUN: -emit-llvm -o - %s | FileCheck --check-prefixes=CHECK,MS %s + +// CHECK-DAG: define {{(dso_local)?}} void @foo({{.*}} !type [[F_TVOID:![0-9]+]] +void foo() { +} + +// CHECK-DAG: define {{(dso_local)?}} void @bar({{.*}} !type [[F_TVOID]] +void bar() { + void (*fp)() = foo; + // ITANIUM: call {{.*}} [ "type"(metadata !"_ZTSFvE.generalized") ] + // MS: call {{.*}} [ "type"(metadata !"?6AX@Z.generalized") ] + fp(); +} + +// CHECK-DAG: define {{(dso_local)?}} i32 @baz({{.*}} !type [[F_TPRIMITIVE:![0-9]+]] +int baz(char a, float b, double c) { + return 1; +} + +// CHECK-DAG: define {{(dso_local)?}} ptr @qux({{.*}} !type [[F_TPTR:![0-9]+]] +int *qux(char *a, float *b, double *c) { + return 0; +} + +// CHECK-DAG: define {{(dso_local)?}} void @corge({{.*}} !type [[F_TVOID]] +void corge() { + int (*fp_baz)(char, float, double) = baz; + // ITANIUM: call i32 {{.*}} [ "type"(metadata !"_ZTSFicfdE.generalized") ] + // MS: call i32 {{.*}} [ "type"(metadata !"?6AHDMN@Z.generalized") ] + fp_baz('a', .0f, .0); + + int *(*fp_qux)(char *, float *, double *) = qux; + // ITANIUM: call ptr {{.*}} [ "type"(metadata !"_ZTSFPvS_S_S_E.generalized") ] + // MS: call ptr {{.*}} [ "type"(metadata !"?6APEAXPEAX00@Z.generalized") ] + fp_qux(0, 0, 0); +} + +struct st1 { + int *(*fp)(char *, float *, double *); +}; + +struct st2 { + struct st1 m; +}; + +// CHECK-DAG: define {{(dso_local)?}} void @stparam({{.*}} !type [[F_TSTRUCT:![0-9]+]] +void stparam(struct st2 a, struct st2 *b) {} + +// CHECK-DAG: define {{(dso_local)?}} void @stf({{.*}} !type [[F_TVOID]] +void stf() { + struct st1 St1; + St1.fp = qux; + // ITANIUM: call ptr {{.*}} [ "type"(metadata !"_ZTSFPvS_S_S_E.generalized") ] + // MS: call ptr {{.*}} [ "type"(metadata !"?6APEAXPEAX00@Z.generalized") ] + St1.fp(0, 0, 0); + + struct st2 St2; + St2.m.fp = qux; + // ITANIUM: call ptr {{.*}} [ "type"(metadata !"_ZTSFPvS_S_S_E.generalized") ] + // MS: call ptr {{.*}} [ "type"(metadata !"?6APEAXPEAX00@Z.generalized") ] + St2.m.fp(0, 0, 0); + + // ITANIUM: call void {{.*}} [ "type"(metadata !"_ZTSFv3st2PvE.generalized") ] + // MS: call void {{.*}} [ "type"(metadata !"?6AXUst2@@PEAX@Z.generalized") ] + void (*fp_stparam)(struct st2, struct st2 *) = stparam; + fp_stparam(St2, &St2); +} + +// ITANIUM-DAG: [[F_TVOID]] = !{i64 0, !"_ZTSFvE.generalized"} +// MS-DAG: [[F_TVOID]] = !{i64 0, !"?6AX@Z.generalized"} + +// ITANIUM-DAG: [[F_TPRIMITIVE]] = !{i64 0, !"_ZTSFicfdE.generalized"} +// MS-DAG: [[F_TPRIMITIVE]] = !{i64 0, !"?6AHDMN@Z.generalized"} + +// ITANIUM-DAG: [[F_TPTR]] = !{i64 0, !"_ZTSFPvS_S_S_E.generalized"} +// MS-DAG: [[F_TPTR]] = !{i64 0, !"?6APEAXPEAX00@Z.generalized"} + +// ITANIUM-DAG: [[F_TSTRUCT]] = !{i64 0, !"_ZTSFv3st2PvE.generalized"} +// MS-DAG: [[F_TSTRUCT]] = !{i64 0, !"?6AXUst2@@PEAX@Z.generalized"} diff --git a/clang/test/Driver/call-graph-section.c b/clang/test/Driver/call-graph-section.c new file mode 100644 index 0000000..5832aa6 --- /dev/null +++ b/clang/test/Driver/call-graph-section.c @@ -0,0 +1,5 @@ +// RUN: %clang -### -S -fcall-graph-section %s 2>&1 | FileCheck --check-prefix=CALL-GRAPH-SECTION %s +// RUN: %clang -### -S -fcall-graph-section -fno-call-graph-section %s 2>&1 | FileCheck --check-prefix=NO-CALL-GRAPH-SECTION %s + +// CALL-GRAPH-SECTION: "-fcall-graph-section" +// NO-CALL-GRAPH-SECTION-NOT: "-fcall-graph-section" |