Home | History | Annotate | Line # | Download | only in Object
      1 //===-- Decompressor.cpp --------------------------------------------------===//
      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-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 
      9 #include "llvm/Object/Decompressor.h"
     10 #include "llvm/BinaryFormat/ELF.h"
     11 #include "llvm/Object/ELFObjectFile.h"
     12 #include "llvm/Support/Compression.h"
     13 #include "llvm/Support/DataExtractor.h"
     14 #include "llvm/Support/Endian.h"
     15 
     16 using namespace llvm;
     17 using namespace llvm::support::endian;
     18 using namespace object;
     19 
     20 Expected<Decompressor> Decompressor::create(StringRef Name, StringRef Data,
     21                                             bool IsLE, bool Is64Bit) {
     22   if (!zlib::isAvailable())
     23     return createError("zlib is not available");
     24 
     25   Decompressor D(Data);
     26   Error Err = isGnuStyle(Name) ? D.consumeCompressedGnuHeader()
     27                                : D.consumeCompressedZLibHeader(Is64Bit, IsLE);
     28   if (Err)
     29     return std::move(Err);
     30   return D;
     31 }
     32 
     33 Decompressor::Decompressor(StringRef Data)
     34     : SectionData(Data), DecompressedSize(0) {}
     35 
     36 Error Decompressor::consumeCompressedGnuHeader() {
     37   if (!SectionData.startswith("ZLIB"))
     38     return createError("corrupted compressed section header");
     39 
     40   SectionData = SectionData.substr(4);
     41 
     42   // Consume uncompressed section size (big-endian 8 bytes).
     43   if (SectionData.size() < 8)
     44     return createError("corrupted uncompressed section size");
     45   DecompressedSize = read64be(SectionData.data());
     46   SectionData = SectionData.substr(8);
     47 
     48   return Error::success();
     49 }
     50 
     51 Error Decompressor::consumeCompressedZLibHeader(bool Is64Bit,
     52                                                 bool IsLittleEndian) {
     53   using namespace ELF;
     54   uint64_t HdrSize = Is64Bit ? sizeof(Elf64_Chdr) : sizeof(Elf32_Chdr);
     55   if (SectionData.size() < HdrSize)
     56     return createError("corrupted compressed section header");
     57 
     58   DataExtractor Extractor(SectionData, IsLittleEndian, 0);
     59   uint64_t Offset = 0;
     60   if (Extractor.getUnsigned(&Offset, Is64Bit ? sizeof(Elf64_Word)
     61                                              : sizeof(Elf32_Word)) !=
     62       ELFCOMPRESS_ZLIB)
     63     return createError("unsupported compression type");
     64 
     65   // Skip Elf64_Chdr::ch_reserved field.
     66   if (Is64Bit)
     67     Offset += sizeof(Elf64_Word);
     68 
     69   DecompressedSize = Extractor.getUnsigned(
     70       &Offset, Is64Bit ? sizeof(Elf64_Xword) : sizeof(Elf32_Word));
     71   SectionData = SectionData.substr(HdrSize);
     72   return Error::success();
     73 }
     74 
     75 bool Decompressor::isGnuStyle(StringRef Name) {
     76   return Name.startswith(".zdebug");
     77 }
     78 
     79 bool Decompressor::isCompressed(const object::SectionRef &Section) {
     80   if (Section.isCompressed())
     81     return true;
     82 
     83   Expected<StringRef> SecNameOrErr = Section.getName();
     84   if (SecNameOrErr)
     85     return isGnuStyle(*SecNameOrErr);
     86 
     87   consumeError(SecNameOrErr.takeError());
     88   return false;
     89 }
     90 
     91 bool Decompressor::isCompressedELFSection(uint64_t Flags, StringRef Name) {
     92   return (Flags & ELF::SHF_COMPRESSED) || isGnuStyle(Name);
     93 }
     94 
     95 Error Decompressor::decompress(MutableArrayRef<char> Buffer) {
     96   size_t Size = Buffer.size();
     97   return zlib::uncompress(SectionData, Buffer.data(), Size);
     98 }
     99