Skip to content
Snippets Groups Projects
Commit 0b2ad01a authored by Simon McVittie's avatar Simon McVittie
Browse files

elf-utils: Add ELF SONAME resolution


This will be used to delete libraries from the runtime if their
SONAME is the same as a library that we brought in from outside,
to avoid conflicts when precedence gets mixed up.

A manual test is included.

Signed-off-by: default avatarSimon McVittie <smcv@collabora.com>
parent d4a5b773
No related branches found
No related tags found
No related merge requests found
......@@ -6,6 +6,7 @@ Standards-Version: 4.4.0
Build-Depends:
debhelper (>= 9),
g++ (>= 4:4.8) | g++-4.8,
libelf-dev,
libglib2.0-dev,
libsteam-runtime-tools-0-dev (>= 0.20200331.1~),
libxau-dev,
......
......@@ -150,6 +150,11 @@ glib_tap_support = dependency(
required : false,
)
xau = dependency('xau', required : true)
libelf = dependency('libelf', required : false)
if not libelf.found()
libelf = declare_dependency(link_args : ['-lelf'])
endif
scripts = [
'pressure-vessel-locale-gen',
......
/*
* Copyright © 2017-2020 Collabora Ltd.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "elf-utils.h"
#include <glib.h>
#include <glib/gstdio.h>
#include <gio/gio.h>
#include <libelf.h>
#include "libglnx/libglnx.h"
#include "glib-backports.h"
#define throw_elf_error(error, format, ...) \
glnx_null_throw (error, format ": %s", ##__VA_ARGS__, elf_errmsg (elf_errno ()))
/*
* pv_elf_open_fd:
* @fd: An open file descriptor
*
* Returns: (transfer full): A libelf object representing the library
*/
Elf *
pv_elf_open_fd (int fd,
GError **error)
{
g_autoptr(Elf) elf = NULL;
g_return_val_if_fail (fd >= 0, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (elf_version (EV_CURRENT) == EV_NONE)
return throw_elf_error (error, "elf_version(EV_CURRENT)");
elf = elf_begin (fd, ELF_C_READ, NULL);
if (elf == NULL)
return throw_elf_error (error, "elf_begin");
return g_steal_pointer (&elf);
}
/*
* pv_elf_get_soname:
* @elf: A libelf object
*
* Return the `DT_SONAME` header, or %NULL on error
*/
gchar *
pv_elf_get_soname (Elf *elf,
GError **error)
{
GElf_Ehdr ehdr;
size_t phdr_count = 0;
size_t i;
GElf_Phdr phdr_mem;
GElf_Phdr *phdr = NULL;
GElf_Dyn *dynamic_header = NULL;
GElf_Dyn dynamic_header_mem;
Elf_Scn *dynamic_section = NULL;
GElf_Shdr dynamic_section_header_mem;
GElf_Shdr *dynamic_section_header = NULL;
Elf_Scn *string_table_section = NULL;
GElf_Shdr string_table_header_mem;
GElf_Shdr *string_table_header = NULL;
Elf_Data *data = NULL;
size_t size_per_dyn;
const char *soname = NULL;
g_return_val_if_fail (elf != NULL, NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (elf_kind (elf) != ELF_K_ELF)
return glnx_null_throw (error, "elf_kind %d, expected ELF_K_ELF=%d",
elf_kind (elf), ELF_K_ELF);
if (gelf_getehdr (elf, &ehdr) == NULL)
return throw_elf_error (error, "elf_getehdr");
if (ehdr.e_type != ET_DYN)
return glnx_null_throw (error, "ehdr.e_type %d, expected ET_DYN=%d",
ehdr.e_type, ET_DYN);
if (elf_getphdrnum (elf, &phdr_count) != 0)
return throw_elf_error (error, "elf_getphdrnum");
for (i = 0; i < phdr_count; i++)
{
phdr = gelf_getphdr (elf, i, &phdr_mem);
if (phdr != NULL && phdr->p_type == PT_DYNAMIC)
{
dynamic_section = gelf_offscn (elf, phdr->p_offset);
dynamic_section_header = gelf_getshdr (dynamic_section,
&dynamic_section_header_mem);
break;
}
}
if (dynamic_section == NULL || dynamic_section_header == NULL)
return glnx_null_throw (error, "Unable to find dynamic section header");
string_table_section = elf_getscn (elf, dynamic_section_header->sh_link);
string_table_header = gelf_getshdr (string_table_section,
&string_table_header_mem);
if (string_table_section == NULL || string_table_header == NULL)
return glnx_null_throw (error, "Unable to find linked string table");
data = elf_getdata (dynamic_section, NULL);
if (data == NULL)
return throw_elf_error (error, "elf_getdata(dynamic_section)");
size_per_dyn = gelf_fsize (elf, ELF_T_DYN, 1, EV_CURRENT);
for (i = 0; i < dynamic_section_header->sh_size / size_per_dyn; i++)
{
dynamic_header = gelf_getdyn (data, i, &dynamic_header_mem);
if (dynamic_header == NULL)
break;
if (dynamic_header->d_tag == DT_SONAME)
{
soname = elf_strptr (elf, dynamic_section_header->sh_link,
dynamic_header->d_un.d_val);
if (soname == NULL)
return glnx_null_throw (error, "Unable to read DT_SONAME");
}
}
if (soname == NULL)
return glnx_null_throw (error, "Unable to find DT_SONAME");
return g_strdup (soname);
}
/*
* Copyright © 2020 Collabora Ltd.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <gelf.h>
#include <glib.h>
#include <libelf.h>
#include "libglnx/libglnx.h"
#include "glib-backports.h"
G_DEFINE_AUTOPTR_CLEANUP_FUNC (Elf, elf_end);
Elf *pv_elf_open_fd (int fd,
GError **error);
gchar *pv_elf_get_soname (Elf *elf,
GError **error);
......@@ -37,6 +37,8 @@ pressure_vessel_utils = static_library(
sources : [
'bwrap-lock.c',
'bwrap-lock.h',
'elf-utils.c',
'elf-utils.h',
'flatpak-utils-base.c',
'flatpak-utils-base-private.h',
'flatpak-utils.c',
......@@ -50,6 +52,7 @@ pressure_vessel_utils = static_library(
],
dependencies : [
gio_unix,
libelf,
libglnx.get_variable('libglnx_dep'),
],
include_directories : project_include_dirs,
......
/*
* Copyright © 2020 Collabora Ltd.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "subprojects/libglnx/config.h"
#include <locale.h>
#include <sysexits.h>
#include "libglnx/libglnx.h"
#include "glib-backports.h"
#include "elf-utils.h"
#include "utils.h"
static GOptionEntry options[] =
{
{ NULL }
};
int
main (int argc,
char *argv[])
{
g_autoptr(GOptionContext) context = NULL;
g_autoptr(GError) local_error = NULL;
GError **error = &local_error;
int ret = EX_USAGE;
int i;
setlocale (LC_ALL, "");
pv_avoid_gvfs ();
context = g_option_context_new ("LIBRARY...");
g_option_context_add_main_entries (context, options, NULL);
if (!g_option_context_parse (context, &argc, &argv, error))
goto out;
if (argc >= 2 && strcmp (argv[1], "--") == 0)
{
argv++;
argc--;
}
if (argc < 2)
{
g_printerr ("A library to open is required\n");
goto out;
}
ret = 0;
for (i = 1; i < argc; i++)
{
int fd = open (argv[i], O_RDONLY | O_CLOEXEC);
g_autoptr(Elf) elf = NULL;
g_autofree gchar *soname = NULL;
if (fd < 0)
{
g_printerr ("Cannot open %s: %s\n", argv[i], g_strerror (errno));
ret = 1;
continue;
}
elf = pv_elf_open_fd (fd, error);
if (elf == NULL)
{
g_printerr ("Unable to open %s: %s\n",
argv[i], local_error->message);
g_clear_error (error);
ret = 1;
continue;
}
soname = pv_elf_get_soname (elf, error);
if (soname == NULL)
{
g_printerr ("Unable to get SONAME of %s: %s\n",
argv[i], local_error->message);
g_clear_error (error);
ret = 1;
continue;
}
g_print ("%s DT_SONAME: %s\n", argv[i], soname);
}
out:
if (local_error != NULL)
g_warning ("%s", local_error->message);
return ret;
}
......@@ -102,20 +102,28 @@ foreach test_name : tests
endif
endforeach
executable(
'test-cheap-copy',
sources : [
'cheap-copy.c',
],
dependencies : [
gio_unix,
libglnx.get_variable('libglnx_dep'),
],
link_with : [
pressure_vessel_utils,
],
include_directories : project_include_dirs,
install : false,
)
# Helper programs and manual tests
helpers = [
'cheap-copy',
'elf-get-soname',
]
foreach helper : helpers
executable(
'test-' + helper,
sources : [
helper + '.c',
],
dependencies : [
gio_unix,
libglnx.get_variable('libglnx_dep'),
],
link_with : [
pressure_vessel_utils,
],
include_directories : project_include_dirs,
install : false,
)
endforeach
# vim:set sw=2 sts=2 et:
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment