-
Simon McVittie authored
Signed-off-by:
Simon McVittie <smcv@collabora.com>
Simon McVittie authoredSigned-off-by:
Simon McVittie <smcv@collabora.com>
elf-utils.c 4.56 KiB
/*
* 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 "steam-runtime-tools/glib-backports-internal.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);
}