From e0785056a91f893ef4235aefb2a26bed3b17537f Mon Sep 17 00:00:00 2001 From: Chih-Hung Hsieh Date: Sun, 31 May 2020 14:06:12 +0100 Subject: [PATCH 3/3] For clang use Blocks instead of nested functions. * Clang has Blocks like closures that can serve similar purpose as the nested functions in gnu99. Syntax of Blocks is similar to nested functions that *NESTED_FUNC macro can be used for the function/block declarations. See spec in http://clang.llvm.org/docs/BlockLanguageSpec.html * Local variables used in a closure should have __BLOCK attribute unless they are constants. * Formal parameters used in a closure should be copied to local variable and declared as __BLOCK. * Cannot goto and jump over __BLOCK variables, so these variables have been moved to be declared before goto. * Clang Blocks cannot copy an array to a closure, and gcc complains about unbounded stack usage from alloca. --- lib/nested_func.h | 85 +++++++++++++++++++ libdwfl/dwfl_segment_report_module.c | 117 ++++++++++++++++----------- libdwfl/link_map.c | 31 ++++--- 3 files changed, 174 insertions(+), 59 deletions(-) create mode 100644 lib/nested_func.h diff --git a/lib/nested_func.h b/lib/nested_func.h new file mode 100644 index 0000000..be44a31 --- /dev/null +++ b/lib/nested_func.h @@ -0,0 +1,85 @@ +/* Copyright (C) 2015 Red Hat, Inc. + This file is part of elfutils. + Written by Chih-Hung Hsieh , 2015. + + This file is free software; you can redistribute it and/or modify + it under the terms of either + + * the GNU Lesser General Public License as published by the Free + Software Foundation; either version 3 of the License, or (at + your option) any later version + + or + + * the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at + your option) any later version + + or both in parallel, as here. + + elfutils 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 copies of the GNU General Public License and + the GNU Lesser General Public License along with this program. If + not, see . */ + +#ifndef _NESTED_FUNC_H +#define _NESTED_FUNC_H 1 + +#if __clang__ + + #define __BLOCK __block + + #define NESTED_FUNC(return_type, function_name, \ + arg_types, arg_types_and_names) \ + return_type (^function_name) arg_types = \ + ^ return_type arg_types_and_names + + /* Clang does not like inline keyword before a block variable. */ + #define INLINE_NESTED_FUNC(r, f, t, a) \ + NESTED_FUNC (r, f, t, a) + + #define INLINE_INTUSE_NESTED_FUNC(r, f, t, a) \ + NESTED_FUNC (r, INTUSE(f), t, a) + + /* Recrusive blocks need to be declared before used. */ + #define RECURSIVE_NESTED_FUNC(return_type, function_name, \ + arg_types, arg_types_and_names) \ + __BLOCK return_type (^function_name) arg_types; \ + function_name = ^ return_type arg_types_and_names + + #define INLINE_RECURSIVE_NESTED_FUNC(r, f, t, a) \ + RECURSIVE_NESTED_FUNC (r, f, t, a) + + #define INLINE_INTUSE_RECURSIVE_NESTED_FUNC(r, f, t, a) \ + RECURSIVE_NESTED_FUNC (r, INTUSE(f), t, a) + +#else /* gcc nested function */ + + #define __BLOCK + + #define NESTED_FUNC(return_type, function_name, \ + arg_types, arg_types_and_names) \ + return_type function_name arg_types_and_names + + #define INLINE_NESTED_FUNC(r, f, t, a) \ + inline NESTED_FUNC (r, f, t, a) + + #define INLINE_INTUSE_NESTED_FUNC(r, f, t, a) \ + inline NESTED_FUNC (r, INTUSE(f), t, a) + + #define RECURSIVE_NESTED_FUNC(r, f, t, a) \ + NESTED_FUNC (r, f, t, a) + + #define INLINE_RECURSIVE_NESTED_FUNC(r, f, t, a) \ + inline RECURSIVE_NESTED_FUNC (r, f, t, a) + + #define INLINE_INTUSE_RECURSIVE_NESTED_FUNC(r, f, t, a) \ + INLINE_RECURSIVE_NESTED_FUNC (r, INTUSE(f), t, a) + +#endif + +#endif /* _NESTED_FUNC_H */ diff --git a/libdwfl/dwfl_segment_report_module.c b/libdwfl/dwfl_segment_report_module.c index 430e13d..b7c6733 100644 --- a/libdwfl/dwfl_segment_report_module.c +++ b/libdwfl/dwfl_segment_report_module.c @@ -31,6 +31,7 @@ #undef _ #include "libdwflP.h" #include "common.h" +#include "nested_func.h" #include #include @@ -257,19 +258,23 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, GElf_Addr start = dwfl->lookup_addr[segment]; - inline bool segment_read (int segndx, - void **buffer, size_t *buffer_available, - GElf_Addr addr, size_t minread) + INLINE_NESTED_FUNC (bool, segment_read, + (int , void **, size_t *, GElf_Addr, size_t), + (int segndx, + void **buffer, size_t *buffer_available, + GElf_Addr addr, size_t minread)) { return ! (*memory_callback) (dwfl, segndx, buffer, buffer_available, addr, minread, memory_callback_arg); - } + }; - inline void release_buffer (void **buffer, size_t *buffer_available) + INLINE_NESTED_FUNC (void, release_buffer, + (void **, size_t *), + (void **buffer, size_t *buffer_available)) { if (*buffer != NULL) (void) segment_read (-1, buffer, buffer_available, 0, 0); - } + }; /* First read in the file header and check its sanity. */ @@ -282,7 +287,7 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, here so we can always safely free it. */ void *phdrsp = NULL; - inline int finish (void) + INLINE_NESTED_FUNC (int, finish, (void), (void)) { free (phdrsp); release_buffer (&buffer, &buffer_available); @@ -291,15 +296,17 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, if (fd != -1) close (fd); return ndx; - } + }; if (segment_read (ndx, &buffer, &buffer_available, start, sizeof (Elf64_Ehdr)) || memcmp (buffer, ELFMAG, SELFMAG) != 0) return finish (); - inline bool read_portion (void **data, size_t *data_size, - GElf_Addr vaddr, size_t filesz) + INLINE_NESTED_FUNC (bool, read_portion, + (void **, size_t *, GElf_Addr, size_t), + (void **data, size_t *data_size, + GElf_Addr vaddr, size_t filesz)) { /* Check whether we will have to read the segment data, or if it can be returned from the existing buffer. */ @@ -320,13 +327,15 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, *data = vaddr - start + buffer; *data_size = 0; return false; - } + }; - inline void finish_portion (void **data, size_t *data_size) + INLINE_NESTED_FUNC (void, finish_portion, + (void **, size_t *), + (void **data, size_t *data_size)) { if (*data_size != 0) release_buffer (data, data_size); - } + }; /* Extract the information we need from the file header. */ const unsigned char *e_ident; @@ -342,13 +351,13 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, uint_fast16_t phnum; uint_fast16_t phentsize; GElf_Off shdrs_end; - Elf_Data xlatefrom = + __BLOCK Elf_Data xlatefrom = { .d_type = ELF_T_EHDR, .d_buf = (void *) buffer, .d_version = EV_CURRENT, }; - Elf_Data xlateto = + __BLOCK Elf_Data xlateto = { .d_type = ELF_T_EHDR, .d_buf = &ehdr, @@ -433,32 +442,33 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, xlateto.d_size = phdrsp_bytes; /* Track the bounds of the file visible in memory. */ - GElf_Off file_trimmed_end = 0; /* Proper p_vaddr + p_filesz end. */ - GElf_Off file_end = 0; /* Rounded up to effective page size. */ - GElf_Off contiguous = 0; /* Visible as contiguous file from START. */ - GElf_Off total_filesz = 0; /* Total size of data to read. */ + __BLOCK GElf_Off file_trimmed_end = 0; /* Proper p_vaddr + p_filesz end. */ + __BLOCK GElf_Off file_end = 0; /* Rounded up to effective page size. */ + __BLOCK GElf_Off contiguous = 0; /* Visible as contiguous file from START. */ + __BLOCK GElf_Off total_filesz = 0; /* Total size of data to read. */ /* Collect the bias between START and the containing PT_LOAD's p_vaddr. */ - GElf_Addr bias = 0; - bool found_bias = false; + __BLOCK GElf_Addr bias = 0; + __BLOCK bool found_bias = false; /* Collect the unbiased bounds of the module here. */ - GElf_Addr module_start = -1l; - GElf_Addr module_end = 0; - GElf_Addr module_address_sync = 0; + __BLOCK GElf_Addr module_start = -1l; + __BLOCK GElf_Addr module_end = 0; + __BLOCK GElf_Addr module_address_sync = 0; /* If we see PT_DYNAMIC, record it here. */ - GElf_Addr dyn_vaddr = 0; - GElf_Xword dyn_filesz = 0; + __BLOCK GElf_Addr dyn_vaddr = 0; + __BLOCK GElf_Xword dyn_filesz = 0; /* Collect the build ID bits here. */ - void *build_id = NULL; - size_t build_id_len = 0; - GElf_Addr build_id_vaddr = 0; + __BLOCK void *build_id = NULL; + __BLOCK size_t build_id_len = 0; + __BLOCK GElf_Addr build_id_vaddr = 0; /* Consider a PT_NOTE we've found in the image. */ - inline void consider_notes (GElf_Addr vaddr, GElf_Xword filesz, - GElf_Xword align) + INLINE_NESTED_FUNC (void, consider_notes, + (GElf_Addr, GElf_Xword, GElf_Xword), + (GElf_Addr vaddr, GElf_Xword filesz, GElf_Xword align)) { /* If we have already seen a build ID, we don't care any more. */ if (build_id != NULL || filesz == 0) @@ -535,13 +545,18 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, if (notes != data) free (notes); finish_portion (&data, &data_size); - } + }; /* Consider each of the program headers we've read from the image. */ - inline void consider_phdr (GElf_Word type, - GElf_Addr vaddr, GElf_Xword memsz, - GElf_Off offset, GElf_Xword filesz, - GElf_Xword align) + INLINE_NESTED_FUNC (void, consider_phdr, + (GElf_Word, + GElf_Addr, GElf_Xword, + GElf_Off, GElf_Xword, + GElf_Xword), + (GElf_Word type, + GElf_Addr vaddr, GElf_Xword memsz, + GElf_Off offset, GElf_Xword filesz, + GElf_Xword align)) { switch (type) { @@ -604,7 +619,7 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, module_end = vaddr_end; break; } - } + }; Elf32_Phdr (*p32)[phnum] = phdrsp; Elf64_Phdr (*p64)[phnum] = phdrsp; @@ -751,11 +766,12 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, We need its DT_STRTAB and DT_STRSZ to decipher DT_SONAME, and they also tell us the essential portion of the file for fetching symbols. */ - GElf_Addr soname_stroff = 0; - GElf_Addr dynstr_vaddr = 0; - GElf_Xword dynstrsz = 0; - bool execlike = false; - inline bool consider_dyn (GElf_Sxword tag, GElf_Xword val) + __BLOCK GElf_Addr soname_stroff = 0; + __BLOCK GElf_Addr dynstr_vaddr = 0; + __BLOCK GElf_Xword dynstrsz = 0; + __BLOCK bool execlike = false; + INLINE_NESTED_FUNC (bool, consider_dyn, (GElf_Sxword, GElf_Xword), + (GElf_Sxword tag, GElf_Xword val)) { switch (tag) { @@ -780,7 +796,7 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, } return soname_stroff != 0 && dynstr_vaddr != 0 && dynstrsz != 0; - } + }; const size_t dyn_entsize = (ei_class == ELFCLASS32 ? sizeof (Elf32_Dyn) : sizeof (Elf64_Dyn)); @@ -915,25 +931,30 @@ dwfl_segment_report_module (Dwfl *dwfl, int ndx, const char *name, if (unlikely (contents == NULL)) return finish (); - inline void final_read (size_t offset, GElf_Addr vaddr, size_t size) + INLINE_NESTED_FUNC (void, final_read, + (size_t, GElf_Addr, size_t), + (size_t offset, GElf_Addr vaddr, size_t size)) { void *into = contents + offset; size_t read_size = size; (void) segment_read (addr_segndx (dwfl, segment, vaddr, false), &into, &read_size, vaddr, size); - } + }; if (contiguous < file_trimmed_end) { /* We can't use the memory image verbatim as the file image. So we'll be reading into a local image of the virtual file. */ - inline void read_phdr (GElf_Word type, GElf_Addr vaddr, - GElf_Off offset, GElf_Xword filesz) + INLINE_NESTED_FUNC (void, read_phdr, + (GElf_Word, GElf_Addr, + GElf_Off, GElf_Xword), + (GElf_Word type, GElf_Addr vaddr, + GElf_Off offset, GElf_Xword filesz)) { if (type == PT_LOAD) final_read (offset, vaddr + bias, filesz); - } + }; if (ei_class == ELFCLASS32) for (uint_fast16_t i = 0; i < phnum; ++i) diff --git a/libdwfl/link_map.c b/libdwfl/link_map.c index 29307c7..05f4da2 100644 --- a/libdwfl/link_map.c +++ b/libdwfl/link_map.c @@ -30,6 +30,7 @@ #include "libdwflP.h" #include "../libdw/memory-access.h" #include "system.h" +#include "nested_func.h" #include #include @@ -245,20 +246,27 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata, struct r_debug_info *r_debug_info) { /* Skip r_version, to aligned r_map field. */ - GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass); + __BLOCK GElf_Addr read_vaddr = r_debug_vaddr + addrsize (elfclass); void *buffer = NULL; size_t buffer_available = 0; - inline int release_buffer (int result) + INLINE_NESTED_FUNC (int, release_buffer, (int), (int result)) { if (buffer != NULL) (void) (*memory_callback) (dwfl, -1, &buffer, &buffer_available, 0, 0, memory_callback_arg); return result; - } + }; +#if __clang__ + /* Clang Blocks cannot copy an array to a closure. */ + __BLOCK GElf_Addr *addrs = alloca(4 * sizeof (GElf_Addr)); +#else + /* gcc complains about unbounded stack usage from alloca. */ GElf_Addr addrs[4]; - inline bool read_addrs (GElf_Addr vaddr, size_t n) +#endif + INLINE_NESTED_FUNC (bool, read_addrs, + (GElf_Addr, size_t), (GElf_Addr vaddr, size_t n)) { size_t nb = n * addrsize (elfclass); /* Address words -> bytes to read. */ @@ -301,7 +309,7 @@ report_r_debug (uint_fast8_t elfclass, uint_fast8_t elfdata, } return false; - } + }; if (unlikely (read_addrs (read_vaddr, 1))) return release_buffer (-1); @@ -754,12 +762,13 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, } /* If we found the phdr dimensions, search phdrs for PT_DYNAMIC. */ - GElf_Addr dyn_vaddr = 0; - GElf_Xword dyn_filesz = 0; - GElf_Addr dyn_bias = (GElf_Addr) -1; + __BLOCK GElf_Addr dyn_vaddr = 0; + __BLOCK GElf_Xword dyn_filesz = 0; + __BLOCK GElf_Addr dyn_bias = (GElf_Addr) -1; - inline bool consider_phdr (GElf_Word type, - GElf_Addr vaddr, GElf_Xword filesz) + INLINE_NESTED_FUNC (bool, consider_phdr, + (GElf_Word, GElf_Addr, GElf_Xword), + (GElf_Word type, GElf_Addr vaddr, GElf_Xword filesz)) { switch (type) { @@ -781,7 +790,7 @@ dwfl_link_map_report (Dwfl *dwfl, const void *auxv, size_t auxv_size, } return false; - } + }; if (phdr != 0 && phnum != 0) { --- a/libdw/Makefile.am 2020-05-31 15:31:02.096994754 +0100 +++ b/libdw/Makefile.am 2020-05-31 15:31:38.456994741 +0100 @@ -109,7 +109,7 @@ ../libcpu/libcpu_pic.a libdw_pic.a ../libdwelf/libdwelf_pic.a \ ../libdwfl/libdwfl_pic.a libdw_so_DEPS = ../lib/libeu.a ../libelf/libelf.so -libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(zip_LIBS) -pthread +libdw_so_LDLIBS = $(libdw_so_DEPS) -ldl -lz $(argp_LDADD) $(zip_LIBS) -pthread -lBlocksRuntime libdw_so_SOURCES = libdw.so$(EXEEXT): $(srcdir)/libdw.map $(libdw_so_LIBS) $(libdw_so_DEPS) $(AM_V_CCLD)$(LINK) $(dso_LDFLAGS) -o $@ \ diff --git a/libdwfl/Makefile.am b/libdwfl/Makefile.am index 47bd62a..cba562f 100644 --- a/libdwfl/Makefile.am +++ b/libdwfl/Makefile.am @@ -40,6 +40,7 @@ noinst_LIBRARIES += libdwfl_pic.a pkginclude_HEADERS = libdwfl.h +AM_CFLAGS += -fblocks libdwfl_a_SOURCES = dwfl_begin.c dwfl_end.c dwfl_error.c dwfl_version.c \ dwfl_module.c dwfl_report_elf.c relocate.c \ dwfl_module_build_id.c dwfl_module_report_build_id.c \ --- a/config/libdw.pc.in 2020-05-31 15:57:15.036994154 +0100 +++ b/config/libdw.pc.in 2020-05-31 15:57:46.646994142 +0100 @@ -19,4 +19,4 @@ # data structures or functions. zlib (gz) is always required, bzip2 (bz2) # lzma (xz) and zstd () are optional. But bzip2 doesn't have a pkg-config file. Requires.private: zlib @LIBLZMA@ @LIBZSTD@ -Libs.private: @BZ2_LIB@ +Libs.private: @BZ2_LIB@ BlocksRuntime -- 2.26.2