From e0785056a91f893ef4235aefb2a26bed3b17537f Mon Sep 17 00:00:00 2001
From: Chih-Hung Hsieh <chh@google.com>
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 <chh@google.com>, 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 <http://www.gnu.org/licenses/>.  */
+
+#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 <elf.h>
 #include <gelf.h>
@@ -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 <byteswap.h>
 #include <endian.h>
@@ -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
