Skip to content

Commit 99dbc12

Browse files
committed
Build both static and shared libraries
Use shims for any shared libraries we require at runtime (zlib, xz, threaddb), so we can detect their presence and adjust behaviour at runtime instead of build time. This allows us to link to libdwelf and libprocman statically with minimal fuss.
1 parent d837748 commit 99dbc12

12 files changed

Lines changed: 258 additions & 112 deletions

File tree

CMakeLists.txt

Lines changed: 36 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cmake_minimum_required(VERSION 3.10)
22
set(PSTACK_SOVERSION 2.17)
3-
set(PSTACK_VERSION 2.17.3)
3+
set(PSTACK_VERSION 2.17.4)
44
project(pstack LANGUAGES C CXX VERSION "${PSTACK_VERSION}" )
55

66
include (GNUInstallDirs)
@@ -18,12 +18,10 @@ set(PSTACK_BIN "pstack" CACHE STRING "Name of the 'pstack' binary")
1818
set(CMAKE_POSITION_INDEPENDENT_CODE ON)
1919

2020
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}/" CACHE STRING "Rpath to install for binaries or the empty string")
21-
set(LIBTYPE "SHARED" CACHE STRING "Build libraries as STATIC or SHARED")
2221
option(TIDY "Run clang-tidy on the source" False)
2322

24-
find_library(LTHREADDB NAMES thread_db PATHS (/usr/lib /usr/local/lib))
25-
find_package(LibLZMA)
26-
find_package(ZLIB)
23+
find_package(LibLZMA REQUIRED)
24+
find_package(ZLIB REQUIRED)
2725

2826
if (PYTHON2)
2927
find_package(Python2 COMPONENTS Development)
@@ -60,18 +58,10 @@ add_definitions("-fno-omit-frame-pointer")
6058
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
6159
# Make sure to use the local libpstack headers rather than what's installed.
6260
include_directories(${CMAKE_SOURCE_DIR})
61+
include_directories(${ZLIB_INCLUDE_DIRS})
6362

64-
if (LIBLZMA_FOUND)
65-
set(lzmasrc lzma.cc)
66-
add_definitions("-DWITH_LZMA")
67-
include_directories(${LIBLZMA_INCLUDES})
68-
endif()
63+
include_directories(${LIBLZMA_INCLUDE_DIRS})
6964

70-
if (ZLIB_FOUND)
71-
set(inflatesrc inflate.cc)
72-
add_definitions("-DWITH_ZLIB")
73-
include_directories(${ZLIB_INCLUDES})
74-
endif()
7565

7666
if (Python3_Development_FOUND OR Python2_Development_FOUND)
7767
set(pysrc python.cc)
@@ -95,7 +85,7 @@ endif()
9585

9686
add_definitions("-g3")
9787

