blob: 890636e06fe83f8a991acbca1b2e0e68956adb64 [file] [log] [blame]
Brad King56fc4a32021-01-29 13:25:51 -05001/* 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
23class cmXCOFFInternal
24{
25public:
26 // Construct and take ownership of the file stream object.
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
38 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
45protected:
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.
57 void SetErrorMessage(const char* msg) { this->External->ErrorMessage = msg; }
58};
59
60namespace {
61
62struct 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};
70const unsigned char xcoff32_magic[] = { 0x01, 0xDF };
71
72struct 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};
80const unsigned char xcoff64_magic[] = { 0x01, 0xF7 };
81
82template <typename XCOFF>
83class 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
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
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
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
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
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
131public:
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
140template <typename XCOFF>
141Impl<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
199template <typename XCOFF>
200cm::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
212template <typename XCOFF>
213bool 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
281template <typename XCOFF>
282bool Impl<XCOFF>::RemoveLibPath()
283{
284 return this->SetLibPath({});
285}
286}
287
288//============================================================================
289// External class implementation.
290
291cmXCOFF::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
327cmXCOFF::~cmXCOFF() = default;
328
329cmXCOFF::cmXCOFF(cmXCOFF&&) = default;
330cmXCOFF& cmXCOFF::operator=(cmXCOFF&&) = default;
331
332bool cmXCOFF::Valid() const
333{
334 return this->Internal && this->ErrorMessage.empty();
335}
336
337cm::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
346bool cmXCOFF::SetLibPath(cm::string_view libPath)
347{
348 return this->Valid() && this->Internal->GetMode() == Mode::ReadWrite &&
349 this->Internal->SetLibPath(libPath);
350}
351
352bool cmXCOFF::RemoveLibPath()
353{
354 return this->Valid() && this->Internal->GetMode() == Mode::ReadWrite &&
355 this->Internal->RemoveLibPath();
356}