1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmXCOFF.h"
4 
5 #include <algorithm>
6 #include <cstddef>
7 
8 #include <cm/memory>
9 
10 #include "cmsys/FStream.hxx"
11 
12 #include "cmStringAlgorithms.h"
13 
14 // Include the XCOFF format information system header.
15 #ifdef _AIX
16 #  define __XCOFF32__
17 #  define __XCOFF64__
18 #  include <xcoff.h>
19 #else
20 #  error "This source may be compiled only on AIX."
21 #endif
22 
23 class cmXCOFFInternal
24 {
25 public:
26   // Construct and take ownership of the file stream object.
cmXCOFFInternal(cmXCOFF * external,std::unique_ptr<std::iostream> fin,cmXCOFF::Mode mode)27   cmXCOFFInternal(cmXCOFF* external, std::unique_ptr<std::iostream> fin,
28                   cmXCOFF::Mode mode)
29     : External(external)
30     , Stream(std::move(fin))
31     , Mode(mode)
32   {
33   }
34 
35   // Destruct and delete the file stream object.
36   virtual ~cmXCOFFInternal() = default;
37 
GetMode() const38   cmXCOFF::Mode GetMode() const { return this->Mode; }
39 
40   virtual cm::optional<cm::string_view> GetLibPath() = 0;
41 
42   virtual bool SetLibPath(cm::string_view libPath) = 0;
43   virtual bool RemoveLibPath() = 0;
44 
45 protected:
46   // Data common to all ELF class implementations.
47 
48   // The external cmXCOFF object.
49   cmXCOFF* External;
50 
51   // The stream from which to read.
52   std::unique_ptr<std::iostream> Stream;
53 
54   cmXCOFF::Mode Mode;
55 
56   // Helper methods for subclasses.
SetErrorMessage(const char * msg)57   void SetErrorMessage(const char* msg) { this->External->ErrorMessage = msg; }
58 };
59 
60 namespace {
61 
62 struct XCOFF32
63 {
64   typedef struct filehdr filehdr;
65   typedef struct aouthdr aouthdr;
66   typedef struct scnhdr scnhdr;
67   typedef struct ldhdr ldhdr;
68   static const std::size_t aouthdr_size = _AOUTHSZ_EXEC;
69 };
70 const unsigned char xcoff32_magic[] = { 0x01, 0xDF };
71 
72 struct XCOFF64
73 {
74   typedef struct filehdr_64 filehdr;
75   typedef struct aouthdr_64 aouthdr;
76   typedef struct scnhdr_64 scnhdr;
77   typedef struct ldhdr_64 ldhdr;
78   static const std::size_t aouthdr_size = _AOUTHSZ_EXEC_64;
79 };
80 const unsigned char xcoff64_magic[] = { 0x01, 0xF7 };
81 
82 template <typename XCOFF>
83 class Impl : public cmXCOFFInternal
84 {
85   static_assert(sizeof(typename XCOFF::aouthdr) == XCOFF::aouthdr_size,
86                 "aouthdr structure size matches _AOUTHSZ_EXEC macro");
87 
88   typename XCOFF::filehdr FileHeader;
89   typename XCOFF::aouthdr AuxHeader;
90   typename XCOFF::scnhdr LoaderSectionHeader;
91   typename XCOFF::ldhdr LoaderHeader;
92 
93   std::streamoff LoaderImportFileTablePos = 0;
94   std::vector<char> LoaderImportFileTable;
95 
Read(typename XCOFF::filehdr & x)96   bool Read(typename XCOFF::filehdr& x)
97   {
98     // FIXME: Add byte swapping if needed.
99     return static_cast<bool>(
100       this->Stream->read(reinterpret_cast<char*>(&x), sizeof(x)));
101   }
102 
Read(typename XCOFF::aouthdr & x)103   bool Read(typename XCOFF::aouthdr& x)
104   {
105     // FIXME: Add byte swapping if needed.
106     return static_cast<bool>(
107       this->Stream->read(reinterpret_cast<char*>(&x), sizeof(x)));
108   }
109 
Read(typename XCOFF::scnhdr & x)110   bool Read(typename XCOFF::scnhdr& x)
111   {
112     // FIXME: Add byte swapping if needed.
113     return static_cast<bool>(
114       this->Stream->read(reinterpret_cast<char*>(&x), sizeof(x)));
115   }
116 
Read(typename XCOFF::ldhdr & x)117   bool Read(typename XCOFF::ldhdr& x)
118   {
119     // FIXME: Add byte swapping if needed.
120     return static_cast<bool>(
121       this->Stream->read(reinterpret_cast<char*>(&x), sizeof(x)));
122   }
123 
WriteLoaderImportFileTableLength(decltype(XCOFF::ldhdr::l_istlen)x)124   bool WriteLoaderImportFileTableLength(decltype(XCOFF::ldhdr::l_istlen) x)
125   {
126     // FIXME: Add byte swapping if needed.
127     return static_cast<bool>(
128       this->Stream->write(reinterpret_cast<char const*>(&x), sizeof(x)));
129   }
130 
131 public:
132   Impl(cmXCOFF* external, std::unique_ptr<std::iostream> fin,
133        cmXCOFF::Mode mode);
134 
135   cm::optional<cm::string_view> GetLibPath() override;
136   bool SetLibPath(cm::string_view libPath) override;
137   bool RemoveLibPath() override;
138 };
139 
140 template <typename XCOFF>
Impl(cmXCOFF * external,std::unique_ptr<std::iostream> fin,cmXCOFF::Mode mode)141 Impl<XCOFF>::Impl(cmXCOFF* external, std::unique_ptr<std::iostream> fin,
142                   cmXCOFF::Mode mode)
143   : cmXCOFFInternal(external, std::move(fin), mode)
144 {
145   if (!this->Read(this->FileHeader)) {
146     this->SetErrorMessage("Failed to read XCOFF file header.");
147     return;
148   }
149   if (this->FileHeader.f_opthdr != XCOFF::aouthdr_size) {
150     this->SetErrorMessage("XCOFF auxiliary header missing.");
151     return;
152   }
153   if (!this->Read(this->AuxHeader)) {
154     this->SetErrorMessage("Failed to read XCOFF auxiliary header.");
155     return;
156   }
157   if (this->AuxHeader.o_snloader == 0) {
158     this->SetErrorMessage("XCOFF loader section missing.");
159     return;
160   }
161   if (!this->Stream->seekg((this->AuxHeader.o_snloader - 1) *
162                              sizeof(typename XCOFF::scnhdr),
163                            std::ios::cur)) {
164     this->SetErrorMessage("Failed to seek to XCOFF loader section header.");
165     return;
166   }
167   if (!this->Read(this->LoaderSectionHeader)) {
168     this->SetErrorMessage("Failed to read XCOFF loader section header.");
169     return;
170   }
171   if ((this->LoaderSectionHeader.s_flags & STYP_LOADER) == 0) {
172     this->SetErrorMessage("XCOFF loader section header missing STYP_LOADER.");
173     return;
174   }
175   if (!this->Stream->seekg(this->LoaderSectionHeader.s_scnptr,
176                            std::ios::beg)) {
177     this->SetErrorMessage("Failed to seek to XCOFF loader header.");
178     return;
179   }
180   if (!this->Read(this->LoaderHeader)) {
181     this->SetErrorMessage("Failed to read XCOFF loader header.");
182     return;
183   }
184   this->LoaderImportFileTablePos =
185     this->LoaderSectionHeader.s_scnptr + this->LoaderHeader.l_impoff;
186   if (!this->Stream->seekg(this->LoaderImportFileTablePos)) {
187     this->SetErrorMessage(
188       "Failed to seek to XCOFF loader import file id table.");
189     return;
190   }
191   this->LoaderImportFileTable.resize(this->LoaderHeader.l_istlen);
192   if (!this->Stream->read(this->LoaderImportFileTable.data(),
193                           this->LoaderImportFileTable.size())) {
194     this->SetErrorMessage("Failed to read XCOFF loader import file id table.");
195     return;
196   }
197 }
198 
199 template <typename XCOFF>
GetLibPath()200 cm::optional<cm::string_view> Impl<XCOFF>::GetLibPath()
201 {
202   cm::optional<cm::string_view> result;
203   auto end = std::find(this->LoaderImportFileTable.begin(),
204                        this->LoaderImportFileTable.end(), '\0');
205   if (end != this->LoaderImportFileTable.end()) {
206     result = cm::string_view(this->LoaderImportFileTable.data(),
207                              end - this->LoaderImportFileTable.begin());
208   }
209   return result;
210 }
211 
212 template <typename XCOFF>
SetLibPath(cm::string_view libPath)213 bool Impl<XCOFF>::SetLibPath(cm::string_view libPath)
214 {
215   // The new LIBPATH must end in the standard AIX LIBPATH.
216 #define CM_AIX_LIBPATH "/usr/lib:/lib"
217   std::string libPathBuf;
218   if (libPath != CM_AIX_LIBPATH &&
219       !cmHasLiteralSuffix(libPath, ":" CM_AIX_LIBPATH)) {
220     libPathBuf = std::string(libPath);
221     if (!libPathBuf.empty() && libPathBuf.back() != ':') {
222       libPathBuf.push_back(':');
223     }
224     libPathBuf += CM_AIX_LIBPATH;
225     libPath = libPathBuf;
226   }
227 #undef CM_AIX_LIBPATH
228 
229   auto oldEnd = std::find(this->LoaderImportFileTable.begin(),
230                           this->LoaderImportFileTable.end(), '\0');
231   if (oldEnd == this->LoaderImportFileTable.end()) {
232     this->SetErrorMessage("XCOFF loader import file id table is invalid.");
233     return false;
234   }
235   if ((this->LoaderImportFileTable.begin() + libPath.size()) > oldEnd) {
236     this->SetErrorMessage("XCOFF loader import file id table is too small.");
237     return false;
238   }
239 
240   {
241     std::vector<char> ift;
242     ift.reserve(this->LoaderImportFileTable.size());
243     // Start with the new LIBPATH.
244     ift.insert(ift.end(), libPath.begin(), libPath.end());
245     // Add the rest of the original table.
246     ift.insert(ift.end(), oldEnd, this->LoaderImportFileTable.end());
247     // If the new table is shorter, zero out the leftover space.
248     ift.resize(this->LoaderImportFileTable.size(), 0);
249     this->LoaderHeader.l_istlen =
250       static_cast<decltype(XCOFF::ldhdr::l_istlen)>(ift.size());
251     this->LoaderImportFileTable = std::move(ift);
252   }
253 
254   if (!this->Stream->seekp(this->LoaderSectionHeader.s_scnptr +
255                              offsetof(typename XCOFF::ldhdr, l_istlen),
256                            std::ios::beg)) {
257     this->SetErrorMessage(
258       "Failed to seek to XCOFF loader header import file id table length.");
259     return false;
260   }
261   if (!this->WriteLoaderImportFileTableLength(this->LoaderHeader.l_istlen)) {
262     this->SetErrorMessage(
263       "Failed to write XCOFF loader header import file id table length.");
264     return false;
265   }
266   if (!this->Stream->seekp(this->LoaderImportFileTablePos, std::ios::beg)) {
267     this->SetErrorMessage(
268       "Failed to seek to XCOFF loader import file id table.");
269     return false;
270   }
271   if (!this->Stream->write(this->LoaderImportFileTable.data(),
272                            this->LoaderImportFileTable.size())) {
273     this->SetErrorMessage(
274       "Failed to write XCOFF loader import file id table.");
275     return false;
276   }
277 
278   return true;
279 }
280 
281 template <typename XCOFF>
RemoveLibPath()282 bool Impl<XCOFF>::RemoveLibPath()
283 {
284   return this->SetLibPath({});
285 }
286 }
287 
288 //============================================================================
289 // External class implementation.
290 
cmXCOFF(const char * fname,Mode mode)291 cmXCOFF::cmXCOFF(const char* fname, Mode mode)
292 {
293   // Try to open the file.
294   std::ios::openmode fmode = std::ios::in | std::ios::binary;
295   if (mode == Mode::ReadWrite) {
296     fmode |= std::ios::out;
297   }
298   auto f = cm::make_unique<cmsys::fstream>(fname, fmode);
299 
300   // Quit now if the file could not be opened.
301   if (!f || !*f) {
302     this->ErrorMessage = "Error opening input file.";
303     return;
304   }
305 
306   // Read the XCOFF magic number.
307   unsigned char magic[2];
308   if (!f->read(reinterpret_cast<char*>(magic), sizeof(magic))) {
309     this->ErrorMessage = "Error reading XCOFF magic number.";
310     return;
311   }
312   if (!f->seekg(0)) {
313     this->ErrorMessage = "Error seeking to beginning of file.";
314     return;
315   }
316 
317   // Check the XCOFF type.
318   if (magic[0] == xcoff32_magic[0] && magic[1] == xcoff32_magic[1]) {
319     this->Internal = cm::make_unique<Impl<XCOFF32>>(this, std::move(f), mode);
320   } else if (magic[0] == xcoff64_magic[0] && magic[1] == xcoff64_magic[1]) {
321     this->Internal = cm::make_unique<Impl<XCOFF64>>(this, std::move(f), mode);
322   } else {
323     this->ErrorMessage = "File is not a XCOFF32 or XCOFF64 binary.";
324   }
325 }
326 
327 cmXCOFF::~cmXCOFF() = default;
328 
329 cmXCOFF::cmXCOFF(cmXCOFF&&) = default;
330 cmXCOFF& cmXCOFF::operator=(cmXCOFF&&) = default;
331 
Valid() const332 bool cmXCOFF::Valid() const
333 {
334   return this->Internal && this->ErrorMessage.empty();
335 }
336 
GetLibPath() const337 cm::optional<cm::string_view> cmXCOFF::GetLibPath() const
338 {
339   cm::optional<cm::string_view> result;
340   if (this->Valid()) {
341     result = this->Internal->GetLibPath();
342   }
343   return result;
344 }
345 
SetLibPath(cm::string_view libPath)346 bool cmXCOFF::SetLibPath(cm::string_view libPath)
347 {
348   return this->Valid() && this->Internal->GetMode() == Mode::ReadWrite &&
349     this->Internal->SetLibPath(libPath);
350 }
351 
RemoveLibPath()352 bool cmXCOFF::RemoveLibPath()
353 {
354   return this->Valid() && this->Internal->GetMode() == Mode::ReadWrite &&
355     this->Internal->RemoveLibPath();
356 }
357