From patchwork Tue Apr 26 20:58:26 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: d wk X-Patchwork-Id: 615297 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3qvb6X5shwz9t5Z for ; Wed, 27 Apr 2016 06:58:44 +1000 (AEST) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; secure) header.d=sourceware.org header.i=@sourceware.org header.b=VRbSargk; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:mime-version:date:message-id:subject:from:to :content-type; q=dns; s=default; b=kTOlFJjsTOnAOLbfMf+1YfwO/TOdZ 5qnwMmKljk1W192k6DASpEN7wRVRgr9j42ofyUS7ffrbHeLRjCvybKofARKqguOR 9HBk+kf+4bxMr9FIhNz6MOz9ObaUNCDVAQmczPdRLp+cdqq5mTotVcbfxnqaAJpz RBOML0F/G3Y/zo= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=sourceware.org; h=list-id :list-unsubscribe:list-subscribe:list-archive:list-post :list-help:sender:mime-version:date:message-id:subject:from:to :content-type; s=default; bh=m0jjWp26EWbyz7FUTo6XPlaNeGo=; b=VRb Sargkct9hzidXk02b8aLzXDOJCJ0oX/Fv+qkHx50Syq1/1OaKTVu3G/X4BvznLwt kVFhYMJ3hpVnyN/k8ZGM9h3fGfQPfYF/Ryv8pOaUw6zGH3Btg+EPivq407jWbitw U9HiaW0zMCQyQtERTAdbuqXQO18PH7y+24EfjCqs= Received: (qmail 55706 invoked by alias); 26 Apr 2016 20:58:39 -0000 Mailing-List: contact libc-alpha-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libc-alpha-owner@sourceware.org Delivered-To: mailing list libc-alpha@sourceware.org Received: (qmail 55696 invoked by uid 89); 26 Apr 2016 20:58:38 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.6 required=5.0 tests=BAYES_00, FREEMAIL_FROM, RCVD_IN_DNSWL_LOW, SPF_PASS autolearn=ham version=3.3.2 spammy=link_map, Hx-spam-relays-external:209.85.223.196, Hx-languages-length:4871, H*RU:209.85.223.196 X-HELO: mail-io0-f196.google.com X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:date:message-id:subject:from:to; bh=0xczv7yLf0wqMFrGaaeu39HdbCJ/UHQq4zf6XPkzheY=; b=lD8aDoMBwJQ51dBdZ15mbZGJGBZk1NTzwyapH6+w57IjZY8vY9Gu/nUWDfYCvPyLvO tcIMQ42Z2HZccJTe2sSmY1FxwVQox4LieJwcnYLLIBRch/EhyX5tP8jZeGk0duyJWT4l WJnPINoCsg20FB2xHkAIZmDi42mlfkMDlz3dhClDWgSkuLlqSlGOvzpNZZAXvKdF+GBK bOPkR8fVGrcxCUfsSrG0zaysD+KDxdWS0kOePyzChOATgl7PlYmdIZmcV8YZqfFUA7PM I+tcqcIParXb5vt+ZsJMuzq6LH6QvHsNmWOT1kFH3e2dJQ/rxiWlWAMzbca0AW/wLskQ hJXg== X-Gm-Message-State: AOPr4FVnFrm3fHbsR1Bes2r0xHSRfO9ZKfd08bF6D28R8uvVgZ4CFqjIaVWcIZmREzsqyQ5x3ckY0nM19Rok+w== MIME-Version: 1.0 X-Received: by 10.107.31.129 with SMTP id f123mr6161885iof.180.1461704306462; Tue, 26 Apr 2016 13:58:26 -0700 (PDT) Date: Tue, 26 Apr 2016 16:58:26 -0400 Message-ID: Subject: [PATCH] Support -z initfirst for multiple shared libraries From: d wk To: libc-alpha@sourceware.org Hello libc developers, In a project of mine, I needed to run some code before any constructors from any system libraries (such as libc or libpthread). The linker/loader -z initfirst feature is perfect for this, but it only supports one shared library. Unfortunately libpthread also uses this feature (I assume the feature exists because pthread needed it), so my project was incompatible with libpthread. So, I wrote a small patch which changes the single dl_initfirst variable into a linked list. This patch does not change the size of any data structures (it's ABI compatible), just turns dl_initfirst into a list. The list is not freed (the allocator wouldn't free it anyway), and insertion into the list is quadratic, but I expect there will never be more than a handful of initfirst libraries! This patch records initfirst libraries in load order, so LD_PRELOAD libraries will have their constructors called before libpthread. If the opposite behaviour is desired, the LD_PRELOAD'd library can always declare a dependency on libpthread. Normally LD_PRELOAD constructors are run last, which is very inconvenient when trying to inject new functionality, and I expect anyone using -z initfirst with LD_PRELOAD to really want to run first. The patch is written against latest glibc 2.23 (I also tested on glibc 2.21, and it's not quite compatible with 2.17 since the other data structures changed). I was not the first person to run into this problem, someone wanted the same thing on stack overflow two years ago. You can see my answer there with a complete test case. http://stackoverflow.com/questions/19796383/linux-ld-preload-z-initfirst/36143861 Hope you will accept this patch. Comments welcome. Thanks, ~ dwk. ----[ cut here ]---- Support -z initfirst for multiple shared libraries (run in load order). This is particularly useful when combined with LD_PRELOAD, as it is then possible to run constructors before any code in other libraries runs. --- elf/dl-init.c | 9 ++++++++- elf/dl-load.c | 19 ++++++++++++++++++- elf/dl-support.c | 4 ++-- sysdeps/generic/ldsodefs.h | 7 +++++-- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/elf/dl-init.c b/elf/dl-init.c index 818c3aa..da59d1f 100644 --- a/elf/dl-init.c +++ b/elf/dl-init.c @@ -84,7 +84,14 @@ _dl_init (struct link_map *main_map, int argc, char **argv, char **env) if (__glibc_unlikely (GL(dl_initfirst) != NULL)) { - call_init (GL(dl_initfirst), argc, argv, env); + struct initfirst_list *initfirst; + for(initfirst = GL(dl_initfirst); initfirst; initfirst = initfirst->next) + { + call_init (initfirst->which, argc, argv, env); + } + + /* We do not try to free this list, as the memory will not be reclaimed + by the allocator unless there were no intervening malloc()'s. */ GL(dl_initfirst) = NULL; } diff --git a/elf/dl-load.c b/elf/dl-load.c index c0d6249..1efabbf 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -1388,7 +1388,24 @@ cannot enable executable stack as shared object requires"); /* Remember whether this object must be initialized first. */ if (l->l_flags_1 & DF_1_INITFIRST) - GL(dl_initfirst) = l; + { + struct initfirst_list *new_node = malloc(sizeof(*node)); + struct initfirst_list *it = GL(dl_initfirst); + new_node->which = l; + new_node->next = NULL; + + /* We append to the end of the linked list. Whichever library was loaded + first has higher initfirst priority. This means that LD_PRELOAD + initfirst overrides initfirst in libraries linked normally. */ + if (!it) + GL(dl_initfirst) = new_node; + else + { + while (it->next) + it = it->next; + it->next = new_node; + } + } /* Finally the file information. */ l->l_file_id = id; diff --git a/elf/dl-support.c b/elf/dl-support.c index c30194c..d8b8acc 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -147,8 +147,8 @@ struct r_search_path_elem *_dl_all_dirs; /* All directories after startup. */ struct r_search_path_elem *_dl_init_all_dirs; -/* The object to be initialized first. */ -struct link_map *_dl_initfirst; +/* The list of objects to be initialized first. */ +struct initfirst_list *_dl_initfirst; /* Descriptor to write debug messages to. */ int _dl_debug_fd = STDERR_FILENO; diff --git a/sysdeps/generic/ldsodefs.h b/sysdeps/generic/ldsodefs.h index ddec0be..198c089 100644 --- a/sysdeps/generic/ldsodefs.h +++ b/sysdeps/generic/ldsodefs.h @@ -326,8 +326,11 @@ struct rtld_global /* Incremented whenever something may have been added to dl_loaded. */ EXTERN unsigned long long _dl_load_adds; - /* The object to be initialized first. */ - EXTERN struct link_map *_dl_initfirst; + /* The list of objects to be initialized first. */ + EXTERN struct initfirst_list { + struct link_map *which; + struct initfirst_list *next; + } *_dl_initfirst; #if HP_SMALL_TIMING_AVAIL /* Start time on CPU clock. */