mirror of
https://github.com/audacity/linuxdeploy.git
synced 2025-12-11 05:46:48 -06:00
Detect dynamically linked and debug symbols only ELF files
TODO: extract ELF stuff into new small C++ wrapper library that can be used in various places (e.g., AppImageLauncher, the AppImage runtime, ...)
This commit is contained in:
parent
86c99ccfb4
commit
3c6096433d
@ -64,6 +64,12 @@ namespace linuxdeploy {
|
||||
|
||||
// return OS ABI
|
||||
uint8_t getElfABI();
|
||||
|
||||
// check if this file is a debug symbols file
|
||||
bool isDebugSymbolsFile();
|
||||
|
||||
// check whether the file contains a dynsym section
|
||||
bool isDynamicallyLinked();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,8 @@ namespace linuxdeploy {
|
||||
const bf::path path;
|
||||
uint8_t elfClass = ELFCLASSNONE;
|
||||
uint8_t elfABI = 0;
|
||||
bool isDebugSymbolsFile = false;
|
||||
bool isDynamicallyLinked = false;
|
||||
|
||||
public:
|
||||
explicit PrivateData(bf::path path) : path(std::move(path)) {}
|
||||
@ -72,6 +74,56 @@ namespace linuxdeploy {
|
||||
return patchelfPath;
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename Ehdr_T, typename Shdr_T, typename Phdr_T>
|
||||
void parseElfHeader(std::shared_ptr<uint8_t> data) {
|
||||
// TODO: the following code will _only_ work if the native byte order equals the program's
|
||||
// this should not be a big problem as we don't offer ARM builds yet, and require the user to
|
||||
// use a matching binary for the target binaries
|
||||
|
||||
auto* ehdr = reinterpret_cast<Ehdr_T*>(data.get());
|
||||
|
||||
elfABI = ehdr->e_ident[EI_OSABI];
|
||||
|
||||
std::vector<Shdr_T> sections;
|
||||
|
||||
// parse section header table
|
||||
// first, we collect all entries in a vector so we can conveniently iterate over it
|
||||
for (uint64_t i = 0; i < ehdr->e_shnum; ++i) {
|
||||
auto* nextShdr = reinterpret_cast<Shdr_T*>(data.get() + ehdr->e_shoff + i * sizeof(Shdr_T));
|
||||
sections.emplace_back(*nextShdr);
|
||||
}
|
||||
|
||||
auto getString = [data, §ions, ehdr](uint64_t offset) {
|
||||
assert(ehdr->e_shstrndx != SHN_UNDEF);
|
||||
const auto& stringTableSection = sections[ehdr->e_shstrndx];
|
||||
return std::string{reinterpret_cast<char*>(data.get() + stringTableSection.sh_offset + offset)};
|
||||
};
|
||||
|
||||
// now that we can look up texts, we can create a map to easily access the sections by name
|
||||
std::unordered_map<std::string, Shdr_T> sectionsMap;
|
||||
std::for_each(sections.begin(), sections.end(), [§ionsMap, &getString](const Shdr_T& shdr) {
|
||||
const auto headerName = getString(shdr.sh_name);
|
||||
sectionsMap.insert(std::make_pair(headerName, shdr));
|
||||
});
|
||||
|
||||
// this function is based on observations of the behavior of:
|
||||
// - strip --only-keep-debug
|
||||
// - objcopy --only-keep-debug
|
||||
isDebugSymbolsFile = (sectionsMap[".text"].sh_type == SHT_NOBITS);
|
||||
|
||||
// https://stackoverflow.com/a/7298931
|
||||
for (uint64_t i = 0; i < ehdr->e_phnum && !isDynamicallyLinked; ++i) {
|
||||
auto* nextPhdr = reinterpret_cast<Phdr_T*>(data.get() + ehdr->e_phoff + i * sizeof(Phdr_T));
|
||||
switch (nextPhdr->p_type) {
|
||||
case PT_DYNAMIC:
|
||||
case PT_INTERP:
|
||||
isDynamicallyLinked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void readDataUsingElfAPI() {
|
||||
int fd = open(path.c_str(), O_RDONLY);
|
||||
@ -96,19 +148,11 @@ namespace linuxdeploy {
|
||||
elfClass = data.get()[EI_CLASS];
|
||||
|
||||
switch (elfClass) {
|
||||
case ELFCLASS32: {
|
||||
auto* ehdr = reinterpret_cast<Elf32_Ehdr*>(data.get());
|
||||
auto* shdr = reinterpret_cast<Elf32_Shdr*>(data.get() + ehdr->e_shoff);
|
||||
|
||||
elfABI = ehdr->e_ident[EI_OSABI];
|
||||
}
|
||||
case ELFCLASS32:
|
||||
parseElfHeader<Elf32_Ehdr, Elf32_Shdr, Elf32_Phdr>(data);
|
||||
break;
|
||||
case ELFCLASS64: {
|
||||
auto* ehdr = reinterpret_cast<Elf32_Ehdr*>(data.get());
|
||||
auto* shdr = reinterpret_cast<Elf32_Shdr*>(data.get() + ehdr->e_shoff);
|
||||
|
||||
elfABI = ehdr->e_ident[EI_OSABI];
|
||||
}
|
||||
case ELFCLASS64:
|
||||
parseElfHeader<Elf64_Ehdr, Elf64_Shdr, Elf64_Phdr>(data);
|
||||
break;
|
||||
default:
|
||||
throw ElfFileParseError("Unknown ELF class: " + std::to_string(elfClass));
|
||||
@ -296,6 +340,14 @@ namespace linuxdeploy {
|
||||
uint8_t ElfFile::getElfABI() {
|
||||
return d->elfABI;
|
||||
}
|
||||
|
||||
bool ElfFile::isDebugSymbolsFile() {
|
||||
return d->isDebugSymbolsFile;
|
||||
}
|
||||
|
||||
bool ElfFile::isDynamicallyLinked() {
|
||||
return d->isDynamicallyLinked;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,3 +14,6 @@ target_include_directories(linuxdeploy_subprocess PUBLIC ${PROJECT_SOURCE_DIR}/i
|
||||
|
||||
add_executable(subprocess_demo subprocess_demo.cpp)
|
||||
target_link_libraries(subprocess_demo PUBLIC linuxdeploy_subprocess)
|
||||
|
||||
add_executable(test_limit test_limit.cpp)
|
||||
target_link_libraries(test_limit PUBLIC linuxdeploy_subprocess)
|
||||
|
||||
@ -18,7 +18,6 @@ ld_add_test(test_appdir)
|
||||
add_dependencies(test_appdir simple_library simple_executable)
|
||||
|
||||
|
||||
|
||||
add_executable(test_linuxdeploy test_linuxdeploy.cpp ../../src/core.cpp)
|
||||
target_link_libraries(test_linuxdeploy PRIVATE linuxdeploy_core args gtest gtest_main)
|
||||
target_include_directories(test_linuxdeploy PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/src)
|
||||
@ -34,6 +33,28 @@ target_compile_definitions(test_linuxdeploy PRIVATE
|
||||
|
||||
# register in CTest
|
||||
ld_add_test(test_linuxdeploy)
|
||||
add_executable(test_elf_file test_elf_file.cpp ../../src/core.cpp)
|
||||
target_link_libraries(test_elf_file PRIVATE linuxdeploy_core args gtest gtest_main)
|
||||
target_include_directories(test_elf_file PRIVATE ${PROJECT_SOURCE_DIR}/include ${PROJECT_SOURCE_DIR}/src)
|
||||
|
||||
# calculate paths to resources using CMake and hardcode them in the test binary
|
||||
target_compile_definitions(test_elf_file PRIVATE
|
||||
-DSIMPLE_LIBRARY_PATH="$<TARGET_FILE:simple_library>"
|
||||
-DSIMPLE_LIBRARY_DEBUG_PATH="$<TARGET_FILE:simple_library>.debug"
|
||||
-DSIMPLE_LIBRARY_STRIPPED_PATH="$<TARGET_FILE:simple_library>.stripped"
|
||||
-DSIMPLE_EXECUTABLE_PATH="$<TARGET_FILE:simple_executable>"
|
||||
-DSIMPLE_EXECUTABLE_STATIC_PATH="$<TARGET_FILE:simple_executable_static>"
|
||||
)
|
||||
|
||||
# make sure library and executable are built before test_appdir
|
||||
add_dependencies(test_linuxdeploy simple_library simple_executable)
|
||||
add_dependencies(test_elf_file
|
||||
simple_executable
|
||||
simple_executable_static
|
||||
simple_library
|
||||
simple_library_stripped
|
||||
simple_library_debug
|
||||
)
|
||||
|
||||
# register in CTest
|
||||
ld_add_test(test_elf_file)
|
||||
|
||||
|
||||
30
tests/core/test_elf_file.cpp
Normal file
30
tests/core/test_elf_file.cpp
Normal file
@ -0,0 +1,30 @@
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "linuxdeploy/core/elf_file.h"
|
||||
|
||||
using namespace std;
|
||||
using namespace linuxdeploy::core;
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
using namespace std;
|
||||
using namespace linuxdeploy::core::elf_file;
|
||||
|
||||
namespace LinuxDeployTest {
|
||||
class ElfFileTest : public ::testing::Test {};
|
||||
|
||||
TEST_F(ElfFileTest, checkIsDebugSymbolsFile) {
|
||||
ElfFile debugSymbolsFile(SIMPLE_LIBRARY_DEBUG_PATH);
|
||||
EXPECT_TRUE(debugSymbolsFile.isDebugSymbolsFile());
|
||||
|
||||
ElfFile regularLibraryFile(SIMPLE_LIBRARY_PATH);
|
||||
EXPECT_FALSE(regularLibraryFile.isDebugSymbolsFile());
|
||||
}
|
||||
|
||||
TEST_F(ElfFileTest, checkIsDynamicallyLinked) {
|
||||
ElfFile regularLibraryFile(SIMPLE_EXECUTABLE_PATH);
|
||||
EXPECT_TRUE(regularLibraryFile.isDynamicallyLinked());
|
||||
|
||||
ElfFile staticLibraryFile(SIMPLE_EXECUTABLE_STATIC_PATH);
|
||||
EXPECT_FALSE(staticLibraryFile.isDynamicallyLinked());
|
||||
}
|
||||
}
|
||||
@ -1,2 +1,6 @@
|
||||
add_executable(simple_executable simple_executable.cpp)
|
||||
target_link_libraries(simple_executable simple_library pthread)
|
||||
target_link_libraries(simple_executable simple_library pthread)
|
||||
|
||||
add_executable(simple_executable_static simple_executable.cpp)
|
||||
target_link_libraries(simple_executable_static simple_library_static pthread)
|
||||
target_link_options(simple_executable_static PUBLIC -static -static-libgcc -static-libstdc++)
|
||||
|
||||
@ -1,3 +1,27 @@
|
||||
add_library(simple_library SHARED simple_library.cpp simple_library.h)
|
||||
target_link_libraries(simple_library PUBLIC CImg)
|
||||
target_include_directories(simple_library PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# generate a debug symbols file
|
||||
add_custom_command(
|
||||
OUTPUT libsimple_library.so.debug
|
||||
COMMAND objcopy --only-keep-debug libsimple_library.so libsimple_library.so.debug
|
||||
DEPENDS simple_library
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(simple_library_debug ALL DEPENDS libsimple_library.so.debug)
|
||||
|
||||
# generate a library whose debug symbols have been stripped, but which contains a GNU debug link to the symbols file
|
||||
add_custom_command(
|
||||
OUTPUT libsimple_library.so.stripped
|
||||
COMMAND objcopy --strip-debug libsimple_library.so libsimple_library.so.stripped
|
||||
COMMAND objcopy --add-gnu-debuglink=libsimple_library.so.debug libsimple_library.so.stripped
|
||||
DEPENDS simple_library_debug
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(simple_library_stripped ALL DEPENDS libsimple_library.so.stripped)
|
||||
|
||||
# add a static version, too
|
||||
add_library(simple_library_static STATIC simple_library.cpp simple_library.h)
|
||||
target_link_libraries(simple_library_static PUBLIC CImg)
|
||||
target_include_directories(simple_library_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user