[llvm] 0bc845f - Return an error when dsymutil might produce an invalid mach-o file.

Greg Clayton via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 11 10:00:49 PST 2022


Author: Greg Clayton
Date: 2022-03-11T10:00:39-08:00
New Revision: 0bc845fe24ce9557d4e0f723de5be2322ea6e547

URL: https://github.com/llvm/llvm-project/commit/0bc845fe24ce9557d4e0f723de5be2322ea6e547
DIFF: https://github.com/llvm/llvm-project/commit/0bc845fe24ce9557d4e0f723de5be2322ea6e547.diff

LOG: Return an error when dsymutil might produce an invalid mach-o file.

64 bit mach-o files have sections that only have 32 bit file offsets. If dsymutil tries to produce an invalid mach-o file, then error out with a good error string.

Differential Revision: https://reviews.llvm.org/D121398

Added: 
    

Modified: 
    llvm/tools/dsymutil/MachOUtils.cpp
    llvm/tools/dsymutil/dsymutil.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/tools/dsymutil/MachOUtils.cpp b/llvm/tools/dsymutil/MachOUtils.cpp
index b4f3f334ba012..324e87b311efb 100644
--- a/llvm/tools/dsymutil/MachOUtils.cpp
+++ b/llvm/tools/dsymutil/MachOUtils.cpp
@@ -304,7 +304,7 @@ static void transferSegmentAndSections(
 }
 
 // Write the __DWARF segment load command to the output file.
-static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset,
+static bool createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset,
                                uint64_t FileSize, unsigned NumSections,
                                MCAsmLayout &Layout, MachObjectWriter &Writer) {
   Writer.writeSegmentLoadCommand("__DWARF", NumSections, VMAddr,
@@ -321,12 +321,16 @@ static void createDwarfSegment(uint64_t VMAddr, uint64_t FileOffset,
     if (Align > 1) {
       VMAddr = alignTo(VMAddr, Align);
       FileOffset = alignTo(FileOffset, Align);
+      if (FileOffset > UINT32_MAX)
+        return error("section " + Sec->getName() + "'s file offset exceeds 4GB."
+            " Refusing to produce an invalid Mach-O file.");
     }
     Writer.writeSection(Layout, *Sec, VMAddr, FileOffset, 0, 0, 0);
 
     FileOffset += Layout.getSectionAddressSize(Sec);
     VMAddr += Layout.getSectionAddressSize(Sec);
   }
+  return true;
 }
 
 static bool isExecutable(const object::MachOObjectFile &Obj) {
@@ -564,8 +568,9 @@ bool generateDsymCompanion(llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
   }
 
   // Write the load command for the __DWARF segment.
-  createDwarfSegment(DwarfVMAddr, DwarfSegmentStart, DwarfSegmentSize,
-                     NumDwarfSections, Layout, Writer);
+  if (!createDwarfSegment(DwarfVMAddr, DwarfSegmentStart, DwarfSegmentSize,
+                          NumDwarfSections, Layout, Writer))
+    return false;
 
   assert(OutFile.tell() == LoadCommandSize + HeaderSize);
   OutFile.write_zeros(SymtabStart - (LoadCommandSize + HeaderSize));

diff  --git a/llvm/tools/dsymutil/dsymutil.cpp b/llvm/tools/dsymutil/dsymutil.cpp
index 3248d190a7b4d..bf742de5220bb 100644
--- a/llvm/tools/dsymutil/dsymutil.cpp
+++ b/llvm/tools/dsymutil/dsymutil.cpp
@@ -730,24 +730,40 @@ int main(int argc, char **argv) {
       return EXIT_FAILURE;
 
     if (NeedsTempFiles) {
+      // Universal Mach-O files can't have an archicture slice that starts
+      // beyond the 4GB boundary. "lipo" can creeate a 64 bit universal header,
+      // but not all tools can parse these files so we want to return an error
+      // if the file can't be encoded as a file with a 32 bit universal header.
+      // To detect this, we check the size of each architecture's skinny Mach-O
+      // file and add up the offsets. If they exceed 4GB, then we return an
+      // error.
+
+      // First we compute the right offset where the first architecture will fit
+      // followin the 32 bit universal header. The 32 bit universal header
+      // starts with a uint32_t magic and a uint32_t number of architecture
+      // infos. Then it is followed by 5 uint32_t values for each architecture.
+      // So we set the start offset to the right value so we can calculate the
+      // exact offset that the first architecture slice can start at.
+      constexpr uint64_t MagicAndCountSize = 2 * 4;
+      constexpr uint64_t UniversalArchInfoSize = 5 * 4;
+      uint64_t FileOffset = MagicAndCountSize +
+          UniversalArchInfoSize * TempFiles.size();
+      for (const auto &File: TempFiles) {
+        ErrorOr<vfs::Status> stat = Options.LinkOpts.VFS->status(File.path());
+        if (!stat)
+          break;
+        FileOffset += stat->getSize();
+        if (FileOffset > UINT32_MAX) {
+          WithColor::error() << "the univesral binary has a slice with an "
+              "offset exceeds 4GB and will produce an invalid Mach-O file.";
+          return EXIT_FAILURE;
+        }
+      }
       if (!MachOUtils::generateUniversalBinary(TempFiles,
                                                OutputLocationOrErr->DWARFFile,
                                                Options.LinkOpts, SDKPath))
         return EXIT_FAILURE;
     }
-
-    // The Mach-O object file format is limited to 4GB. Make sure that we print
-    // an error when we emit an invalid Mach-O companion file. Leave the
-    // invalid object file around on disk for inspection.
-    ErrorOr<vfs::Status> stat =
-        Options.LinkOpts.VFS->status(OutputLocationOrErr->DWARFFile);
-    if (stat) {
-      if (stat->getSize() > std::numeric_limits<uint32_t>::max()) {
-        WithColor::error() << "the linked debug info exceeds the 4GB Mach-O "
-                              "object file format.";
-        return EXIT_FAILURE;
-      }
-    }
   }
 
   return EXIT_SUCCESS;


        


More information about the llvm-commits mailing list