[LLVMdev] [llvm] r188726 - Adding PIC support for ELF on x86_64 platforms
Lang Hames
lhames at gmail.com
Mon Jan 26 21:07:55 PST 2015
Hi Andy,
Thanks very much for the insight. I'll see if I can come up with a scheme
to support reapplication without having the object file present.
Cheers,
Lang.
On Mon, Jan 26, 2015 at 1:59 PM, Kaylor, Andrew <andrew.kaylor at intel.com>
wrote:
> Hi Lang,
>
>
>
> Yeah, I remember this case. Basically what’s happening is that there are
> relocations for ELF on x86 that use a value that is present in the object
> image as part of the calculation for the final value that goes in the same
> location. If you ever find yourself applying relocations for a second time
> (for instance, because the loaded object location is remapped for
> out-of-proc execution) the original value is no longer in the loaded object.
>
>
>
> You could probably figure out a way to combine the placeholder value with
> the addend field, either while the relocation records are being built
> (though I’m not sure if the relevant sections have been loaded yet at that
> point) or the first time the relocation is applied (though it might be a
> bit cumbersome to know whether or not any given application was the first).
>
>
>
> -Andy
>
>
>
>
>
> *From:* Lang Hames [mailto:lhames at gmail.com]
> *Sent:* Monday, January 26, 2015 1:14 PM
> *To:* Kaylor, Andrew
> *Cc:* Commit Messages and Patches for LLVM
> *Subject:* Re: [llvm] r188726 - Adding PIC support for ELF on x86_64
> platforms
>
>
>
> Hi Andy,
>
>
>
> In the following snippet, do you recall what made it necessary to read the
> value from the object file via Placeholder, rather than from the copied
> section?
>
>
>
> + // Get the placeholder value from the generated object since
> + // a previous relocation attempt may have overwritten the loaded
> version
> + uint64_t *Placeholder = reinterpret_cast<uint64_t*>(Section.ObjAddress
> + +
> Offset);
> + uint64_t *Target = reinterpret_cast<uint64_t*>(Section.Address +
> Offset);
> + uint64_t FinalAddress = Section.LoadAddress + Offset;
> + *Target = *Placeholder + Value + Addend - FinalAddress;
> + break;
>
>
>
> I''d like to make my new JIT APIs more aggressive about freeing the
> ObjectFile instances (ideally we'd be able to free immediately after a call
> to loadObject), but at the moment I have to hold it at least until
> resolveRelocations is called.
>
>
>
> I had a quick chat with Tim Northover about this and our guess was that it
> had something to do with relocations being applied more than once?
>
>
>
> Cheers,
>
> Lang.
>
>
>
>
>
> On Mon, Aug 19, 2013 at 4:27 PM, Andrew Kaylor <andrew.kaylor at intel.com>
> wrote:
>
> Author: akaylor
> Date: Mon Aug 19 18:27:43 2013
> New Revision: 188726
>
> URL: http://llvm.org/viewvc/llvm-project?rev=188726&view=rev
> Log:
> Adding PIC support for ELF on x86_64 platforms
>
> Modified:
> llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp
> llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp
> llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
> llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h
>
> Modified: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp?rev=188726&r1=188725&r2=188726&view=diff
>
> ==============================================================================
> --- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp (original)
> +++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyld.cpp Mon Aug 19
> 18:27:43 2013
> @@ -169,6 +169,9 @@ ObjectImage *RuntimeDyldImpl::loadObject
> }
> }
>
> + // Give the subclasses a chance to tie-up any loose ends.
> + finalizeLoad();
> +
> return obj.take();
> }
>
> @@ -424,6 +427,10 @@ uint8_t *RuntimeDyldImpl::createStubFunc
> writeInt16BE(Addr+6, 0x07F1); // brc 15,%r1
> // 8-byte address stored at Addr + 8
> return Addr;
> + } else if (Arch == Triple::x86_64) {
> + *Addr = 0xFF; // jmp
> + *(Addr+1) = 0x25; // rip
> + // 32-bit PC-relative address of the GOT entry will be stored at
> Addr+2
> }
> return Addr;
> }
> @@ -473,6 +480,7 @@ void RuntimeDyldImpl::resolveExternalSym
> // MemoryManager.
> uint8_t *Addr = (uint8_t*)
> MemMgr->getPointerToNamedFunction(Name.data(),
> true);
> + updateGOTEntries(Name, (uint64_t)Addr);
> DEBUG(dbgs() << "Resolving relocations Name: " << Name
> << "\t" << format("%p", Addr)
> << "\n");
>
> Modified: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp?rev=188726&r1=188725&r2=188726&view=diff
>
> ==============================================================================
> --- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp
> (original)
> +++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.cpp Mon Aug
> 19 18:27:43 2013
> @@ -202,7 +202,8 @@ void RuntimeDyldELF::resolveX86_64Reloca
> uint64_t Offset,
> uint64_t Value,
> uint32_t Type,
> - int64_t Addend) {
> + int64_t Addend,
> + uint64_t SymOffset) {
> switch (Type) {
> default:
> llvm_unreachable("Relocation type not implemented yet!");
> @@ -227,6 +228,21 @@ void RuntimeDyldELF::resolveX86_64Reloca
> << " at " << format("%p\n",Target));
> break;
> }
> + case ELF::R_X86_64_GOTPCREL: {
> + // findGOTEntry returns the 'G + GOT' part of the relocation
> calculation
> + // based on the load/target address of the GOT (not the current/local
> addr).
> + uint64_t GOTAddr = findGOTEntry(Value, SymOffset);
> + uint32_t *Target = reinterpret_cast<uint32_t*>(Section.Address +
> Offset);
> + uint64_t FinalAddress = Section.LoadAddress + Offset;
> + // The processRelocationRef method combines the symbol offset and the
> addend
> + // and in most cases that's what we want. For this relocation type,
> we need
> + // the raw addend, so we subtract the symbol offset to get it.
> + int64_t RealOffset = GOTAddr + Addend - SymOffset - FinalAddress;
> + assert(RealOffset <= INT32_MAX && RealOffset >= INT32_MIN);
> + int32_t TruncOffset = (RealOffset & 0xFFFFFFFF);
> + *Target = TruncOffset;
> + break;
> + }
> case ELF::R_X86_64_PC32: {
> // Get the placeholder value from the generated object since
> // a previous relocation attempt may have overwritten the loaded
> version
> @@ -240,6 +256,16 @@ void RuntimeDyldELF::resolveX86_64Reloca
> *Target = TruncOffset;
> break;
> }
> + case ELF::R_X86_64_PC64: {
> + // Get the placeholder value from the generated object since
> + // a previous relocation attempt may have overwritten the loaded
> version
> + uint64_t *Placeholder = reinterpret_cast<uint64_t*>(Section.ObjAddress
> + +
> Offset);
> + uint64_t *Target = reinterpret_cast<uint64_t*>(Section.Address +
> Offset);
> + uint64_t FinalAddress = Section.LoadAddress + Offset;
> + *Target = *Placeholder + Value + Addend - FinalAddress;
> + break;
> + }
> }
> }
>
> @@ -584,7 +610,7 @@ void RuntimeDyldELF::findOPDEntrySection
> // Finally compares the Symbol value and the target symbol offset
> // to check if this .opd entry refers to the symbol the relocation
> // points to.
> - if (Rel.Addend != (intptr_t)TargetSymbolOffset)
> + if (Rel.Addend != (int64_t)TargetSymbolOffset)
> continue;
>
> section_iterator tsi(Obj.end_sections());
> @@ -757,17 +783,19 @@ void RuntimeDyldELF::resolveSystemZReloc
> void RuntimeDyldELF::resolveRelocation(const RelocationEntry &RE,
> uint64_t Value) {
> const SectionEntry &Section = Sections[RE.SectionID];
> - return resolveRelocation(Section, RE.Offset, Value, RE.RelType,
> RE.Addend);
> + return resolveRelocation(Section, RE.Offset, Value, RE.RelType,
> RE.Addend,
> + RE.SymOffset);
> }
>
> void RuntimeDyldELF::resolveRelocation(const SectionEntry &Section,
> uint64_t Offset,
> uint64_t Value,
> uint32_t Type,
> - int64_t Addend) {
> + int64_t Addend,
> + uint64_t SymOffset) {
> switch (Arch) {
> case Triple::x86_64:
> - resolveX86_64Relocation(Section, Offset, Value, Type, Addend);
> + resolveX86_64Relocation(Section, Offset, Value, Type, Addend,
> SymOffset);
> break;
> case Triple::x86:
> resolveX86Relocation(Section, Offset,
> @@ -830,6 +858,7 @@ void RuntimeDyldELF::processRelocationRe
> }
> if (lsi != Symbols.end()) {
> Value.SectionID = lsi->second.first;
> + Value.Offset = lsi->second.second;
> Value.Addend = lsi->second.second + Addend;
> } else {
> // Search for the symbol in the global symbol table
> @@ -838,6 +867,7 @@ void RuntimeDyldELF::processRelocationRe
> gsi = GlobalSymbolTable.find(TargetName.data());
> if (gsi != GlobalSymbolTable.end()) {
> Value.SectionID = gsi->second.first;
> + Value.Offset = gsi->second.second;
> Value.Addend = gsi->second.second + Addend;
> } else {
> switch (SymType) {
> @@ -860,6 +890,7 @@ void RuntimeDyldELF::processRelocationRe
> Value.Addend = Addend;
> break;
> }
> + case SymbolRef::ST_Data:
> case SymbolRef::ST_Unknown: {
> Value.SymbolName = TargetName.data();
> Value.Addend = Addend;
> @@ -1150,8 +1181,67 @@ void RuntimeDyldELF::processRelocationRe
> ELF::R_390_PC32DBL, Addend);
> else
> resolveRelocation(Section, Offset, StubAddress, RelType, Addend);
> + } else if (Arch == Triple::x86_64 && RelType == ELF::R_X86_64_PLT32) {
> + // The way the PLT relocations normally work is that the linker
> allocates the
> + // PLT and this relocation makes a PC-relative call into the PLT.
> The PLT
> + // entry will then jump to an address provided by the GOT. On first
> call, the
> + // GOT address will point back into PLT code that resolves the
> symbol. After
> + // the first call, the GOT entry points to the actual function.
> + //
> + // For local functions we're ignoring all of that here and just
> replacing
> + // the PLT32 relocation type with PC32, which will translate the
> relocation
> + // into a PC-relative call directly to the function. For external
> symbols we
> + // can't be sure the function will be within 2^32 bytes of the call
> site, so
> + // we need to create a stub, which calls into the GOT. This case is
> + // equivalent to the usual PLT implementation except that we use the
> stub
> + // mechanism in RuntimeDyld (which puts stubs at the end of the
> section)
> + // rather than allocating a PLT section.
> + if (Value.SymbolName) {
> + // This is a call to an external function.
> + // Look for an existing stub.
> + SectionEntry &Section = Sections[SectionID];
> + StubMap::const_iterator i = Stubs.find(Value);
> + uintptr_t StubAddress;
> + if (i != Stubs.end()) {
> + StubAddress = uintptr_t(Section.Address) + i->second;
> + DEBUG(dbgs() << " Stub function found\n");
> + } else {
> + // Create a new stub function (equivalent to a PLT entry).
> + DEBUG(dbgs() << " Create a new stub function\n");
> +
> + uintptr_t BaseAddress = uintptr_t(Section.Address);
> + uintptr_t StubAlignment = getStubAlignment();
> + StubAddress = (BaseAddress + Section.StubOffset +
> + StubAlignment - 1) & -StubAlignment;
> + unsigned StubOffset = StubAddress - BaseAddress;
> + Stubs[Value] = StubOffset;
> + createStubFunction((uint8_t *)StubAddress);
> +
> + // Create a GOT entry for the external function.
> + GOTEntries.push_back(Value);
> +
> + // Make our stub function a relative call to the GOT entry.
> + RelocationEntry RE(SectionID, StubOffset + 2,
> + ELF::R_X86_64_GOTPCREL, -4);
> + addRelocationForSymbol(RE, Value.SymbolName);
> +
> + // Bump our stub offset counter
> + Section.StubOffset = StubOffset + getMaxStubSize();
> + }
> +
> + // Make the target call a call into the stub table.
> + resolveRelocation(Section, Offset, StubAddress,
> + ELF::R_X86_64_PC32, Addend);
> + } else {
> + RelocationEntry RE(SectionID, Offset, ELF::R_X86_64_PC32,
> Value.Addend,
> + Value.Offset);
> + addRelocationForSection(RE, Value.SectionID);
> + }
> } else {
> - RelocationEntry RE(SectionID, Offset, RelType, Value.Addend);
> + if (Arch == Triple::x86_64 && RelType == ELF::R_X86_64_GOTPCREL) {
> + GOTEntries.push_back(Value);
> + }
> + RelocationEntry RE(SectionID, Offset, RelType, Value.Addend,
> Value.Offset);
> if (Value.SymbolName)
> addRelocationForSymbol(RE, Value.SymbolName);
> else
> @@ -1159,6 +1249,106 @@ void RuntimeDyldELF::processRelocationRe
> }
> }
>
> +void RuntimeDyldELF::updateGOTEntries(StringRef Name, uint64_t Addr) {
> + for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
> + if (GOTEntries[i].SymbolName != 0 && GOTEntries[i].SymbolName ==
> Name) {
> + GOTEntries[i].Offset = Addr;
> + }
> + }
> +}
> +
> +size_t RuntimeDyldELF::getGOTEntrySize() {
> + // We don't use the GOT in all of these cases, but it's essentially free
> + // to put them all here.
> + size_t Result = 0;
> + switch (Arch) {
> + case Triple::x86_64:
> + case Triple::aarch64:
> + case Triple::ppc64:
> + case Triple::ppc64le:
> + case Triple::systemz:
> + Result = sizeof(uint64_t);
> + break;
> + case Triple::x86:
> + case Triple::arm:
> + case Triple::thumb:
> + case Triple::mips:
> + case Triple::mipsel:
> + Result = sizeof(uint32_t);
> + break;
> + default: llvm_unreachable("Unsupported CPU type!");
> + }
> + return Result;
> +}
> +
> +uint64_t RuntimeDyldELF::findGOTEntry(uint64_t LoadAddress,
> + uint64_t Offset) {
> + assert(GOTSectionID != 0
> + && "Attempting to lookup GOT entry but the GOT was never
> allocated.");
> + if (GOTSectionID == 0) {
> + return 0;
> + }
> +
> + size_t GOTEntrySize = getGOTEntrySize();
> +
> + // Find the matching entry in our vector.
> + int GOTIndex = -1;
> + uint64_t SymbolOffset = 0;
> + for (int i = 0, e = GOTEntries.size(); i != e; ++i) {
> + if (GOTEntries[i].SymbolName == 0) {
> + if (getSectionLoadAddress(GOTEntries[i].SectionID) == LoadAddress &&
> + GOTEntries[i].Offset == Offset) {
> + GOTIndex = i;
> + SymbolOffset = GOTEntries[i].Offset;
> + break;
> + }
> + } else {
> + // GOT entries for external symbols use the addend as the address
> when
> + // the external symbol has been resolved.
> + if (GOTEntries[i].Offset == LoadAddress) {
> + GOTIndex = i;
> + // Don't use the Addend here. The relocation handler will use it.
> + break;
> + }
> + }
> + }
> + assert(GOTIndex != -1 && "Unable to find requested GOT entry.");
> + if (GOTIndex == -1)
> + return 0;
> +
> + if (GOTEntrySize == sizeof(uint64_t)) {
> + uint64_t *LocalGOTAddr = (uint64_t*)getSectionAddress(GOTSectionID);
> + // Fill in this entry with the address of the symbol being referenced.
> + LocalGOTAddr[GOTIndex] = LoadAddress + SymbolOffset;
> + } else {
> + uint32_t *LocalGOTAddr = (uint32_t*)getSectionAddress(GOTSectionID);
> + // Fill in this entry with the address of the symbol being referenced.
> + LocalGOTAddr[GOTIndex] = (uint32_t)(LoadAddress + SymbolOffset);
> + }
> +
> + // Calculate the load address of this entry
> + return getSectionLoadAddress(GOTSectionID) + (GOTIndex * GOTEntrySize);
> +}
> +
> +void RuntimeDyldELF::finalizeLoad() {
> + // Allocate the GOT if necessary
> + size_t numGOTEntries = GOTEntries.size();
> + if (numGOTEntries != 0) {
> + // Allocate memory for the section
> + unsigned SectionID = Sections.size();
> + size_t TotalSize = numGOTEntries * getGOTEntrySize();
> + uint8_t *Addr = MemMgr->allocateDataSection(TotalSize,
> getGOTEntrySize(),
> + SectionID, false);
> + if (!Addr)
> + report_fatal_error("Unable to allocate memory for GOT!");
> + Sections.push_back(SectionEntry(".got", Addr, TotalSize, 0));
> + // For now, initialize all GOT entries to zero. We'll fill them in as
> + // needed when GOT-based relocations are applied.
> + memset(Addr, 0, TotalSize);
> + GOTSectionID = SectionID;
> + }
> +}
> +
> bool RuntimeDyldELF::isCompatibleFormat(const ObjectBuffer *Buffer) const
> {
> if (Buffer->getBufferSize() < strlen(ELF::ElfMagic))
> return false;
>
> Modified: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h?rev=188726&r1=188725&r2=188726&view=diff
>
> ==============================================================================
> --- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h (original)
> +++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldELF.h Mon Aug 19
> 18:27:43 2013
> @@ -35,13 +35,15 @@ class RuntimeDyldELF : public RuntimeDyl
> uint64_t Offset,
> uint64_t Value,
> uint32_t Type,
> - int64_t Addend);
> + int64_t Addend,
> + uint64_t SymOffset=0);
>
> void resolveX86_64Relocation(const SectionEntry &Section,
> uint64_t Offset,
> uint64_t Value,
> uint32_t Type,
> - int64_t Addend);
> + int64_t Addend,
> + uint64_t SymOffset);
>
> void resolveX86Relocation(const SectionEntry &Section,
> uint64_t Offset,
> @@ -84,8 +86,18 @@ class RuntimeDyldELF : public RuntimeDyl
> ObjSectionToIDMap &LocalSections,
> RelocationValueRef &Rel);
>
> + uint64_t findGOTEntry(uint64_t LoadAddr, uint64_t Offset);
> + size_t getGOTEntrySize();
> +
> + virtual void updateGOTEntries(StringRef Name, uint64_t Addr);
> +
> + SmallVector<RelocationValueRef, 2> GOTEntries;
> + unsigned GOTSectionID;
> +
> public:
> - RuntimeDyldELF(RTDyldMemoryManager *mm) : RuntimeDyldImpl(mm) {}
> + RuntimeDyldELF(RTDyldMemoryManager *mm) : RuntimeDyldImpl(mm),
> + GOTSectionID(0)
> + {}
>
> virtual void resolveRelocation(const RelocationEntry &RE, uint64_t
> Value);
> virtual void processRelocationRef(unsigned SectionID,
> @@ -97,6 +109,7 @@ public:
> virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const;
> virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer);
> virtual StringRef getEHFrameSection();
> + virtual void finalizeLoad();
> virtual ~RuntimeDyldELF();
> };
>
>
> Modified: llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h
> URL:
> http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h?rev=188726&r1=188725&r2=188726&view=diff
>
> ==============================================================================
> --- llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h (original)
> +++ llvm/trunk/lib/ExecutionEngine/RuntimeDyld/RuntimeDyldImpl.h Mon Aug
> 19 18:27:43 2013
> @@ -80,14 +80,18 @@ public:
> unsigned SectionID;
>
> /// Offset - offset into the section.
> - uintptr_t Offset;
> + uint64_t Offset;
>
> /// RelType - relocation type.
> uint32_t RelType;
>
> /// Addend - the relocation addend encoded in the instruction itself.
> Also
> /// used to make a relocation section relative instead of symbol
> relative.
> - intptr_t Addend;
> + int64_t Addend;
> +
> + /// SymOffset - Section offset of the relocation entry's symbol (used
> for GOT
> + /// lookup).
> + uint64_t SymOffset;
>
> /// True if this is a PCRel relocation (MachO specific).
> bool IsPCRel;
> @@ -97,20 +101,26 @@ public:
>
> RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t
> addend)
> : SectionID(id), Offset(offset), RelType(type), Addend(addend),
> - IsPCRel(false), Size(0) {}
> + SymOffset(0), IsPCRel(false), Size(0) {}
> +
> + RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t
> addend,
> + uint64_t symoffset)
> + : SectionID(id), Offset(offset), RelType(type), Addend(addend),
> + SymOffset(symoffset), IsPCRel(false), Size(0) {}
>
> RelocationEntry(unsigned id, uint64_t offset, uint32_t type, int64_t
> addend,
> bool IsPCRel, unsigned Size)
> : SectionID(id), Offset(offset), RelType(type), Addend(addend),
> - IsPCRel(IsPCRel), Size(Size) {}
> + SymOffset(0), IsPCRel(IsPCRel), Size(Size) {}
> };
>
> class RelocationValueRef {
> public:
> unsigned SectionID;
> - intptr_t Addend;
> + uint64_t Offset;
> + int64_t Addend;
> const char *SymbolName;
> - RelocationValueRef(): SectionID(0), Addend(0), SymbolName(0) {}
> + RelocationValueRef(): SectionID(0), Offset(0), Addend(0), SymbolName(0)
> {}
>
> inline bool operator==(const RelocationValueRef &Other) const {
> return std::memcmp(this, &Other, sizeof(RelocationValueRef)) == 0;
> @@ -175,7 +185,7 @@ protected:
> else if (Arch == Triple::ppc64 || Arch == Triple::ppc64le)
> return 44;
> else if (Arch == Triple::x86_64)
> - return 8; // GOT
> + return 6; // 2-byte jmp instruction + 32-bit relative address
> else if (Arch == Triple::systemz)
> return 16;
> else
> @@ -292,6 +302,11 @@ protected:
>
> /// \brief Resolve relocations to external symbols.
> void resolveExternalSymbols();
> +
> + /// \brief Update GOT entries for external symbols.
> + // The base class does nothing. ELF overrides this.
> + virtual void updateGOTEntries(StringRef Name, uint64_t Addr) {}
> +
> virtual ObjectImage *createObjectImage(ObjectBuffer *InputBuffer);
> public:
> RuntimeDyldImpl(RTDyldMemoryManager *mm) : MemMgr(mm), HasError(false)
> {}
> @@ -336,6 +351,8 @@ public:
> virtual bool isCompatibleFormat(const ObjectBuffer *Buffer) const = 0;
>
> virtual StringRef getEHFrameSection();
> +
> + virtual void finalizeLoad() {}
> };
>
> } // end namespace llvm
>
>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at cs.uiuc.edu
> http://lists.cs.uiuc.edu/mailman/listinfo/llvm-commits
>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20150126/b0e4a683/attachment.html>
More information about the llvm-dev
mailing list