Skip to content

Commit

Permalink
Update global ELF ctor/dtor instrumentation for static executables (#…
Browse files Browse the repository at this point in the history
…1355)

If __libc_csu_init doesn't exist, try to insert the constructor call into the beginning of 'main'. If __libc_csu_fini doesn't exist, try to insert the constructor call into the beginning of 'exit'.

* Use C-style strings for symbol names

There's no need to have a std::string object that incurs the overhead of a constructor/destructor call to just hold a literal that's only visible in the current translation unit.
  • Loading branch information
hainest committed Feb 23, 2023
1 parent 334b685 commit 3046332
Show file tree
Hide file tree
Showing 3 changed files with 205 additions and 178 deletions.
104 changes: 57 additions & 47 deletions dyninstAPI/src/parse-aarch64.C
Original file line number Diff line number Diff line change
Expand Up @@ -61,14 +61,12 @@
//#warning "This file is not implemented yet!"
using namespace Dyninst::SymtabAPI;

static const std::string LIBC_CTOR_HANDLER("__libc_csu_init");
static const std::string LIBC_DTOR_HANDLER("__libc_csu_fini");
static const std::string DYNINST_CTOR_HANDLER("DYNINSTglobal_ctors_handler");
static const std::string DYNINST_CTOR_LIST("DYNINSTctors_addr");
static const std::string DYNINST_DTOR_HANDLER("DYNINSTglobal_dtors_handler");
static const std::string DYNINST_DTOR_LIST("DYNINSTdtors_addr");
static const std::string SYMTAB_CTOR_LIST_REL("__SYMTABAPI_CTOR_LIST__");
static const std::string SYMTAB_DTOR_LIST_REL("__SYMTABAPI_DTOR_LIST__");
namespace {
char const* LIBC_CTOR_HANDLER("__libc_csu_init");
char const* LIBC_DTOR_HANDLER("__libc_csu_fini");
char const* DYNINST_CTOR_HANDLER("DYNINSTglobal_ctors_handler");
char const* DYNINST_DTOR_HANDLER("DYNINSTglobal_dtors_handler");
}

/*
By parsing the function that actually sets up the parameters for the OMP
Expand All @@ -77,6 +75,7 @@ dealing with */
bool parse_func::parseOMPParent(image_parRegion * /*iPar*/, int /*desiredNum*/, int & /*currentSectionNum*/ )
{
assert(0);
return false;
}


Expand All @@ -86,6 +85,7 @@ std::string parse_func::calcParentFunc(const parse_func *,
std::vector<image_parRegion *> &/*pR*/)
{
assert(0);
return {};
}


Expand Down Expand Up @@ -172,58 +172,67 @@ using namespace Dyninst::SymtabAPI;
*/

