Skip to content

Commit 177c8c1

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 e80008e commit 177c8c1

File tree

2 files changed

+132
-120
lines changed

2 files changed

+132
-120
lines changed

src/goto-cc/compile.cpp

+131-119
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,114 @@ bool compilet::doit()
135136
warnings_before;
136137
}
137138

138-
/// puts input file names into a list and does preprocessing for libraries.
139-
/// \return false on success, true on error.
140-
bool compilet::add_input_file(const std::string &file_name)
139+
enum class file_typet
140+
{
141+
FAILED_TO_OPEN_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)
141151
{
142152
// first of all, try to open the file
143-
{
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-
}
153+
std::ifstream in(file_name);
154+
if(!in)
155+
return file_typet::FAILED_TO_OPEN_FILE;
156+
157+
const std::string::size_type r = file_name.rfind('.');
151158

152-
size_t r=file_name.rfind('.', file_name.length()-1);
159+
const std::string ext =
160+
r == std::string::npos ? "" : file_name.substr(r + 1, file_name.length());
153161

154-
if(r==std::string::npos)
162+
if(
163+
ext == "c" || ext == "cc" || ext == "cp" || ext == "cpp" || ext == "CPP" ||
164+
ext == "c++" || ext == "C" || ext == "i" || ext == "ii" || ext == "class" ||
165+
ext == "jar" || ext == "jsil")
155166
{
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;
167+
return file_typet::SOURCE_FILE;
160168
}
161169

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")
170+
char hdr[8];
171+
in.get(hdr, 8);
172+
if((ext == "a" || ext == "o") && strncmp(hdr, "!<thin>", 8) == 0)
173+
return file_typet::THIN_ARCHIVE;
174+
175+
if(ext == "a")
176+
return file_typet::NORMAL_ARCHIVE;
177+
178+
if(is_goto_binary(file_name))
179+
return file_typet::GOTO_BINARY;
180+
181+
if(hdr[0] == 0x7f && memcmp(hdr + 1, "ELF", 3) == 0)
182+
return file_typet::ELF_OBJECT;
183+
184+
return file_typet::UNKNOWN;
185+
}
186+
187+
/// puts input file names into a list and does preprocessing for libraries.
188+
/// \return false on success, true on error.
189+
bool compilet::add_input_file(const std::string &file_name)
190+
{
191+
switch(detect_file_type(file_name))
176192
{
193+
case file_typet::FAILED_TO_OPEN_FILE:
194+
warning() << "failed to open file `" << file_name
195+
<< "': " << std::strerror(errno) << eom;
196+
return warning_is_fatal; // generously ignore unless -Werror
197+
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;
202+
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;
208+
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,
231+
bool thin_archive)
232+
{
233+
#ifdef _WIN32
234+
char td[MAX_PATH + 1];
235+
#else
236+
char td[] = "goto-cc.XXXXXX";
237+
#endif
238+
239+
std::stringstream cmd;
240+
FILE *stream;
241+
242+
std::string tstr = working_directory;
243+
244+
if(!thin_archive)
245+
{
246+
tstr = get_temporary_directory(td);
188247

189248
if(tstr=="")
190249
{
@@ -193,7 +252,6 @@ bool compilet::add_input_file(const std::string &file_name)
193252
}
194253

195254
tmp_dirs.push_back(tstr);
196-
std::stringstream cmd("");
197255
if(chdir(tmp_dirs.back().c_str())!=0)
198256
{
199257
error() << "Cannot switch to temporary directory" << eom;
@@ -203,76 +261,47 @@ bool compilet::add_input_file(const std::string &file_name)
203261
// unpack now
204262
cmd << "ar x " << concat_dir_file(working_directory, file_name);
205263

206-
FILE *stream;
207-
208264
stream=popen(cmd.str().c_str(), "r");
209265
pclose(stream);
210266

211267
cmd.clear();
212268
cmd.str("");
269+
}
213270

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-
}
271+
// add the files from "ar t"
272+
cmd << "ar t " << concat_dir_file(working_directory, file_name);
232273

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

235-
if(stream!=nullptr)
276+
if(stream != nullptr)
277+
{
278+
std::string line;
279+
int ch; // fgetc returns an int, not char
280+
while((ch = fgetc(stream)) != EOF)
236281
{
237-
std::string line;
238-
int ch; // fgetc returns an int, not char
239-
while((ch=fgetc(stream))!=EOF)
282+
if(ch != '\n')
240283
{
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-
}
284+
line += static_cast<char>(ch);
259285
}
286+
else
287+
{
288+
std::string t = concat_dir_file(tstr, line);
260289

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

264-
cmd.str("");
295+
line = "";
296+
}
297+
}
265298

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
299+
pclose(stream);
274300
}
275301

302+
if(!thin_archive && chdir(working_directory.c_str()) != 0)
303+
error() << "Could not change back to working directory" << eom;
304+
276305
return false;
277306
}
278307

@@ -302,41 +331,24 @@ bool compilet::find_library(const std::string &name)
302331
{
303332
std::string libname=tmp+name+".so";
304333

305-
if(is_goto_binary(libname))
306-
return !add_input_file(libname);
307-
else if(is_elf_file(libname))
334+
switch(detect_file_type(libname))
308335
{
336+
case file_typet::GOTO_BINARY:
337+
return !add_input_file(libname);
338+
339+
case file_typet::ELF_OBJECT:
309340
warning() << "Warning: Cannot read ELF library " << libname << eom;
310341
return warning_is_fatal;
342+
343+
default:
344+
break;
311345
}
312346
}
313347
}
314348

315349
return false;
316350
}
317351

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-
340352
/// parses object files and links them
341353
/// \return true on error, false otherwise
342354
bool compilet::link()

src/goto-cc/compile.h

+1-1
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)