319 lines · cpp
1//===- StripSymbols.cpp - Strip symbols and debug info from a module ------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// The StripSymbols transformation implements code stripping. Specifically, it10// can delete:11//12// * names for virtual registers13// * symbols for internal globals and functions14// * debug information15//16// Note that this transformation makes code much less readable, so it should17// only be used in situations where the 'strip' utility would be used, such as18// reducing code size or making it harder to reverse engineer code.19//20//===----------------------------------------------------------------------===//21 22#include "llvm/Transforms/IPO/StripSymbols.h"23 24#include "llvm/ADT/SmallPtrSet.h"25#include "llvm/IR/Constants.h"26#include "llvm/IR/DebugInfo.h"27#include "llvm/IR/DerivedTypes.h"28#include "llvm/IR/InstIterator.h"29#include "llvm/IR/Instructions.h"30#include "llvm/IR/Metadata.h"31#include "llvm/IR/Module.h"32#include "llvm/IR/PassManager.h"33#include "llvm/IR/TypeFinder.h"34#include "llvm/IR/ValueSymbolTable.h"35#include "llvm/Support/Casting.h"36#include "llvm/Support/CommandLine.h"37#include "llvm/Transforms/Utils/Local.h"38 39using namespace llvm;40 41static cl::opt<bool>42 StripGlobalConstants("strip-global-constants", cl::init(false), cl::Hidden,43 cl::desc("Removes debug compile units which reference "44 "to non-existing global constants"));45 46/// OnlyUsedBy - Return true if V is only used by Usr.47static bool OnlyUsedBy(Value *V, Value *Usr) {48 for (User *U : V->users())49 if (U != Usr)50 return false;51 52 return true;53}54 55static void RemoveDeadConstant(Constant *C) {56 assert(C->use_empty() && "Constant is not dead!");57 SmallPtrSet<Constant*, 4> Operands;58 for (Value *Op : C->operands())59 if (OnlyUsedBy(Op, C))60 Operands.insert(cast<Constant>(Op));61 if (GlobalVariable *GV = dyn_cast<GlobalVariable>(C)) {62 if (!GV->hasLocalLinkage()) return; // Don't delete non-static globals.63 GV->eraseFromParent();64 } else if (!isa<Function>(C)) {65 // FIXME: Why does the type of the constant matter here?66 if (isa<StructType>(C->getType()) || isa<ArrayType>(C->getType()) ||67 isa<VectorType>(C->getType()))68 C->destroyConstant();69 }70 71 // If the constant referenced anything, see if we can delete it as well.72 for (Constant *O : Operands)73 RemoveDeadConstant(O);74}75 76// Strip the symbol table of its names.77//78static void StripSymtab(ValueSymbolTable &ST, bool PreserveDbgInfo) {79 for (ValueSymbolTable::iterator VI = ST.begin(), VE = ST.end(); VI != VE; ) {80 Value *V = VI->getValue();81 ++VI;82 if (!isa<GlobalValue>(V) || cast<GlobalValue>(V)->hasLocalLinkage()) {83 if (!PreserveDbgInfo || !V->getName().starts_with("llvm.dbg"))84 // Set name to "", removing from symbol table!85 V->setName("");86 }87 }88}89 90// Strip any named types of their names.91static void StripTypeNames(Module &M, bool PreserveDbgInfo) {92 TypeFinder StructTypes;93 StructTypes.run(M, false);94 95 for (StructType *STy : StructTypes) {96 if (STy->isLiteral() || STy->getName().empty()) continue;97 98 if (PreserveDbgInfo && STy->getName().starts_with("llvm.dbg"))99 continue;100 101 STy->setName("");102 }103}104 105/// Find values that are marked as llvm.used.106static void findUsedValues(GlobalVariable *LLVMUsed,107 SmallPtrSetImpl<const GlobalValue*> &UsedValues) {108 if (!LLVMUsed) return;109 UsedValues.insert(LLVMUsed);110 111 ConstantArray *Inits = cast<ConstantArray>(LLVMUsed->getInitializer());112 113 for (unsigned i = 0, e = Inits->getNumOperands(); i != e; ++i)114 if (GlobalValue *GV =115 dyn_cast<GlobalValue>(Inits->getOperand(i)->stripPointerCasts()))116 UsedValues.insert(GV);117}118 119/// StripSymbolNames - Strip symbol names.120static bool StripSymbolNames(Module &M, bool PreserveDbgInfo) {121 122 SmallPtrSet<const GlobalValue*, 8> llvmUsedValues;123 findUsedValues(M.getGlobalVariable("llvm.used"), llvmUsedValues);124 findUsedValues(M.getGlobalVariable("llvm.compiler.used"), llvmUsedValues);125 126 for (GlobalVariable &GV : M.globals()) {127 if (GV.hasLocalLinkage() && !llvmUsedValues.contains(&GV))128 if (!PreserveDbgInfo || !GV.getName().starts_with("llvm.dbg"))129 GV.setName(""); // Internal symbols can't participate in linkage130 }131 132 for (Function &I : M) {133 if (I.hasLocalLinkage() && !llvmUsedValues.contains(&I))134 if (!PreserveDbgInfo || !I.getName().starts_with("llvm.dbg"))135 I.setName(""); // Internal symbols can't participate in linkage136 if (auto *Symtab = I.getValueSymbolTable())137 StripSymtab(*Symtab, PreserveDbgInfo);138 }139 140 // Remove all names from types.141 StripTypeNames(M, PreserveDbgInfo);142 143 return true;144}145 146static bool stripDebugDeclareImpl(Module &M) {147 Function *Declare =148 Intrinsic::getDeclarationIfExists(&M, Intrinsic::dbg_declare);149 std::vector<Constant*> DeadConstants;150 151 if (Declare) {152 while (!Declare->use_empty()) {153 CallInst *CI = cast<CallInst>(Declare->user_back());154 Value *Arg1 = CI->getArgOperand(0);155 Value *Arg2 = CI->getArgOperand(1);156 assert(CI->use_empty() && "llvm.dbg intrinsic should have void result");157 CI->eraseFromParent();158 if (Arg1->use_empty()) {159 if (Constant *C = dyn_cast<Constant>(Arg1))160 DeadConstants.push_back(C);161 else162 RecursivelyDeleteTriviallyDeadInstructions(Arg1);163 }164 if (Arg2->use_empty())165 if (Constant *C = dyn_cast<Constant>(Arg2))166 DeadConstants.push_back(C);167 }168 Declare->eraseFromParent();169 }170 171 while (!DeadConstants.empty()) {172 Constant *C = DeadConstants.back();173 DeadConstants.pop_back();174 if (GlobalVariable *GV = dyn_cast<GlobalVariable>(C)) {175 if (GV->hasLocalLinkage())176 RemoveDeadConstant(GV);177 } else178 RemoveDeadConstant(C);179 }180 181 return true;182}183 184static bool stripDeadDebugInfoImpl(Module &M) {185 bool Changed = false;186 187 LLVMContext &C = M.getContext();188 189 // Find all debug info in F. This is actually overkill in terms of what we190 // want to do, but we want to try and be as resilient as possible in the face191 // of potential debug info changes by using the formal interfaces given to us192 // as much as possible.193 DebugInfoFinder F;194 F.processModule(M);195 196 // For each compile unit, find the live set of global variables/functions and197 // replace the current list of potentially dead global variables/functions198 // with the live list.199 SmallVector<Metadata *, 64> LiveGlobalVariables;200 DenseSet<DIGlobalVariableExpression *> VisitedSet;201 202 std::set<DIGlobalVariableExpression *> LiveGVs;203 for (GlobalVariable &GV : M.globals()) {204 SmallVector<DIGlobalVariableExpression *, 1> GVEs;205 GV.getDebugInfo(GVEs);206 for (auto *GVE : GVEs)207 LiveGVs.insert(GVE);208 }209 210 std::set<DICompileUnit *> LiveCUs;211 DebugInfoFinder LiveCUFinder;212 for (const Function &F : M.functions()) {213 if (auto *SP = cast_or_null<DISubprogram>(F.getSubprogram()))214 LiveCUFinder.processSubprogram(SP);215 for (const Instruction &I : instructions(F))216 LiveCUFinder.processInstruction(M, I);217 }218 auto FoundCUs = LiveCUFinder.compile_units();219 LiveCUs.insert(FoundCUs.begin(), FoundCUs.end());220 221 bool HasDeadCUs = false;222 for (DICompileUnit *DIC : F.compile_units()) {223 // Create our live global variable list.224 bool GlobalVariableChange = false;225 for (auto *DIG : DIC->getGlobalVariables()) {226 if (DIG->getExpression() && DIG->getExpression()->isConstant() &&227 !StripGlobalConstants)228 LiveGVs.insert(DIG);229 230 // Make sure we only visit each global variable only once.231 if (!VisitedSet.insert(DIG).second)232 continue;233 234 // If a global variable references DIG, the global variable is live.235 if (LiveGVs.count(DIG))236 LiveGlobalVariables.push_back(DIG);237 else238 GlobalVariableChange = true;239 }240 241 if (!LiveGlobalVariables.empty())242 LiveCUs.insert(DIC);243 else if (!LiveCUs.count(DIC))244 HasDeadCUs = true;245 246 // If we found dead global variables, replace the current global247 // variable list with our new live global variable list.248 if (GlobalVariableChange) {249 DIC->replaceGlobalVariables(MDTuple::get(C, LiveGlobalVariables));250 Changed = true;251 }252 253 // Reset lists for the next iteration.254 LiveGlobalVariables.clear();255 }256 257 if (HasDeadCUs) {258 // Delete the old node and replace it with a new one259 NamedMDNode *NMD = M.getOrInsertNamedMetadata("llvm.dbg.cu");260 NMD->clearOperands();261 if (!LiveCUs.empty()) {262 for (DICompileUnit *CU : LiveCUs)263 NMD->addOperand(CU);264 }265 Changed = true;266 }267 268 return Changed;269}270 271PreservedAnalyses StripSymbolsPass::run(Module &M, ModuleAnalysisManager &AM) {272 StripDebugInfo(M);273 StripSymbolNames(M, false);274 PreservedAnalyses PA;275 PA.preserveSet<CFGAnalyses>();276 return PA;277}278 279PreservedAnalyses StripNonDebugSymbolsPass::run(Module &M,280 ModuleAnalysisManager &AM) {281 StripSymbolNames(M, true);282 PreservedAnalyses PA;283 PA.preserveSet<CFGAnalyses>();284 return PA;285}286 287PreservedAnalyses StripDebugDeclarePass::run(Module &M,288 ModuleAnalysisManager &AM) {289 stripDebugDeclareImpl(M);290 PreservedAnalyses PA;291 PA.preserveSet<CFGAnalyses>();292 return PA;293}294 295PreservedAnalyses StripDeadDebugInfoPass::run(Module &M,296 ModuleAnalysisManager &AM) {297 stripDeadDebugInfoImpl(M);298 PreservedAnalyses PA;299 PA.preserveSet<CFGAnalyses>();300 return PA;301}302 303PreservedAnalyses StripDeadCGProfilePass::run(Module &M,304 ModuleAnalysisManager &AM) {305 auto *CGProf = dyn_cast_or_null<MDTuple>(M.getModuleFlag("CG Profile"));306 if (!CGProf)307 return PreservedAnalyses::all();308 309 SmallVector<Metadata *, 16> ValidCGEdges;310 for (Metadata *Edge : CGProf->operands()) {311 if (auto *EdgeAsNode = dyn_cast_or_null<MDNode>(Edge))312 if (!llvm::is_contained(EdgeAsNode->operands(), nullptr))313 ValidCGEdges.push_back(Edge);314 }315 M.setModuleFlag(Module::Append, "CG Profile",316 MDTuple::getDistinct(M.getContext(), ValidCGEdges));317 return PreservedAnalyses::none();318}319