98-
add_library(dwelf ${LIBTYPE}
88+
add_library(dwelf_objects OBJECT
9989
dwarf_die.cc
10090
dwarf_frame.cc
10191
dwarf_info.cc
@@ -109,35 +99,43 @@ add_library(dwelf ${LIBTYPE}
10999
elf.cc
110100
flags.cc
111101
reader.cc
112-
${inflatesrc}
113-
${lzmasrc}
102+
inflate.cc
103+
lzma.cc
114104
)
115105

116-
add_library(procman ${LIBTYPE} dead.cc self.cc live.cc process.cc proc_service.cc
117-
dwarfproc.cc procdump.cc ${stubsrc} ${pysrc})
106+
add_library(procman_objects OBJECT dead.cc self.cc live.cc process.cc proc_service.cc
107+
dwarfproc.cc procdump.cc threaddb.cc ${pysrc})
108+
109+
add_library(dwelf SHARED $<TARGET_OBJECTS:dwelf_objects>)
110+
add_library(dwelf_static STATIC $<TARGET_OBJECTS:dwelf_objects>)
111+
set_target_properties(dwelf_static PROPERTIES OUTPUT_NAME dwelf_s)
112+
113+
add_library(procman SHARED $<TARGET_OBJECTS:procman_objects>)
114+
add_library(procman_static STATIC $<TARGET_OBJECTS:procman_objects>)
115+
set_target_properties(procman_static PROPERTIES OUTPUT_NAME procman_s)
116+
117+
option(LINK_STATIC "Link binaries against static libraries" OFF)
118+
if (LINK_STATIC)
119+
set(PSTACK_LINK_LIBS procman_static dwelf_static)
120+
else()
121+
set(PSTACK_LINK_LIBS dwelf procman)
122+
endif()
118123

119124
add_executable(canal canal.cc)
120125

121126
add_executable(${PSTACK_BIN} pstack.cc)
122127

123-
target_link_libraries(procman ${LTHREADDB} dwelf dl)
124-
target_link_libraries(${PSTACK_BIN} dwelf procman)
125-
target_link_libraries(canal dwelf procman)
128+
target_link_libraries(procman dwelf dl)
129+
target_link_libraries(procman_static dwelf_static dl)
130+
target_link_libraries(${PSTACK_BIN} ${PSTACK_LINK_LIBS})
131+
target_link_libraries(canal ${PSTACK_LINK_LIBS})
132+
target_link_options(${PSTACK_BIN} PRIVATE -Wl,--export-dynamic)
133+
target_link_options(canal PRIVATE -Wl,--export-dynamic)
126134

127-
set_target_properties(dwelf PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION "${PSTACK_SOVERSION}" )
128-
set_target_properties(procman PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION "${PSTACK_SOVERSION}" )
135+
set_target_properties(dwelf PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION "${PSTACK_SOVERSION}")
136+
set_target_properties(procman PROPERTIES VERSION ${PROJECT_VERSION} SOVERSION "${PSTACK_SOVERSION}")
129137

130-
if (ZLIB_FOUND)
131-
target_link_libraries(dwelf ${ZLIB_LIBRARIES})
132-
else()
133-
message(WARNING "no ZLIB support found")
134-
endif()
135138

136-
if (LIBLZMA_FOUND)
137-
target_link_libraries(dwelf ${LIBLZMA_LIBRARIES})
138-
else()
139-
message(WARNING "no LZMA support found")
140-
endif()
141139

142140
if ((NOT (Python3_Development_FOUND)) AND PYTHON3)
143141
message(WARNING "no python3 support found")
@@ -151,13 +149,13 @@ endif()
151149
add_library(hdbg SHARED heap.c)
152150
add_executable(hdmp hdmp.cc)
153151
add_executable(stackusers stackusers.cc)
154-
target_link_libraries(hdmp dwelf procman)
152+
target_link_libraries(hdmp ${PSTACK_LINK_LIBS})
155153
target_link_libraries(hdbg dl)
156-
target_link_libraries(stackusers dwelf procman)
154+
target_link_libraries(stackusers ${PSTACK_LINK_LIBS})
157155

158156
install(TARGETS ${PSTACK_BIN} canal)
159157
install(TARGETS hdmp)
160-
install(TARGETS dwelf procman hdbg)
158+
install(TARGETS dwelf procman dwelf_static procman_static hdbg)
161159
install(FILES ${CMAKE_SOURCE_DIR}/pstack.1 DESTINATION share/man/man1 RENAME ${PSTACK_BIN}.1 )
162160
install(DIRECTORY libpstack DESTINATION include)
163161
install(CODE "execute_process (COMMAND setcap cap_sys_ptrace+ep ${DESTDIR}${CMAKE_INSTALL_PREFIX}/bin/${PSTACK_BIN} RESULT_VARIABLE ret)

elf.cc

Lines changed: 33 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
#include "libpstack/elf.h"
22
#include "libpstack/stringify.h"
33
#include "libpstack/ioflag.h"
4-
#ifdef WITH_ZLIB
54
#include "libpstack/inflatereader.h"
6-
#endif
7-
#ifdef WITH_LZMA
85
#include "libpstack/lzmareader.h"
9-
#endif
106

117
#include <algorithm>
128
#include <filesystem>
@@ -400,20 +396,20 @@ Object::getInterpreter() const
400396

401397
Elf::Object::sptr Object::debugData() const {
402398
if (debugData_ == nullptr) {
403-
#ifdef WITH_LZMA
404-
auto &gnu_debugdata = getSection(".gnu_debugdata", SHT_NULL );
405-
if (gnu_debugdata) {
406-
auto reader = make_shared<const LzmaReader>(gnu_debugdata.io());
407-
debugData_ = make_shared<Object>(context, reader, true);
408-
}
409-
#else
410-
static bool warned = false;
411-
if (!warned && context.debug != nullptr) {
412-
*context.debug << "warning: no compiled support for LZMA - "
413-
"can't decode debug data in " << *io << "\n";
414-
warned = true;
399+
if (lzmaAvailable()) {
400+
auto &gnu_debugdata = getSection(".gnu_debugdata", SHT_NULL );
401+
if (gnu_debugdata) {
402+
auto reader = make_shared<const LzmaReader>(gnu_debugdata.io());
403+
debugData_ = make_shared<Object>(context, reader, true);
404+
}
405+
} else {
406+
static bool warned = false;
407+
if (!warned && context.debug != nullptr) {
408+
*context.debug << "warning: lzma not available at runtime - "
409+
"can't decode debug data in " << *io << "\n";
410+
warned = true;
411+
}
415412
}
416-
#endif
417413
}
418414
return debugData_;
419415
}
@@ -696,50 +692,46 @@ Reader::csptr Section::io() const {
696692

697693
// deal with two possible zlib-compressed sections. The sane,
698694
// "SHF_COMPRESSED" version, and the hacky ".zdebug_" versions.
699-
#ifndef WITH_ZLIB
700695
bool wantedZlib = false;
701-
#endif
702696

703697
auto rawIo = elf->io->view(name, shdr.sh_offset, shdr.sh_size);
704698
if ((shdr.sh_flags & SHF_COMPRESSED) != 0) {
705-
#ifdef WITH_ZLIB
706-
auto chdr = rawIo->readObj<Chdr>(0);
707-
io_ = make_shared<InflateReader>(
708-
chdr.ch_size,
709-
*rawIo->view("ZLIB compressed content after chdr", sizeof chdr, shdr.sh_size - sizeof chdr));
710-
#else
711-
wantedZlib = true;
712-
#endif
699+
if (zlibAvailable()) {
700+
auto chdr = rawIo->readObj<Chdr>(0);
701+
io_ = make_shared<InflateReader>(
702+
chdr.ch_size,
703+
*rawIo->view("ZLIB compressed content after chdr", sizeof chdr, shdr.sh_size - sizeof chdr));
704+
} else {
705+
wantedZlib = true;
706+
}
713707
} else if (name.rfind(".zdebug_", 0) == 0) {
714708
unsigned char sig[12];
715709
rawIo->readObj(0, sig, sizeof sig);
716710
if (std::memcmp((const char *)sig, "ZLIB", 4) == 0) {
717-
#ifdef WITH_ZLIB
718-
uint64_t sz = 0;
719-
for (size_t i = 4; i < 12; ++i) {
720-
sz <<= 8;
721-
sz |= sig[i];
711+
if (zlibAvailable()) {
712+
uint64_t sz = 0;
713+
for (size_t i = 4; i < 12; ++i) {
714+
sz <<= 8;
715+
sz |= sig[i];
716+
}
717+
io_ = make_shared<InflateReader>(
718+
sz,
719+
*rawIo->view("ZLIB compressed content after magic signature", sizeof sig, sz));
720+
} else {
721+
wantedZlib = true;
722722
}
723-
io_ = make_shared<InflateReader>(
724-
sz,
725-
*rawIo->view("ZLIB compressed content after magic signature", sizeof sig, sz));
726-
#else
727-
wantedZlib = true;
728-
#endif
729723
}
730724
} else {
731725
io_ = rawIo;
732726
}
733-
#ifndef WITH_ZLIB
734727
if (wantedZlib) {
735728
static bool warned = false;
736729
if (!warned && elf->context.debug) {
737730
warned = true;
738-
*(elf->context.debug) <<"warning: no support configured for compressed debug info in section "
731+
*(elf->context.debug) << "warning: zlib not available at runtime, cannot decompress section "
739732
<< name << " of " << *elf->io << std::endl;
740733
}
741734
}
742-
#endif
743735
if (io_ == nullptr)
744736
io_ = make_shared<NullReader>();
745737
return io_;

inflate.cc

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,81 @@
11
#include "libpstack/inflatereader.h"
22
#include "libpstack/stringify.h"
33

4+
#include <dlfcn.h>
45
#include <zlib.h>
6+
7+
namespace {
8+
9+
struct Zlib {
10+
int (*inflateInit2_)(z_streamp, int, const char *, int);
11+
int (*inflate)(z_streamp, int);
12+
int (*inflateEnd)(z_streamp);
13+
};
14+
15+
const Zlib *loadZlib() {
16+
static Zlib zlib;
17+
static const Zlib *result = [] () -> const Zlib * {
18+
void *handle = dlopen("libz.so.1", RTLD_LAZY | RTLD_GLOBAL);
19+
if (!handle)
20+
return nullptr;
21+
zlib.inflateInit2_ = reinterpret_cast<decltype(zlib.inflateInit2_)>(dlsym(handle, "inflateInit2_"));
22+
zlib.inflate = reinterpret_cast<decltype(zlib.inflate)> (dlsym(handle, "inflate"));
23+
zlib.inflateEnd = reinterpret_cast<decltype(zlib.inflateEnd)> (dlsym(handle, "inflateEnd"));
24+
if (!zlib.inflateInit2_ || !zlib.inflate || !zlib.inflateEnd) {
25+
dlclose(handle);
26+
return nullptr;
27+
}
28+
return &zlib;
29+
}();
30+
return result;
31+
}
32+
33+
} // namespace
34+
535
namespace pstack {
636

37+
bool zlibAvailable() {
38+
return loadZlib() != nullptr;
39+
}
40+
741
InflateReader::InflateReader(size_t inflatedSize, const Reader &upstream)
842
: AbstractMemReader(std::string("inflated content from ") + stringify(upstream))
943
, data_(inflatedSize)
1044
{
11-
char xferbuf[32768];
45+
auto *zlib = loadZlib();
46+
if (!zlib)
47+
throw (Exception() << "zlib not available at runtime");
1248

49+
char xferbuf[32768];
1350
z_stream stream{};
1451

1552
int window = 15;
16-
if (inflateInit2(&stream, window) != Z_OK)
53+
if (zlib->inflateInit2_(&stream, window, ZLIB_VERSION, (int)sizeof stream) != Z_OK)
1754
throw (Exception() << "inflateInit2 failed");
1855

1956
stream.avail_out = inflatedSize;
20-
using bytep = Bytef *;
21-
stream.next_out = bytep(data());
57+
stream.next_out = reinterpret_cast<Bytef *>(data_.data());
2258
bool eof = false;
2359
size_t inputOffset = 0;
2460
for (bool done = false; !done; ) {
2561
if (stream.avail_in == 0 && !eof) {
26-
// keep the input buffer full
2762
stream.avail_in = upstream.read(inputOffset, sizeof xferbuf, xferbuf);
2863
inputOffset += stream.avail_in;
29-
stream.next_in = bytep(xferbuf);
64+
stream.next_in = reinterpret_cast<Bytef *>(xferbuf);
3065
if (stream.avail_in == 0)
3166
eof = true;
3267
}
33-
switch (inflate(&stream, eof ? Z_FINISH : Z_SYNC_FLUSH)) {
68+
switch (zlib->inflate(&stream, eof ? Z_FINISH : Z_SYNC_FLUSH)) {
3469
case Z_STREAM_END:
3570
done = true;
36-
// fallthrough
71+
[[fallthrough]];
3772
case Z_OK:
3873
break;
3974
default:
4075
throw (Exception() << "inflate failed");
4176
}
4277
}
43-
inflateEnd(&stream);
78+
zlib->inflateEnd(&stream);
4479
}
4580

46-
}
81+
} // namespace pstack

libpstack/inflatereader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#include "libpstack/reader.h"
44

55
namespace pstack {
6+
7+
bool zlibAvailable();
8+
69
// A Reader that zlib inflates the underlying downstream reader.
710
// Currently requires knowing the resulting output size.
811
class InflateReader : public AbstractMemReader {

libpstack/lzmareader.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
#include "libpstack/reader.h"
88

99
namespace pstack {
10+
11+
bool lzmaAvailable();
12+
1013
/*
1114
* Provides an LZMA-decoded view of downstream. LZMA API allows random-access
1215
* to the data, and we cache each decompressed block as we decode it.

libpstack/proc.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
#include <string_view>
1313
#include <sys/stat.h> // for ino_t
1414
#include <ucontext.h> // for gregset_t
15-
extern "C" { // sigh.
16-
#include <thread_db.h>
17-
}
15+
#include "libpstack/threaddb.h"
1816

1917
#include "libpstack/dwarf.h"
2018
#include "libpstack/arch.h"
@@ -334,7 +332,7 @@ threadListCb(const td_thrhandle_t *thr, void *v)
334332
template <typename T> void
335333
Process::listThreads(const T &callback)
336334
{
337-
td_ta_thr_iter(agent,
335+
loadThreadDb()->ta_thr_iter(agent,
338336
threadListCb<T>,
339337
(void *)&callback, TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
340338
}

0 commit comments

Comments
 (0)