485 lines
17 KiB
Diff
485 lines
17 KiB
Diff
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
|