bool BinaryEdit::doStaticBinarySpecialCases() {
Symtab *origBinary = mobj->parse_img()->getObject();

/* Special Case 1: Handling global constructor and destructor Regions
* Invoke Dyninst constructor after all static constructors are called
* and invoke Dyninst destructor before staitc destructors
*/

// First, find all the necessary symbol info.

func_instance *globalCtorHandler = mobj->findGlobalConstructorFunc(LIBC_CTOR_HANDLER);
if( !globalCtorHandler ) {
logLine("failed to find libc constructor handler\n");
fprintf (stderr, "failed to find libc constructor handler\n");
return false;
}

/* Special Case 1A: Handling global constructors
*
* Place the Dyninst constructor handler after the global ELF ctors so it is invoked last.
*
* Prior to glibc-2.34, this was in the exit point(s) of __libc_csu_init which
* calls all of the initializers in preinit_array and init_array as per SystemV
* before __libc_start_main is invoked.
*
* In glibc-2.34, the code from the csu_* functions was moved into __libc_start_main, so
* now the only place where we are guaranteed that the global constructors have all been
* called is at the beginning of 'main'.
*/
func_instance *dyninstCtorHandler = findOnlyOneFunction(DYNINST_CTOR_HANDLER);
if( !dyninstCtorHandler ) {
logLine("failed to find Dyninst constructor handler\n");
fprintf (stderr,"failed to find Dyninst constructor handler\n");
return false;
}

func_instance *globalDtorHandler = mobj->findGlobalDestructorFunc(LIBC_DTOR_HANDLER);
if( !globalDtorHandler ) {
logLine ("failed to find libc destructor handler\n");
fprintf (stderr,"failed to find libc destructor handler\n");
return false;
if(auto *ctor = mobj->findGlobalConstructorFunc(LIBC_CTOR_HANDLER)) {
// Wire in our handler at libc ctor exits
vector<instPoint*> init_pts;
ctor->funcExitPoints(&init_pts);
for(auto *exit_pt : init_pts) {
add_handler(exit_pt, dyninstCtorHandler);
}
} else if(auto *main = findOnlyOneFunction("main")) {
// Insert constructor into the beginning of 'main'
add_handler(main->funcEntryPoint(true), dyninstCtorHandler);
} else {
logLine("failed to find place to insert Dyninst constructors\n");
return false;
}

/* Special Case 1B: Handling global destructors
*
* Place the Dyninst destructor handler before the global ELF dtors so it is invoked first.
*
* Prior to glibc-2.34, this was in the entry point of __libc_csu_fini.
*
* In glibc-2.34, the code in __libc_csu_fini was moved into a hidden function that is
* registered with atexit. To ensure the Dyninst destructors are always called first, we
* have to insert the handler at the beginning of `exit`.
*
* This is a fragile solution as there is no requirement that a symbol for `exit` is
* exported. If we can't find it, we'll just fail here.
*/
func_instance *dyninstDtorHandler = findOnlyOneFunction(DYNINST_DTOR_HANDLER);
if( !dyninstDtorHandler ) {
logLine("failed to find Dyninst destructor handler\n");
fprintf (stderr,"failed to find Dyninst destructor handler\n");
return false;
}

// Instrument the exits of global constructor function
vector<instPoint*> init_pts;
instPoint* fini_point;
globalCtorHandler->funcExitPoints(&init_pts);

// Instrument the entry of global destructor function
fini_point = globalDtorHandler->funcEntryPoint(true);
// convert points to instpoints
for(auto exit_pt = init_pts.begin();
exit_pt != init_pts.end();
++exit_pt)
{
add_handler(*exit_pt, dyninstCtorHandler);
if(auto *dtor = mobj->findGlobalDestructorFunc(LIBC_DTOR_HANDLER)) {
// Insert destructor into beginning of libc global dtor handler
add_handler(dtor->funcEntryPoint(true), dyninstDtorHandler);
} else if(auto *exit_ = findOnlyOneFunction("exit")) {
// Insert destructor into beginning of `exit`
add_handler(exit_->funcEntryPoint(true), dyninstDtorHandler);
} else {
logLine("failed to find place to insert Dyninst destructors\n");
return false;
}
add_handler(fini_point, dyninstDtorHandler);

AddressSpace::patch(this);


Expand All @@ -243,6 +252,7 @@ bool BinaryEdit::doStaticBinarySpecialCases() {

vector<Archive *> libs;
vector<Archive *>::iterator libIter;
Symtab *origBinary = mobj->parse_img()->getObject();
if( origBinary->getLinkingResources(libs) ) {
for(libIter = libs.begin(); libIter != libs.end(); ++libIter) {
if( (*libIter)->name().find("libpthread") != std::string::npos ||
Expand Down
103 changes: 54 additions & 49 deletions dyninstAPI/src/parse-power.C
Original file line number Diff line number Diff line change
Expand Up @@ -60,16 +60,12 @@


using namespace Dyninst::SymtabAPI;
static const std::string LIBC_CTOR_HANDLER("__libc_csu_init");
static const std::string LIBC_DTOR_HANDLER("__libc_csu_fini");
static const std::string DYNINST_CTOR_HANDLER("DYNINSTglobal_ctors_handler");
static const std::string DYNINST_CTOR_BEGIN("DYNINSTctors_begin");
static const std::string DYNINST_CTOR_END("DYNINSTctors_end");
static const std::string DYNINST_DTOR_HANDLER("DYNINSTglobal_dtors_handler");
static const std::string DYNINST_DTOR_BEGIN("DYNINSTdtors_begin");
static const std::string DYNINST_DTOR_END("DYNINSTdtors_end");
static const std::string SYMTAB_CTOR_LIST_REL("__SYMTABAPI_CTOR_LIST__");
static const std::string SYMTAB_DTOR_LIST_REL("__SYMTABAPI_DTOR_LIST__");
namespace {
char const* LIBC_CTOR_HANDLER("__libc_csu_init");
char const* LIBC_DTOR_HANDLER("__libc_csu_fini");
char const* DYNINST_CTOR_HANDLER("DYNINSTglobal_ctors_handler");
char const* DYNINST_DTOR_HANDLER("DYNINSTglobal_dtors_handler");
}

static void add_handler(instPoint* pt, func_instance* add_me)
{
Expand Down Expand Up @@ -257,58 +253,66 @@ using namespace Dyninst::SymtabAPI;
*/

bool BinaryEdit::doStaticBinarySpecialCases() {
Symtab *origBinary = mobj->parse_img()->getObject();

/* Special Case 1: Handling global constructor and destructor Regions
* Invoke Dyninst constructor after all static constructors are called
* and invoke Dyninst destructor before staitc destructors
*/

// First, find all the necessary symbol info.

func_instance *globalCtorHandler = mobj->findGlobalConstructorFunc(LIBC_CTOR_HANDLER);
if( !globalCtorHandler ) {
logLine("failed to find libc constructor handler\n");
fprintf (stderr, "failed to find libc constructor handler\n");
return false;
}

/* Special Case 1A: Handling global constructors
*
* Place the Dyninst constructor handler after the global ELF ctors so it is invoked last.
*
* Prior to glibc-2.34, this was in the exit point(s) of __libc_csu_init which
* calls all of the initializers in preinit_array and init_array as per SystemV
* before __libc_start_main is invoked.
*
* In glibc-2.34, the code from the csu_* functions was moved into __libc_start_main, so
* now the only place where we are guaranteed that the global constructors have all been
* called is at the beginning of 'main'.
*/
func_instance *dyninstCtorHandler = findOnlyOneFunction(DYNINST_CTOR_HANDLER);
if( !dyninstCtorHandler ) {
logLine("failed to find Dyninst constructor handler\n");
fprintf (stderr,"failed to find Dyninst constructor handler\n");
return false;
}

func_instance *globalDtorHandler = mobj->findGlobalDestructorFunc(LIBC_DTOR_HANDLER);
if( !globalDtorHandler ) {
logLine ("failed to find libc destructor handler\n");
fprintf (stderr,"failed to find libc destructor handler\n");
return false;
if(auto *ctor = mobj->findGlobalConstructorFunc(LIBC_CTOR_HANDLER)) {
// Wire in our handler at libc ctor exits
vector<instPoint*> init_pts;
ctor->funcExitPoints(&init_pts);
for(auto *exit_pt : init_pts) {
add_handler(exit_pt, dyninstCtorHandler);
}
} else if(auto *main = findOnlyOneFunction("main")) {
// Insert constructor into the beginning of 'main'
add_handler(main->funcEntryPoint(true), dyninstCtorHandler);
} else {
logLine("failed to find place to insert Dyninst constructors\n");
return false;
}

/* Special Case 1B: Handling global destructors
*
* Place the Dyninst destructor handler before the global ELF dtors so it is invoked first.
*
* Prior to glibc-2.34, this was in the entry point of __libc_csu_fini.
*
* In glibc-2.34, the code in __libc_csu_fini was moved into a hidden function that is
* registered with atexit. To ensure the Dyninst destructors are always called first, we
* have to insert the handler at the beginning of `exit`.
*
* This is a fragile solution as there is no requirement that a symbol for `exit` is
* exported. If we can't find it, we'll just fail here.
*/
func_instance *dyninstDtorHandler = findOnlyOneFunction(DYNINST_DTOR_HANDLER);
if( !dyninstDtorHandler ) {
logLine("failed to find Dyninst destructor handler\n");
fprintf (stderr,"failed to find Dyninst destructor handler\n");
return false;
}

// Instrument the exits of global constructor function
vector<instPoint*> init_pts;
instPoint* fini_point;
globalCtorHandler->funcExitPoints(&init_pts);

// Instrument the entry of global destructor function
fini_point = globalDtorHandler->funcEntryPoint(true);
// convert points to instpoints
for(auto exit_pt = init_pts.begin();
exit_pt != init_pts.end();
++exit_pt)
{
add_handler(*exit_pt, dyninstCtorHandler);
if(auto *dtor = mobj->findGlobalDestructorFunc(LIBC_DTOR_HANDLER)) {
// Insert destructor into beginning of libc global dtor handler
add_handler(dtor->funcEntryPoint(true), dyninstDtorHandler);
} else if(auto *exit_ = findOnlyOneFunction("exit")) {
// Insert destructor into beginning of `exit`
add_handler(exit_->funcEntryPoint(true), dyninstDtorHandler);
} else {
logLine("failed to find place to insert Dyninst destructors\n");
return false;
}
add_handler(fini_point, dyninstDtorHandler);
AddressSpace::patch(this);


Expand All @@ -328,6 +332,7 @@ bool BinaryEdit::doStaticBinarySpecialCases() {

vector<Archive *> libs;
vector<Archive *>::iterator libIter;
Symtab *origBinary = mobj->parse_img()->getObject();
if( origBinary->getLinkingResources(libs) ) {
for(libIter = libs.begin(); libIter != libs.end(); ++libIter) {
if( (*libIter)->name().find("libpthread") != std::string::npos ||
Expand Down

0 comments on commit 3046332

Please sign in to comment.