Skip to content

Commit 8fc3375

Browse files
committed
goto-cc: support thin ar archives, refactoring
The Linux kernel uses thin ar archives during the build process, which must be extracted the same way this was already done for .a files. To implement this, the file-type detection was factored out into a separate procedure. Additional behavioural change: files without extension are no longer ignored, but are instead tested for being goto binaries.
1 parent f8caa15 commit 8fc3375

File tree

2 files changed

+131
-120
lines changed

2 files changed

+131
-120
lines changed

src/goto-cc/compile.cpp

Lines changed: 130 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Date: June 2006
1313

1414
#include "compile.h"
1515

16+
#include <cstring>
1617
#include <fstream>
1718
#include <sstream>
1819
#include <iostream>
@@ -135,56 +136,113 @@ bool compilet::doit()
135136
warnings_before;
136137
}
137138

139+
enum class file_typet
140+
{
141+
NO_SUCH_FILE,
142+
UNKNOWN,
143+
SOURCE_FILE,
144+
NORMAL_ARCHIVE,
145+
THIN_ARCHIVE,
146+
GOTO_BINARY,
147+
ELF_OBJECT
148+
};
149+
150+
static file_typet detect_file_type(const std::string &file_name)
151+
{
152+
// first of all, try to open the file
153+
std::ifstream in(file_name);
154+
if(!in)
155+
return file_typet::NO_SUCH_FILE;
156+
157+
const std::string::size_type r = file_name.rfind('.', file_name.length() - 1);
158+
159+
const std::string ext =
160+
r == std::string::npos ? "" : file_name.substr(r + 1, file_name.length());
161+
162+
if(
163+
ext == "c" ||
164+
ext == "cc" || ext == "cp" || ext == "cpp" || ext == "CPP" ||
165+
ext == "c++" || ext == "C" ||
166+
ext == "i" || ext == "ii" ||
167+
ext == "class" || ext == "jar" ||
168+
ext == "jsil")
169+
return file_typet::SOURCE_FILE;
170+
171+
char hdr[8];
172+
in.get(hdr, 8);
173+
if((ext == "a" || ext == "o") && strncmp(hdr, "!<thin>", 8) == 0)
174+
return file_typet::THIN_ARCHIVE;
175+
176+
if(ext == "a")
177+
return file_typet::NORMAL_ARCHIVE;
178+
179+
if(is_goto_binary(file_name))
180+
return file_typet::GOTO_BINARY;
181+
182+
if(hdr[0] == 0x7f && memcmp(hdr + 1, "ELF", 3) == 0)
183+
return file_typet::ELF_OBJECT;
184+
185+
return file_typet::UNKNOWN;
186+
}
187+
138188
/// puts input file names into a list and does preprocessing for libraries.
139189
/// \return false on success, true on error.
140190
bool compilet::add_input_file(const std::string &file_name)
141191
{
142-
// first of all, try to open the file
192+
switch(detect_file_type(file_name))
143193
{
144-
std::ifstream in(file_name);
145-
if(!in)
146-
{
147-
warning() << "failed to open file `" << file_name << "'" << eom;
148-
return warning_is_fatal; // generously ignore unless -Werror
149-
}
150-
}
194+
case file_typet::NO_SUCH_FILE:
195+
warning() << "failed to open file `" << file_name << "'" << eom;
196+
return warning_is_fatal; // generously ignore unless -Werror
151197

152-
size_t r=file_name.rfind('.', file_name.length()-1);
198+
case file_typet::UNKNOWN:
199+
// unknown extension, not a goto binary, will silently ignore
200+
debug() << "unknown file type in `" << file_name << "'" << eom;
201+
return false;
153202

154-
if(r==std::string::npos)
155-
{
156-
// a file without extension; will ignore
157-
warning() << "input file `" << file_name
158-
<< "' has no extension, not considered" << eom;
159-
return warning_is_fatal;
160-
}
203+
case file_typet::ELF_OBJECT:
204+
// ELF file without goto-cc section, silently ignore
205+
debug() << "ELF object without goto-cc section: `" << file_name << "'"
206+
<< eom;
207+
return false;
161208

162-
std::string ext = file_name.substr(r+1, file_name.length());
163-
164-
if(ext=="c" ||
165-
ext=="cc" ||
166-
ext=="cp" ||
167-
ext=="cpp" ||
168-
ext=="CPP" ||
169-
ext=="c++" ||
170-
ext=="C" ||
171-
ext=="i" ||
172-
ext=="ii" ||
173-
ext=="class" ||
174-
ext=="jar" ||
175-
ext=="jsil")
176-
{
209+
case file_typet::SOURCE_FILE:
177210
source_files.push_back(file_name);
211+
return false;
212+
213+
case file_typet::NORMAL_ARCHIVE:
214+
return add_files_from_archive(file_name, false);
215+
216+
case file_typet::THIN_ARCHIVE:
217+
return add_files_from_archive(file_name, true);
218+
219+
case file_typet::GOTO_BINARY:
220+
object_files.push_back(file_name);
221+
return false;
178222
}
179-
else if(ext=="a")
180-
{
181-
#ifdef _WIN32
182-
char td[MAX_PATH+1];
183-
#else
184-
char td[] = "goto-cc.XXXXXX";
185-
#endif
186223

187-
std::string tstr=get_temporary_directory(td);
224+
UNREACHABLE;
225+
}
226+
227+
/// extracts goto binaries from AR archive and add them as input files.
228+
/// \return false on success, true on error.
229+
bool compilet::add_files_from_archive(
230+
const std::string &file_name, bool thin_archive)
231+
{
232+
#ifdef _WIN32
233+
char td[MAX_PATH+1];
234+
#else
235+
char td[] = "goto-cc.XXXXXX";
236+
#endif
237+
238+
std::stringstream cmd;
239+
FILE *stream;
240+
241+
std::string tstr=working_directory;
242+
243+
if(!thin_archive)
244+
{
245+
tstr = get_temporary_directory(td);
188246

189247
if(tstr=="")
190248
{
@@ -193,7 +251,6 @@ bool compilet::add_input_file(const std::string &file_name)
193251
}
194252

195253
tmp_dirs.push_back(tstr);
196-
std::stringstream cmd("");
197254
if(chdir(tmp_dirs.back().c_str())!=0)
198255
{
199256
error() << "Cannot switch to temporary directory" << eom;
@@ -203,76 +260,47 @@ bool compilet::add_input_file(const std::string &file_name)
203260
// unpack now
204261
cmd << "ar x " << concat_dir_file(working_directory, file_name);
205262

206-
FILE *stream;
207-
208263
stream=popen(cmd.str().c_str(), "r");
209264
pclose(stream);
210265

211266
cmd.clear();
212267
cmd.str("");
268+
}
213269

214-
// add the files from "ar t"
215-
#ifdef _WIN32
216-
if(file_name[0]!='/' && file_name[1]!=':') // NOLINT(readability/braces)
217-
#else
218-
if(file_name[0]!='/') // NOLINT(readability/braces)
219-
#endif
220-
{
221-
cmd << "ar t " <<
222-
#ifdef _WIN32
223-
working_directory << "\\" << file_name;
224-
#else
225-
working_directory << "/" << file_name;
226-
#endif
227-
}
228-
else
229-
{
230-
cmd << "ar t " << file_name;
231-
}
270+
// add the files from "ar t"
271+
cmd << "ar t " << concat_dir_file(working_directory, file_name);
232272

233-
stream=popen(cmd.str().c_str(), "r");
273+
stream=popen(cmd.str().c_str(), "r");
234274

235-
if(stream!=nullptr)
275+
if(stream!=nullptr)
276+
{
277+
std::string line;
278+
int ch; // fgetc returns an int, not char
279+
while((ch=fgetc(stream))!=EOF)
236280
{
237-
std::string line;
238-
int ch; // fgetc returns an int, not char
239-
while((ch=fgetc(stream))!=EOF)
281+
if(ch!='\n')
240282
{
241-
if(ch!='\n')
242-
{
243-
line+=static_cast<char>(ch);
244-
}
245-
else
246-
{
247-
std::string t;
248-
#ifdef _WIN32
249-
t = tmp_dirs.back() + '\\' + line;
250-
#else
251-
t = tmp_dirs.back() + '/' + line;
252-
#endif
253-
254-
if(is_goto_binary(t))
255-
object_files.push_back(t);
256-
257-
line = "";
258-
}
283+
line+=static_cast<char>(ch);
259284
}
285+
else
286+
{
287+
std::string t = concat_dir_file(tstr, line);
260288

261-
pclose(stream);
262-
}
289+
if(is_goto_binary(t))
290+
object_files.push_back(t);
291+
else
292+
debug() << "Object file is not a goto binary: " << line << eom;
263293

264-
cmd.str("");
294+
line = "";
295+
}
296+
}
265297

266-
if(chdir(working_directory.c_str())!=0)
267-
error() << "Could not change back to working directory" << eom;
268-
}
269-
else if(is_goto_binary(file_name))
270-
object_files.push_back(file_name);
271-
else
272-
{
273-
// unknown extension, not a goto binary, will silently ignore
298+
pclose(stream);
274299
}
275300

301+
if(!thin_archive && chdir(working_directory.c_str()) != 0)
302+
error() << "Could not change back to working directory" << eom;
303+
276304
return false;
277305
}
278306

@@ -302,41 +330,24 @@ bool compilet::find_library(const std::string &name)
302330
{
303331
std::string libname=tmp+name+".so";
304332

305-
if(is_goto_binary(libname))
306-
return !add_input_file(libname);
307-
else if(is_elf_file(libname))
333+
switch(detect_file_type(libname))
308334
{
335+
case file_typet::GOTO_BINARY:
336+
return !add_input_file(libname);
337+
338+
case file_typet::ELF_OBJECT:
309339
warning() << "Warning: Cannot read ELF library " << libname << eom;
310340
return warning_is_fatal;
341+
342+
default:
343+
break;
311344
}
312345
}
313346
}
314347

315348
return false;
316349
}
317350

318-
/// checking if we can load an object file
319-
/// \par parameters: file name
320-
/// \return true if the given file name exists and is an ELF file, false
321-
/// otherwise
322-
bool compilet::is_elf_file(const std::string &file_name)
323-
{
324-
std::fstream in;
325-
326-
in.open(file_name, std::ios::in);
327-
if(in.is_open())
328-
{
329-
char buf[4];
330-
for(std::size_t i=0; i<4; i++)
331-
buf[i]=static_cast<char>(in.get());
332-
if(buf[0]==0x7f && buf[1]=='E' &&
333-
buf[2]=='L' && buf[3]=='F')
334-
return true;
335-
}
336-
337-
return false;
338-
}
339-
340351
/// parses object files and links them
341352
/// \return true on error, false otherwise
342353
bool compilet::link()

src/goto-cc/compile.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ class compilet:public language_uit
5353

5454
bool add_input_file(const std::string &);
5555
bool find_library(const std::string &);
56-
bool is_elf_file(const std::string &);
56+
bool add_files_from_archive(const std::string &file_name, bool thin_archive);
5757

5858
bool parse(const std::string &filename);
5959
bool parse_stdin();

0 commit comments

Comments
 (0)