[llvm] r188726 - Adding PIC support for ELF on x86_64 platforms

Lang Hames lhames at gmail.com
Mon Jan 26 13:14:04 PST 2015


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-commits/attachments/20150126/add75119/attachment.html>


More information about the llvm-commits mailing list