Skip to content

goto-ld: handle EFI binaries gracefully #2557

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/build-and-test-Xen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
ln -s goto-cc src/goto-cc/goto-g++

- name: Compile Xen with CBMC via one-line-scan, and check for goto-cc section
run: one-line-scan/one-line-scan --add-to-path $(pwd)/src/cbmc --add-to-path $(pwd)/src/goto-diff --add-to-path $(pwd)/src/goto-cc --no-analysis --trunc-existing --extra-cflags -Wno-error -o CPROVER -j 3 -- make -C xen_4_13 xen -j $(nproc) -k || true
run: one-line-scan/one-line-scan --add-to-path $(pwd)/src/cbmc --add-to-path $(pwd)/src/goto-diff --add-to-path $(pwd)/src/goto-cc --no-analysis --trunc-existing --extra-cflags -Wno-error -o CPROVER -j 3 -- make -C xen_4_13 xen -j $(nproc)

- name: Check for goto-cc section in xen-syms binary
run: objdump -h xen_4_13/xen/xen-syms | grep "goto-cc"
40 changes: 27 additions & 13 deletions src/goto-cc/hybrid_binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,31 +21,37 @@ Author: Michael Tautschnig, 2018
# include <sys/stat.h>
#endif

std::string objcopy_command(const std::string &compiler_or_linker)
{
if(has_suffix(compiler_or_linker, "-ld"))
{
std::string objcopy_cmd = compiler_or_linker;
objcopy_cmd.erase(objcopy_cmd.size() - 2);
objcopy_cmd += "objcopy";

return objcopy_cmd;
}
else
return "objcopy";
}

int hybrid_binary(
const std::string &compiler_or_linker,
const std::string &goto_binary_file,
const std::string &output_file,
bool building_executable,
message_handlert &message_handler)
message_handlert &message_handler,
bool linking_efi)
{
messaget message(message_handler);

int result;
int result = 0;

#if defined(__linux__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__)
// we can use objcopy for both object files and executables
(void)building_executable;

std::string objcopy_cmd;

if(has_suffix(compiler_or_linker, "-ld"))
{
objcopy_cmd = compiler_or_linker;
objcopy_cmd.erase(objcopy_cmd.size() - 2);
objcopy_cmd += "objcopy";
}
else
objcopy_cmd = "objcopy";
const std::string objcopy_cmd = objcopy_command(compiler_or_linker);

// merge output from gcc or ld with goto-binary using objcopy

Expand All @@ -61,7 +67,15 @@ int hybrid_binary(
"--remove-section", "goto-cc",
"--add-section", "goto-cc=" + goto_binary_file, output_file};

result = run(objcopy_argv[0], objcopy_argv);
const int add_section_result = run(objcopy_argv[0], objcopy_argv);
if(add_section_result != 0)
{
if(linking_efi)
message.warning() << "cannot merge EFI binaries: goto-cc section lost"
<< messaget::eom;
else
result = add_section_result;
}
}

// delete the goto binary
Expand Down
10 changes: 9 additions & 1 deletion src/goto-cc/hybrid_binary.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,19 @@ Date: May 2018
/// \param output_file: The name of the object file; the result is stored here.
/// \param building_executable: The \p output_file is an executable.
/// \param message_handler: Message handler for output.
/// \param linking_efi: Set to true if linking x86 EFI binaries to relax error
/// checking.
int hybrid_binary(
const std::string &compiler_or_linker,
const std::string &goto_binary_file,
const std::string &output_file,
bool building_executable,
message_handlert &message_handler);
message_handlert &message_handler,
bool linking_efi = false);

/// Return the name of the objcopy tool matching the chosen compiler or linker
/// command.
/// \param compiler_or_linker: Compiler or linker commmand
std::string objcopy_command(const std::string &compiler_or_linker);

#endif // CPROVER_GOTO_CC_HYBRID_BINARY_H
65 changes: 62 additions & 3 deletions src/goto-cc/ld_mode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ int ld_modet::doit()

// We can generate hybrid ELF and Mach-O binaries
// containing both executable machine code and the goto-binary.
return ld_hybrid_binary(compiler.mode == compilet::COMPILE_LINK_EXECUTABLE);
return ld_hybrid_binary(
compiler.mode == compilet::COMPILE_LINK_EXECUTABLE, compiler.object_files);
}

int ld_modet::run_ld()
Expand All @@ -170,7 +171,9 @@ int ld_modet::run_ld()
return run(new_argv[0], new_argv, cmdline.stdin_file, "", "");
}

int ld_modet::ld_hybrid_binary(bool building_executable)
int ld_modet::ld_hybrid_binary(
bool building_executable,
const std::list<std::string> &object_files)
{
std::string output_file;

Expand Down Expand Up @@ -201,6 +204,42 @@ int ld_modet::ld_hybrid_binary(bool building_executable)
return 1;
}

const bool linking_efi = cmdline.get_value('m') == "i386pep";

#ifdef __linux__
if(linking_efi)
{
const std::string objcopy_cmd = objcopy_command(native_tool_name);

for(const auto &object_file : object_files)
{
log.debug() << "stripping goto-cc sections before building EFI binary"
<< messaget::eom;
// create a backup copy
const std::string bin_name = object_file + goto_binary_tmp_suffix;

std::ifstream in(object_file, std::ios::binary);
std::ofstream out(bin_name, std::ios::binary);
out << in.rdbuf();

// remove any existing goto-cc section
std::vector<std::string> objcopy_argv;

objcopy_argv.push_back(objcopy_cmd);
objcopy_argv.push_back("--remove-section=goto-cc");
objcopy_argv.push_back(object_file);

if(run(objcopy_argv[0], objcopy_argv) != 0)
{
log.debug() << "EFI binary preparation: removing goto-cc section failed"
<< messaget::eom;
}
}
}
#else
(void)object_files; // unused parameter
#endif

int result = run_ld();

if(result == 0 && cmdline.isset('T'))
Expand All @@ -210,6 +249,25 @@ int ld_modet::ld_hybrid_binary(bool building_executable)
result = ls_merge.add_linker_script_definitions();
}

#ifdef __linux__
if(linking_efi)
{
log.debug() << "arch set with " << object_files.size() << messaget::eom;
for(const auto &object_file : object_files)
{
log.debug() << "EFI binary preparation: restoring object files"
<< messaget::eom;
const std::string bin_name = object_file + goto_binary_tmp_suffix;
const int mv_result = rename(bin_name.c_str(), object_file.c_str());
if(mv_result != 0)
{
log.debug() << "Rename failed: " << std::strerror(errno)
<< messaget::eom;
}
}
}
#endif

if(result == 0)
{
std::string native_linker = linker_name(cmdline, base_name);
Expand All @@ -219,7 +277,8 @@ int ld_modet::ld_hybrid_binary(bool building_executable)
goto_binary,
output_file,
building_executable,
message_handler);
message_handler,
linking_efi);
}

return result;
Expand Down
9 changes: 8 additions & 1 deletion src/goto-cc/ld_mode.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,14 @@ class ld_modet : public goto_cc_modet
/// \brief call ld with original command line
int run_ld();

int ld_hybrid_binary(bool);
/// Build an ELF or Mach-O binary containing a goto-cc section.
/// \param building_executable: set to true iff the target file is an
/// executable
/// \param object_files: object files to be linked
/// \return zero, unless an error occurred
int ld_hybrid_binary(
bool building_executable,
const std::list<std::string> &object_files);
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a case where I would include the parameter declarators, as a means of documenting what the arguments would do.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done and documentation for the method added.


#endif // CPROVER_GOTO_CC_LD_MODE_H