From patchwork Fri May 14 06:56:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Michael Clark X-Patchwork-Id: 43428 Return-Path: X-Original-To: patchwork@sourceware.org Delivered-To: patchwork@sourceware.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 426DB393BC3D; Fri, 14 May 2021 06:58:20 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 426DB393BC3D DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=sourceware.org; s=default; t=1620975500; bh=ExUMgac+nLCrDrF0hgOiVXqcVX9BoVcPfu47NcCN4nQ=; h=To:Subject:Date:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:From; b=m3Qnf0LPPHiXg5O9CXfrgX6Bq8LWFMw7iFrYzPLHiWTZNRAceE5Q9DjJYqsaJBku2 EEI1sZW/GZ9pGThoT5p0sDePk8ZlzowexeC1jaIWrxpkWkhkAzET4IuzrlKqdfK2BI JmD2ZjZLLxPG7FicdvJ7NRzowMGoqgDazIGKfGRY= X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mr85p00im-ztdg06011801.me.com (mr85p00im-ztdg06011801.me.com [17.58.23.199]) by sourceware.org (Postfix) with ESMTPS id EB2C7393BC37 for ; Fri, 14 May 2021 06:58:16 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.3.2 sourceware.org EB2C7393BC37 Received: from miqi.Home (unknown [202.150.110.104]) by mr85p00im-ztdg06011801.me.com (Postfix) with ESMTPSA id 529CCC033A; Fri, 14 May 2021 06:58:14 +0000 (UTC) To: libc-alpha@sourceware.org Subject: [PATCH v2] rtld: make ldd show relative addresses and add option for aligned output Date: Fri, 14 May 2021 18:56:52 +1200 Message-Id: <20210514065652.114062-1-michaeljclark@mac.com> X-Mailer: git-send-email 2.30.2 MIME-Version: 1.0 X-Proofpoint-Virus-Version: =?utf-8?q?vendor=3Dfsecure_engine=3D1=2E1=2E170-?= =?utf-8?q?22c6f66c430a71ce266a39bfe25bc2903e8d5c8f=3A6=2E0=2E391=2C18=2E0?= =?utf-8?q?=2E761=2C17=2E0=2E607=2E475=2E0000000_definitions=3D2021-05-14=5F?= =?utf-8?q?02=3A2021-05-13=5F01=2C2021-05-14=5F02=2C2020-04-07=5F01_signatur?= =?utf-8?q?es=3D0?= X-Proofpoint-Spam-Details: rule=notspam policy=default score=0 clxscore=1015 bulkscore=0 suspectscore=0 phishscore=0 malwarescore=0 spamscore=0 mlxlogscore=999 mlxscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.12.0-2009150000 definitions=main-2105140050 X-Spam-Status: No, score=-11.5 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H2, SPF_HELO_NONE, SPF_PASS, TXREP autolearn=ham autolearn_force=no version=3.4.2 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-Patchwork-Original-From: Michael Clark via Libc-alpha From: Michael Clark Reply-To: Michael Clark Errors-To: libc-alpha-bounces@sourceware.org Sender: "Libc-alpha" This change increases `ldd` readability: - modifies trace output to use relative addresses by default. - add alternative trace output mode with left-justified addresses. The relative addresses are composed by subtracting the ELF ehdr address which makes the output constant under address space layout randomization. This should be a safe change because the default format is preserved. The intention is to make `ldd` easier to cross reference with objdump. Also, log files including `ldd` output will contain less differences. The vdso is the only address that changes when using relative addresses. * Justified output * The new trace format is enabled with `LD_TRACE_ADDR_JUSTIFY=1`, otherwise the default `ldd` trace format is selected for compatibility. * Relative addresses * `ldd` load addresses are displayed relative to the ld.so executable header address. Relative addresses are enabled by default, given the output mimics systems without ASLR, thus there should be minimal compatibility issues. There is also an option to negate addresses as an aid in interpreting them, seeing library addresses relative to the loader with negative offsets. The patch adds three new ld.so flags accessible via environment variables: - `LD_TRACE_ADDR_JUSTIFY=1` - Show addresses left-justified - `LD_TRACE_ADDR_ABSOLUTE=1` - Show absolute addresses (backwards compat) - `LD_TRACE_ADDR_NEGATE=1` - Show negated addresses (combination option) This is a resend. Previous feedback was why do we want to read the load addresses. Why are they even displayed then? Q.E.D. Relative addresses make sense, but they are not very readable unless left justified. Signed-off-by: Michael Clark --- elf/rtld.c | 133 +++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 113 insertions(+), 20 deletions(-) diff --git a/elf/rtld.c b/elf/rtld.c index fbbd60b446da..7fbd283ce14f 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -143,6 +143,24 @@ static void dl_main_state_init (struct dl_main_state *state); extern char **_environ attribute_hidden; static void process_envvars (struct dl_main_state *state); +/* Option to display relative load addresses, with display address + * having executable base address subtracted, making output constant + * in the presence of ASLR, as well as reducing output differences. + * `LD_TRACE_ADDR_ABSOLUTE=1` restores prior behavior. */ +static int trace_addr_relative = 1; + +/* Option to negate load addresses, otherwise the default shows + * negative relative offsets beacuase ld.so loads libs downwards, + * and brk space is just after the executable in memory. The + * justified format has sign to show negated address offsets. + * `LD_TRACE_ADDR_NEGATE=1` to show signed positive offsets. */ +static int trace_addr_negate = 0; + +/* Option to display left-justified addresses, making the listing + * easier to read because addresses are all lined up in one column. + * `LD_TRACE_ADDR_JUSTIFY=1` will left-justify addresses. */ +static int trace_addr_justify = 0; + #ifdef DL_ARGV_NOT_RELRO int _dl_argc attribute_hidden; char **_dl_argv = NULL; @@ -1708,6 +1726,8 @@ dl_main (const ElfW(Phdr) *phdr, GL(dl_rtld_map).l_phdr = rtld_phdr; GL(dl_rtld_map).l_phnum = rtld_ehdr->e_phnum; + /* base address (ld.so ehdr) used for relative display addresses */ + size_t disp_addr, base_addr = (size_t) rtld_ehdr; /* PT_GNU_RELRO is usually the last phdr. */ size_t cnt = rtld_ehdr->e_phnum; @@ -2022,6 +2042,12 @@ dl_main (const ElfW(Phdr) *phdr, for (i = 0; i < scope->r_nlist; i++) { l = scope->r_list [i]; + + /* Subtract and negate base from load address if requested */ + disp_addr = (size_t) l->l_map_start; + if (trace_addr_relative) disp_addr -= base_addr; + if (trace_addr_negate) disp_addr = -disp_addr; + if (l->l_faked) { _dl_printf ("\t%s => not found\n", l->l_libname->name); @@ -2029,20 +2055,42 @@ dl_main (const ElfW(Phdr) *phdr, } if (_dl_name_match_p (GLRO(dl_trace_prelink), l)) GLRO(dl_trace_prelink_map) = l; - _dl_printf ("\t%s => %s (0x%0*Zx, 0x%0*Zx)", - DSO_FILENAME (l->l_libname->name), - DSO_FILENAME (l->l_name), - (int) sizeof l->l_map_start * 2, - (size_t) l->l_map_start, - (int) sizeof l->l_addr * 2, - (size_t) l->l_addr); - - if (l->l_tls_modid) - _dl_printf (" TLS(0x%Zx, 0x%0*Zx)\n", l->l_tls_modid, - (int) sizeof l->l_tls_offset * 2, - (size_t) l->l_tls_offset); + if (trace_addr_justify) + { + _dl_printf ("\t(%s0x%0*Zx, 0x%0*Zx)", + trace_addr_negate ? "-" : "+", + (int) sizeof l->l_map_start * 2, + disp_addr, + (int) sizeof l->l_addr * 2, + (size_t) l->l_addr); + + if (l->l_tls_modid) + _dl_printf (" TLS(0x%Zx, 0x%0*Zx)", l->l_tls_modid, + (int) sizeof l->l_tls_offset * 2, + (size_t) l->l_tls_offset); + + _dl_printf (" %s => %s\n", + DSO_FILENAME (l->l_libname->name), + DSO_FILENAME (l->l_name)); + + } else - _dl_printf ("\n"); + { + _dl_printf ("\t%s => %s (0x%0*Zx, 0x%0*Zx)", + DSO_FILENAME (l->l_libname->name), + DSO_FILENAME (l->l_name), + (int) sizeof l->l_map_start * 2, + disp_addr, + (int) sizeof l->l_addr * 2, + (size_t) l->l_addr); + + if (l->l_tls_modid) + _dl_printf (" TLS(0x%Zx, 0x%0*Zx)\n", l->l_tls_modid, + (int) sizeof l->l_tls_offset * 2, + (size_t) l->l_tls_offset); + else + _dl_printf ("\n"); + } } } else if (GLRO(dl_debug_mask) & DL_DEBUG_UNUSED) @@ -2096,17 +2144,41 @@ dl_main (const ElfW(Phdr) *phdr, else { for (l = main_map->l_next; l; l = l->l_next) + { + /* Subtract and negate base from load address if requested */ + disp_addr = (size_t) l->l_map_start; + if (trace_addr_relative) disp_addr -= base_addr; + if (trace_addr_negate) disp_addr = -disp_addr; + if (l->l_faked) /* The library was not found. */ _dl_printf ("\t%s => not found\n", l->l_libname->name); - else if (strcmp (l->l_libname->name, l->l_name) == 0) - _dl_printf ("\t%s (0x%0*Zx)\n", l->l_libname->name, - (int) sizeof l->l_map_start * 2, - (size_t) l->l_map_start); else - _dl_printf ("\t%s => %s (0x%0*Zx)\n", l->l_libname->name, - l->l_name, (int) sizeof l->l_map_start * 2, - (size_t) l->l_map_start); + if (trace_addr_justify) + if (strcmp (l->l_libname->name, l->l_name) == 0) + _dl_printf ("\t(%s0x%0*Zx) %s\n", + trace_addr_negate ? "-" : "+", + (int) sizeof l->l_map_start * 2, + disp_addr, + l->l_libname->name); + else + _dl_printf ("\t(%s0x%0*Zx) %s => %s\n", + trace_addr_negate ? "-" : "+", + (int) sizeof l->l_map_start * 2, + disp_addr, + l->l_libname->name, l->l_name); + else + if (strcmp (l->l_libname->name, l->l_name) == 0) + _dl_printf ("\t%s (0x%0*Zx)\n", + l->l_libname->name, + (int) sizeof l->l_map_start * 2, + disp_addr); + else + _dl_printf ("\t%s => %s (0x%0*Zx)\n", + l->l_libname->name, + l->l_name, (int) sizeof l->l_map_start * 2, + disp_addr); + } } if (__glibc_unlikely (state.mode != rtld_mode_trace)) @@ -2792,6 +2864,27 @@ process_envvars (struct dl_main_state *state) } break; + case 17: + /* Addresses can be negated. */ + if (!__libc_enable_secure + && memcmp (envline, "TRACE_ADDR_NEGATE", 17) == 0) + trace_addr_negate = envline[18] != '\0'; + break; + + case 18: + /* Addresses can be left-justified. */ + if (!__libc_enable_secure + && memcmp (envline, "TRACE_ADDR_JUSTIFY", 18) == 0) + trace_addr_justify = envline[19] != '\0'; + break; + + case 19: + /* Absolute addresses can be displayed. */ + if (!__libc_enable_secure + && memcmp (envline, "TRACE_ADDR_ABSOLUTE", 19) == 0) + trace_addr_relative = envline[20] == '\0'; + break; + case 20: /* The mode of the dynamic linker can be set. */ if (memcmp (envline, "TRACE_LOADED_OBJECTS", 20) == 0)