Exheredludis/paludis/stripper.cc
Heiko Becker 371aa4a56a Adjust the stripper for file>=5.33 and PIE executables
It can recognize executables built with PIE now, which are just
shared objects with the executable bit set (and were recognized by
stripper via the "shared object" text returned from earlier
versions).

With file 5.32:
$ file .../file: ".../file: ELF 64-bit LSB shared object"
$ file .../libmagic.so.1.0.0: ".../libmagic.so.1.0.0: ELF 64-bit LSB
shared object"

With file 5.33:
$ file .../file: ".../file: ELF 64-bit LSB pie executable x86-64"
$ file .../libmagic.so.1.0.0: ".../libmagic.so.1.0.0: ELF 64-bit LSB
pie executable x86-64"
2018-07-23 22:10:30 +02:00

328 lines
10 KiB
C++

/* vim: set sw=4 sts=4 et foldmethod=syntax : */
/*
* Copyright (c) 2008, 2010, 2011 Ciaran McCreesh
*
* This file is part of the Paludis package manager. Paludis is free software;
* you can redistribute it and/or modify it under the terms of the GNU General
* Public License version 2, as published by the Free Software Foundation.
*
* Paludis is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 59 Temple
* Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <paludis/stripper.hh>
#include <paludis/stripper_extras.hh>
#include <paludis/about.hh>
#include <paludis/util/pimp-impl.hh>
#include <paludis/util/strip.hh>
#include <paludis/util/stringify.hh>
#include <paludis/util/log.hh>
#include <paludis/util/process.hh>
#include <paludis/util/fs_iterator.hh>
#include <paludis/util/fs_stat.hh>
#include <paludis/util/options.hh>
#include <paludis/util/singleton-impl.hh>
#include <functional>
#include <sstream>
#include <list>
#include <set>
#include <algorithm>
#include <sys/stat.h>
#include <dlfcn.h>
#include <stdint.h>
#include "config.h"
#define STUPID_CAST(type, val) reinterpret_cast<type>(reinterpret_cast<uintptr_t>(val))
using namespace paludis;
typedef std::set<std::pair<dev_t, ino_t> > StrippedSet;
StripperError::StripperError(const std::string & s) noexcept :
Exception(s)
{
}
namespace
{
struct StripperHandle :
Singleton<StripperHandle>
{
typedef PaludisStripperExtras * (* InitPtr) ();
typedef const std::string (* LookupPtr) (PaludisStripperExtras * const, const std::string &);
typedef void (* CleanupPtr) (PaludisStripperExtras * const);
void * handle;
InitPtr init;
LookupPtr lookup;
CleanupPtr cleanup;
StripperHandle() :
handle(nullptr),
init(nullptr),
lookup(nullptr),
cleanup(nullptr)
{
#ifndef ENABLE_STRIPPER
Log::get_instance()->message("strip.unsupported", ll_warning, lc_context)
<< "Paludis was built without support for stripping. No stripping will be done.";
#else
do
{
handle = ::dlopen(("libpaludisstripperextras_" + stringify(PALUDIS_PC_SLOT) + ".so").c_str(), RTLD_NOW | RTLD_GLOBAL);
if (! handle)
break;
init = STUPID_CAST(InitPtr, ::dlsym(handle, "paludis_stripper_extras_init"));
if (! init)
break;
lookup = STUPID_CAST(LookupPtr, ::dlsym(handle, "paludis_stripper_extras_lookup"));
if (! lookup)
break;
cleanup = STUPID_CAST(CleanupPtr, ::dlsym(handle, "paludis_stripper_extras_cleanup"));
if (! cleanup)
break;
}
while (false);
if (! (handle && init && lookup && cleanup))
{
Log::get_instance()->message("strip.broken", ll_warning, lc_context)
<< "Stripping cannot be used due to error '" << stringify(::dlerror()) << "' when using libpaludisstripperextras";
if (handle)
::dlclose(handle);
handle = nullptr;
}
#endif
}
~StripperHandle()
{
if (handle)
::dlclose(handle);
}
};
}
namespace paludis
{
template <>
struct Imp<Stripper>
{
StripperOptions options;
StrippedSet stripped_ids;
PaludisStripperExtras * stripper_extras;
Imp(const StripperOptions & o) :
options(o),
stripper_extras(nullptr)
{
try
{
if (StripperHandle::get_instance()->handle)
stripper_extras = StripperHandle::get_instance()->init();
}
catch (const StripperError & e)
{
Log::get_instance()->message("strip.broken", ll_warning, lc_context)
<< "Got error '" << e.message() << "' (" << e.what() << ") when attempting to strip";
stripper_extras = nullptr;
}
}
~Imp()
{
if (stripper_extras)
StripperHandle::get_instance()->cleanup(stripper_extras);
}
};
}
Stripper::Stripper(const StripperOptions & options) :
_imp(options)
{
}
Stripper::~Stripper() = default;
void
Stripper::strip()
{
Context context("When stripping image '" + stringify(_imp->options.image_dir()) + "':");
if (! _imp->options.strip())
return;
do_dir_recursive(_imp->options.image_dir());
}
void
Stripper::do_dir_recursive(const FSPath & f)
{
Context context("When stripping inside '" + stringify(f) + "':");
if (f == _imp->options.debug_dir())
return;
on_enter_dir(f);
for (FSIterator d(f, { fsio_include_dotfiles, fsio_inode_sort }), d_end ; d != d_end ; ++d)
{
FSStat d_stat(*d);
if (d_stat.is_symlink())
continue;
if (_imp->stripped_ids.end() != _imp->stripped_ids.find(d_stat.lowlevel_id()))
continue;
if (d_stat.is_directory())
do_dir_recursive(*d);
else if (d_stat.is_regular_file())
{
if ((0 != (d_stat.permissions() & (S_IXUSR | S_IXGRP | S_IXOTH))) ||
(std::string::npos != d->basename().find(".so.")) ||
(d->basename() != strip_trailing_string(d->basename(), ".so")))
{
std::string t(file_type(*d));
if (std::string::npos != t.find("SB executable") || std::string::npos != t.find("SB shared object") ||
std::string::npos != t.find("SB pie executable"))
{
if (_imp->options.dwarf_compression())
do_dwarf_compress(*d);
if (_imp->options.split())
{
FSPath target(_imp->options.debug_dir() / d->strip_leading(_imp->options.image_dir()));
target = target.dirname() / (target.basename() + ".debug");
do_split(*d, target);
}
do_strip(*d, "");
}
else if (std::string::npos != t.find("current ar archive"))
{
do_strip(*d, "-g");
}
else
on_unknown(*d);
}
}
}
on_leave_dir(f);
}
std::string
Stripper::file_type(const FSPath & f)
{
Context context("When finding the file type of '" + stringify(f) + "':");
if (_imp->stripper_extras)
{
std::string result(StripperHandle::get_instance()->lookup(_imp->stripper_extras, stringify(f)));
Log::get_instance()->message("strip.type", ll_debug, lc_context)
<< "Magic says '" << f << "' is '" << result << "'";
return result;
}
else
return "";
}
void
Stripper::do_strip(const FSPath & f, const std::string & options)
{
Context context("When stripping '" + stringify(f) + "':");
on_strip(f);
Process strip_process(options.empty() ?
ProcessCommand({ "strip", stringify(f) }) :
ProcessCommand({ "strip", options, stringify(f) }));
if (0 != strip_process.run().wait())
Log::get_instance()->message("strip.failure", ll_warning, lc_context) << "Couldn't strip '" << f << "'";
_imp->stripped_ids.insert(f.stat().lowlevel_id());
}
void
Stripper::do_split(const FSPath & f, const FSPath & g)
{
Context context("When splitting '" + stringify(f) + "' to '" + stringify(g) + "':");
on_split(f, g);
{
std::list<FSPath> to_make;
for (FSPath d(g.dirname()) ; (! d.stat().exists()) && (d != _imp->options.image_dir()) ; d = d.dirname())
to_make.push_front(d);
using namespace std::placeholders;
std::for_each(to_make.begin(), to_make.end(), std::bind(std::mem_fn(&FSPath::mkdir), _1, 0755, FSPathMkdirOptions() + fspmkdo_ok_if_exists));
}
ProcessCommand objcopy_copy_process_args({ "objcopy", "--only-keep-debug", stringify(f), stringify(g) });
if (_imp->options.compress_splits())
objcopy_copy_process_args.append_args({ "--compress-debug-sections" });
Process objcopy_copy_process(std::move(objcopy_copy_process_args));
Process objcopy_link_process(ProcessCommand({ "objcopy", "--add-gnu-debuglink=" + stringify(g), stringify(f) }));
if (0 != objcopy_copy_process.run().wait())
Log::get_instance()->message("strip.failure", ll_warning, lc_context) << "Couldn't copy debug information for '" << f << "'";
else if (0 != objcopy_link_process.run().wait())
Log::get_instance()->message("strip.failure", ll_warning, lc_context) << "Couldn't add debug link for '" << f << "'";
else
g.chmod(g.stat().permissions() & ~(S_IXGRP | S_IXUSR | S_IXOTH | S_IWOTH));
}
void
Stripper::do_dwarf_compress(const FSPath & f)
{
Context context("When compressing DWARF information for '" + stringify(f) + "'");
on_dwarf_compress(f);
Process dwz_process(ProcessCommand({ "dwz", /* quiet => */ "-q", stringify(f) }));
if (dwz_process.run().wait() != 0)
Log::get_instance()->message("strip.failure", ll_warning, lc_context)
<< "Couldn't compress DWARF information for '" << f << "'";
}
std::string
Stripper::strip_action_desc() const
{
return "str";
}
std::string
Stripper::split_action_desc() const
{
const char desc[3] = {
_imp->options.dwarf_compression() ? 'd' : 's',
'p',
_imp->options.compress_splits() ? 'z' : 'l',
};
return std::string(desc, sizeof(desc));
}
std::string
Stripper::unknown_action_desc() const
{
return "---";
}
std::string
Stripper::dwarf_compress_desc() const
{
return "dwz";
}