// Initialize the BytesFlops pass. bool BytesFlops::doInitialization(Module& module) { // Inject external declarations to various variables defined in byfl.c. LLVMContext& globctx = module.getContext(); IntegerType* i64type = Type::getInt64Ty(globctx); PointerType* i64ptrtype = Type::getInt64PtrTy(globctx); mem_insts_var = declare_global_var(module, i64ptrtype, "bf_mem_insts_count", true); inst_mix_histo_var = declare_global_var(module, i64ptrtype, "bf_inst_mix_histo", true); terminator_var = declare_global_var(module, i64ptrtype, "bf_terminator_count", true); mem_intrinsics_var = declare_global_var(module, i64ptrtype, "bf_mem_intrin_count", true); load_var = declare_global_var(module, i64type, "bf_load_count"); store_var = declare_global_var(module, i64type, "bf_store_count"); load_inst_var = declare_global_var(module, i64type, "bf_load_ins_count"); store_inst_var = declare_global_var(module, i64type, "bf_store_ins_count"); flop_var = declare_global_var(module, i64type, "bf_flop_count"); fp_bits_var = declare_global_var(module, i64type, "bf_fp_bits_count"); op_var = declare_global_var(module, i64type, "bf_op_count"); op_bits_var = declare_global_var(module, i64type, "bf_op_bits_count"); // Assign a few constant values. not_end_of_bb = ConstantInt::get(globctx, APInt(32, 0)); uncond_end_bb = ConstantInt::get(globctx, APInt(32, 1)); cond_end_bb = ConstantInt::get(globctx, APInt(32, 2)); zero = ConstantInt::get(globctx, APInt(64, 0)); one = ConstantInt::get(globctx, APInt(64, 1)); // Construct a set of functions to instrument and a set of // functions not to instrument. instrument_only = parse_function_names(IncludedFunctions); dont_instrument = parse_function_names(ExcludedFunctions); if (instrument_only && dont_instrument) report_fatal_error("-bf-include and -bf-exclude are mutually exclusive"); // Assign a value to bf_bb_merge. create_global_constant(module, "bf_bb_merge", uint64_t(BBMergeCount)); // Assign a value to bf_every_bb. create_global_constant(module, "bf_every_bb", bool(InstrumentEveryBB)); // Assign a value to bf_types. create_global_constant(module, "bf_types", bool(TallyTypes)); // Assign a value to bf_tally_inst_mix (instruction mix). create_global_constant(module, "bf_tally_inst_mix", bool(TallyInstMix)); // Assign a value to bf_per_func. create_global_constant(module, "bf_per_func", bool(TallyByFunction)); // Assign a value to bf_call_stack. if (TrackCallStack && !TallyByFunction) report_fatal_error("-bf-call-stack is allowed only in conjuction with -bf-by-func"); create_global_constant(module, "bf_call_stack", bool(TrackCallStack)); // Assign a value to bf_unique_bytes. create_global_constant(module, "bf_unique_bytes", bool(TrackUniqueBytes)); // Assign a value to bf_mem_footprint. create_global_constant(module, "bf_mem_footprint", bool(FindMemFootprint)); // Assign a value to bf_vectors. create_global_constant(module, "bf_vectors", bool(TallyVectors)); // Assign a value to bf_max_reuse_dist. create_global_constant(module, "bf_max_reuse_distance", uint64_t(MaxReuseDist)); // Create a global string that stores all of our command-line options. ifstream cmdline("/proc/self/cmdline"); // Full command line passed to opt string bf_cmdline("[failed to read /proc/self/cmdline]"); // Reconstructed command line with -bf-* options only if (cmdline.is_open()) { // Read the command line into a buffer. const size_t maxcmdlinelen = 65536; char cmdline_chars[maxcmdlinelen] = {0}; cmdline.read(cmdline_chars, maxcmdlinelen); cmdline.close(); // Parse the command line. Each argument is terminated by a // null character, and the command line as a whole is terminated // by two null characters. if (!cmdline.bad()) { char* arg = cmdline_chars; bf_cmdline = ""; while (1) { size_t arglen = strlen(arg); if (arglen == 0) break; if (!strncmp(arg, "-bf", 3)) { bf_cmdline += ' '; bf_cmdline += arg; } arg += arglen + 1; } } } const char *bf_cmdline_str = bf_cmdline.c_str(); if (bf_cmdline_str[0] == ' ') bf_cmdline_str++; bf_cmdline_str = strdup(bf_cmdline_str); create_global_constant(module, "bf_option_string", bf_cmdline_str); /** * Instead of using a map with the function names as keys, we associate a unique * integer with each function and use that as the key. We maintain the association * of the function names to their integer keys at compile time, then create a * constructor to record the map via a call to bf_record_funcs2keys. */ vector<Type*> func_arg; func_arg.push_back(IntegerType::get(globctx, 8*sizeof(uint32_t))); func_arg.push_back(PointerType::get(IntegerType::get(globctx, 8*sizeof(uint64_t)),0)); PointerType* char_ptr = PointerType::get(IntegerType::get(globctx, 8), 0); PointerType* char_ptr_ptr = PointerType::get(char_ptr, 0); func_arg.push_back(char_ptr_ptr); FunctionType* void_int_func_result = FunctionType::get(Type::getVoidTy(globctx), func_arg, false); record_funcs2keys = declare_extern_c(void_int_func_result, "bf_record_funcs2keys", &module); // Inject external declarations for // bf_initialize_if_necessary(), bf_push_basic_block(), and // bf_pop_basic_block(). init_if_necessary = declare_thunk(&module, "bf_initialize_if_necessary"); push_bb = declare_thunk(&module, "bf_push_basic_block"); pop_bb = declare_thunk(&module, "bf_pop_basic_block"); // Inject external declarations for bf_accumulate_bb_tallies(), // bf_reset_bb_tallies(), and bf_report_bb_tallies(). if (InstrumentEveryBB) { accum_bb_tallies = declare_thunk(&module, "bf_accumulate_bb_tallies"); reset_bb_tallies = declare_thunk(&module, "bf_reset_bb_tallies"); report_bb_tallies = declare_thunk(&module, "bf_report_bb_tallies"); } // Inject an external declarations for bf_increment_func_tally(). assoc_counts_with_func = 0; tally_function = 0; push_function = 0; pop_function = 0; if (TallyByFunction) { // bf_assoc_counters_with_func vector<Type*> func_arg; // arg: key ID func_arg.push_back(IntegerType::get(globctx, 8*sizeof(FunctionKeyGen::KeyID))); FunctionType* void_func_result = FunctionType::get(Type::getVoidTy(globctx), func_arg, false); assoc_counts_with_func = declare_extern_c(void_func_result, "bf_assoc_counters_with_func", &module); vector<Type*> taly_func_arg; taly_func_arg.push_back(PointerType::get(IntegerType::get(globctx, 8), 0)); // add 2nd arg for function key taly_func_arg.push_back(IntegerType::get(globctx, 8*sizeof(FunctionKeyGen::KeyID))); FunctionType* void_str_int_func_result = FunctionType::get(Type::getVoidTy(globctx), taly_func_arg, false); tally_function = declare_extern_c(void_func_result, "bf_incr_func_tally", &module); if ( TrackCallStack ) { // Inject external declarations for bf_push_function() and // bf_pop_function(). // bf_push_function() push_function = declare_extern_c(void_str_int_func_result, "bf_push_function", &module); // bf_pop_function() pop_function = declare_thunk(&module, "bf_pop_function"); } } // Declare bf_tally_vector_operation() only if we were asked // to track vector operations. if (TallyVectors) { vector<Type*> all_function_args; all_function_args.push_back(PointerType::get(IntegerType::get(globctx, 8), 0)); all_function_args.push_back(IntegerType::get(globctx, 64)); all_function_args.push_back(IntegerType::get(globctx, 64)); all_function_args.push_back(IntegerType::get(globctx, 8)); FunctionType* void_func_result = FunctionType::get(Type::getVoidTy(globctx), all_function_args, false); tally_vector = declare_extern_c(void_func_result, "bf_tally_vector_operation", &module); } // Inject external declarations for bf_assoc_addresses_with_prog() // and bf_assoc_addresses_with_func(). if (TrackUniqueBytes) { // Declare bf_assoc_addresses_with_prog() any time we need to // track unique bytes. vector<Type*> all_function_args; all_function_args.push_back(IntegerType::get(globctx, 64)); all_function_args.push_back(IntegerType::get(globctx, 64)); FunctionType* void_func_result = FunctionType::get(Type::getVoidTy(globctx), all_function_args, false); assoc_addrs_with_prog = declare_extern_c(void_func_result, FindMemFootprint ? "bf_assoc_addresses_with_prog_tb" : "bf_assoc_addresses_with_prog", &module); // Declare bf_assoc_addresses_with_func() only if we were // asked to track unique addresses by function. if (TallyByFunction) { vector<Type*> all_function_args; all_function_args.push_back(PointerType::get(IntegerType::get(globctx, 8), 0)); all_function_args.push_back(IntegerType::get(globctx, 64)); all_function_args.push_back(IntegerType::get(globctx, 64)); FunctionType* void_func_result = FunctionType::get(Type::getVoidTy(globctx), all_function_args, false); assoc_addrs_with_func = declare_extern_c(void_func_result, FindMemFootprint ? "bf_assoc_addresses_with_func_tb" : "bf_assoc_addresses_with_func", &module); } } // Inject an external declaration for llvm.memset.p0i8.i64(). memset_intrinsic = module.getFunction("llvm.memset.p0i8.i64"); if (memset_intrinsic == NULL) { vector<Type*> all_function_args; all_function_args.push_back(PointerType::get(IntegerType::get(globctx, 8), 0)); all_function_args.push_back(IntegerType::get(globctx, 8)); all_function_args.push_back(IntegerType::get(globctx, 64)); all_function_args.push_back(IntegerType::get(globctx, 32)); all_function_args.push_back(IntegerType::get(globctx, 1)); FunctionType* void_func_result = FunctionType::get(Type::getVoidTy(globctx), all_function_args, false); memset_intrinsic = declare_extern_c(void_func_result, "llvm.memset.p0i8.i64", &module); } // Simplify ReuseDist.getBits() into rd_bits. rd_bits = ReuseDist.getBits(); if ((rd_bits&(1<<RD_BOTH)) != 0) rd_bits = (1<<RD_LOADS) | (1<<RD_STORES); // Inject external declarations for bf_reuse_dist_addrs_prog(). if (rd_bits > 0) { vector<Type*> all_function_args; all_function_args.push_back(IntegerType::get(globctx, 64)); all_function_args.push_back(IntegerType::get(globctx, 64)); FunctionType* void_func_result = FunctionType::get(Type::getVoidTy(globctx), all_function_args, false); reuse_dist_prog = declare_extern_c(void_func_result, "bf_reuse_dist_addrs_prog", &module); } // Inject external declarations for bf_acquire_mega_lock() and // bf_release_mega_lock(). if (ThreadSafety) { take_mega_lock = declare_thunk(&module, "bf_acquire_mega_lock"); release_mega_lock = declare_thunk(&module, "bf_release_mega_lock"); } // initialize the function key generator FunctionKeyGen::Seed_t seed; std::hash<std::string> hash_key; seed = hash_key(module.getModuleIdentifier()); m_keygen = std::unique_ptr<FunctionKeyGen>(new FunctionKeyGen(seed)); return true; }
ObjectImage *RuntimeDyldImpl::loadObject(ObjectBuffer *InputBuffer) { OwningPtr<ObjectImage> obj(createObjectImage(InputBuffer)); if (!obj) report_fatal_error("Unable to create object image from memory buffer!"); Arch = (Triple::ArchType)obj->getArch(); // Symbols found in this object StringMap<SymbolLoc> LocalSymbols; // Used sections from the object file ObjSectionToIDMap LocalSections; // Common symbols requiring allocation, with their sizes and alignments CommonSymbolMap CommonSymbols; // Maximum required total memory to allocate all common symbols uint64_t CommonSize = 0; error_code err; // Parse symbols DEBUG(dbgs() << "Parse symbols:\n"); for (symbol_iterator i = obj->begin_symbols(), e = obj->end_symbols(); i != e; i.increment(err)) { Check(err); object::SymbolRef::Type SymType; StringRef Name; Check(i->getType(SymType)); Check(i->getName(Name)); uint32_t flags; Check(i->getFlags(flags)); bool isCommon = flags & SymbolRef::SF_Common; if (isCommon) { // Add the common symbols to a list. We'll allocate them all below. uint32_t Align; Check(i->getAlignment(Align)); uint64_t Size = 0; Check(i->getSize(Size)); CommonSize += Size + Align; CommonSymbols[*i] = CommonSymbolInfo(Size, Align); } else { if (SymType == object::SymbolRef::ST_Function || SymType == object::SymbolRef::ST_Data || SymType == object::SymbolRef::ST_Unknown) { uint64_t FileOffset; StringRef SectionData; bool IsCode; section_iterator si = obj->end_sections(); Check(i->getFileOffset(FileOffset)); Check(i->getSection(si)); if (si == obj->end_sections()) continue; Check(si->getContents(SectionData)); Check(si->isText(IsCode)); const uint8_t* SymPtr = (const uint8_t*)InputBuffer->getBufferStart() + (uintptr_t)FileOffset; uintptr_t SectOffset = (uintptr_t)(SymPtr - (const uint8_t*)SectionData.begin()); unsigned SectionID = findOrEmitSection(*obj, *si, IsCode, LocalSections); LocalSymbols[Name.data()] = SymbolLoc(SectionID, SectOffset); DEBUG(dbgs() << "\tFileOffset: " << format("%p", (uintptr_t)FileOffset) << " flags: " << flags << " SID: " << SectionID << " Offset: " << format("%p", SectOffset)); GlobalSymbolTable[Name] = SymbolLoc(SectionID, SectOffset); } } DEBUG(dbgs() << "\tType: " << SymType << " Name: " << Name << "\n"); } // Allocate common symbols if (CommonSize != 0) emitCommonSymbols(*obj, CommonSymbols, CommonSize, LocalSymbols); // Parse and process relocations DEBUG(dbgs() << "Parse relocations:\n"); for (section_iterator si = obj->begin_sections(), se = obj->end_sections(); si != se; si.increment(err)) { Check(err); bool isFirstRelocation = true; unsigned SectionID = 0; StubMap Stubs; for (relocation_iterator i = si->begin_relocations(), e = si->end_relocations(); i != e; i.increment(err)) { Check(err); // If it's the first relocation in this section, find its SectionID if (isFirstRelocation) { SectionID = findOrEmitSection(*obj, *si, true, LocalSections); DEBUG(dbgs() << "\tSectionID: " << SectionID << "\n"); isFirstRelocation = false; } processRelocationRef(SectionID, *i, *obj, LocalSections, LocalSymbols, Stubs); } } return obj.take(); }
template <class ELFT> void printProgramHeaders(const ELFFile<ELFT> *o) { outs() << "Program Header:\n"; auto ProgramHeaderOrError = o->program_headers(); if (!ProgramHeaderOrError) report_fatal_error(toString(ProgramHeaderOrError.takeError())); for (const typename ELFT::Phdr &Phdr : *ProgramHeaderOrError) { switch (Phdr.p_type) { case ELF::PT_DYNAMIC: outs() << " DYNAMIC "; break; case ELF::PT_GNU_EH_FRAME: outs() << "EH_FRAME "; break; case ELF::PT_GNU_RELRO: outs() << " RELRO "; break; case ELF::PT_GNU_STACK: outs() << " STACK "; break; case ELF::PT_INTERP: outs() << " INTERP "; break; case ELF::PT_LOAD: outs() << " LOAD "; break; case ELF::PT_NOTE: outs() << " NOTE "; break; case ELF::PT_OPENBSD_BOOTDATA: outs() << " OPENBSD_BOOTDATA "; break; case ELF::PT_OPENBSD_RANDOMIZE: outs() << " OPENBSD_RANDOMIZE "; break; case ELF::PT_OPENBSD_WXNEEDED: outs() << " OPENBSD_WXNEEDED "; break; case ELF::PT_PHDR: outs() << " PHDR "; break; case ELF::PT_TLS: outs() << " TLS "; break; default: outs() << " UNKNOWN "; } const char *Fmt = ELFT::Is64Bits ? "0x%016" PRIx64 " " : "0x%08" PRIx64 " "; outs() << "off " << format(Fmt, (uint64_t)Phdr.p_offset) << "vaddr " << format(Fmt, (uint64_t)Phdr.p_vaddr) << "paddr " << format(Fmt, (uint64_t)Phdr.p_paddr) << format("align 2**%u\n", countTrailingZeros<uint64_t>(Phdr.p_align)) << " filesz " << format(Fmt, (uint64_t)Phdr.p_filesz) << "memsz " << format(Fmt, (uint64_t)Phdr.p_memsz) << "flags " << ((Phdr.p_flags & ELF::PF_R) ? "r" : "-") << ((Phdr.p_flags & ELF::PF_W) ? "w" : "-") << ((Phdr.p_flags & ELF::PF_X) ? "x" : "-") << "\n"; } outs() << "\n"; }
unsigned RuntimeDyldImpl::emitSection(ObjectImage &Obj, const SectionRef &Section, bool IsCode) { unsigned StubBufSize = 0, StubSize = getMaxStubSize(); error_code err; if (StubSize > 0) { for (relocation_iterator i = Section.begin_relocations(), e = Section.end_relocations(); i != e; i.increment(err), Check(err)) StubBufSize += StubSize; } StringRef data; uint64_t Alignment64; Check(Section.getContents(data)); Check(Section.getAlignment(Alignment64)); unsigned Alignment = (unsigned)Alignment64 & 0xffffffffL; bool IsRequired; bool IsVirtual; bool IsZeroInit; bool IsReadOnly; uint64_t DataSize; StringRef Name; Check(Section.isRequiredForExecution(IsRequired)); Check(Section.isVirtual(IsVirtual)); Check(Section.isZeroInit(IsZeroInit)); Check(Section.isReadOnlyData(IsReadOnly)); Check(Section.getSize(DataSize)); Check(Section.getName(Name)); if (StubSize > 0) { unsigned StubAlignment = getStubAlignment(); unsigned EndAlignment = (DataSize | Alignment) & -(DataSize | Alignment); if (StubAlignment > EndAlignment) StubBufSize += StubAlignment - EndAlignment; } unsigned Allocate; unsigned SectionID = Sections.size(); uint8_t *Addr; const char *pData = 0; // Some sections, such as debug info, don't need to be loaded for execution. // Leave those where they are. if (IsRequired) { Allocate = DataSize + StubBufSize; Addr = IsCode ? MemMgr->allocateCodeSection(Allocate, Alignment, SectionID) : MemMgr->allocateDataSection(Allocate, Alignment, SectionID, IsReadOnly); if (!Addr) report_fatal_error("Unable to allocate section memory!"); // Virtual sections have no data in the object image, so leave pData = 0 if (!IsVirtual) pData = data.data(); // Zero-initialize or copy the data from the image if (IsZeroInit || IsVirtual) memset(Addr, 0, DataSize); else memcpy(Addr, pData, DataSize); DEBUG(dbgs() << "emitSection SectionID: " << SectionID << " Name: " << Name << " obj addr: " << format("%p", pData) << " new addr: " << format("%p", Addr) << " DataSize: " << DataSize << " StubBufSize: " << StubBufSize << " Allocate: " << Allocate << "\n"); Obj.updateSectionAddress(Section, (uint64_t)Addr); } else { // Even if we didn't load the section, we need to record an entry for it // to handle later processing (and by 'handle' I mean don't do anything // with these sections). Allocate = 0; Addr = 0; DEBUG(dbgs() << "emitSection SectionID: " << SectionID << " Name: " << Name << " obj addr: " << format("%p", data.data()) << " new addr: 0" << " DataSize: " << DataSize << " StubBufSize: " << StubBufSize << " Allocate: " << Allocate << "\n"); } Sections.push_back(SectionEntry(Name, Addr, DataSize, (uintptr_t)pData)); return SectionID; }