1 //===- WasmObjcopy.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/ObjCopy/wasm/WasmObjcopy.h"
10 #include "WasmObject.h"
11 #include "WasmReader.h"
12 #include "WasmWriter.h"
13 #include "llvm/ObjCopy/CommonConfig.h"
14 #include "llvm/Support/Errc.h"
15 #include "llvm/Support/FileOutputBuffer.h"
16 
17 namespace llvm {
18 namespace objcopy {
19 namespace wasm {
20 
21 using namespace object;
22 using SectionPred = std::function<bool(const Section &Sec)>;
23 
24 static bool isDebugSection(const Section &Sec) {
25   return Sec.Name.startswith(".debug");
26 }
27 
28 static bool isLinkerSection(const Section &Sec) {
29   return Sec.Name.startswith("reloc.") || Sec.Name == "linking";
30 }
31 
32 static bool isNameSection(const Section &Sec) { return Sec.Name == "name"; }
33 
34 // Sections which are known to be "comments" or informational and do not affect
35 // program semantics.
36 static bool isCommentSection(const Section &Sec) {
37   return Sec.Name == "producers";
38 }
39 
40 static Error dumpSectionToFile(StringRef SecName, StringRef Filename,
41                                Object &Obj) {
42   for (const Section &Sec : Obj.Sections) {
43     if (Sec.Name == SecName) {
44       ArrayRef<uint8_t> Contents = Sec.Contents;
45       Expected<std::unique_ptr<FileOutputBuffer>> BufferOrErr =
46           FileOutputBuffer::create(Filename, Contents.size());
47       if (!BufferOrErr)
48         return BufferOrErr.takeError();
49       std::unique_ptr<FileOutputBuffer> Buf = std::move(*BufferOrErr);
50       std::copy(Contents.begin(), Contents.end(), Buf->getBufferStart());
51       if (Error E = Buf->commit())
52         return E;
53       return Error::success();
54     }
55   }
56   return createStringError(errc::invalid_argument, "section '%s' not found",
57                            SecName.str().c_str());
58 }
59 
60 static void removeSections(const CommonConfig &Config, Object &Obj) {
61   SectionPred RemovePred = [](const Section &) { return false; };
62 
63   // Explicitly-requested sections.
64   if (!Config.ToRemove.empty()) {
65     RemovePred = [&Config](const Section &Sec) {
66       return Config.ToRemove.matches(Sec.Name);
67     };
68   }
69 
70   if (Config.StripDebug) {
71     RemovePred = [RemovePred](const Section &Sec) {
72       return RemovePred(Sec) || isDebugSection(Sec);
73     };
74   }
75 
76   if (Config.StripAll) {
77     RemovePred = [RemovePred](const Section &Sec) {
78       return RemovePred(Sec) || isDebugSection(Sec) || isLinkerSection(Sec) ||
79              isNameSection(Sec) || isCommentSection(Sec);
80     };
81   }
82 
83   if (Config.OnlyKeepDebug) {
84     RemovePred = [&Config](const Section &Sec) {
85       // Keep debug sections, unless explicitly requested to remove.
86       // Remove everything else, including known sections.
87       return Config.ToRemove.matches(Sec.Name) || !isDebugSection(Sec);
88     };
89   }
90 
91   if (!Config.OnlySection.empty()) {
92     RemovePred = [&Config](const Section &Sec) {
93       // Explicitly keep these sections regardless of previous removes.
94       // Remove everything else, inluding known sections.
95       return !Config.OnlySection.matches(Sec.Name);
96     };
97   }
98 
99   if (!Config.KeepSection.empty()) {
100     RemovePred = [&Config, RemovePred](const Section &Sec) {
101       // Explicitly keep these sections regardless of previous removes.
102       if (Config.KeepSection.matches(Sec.Name))
103         return false;
104       // Otherwise defer to RemovePred.
105       return RemovePred(Sec);
106     };
107   }
108 
109   Obj.removeSections(RemovePred);
110 }
111 
112 static Error handleArgs(const CommonConfig &Config, Object &Obj) {
113   // Only support AddSection, DumpSection, RemoveSection for now.
114   for (StringRef Flag : Config.DumpSection) {
115     StringRef SecName;
116     StringRef FileName;
117     std::tie(SecName, FileName) = Flag.split("=");
118     if (Error E = dumpSectionToFile(SecName, FileName, Obj))
119       return createFileError(FileName, std::move(E));
120   }
121 
122   removeSections(Config, Obj);
123 
124   for (const NewSectionInfo &NewSection : Config.AddSection) {
125     Section Sec;
126     Sec.SectionType = llvm::wasm::WASM_SEC_CUSTOM;
127     Sec.Name = NewSection.SectionName;
128 
129     llvm::StringRef InputData =
130         llvm::StringRef(NewSection.SectionData->getBufferStart(),
131                         NewSection.SectionData->getBufferSize());
132     std::unique_ptr<MemoryBuffer> BufferCopy = MemoryBuffer::getMemBufferCopy(
133         InputData, NewSection.SectionData->getBufferIdentifier());
134     Sec.Contents = ArrayRef<uint8_t>(
135         reinterpret_cast<const uint8_t *>(BufferCopy->getBufferStart()),
136         BufferCopy->getBufferSize());
137 
138     Obj.addSectionWithOwnedContents(Sec, std::move(BufferCopy));
139   }
140 
141   return Error::success();
142 }
143 
144 Error executeObjcopyOnBinary(const CommonConfig &Config, const WasmConfig &,
145                              object::WasmObjectFile &In, raw_ostream &Out) {
146   Reader TheReader(In);
147   Expected<std::unique_ptr<Object>> ObjOrErr = TheReader.create();
148   if (!ObjOrErr)
149     return createFileError(Config.InputFilename, ObjOrErr.takeError());
150   Object *Obj = ObjOrErr->get();
151   assert(Obj && "Unable to deserialize Wasm object");
152   if (Error E = handleArgs(Config, *Obj))
153     return E;
154   Writer TheWriter(*Obj, Out);
155   if (Error E = TheWriter.write())
156     return createFileError(Config.OutputFilename, std::move(E));
157   return Error::success();
158 }
159 
160 } // end namespace wasm
161 } // end namespace objcopy
162 } // end namespace llvm
163