1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
|
/* Opening CTF files with BFD.
Copyright (C) 2019-2024 Free Software Foundation, Inc.
This file is part of libctf.
libctf is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.
This program 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 a copy of the GNU General Public License
along with this program; see the file COPYING. If not see
<http://www.gnu.org/licenses/>. */
#include <ctf-impl.h>
#include <stddef.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
#include <bfd.h>
#include "swap.h"
#include "ctf-endian.h"
#include "elf-bfd.h"
/* Free the BFD bits of a CTF file on ctf_arc_close(). */
static void
ctf_bfdclose (struct ctf_archive_internal *arci)
{
if (arci->ctfi_abfd != NULL)
if (!bfd_close_all_done (arci->ctfi_abfd))
ctf_err_warn (NULL, 0, 0, _("cannot close BFD: %s"),
bfd_errmsg (bfd_get_error ()));
}
/* Open a CTF file given the specified BFD. */
ctf_archive_t *
ctf_bfdopen (struct bfd *abfd, int *errp)
{
ctf_archive_t *arc;
asection *ctf_asect;
bfd_byte *contents;
ctf_sect_t ctfsect;
libctf_init_debug();
if ((ctf_asect = bfd_get_section_by_name (abfd, _CTF_SECTION)) == NULL)
{
return (ctf_set_open_errno (errp, ECTF_NOCTFDATA));
}
if (!bfd_malloc_and_get_section (abfd, ctf_asect, &contents))
{
ctf_err_warn (NULL, 0, 0, _("ctf_bfdopen(): cannot malloc "
"CTF section: %s"),
bfd_errmsg (bfd_get_error ()));
return (ctf_set_open_errno (errp, ECTF_FMT));
}
ctfsect.cts_name = _CTF_SECTION;
ctfsect.cts_entsize = 1;
ctfsect.cts_size = bfd_section_size (ctf_asect);
ctfsect.cts_data = contents;
if ((arc = ctf_bfdopen_ctfsect (abfd, &ctfsect, errp)) != NULL)
{
/* This frees the cts_data later. */
arc->ctfi_data = (void *) ctfsect.cts_data;
return arc;
}
free (contents);
return NULL; /* errno is set for us. */
}
/* Open a CTF file given the specified BFD and CTF section (which may contain a
CTF archive or a file). */
ctf_archive_t *
ctf_bfdopen_ctfsect (struct bfd *abfd _libctf_unused_,
const ctf_sect_t *ctfsect, int *errp)
{
ctf_archive_t *arci;
ctf_sect_t *symsectp = NULL;
ctf_sect_t *strsectp = NULL;
const char *bfderrstr = NULL;
char *strtab_alloc = NULL;
int symsect_endianness = -1;
libctf_init_debug();
#ifdef HAVE_BFD_ELF
ctf_sect_t symsect, strsect;
Elf_Internal_Shdr *symhdr;
size_t symcount;
Elf_Internal_Sym *isymbuf;
bfd_byte *symtab = NULL;
const char *symtab_name;
const char *strtab = NULL;
const char *strtab_name;
size_t strsize;
const ctf_preamble_t *preamble;
if (ctfsect->cts_data == NULL)
{
bfderrstr = N_("CTF section is NULL");
goto err;
}
preamble = ctf_arc_bufpreamble (ctfsect);
if (preamble->ctp_flags & CTF_F_DYNSTR)
{
symhdr = &elf_tdata (abfd)->dynsymtab_hdr;
strtab_name = ".dynstr";
symtab_name = ".dynsym";
}
else
{
symhdr = &elf_tdata (abfd)->symtab_hdr;
strtab_name = ".strtab";
symtab_name = ".symtab";
}
/* TODO: handle SYMTAB_SHNDX. */
/* Get the symtab, and the strtab associated with it. */
if (elf_tdata (abfd) && symhdr && symhdr->sh_size && symhdr->sh_entsize)
{
symcount = symhdr->sh_size / symhdr->sh_entsize;
if ((symtab = malloc (symhdr->sh_size)) == NULL)
{
bfderrstr = N_("cannot malloc symbol table");
goto err;
}
isymbuf = bfd_elf_get_elf_syms (abfd, symhdr, symcount, 0,
NULL, symtab, NULL);
free (isymbuf);
if (isymbuf == NULL)
{
bfderrstr = N_("cannot read symbol table");
goto err_free_sym;
}
if (elf_elfsections (abfd) != NULL
&& symhdr->sh_link < elf_numsections (abfd))
{
Elf_Internal_Shdr *strhdr = elf_elfsections (abfd)[symhdr->sh_link];
strsize = strhdr->sh_size;
if (strhdr->contents == NULL)
{
if ((strtab = bfd_elf_get_str_section (abfd, symhdr->sh_link)) == NULL)
{
bfderrstr = N_("cannot read string table");
goto err_free_sym;
}
}
else
strtab = (const char *) strhdr->contents;
}
}
else /* No symtab: just try getting .strtab or .dynstr by name. */
{
bfd_byte *str_bcontents;
asection *str_asect;
if ((str_asect = bfd_get_section_by_name (abfd, strtab_name)) != NULL)
{
if (bfd_malloc_and_get_section (abfd, str_asect, &str_bcontents))
{
strtab = (const char *) str_bcontents;
strtab_alloc = (char *) str_bcontents;
strsize = str_asect->size;
}
}
}
if (strtab)
{
/* The names here are more or less arbitrary, but there is no point
thrashing around digging the name out of the shstrtab given that we don't
use it for anything but debugging. */
strsect.cts_data = strtab;
strsect.cts_name = strtab_name;
strsect.cts_size = strsize;
strsectp = &strsect;
}
if (symtab)
{
assert (symhdr->sh_entsize == get_elf_backend_data (abfd)->s->sizeof_sym);
symsect.cts_name = symtab_name;
symsect.cts_entsize = symhdr->sh_entsize;
symsect.cts_size = symhdr->sh_size;
symsect.cts_data = symtab;
symsectp = &symsect;
}
symsect_endianness = bfd_little_endian (abfd);
#endif
arci = ctf_arc_bufopen (ctfsect, symsectp, strsectp, errp);
if (arci)
{
/* Request freeing of the symsect and possibly the strsect. */
arci->ctfi_free_symsect = 1;
if (strtab_alloc)
arci->ctfi_free_strsect = 1;
/* Get the endianness right. */
if (symsect_endianness > -1)
ctf_arc_symsect_endianness (arci, symsect_endianness);
return arci;
}
#ifdef HAVE_BFD_ELF
err_free_sym:
free (symtab);
free (strtab_alloc);
#endif
err: _libctf_unused_;
if (bfderrstr)
{
ctf_err_warn (NULL, 0, 0, "ctf_bfdopen(): %s: %s", gettext (bfderrstr),
bfd_errmsg (bfd_get_error()));
ctf_set_open_errno (errp, ECTF_FMT);
}
return NULL;
}
/* Open the specified file descriptor and return a pointer to a CTF archive that
contains one or more CTF dicts. The file can be an ELF file, a file
containing raw CTF, or a CTF archive. The caller is responsible for closing
the file descriptor when it is no longer needed. If this is an ELF file,
TARGET, if non-NULL, should be the name of a suitable BFD target. */
ctf_archive_t *
ctf_fdopen (int fd, const char *filename, const char *target, int *errp)
{
ctf_archive_t *arci;
bfd *abfd;
int nfd;
struct stat st;
ssize_t nbytes;
ctf_preamble_t ctfhdr;
uint64_t arc_magic;
memset (&ctfhdr, 0, sizeof (ctfhdr));
libctf_init_debug();
if (fstat (fd, &st) == -1)
return (ctf_set_open_errno (errp, errno));
if ((nbytes = ctf_pread (fd, &ctfhdr, sizeof (ctfhdr), 0)) <= 0)
return (ctf_set_open_errno (errp, nbytes < 0 ? errno : ECTF_FMT));
/* If we have read enough bytes to form a CTF header and the magic string
matches, in either endianness, attempt to interpret the file as raw
CTF. */
if ((size_t) nbytes >= sizeof (ctf_preamble_t)
&& (ctfhdr.ctp_magic == CTF_MAGIC
|| ctfhdr.ctp_magic == bswap_16 (CTF_MAGIC)))
{
ctf_dict_t *fp = NULL;
void *data;
if ((data = ctf_mmap (st.st_size, 0, fd)) == NULL)
return (ctf_set_open_errno (errp, errno));
if ((fp = ctf_simple_open (data, (size_t) st.st_size, NULL, 0, 0,
NULL, 0, errp)) == NULL)
{
ctf_munmap (data, (size_t) st.st_size);
return NULL; /* errno is set for us. */
}
fp->ctf_data_mmapped = data;
fp->ctf_data_mmapped_len = (size_t) st.st_size;
return ctf_new_archive_internal (0, 1, NULL, fp, NULL, NULL, errp);
}
if ((nbytes = ctf_pread (fd, &arc_magic, sizeof (arc_magic), 0)) <= 0)
return (ctf_set_open_errno (errp, nbytes < 0 ? errno : ECTF_FMT));
if ((size_t) nbytes >= sizeof (uint64_t) && le64toh (arc_magic) == CTFA_MAGIC)
{
struct ctf_archive *arc;
if ((arc = ctf_arc_open_internal (filename, errp)) == NULL)
return NULL; /* errno is set for us. */
return ctf_new_archive_internal (1, 1, arc, NULL, NULL, NULL, errp);
}
/* Attempt to open the file with BFD. We must dup the fd first, since bfd
takes ownership of the passed fd. */
if ((nfd = dup (fd)) < 0)
return (ctf_set_open_errno (errp, errno));
if ((abfd = bfd_fdopenr (filename, target, nfd)) == NULL)
{
ctf_err_warn (NULL, 0, 0, _("cannot open BFD from %s: %s"),
filename ? filename : _("(unknown file)"),
bfd_errmsg (bfd_get_error ()));
return (ctf_set_open_errno (errp, ECTF_FMT));
}
bfd_set_cacheable (abfd, 1);
if (!bfd_check_format (abfd, bfd_object))
{
ctf_err_warn (NULL, 0, 0, _("BFD format problem in %s: %s"),
filename ? filename : _("(unknown file)"),
bfd_errmsg (bfd_get_error ()));
if (bfd_get_error() == bfd_error_file_ambiguously_recognized)
return (ctf_set_open_errno (errp, ECTF_BFD_AMBIGUOUS));
else
return (ctf_set_open_errno (errp, ECTF_FMT));
}
if ((arci = ctf_bfdopen (abfd, errp)) == NULL)
{
if (!bfd_close_all_done (abfd))
ctf_err_warn (NULL, 0, 0, _("cannot close BFD: %s"),
bfd_errmsg (bfd_get_error ()));
return NULL; /* errno is set for us. */
}
arci->ctfi_bfd_close = ctf_bfdclose;
arci->ctfi_abfd = abfd;
return arci;
}
/* Open the specified file and return a pointer to a CTF dict. The file
can be either an ELF file or raw CTF file. This is just a convenient
wrapper around ctf_fdopen() for callers. */
ctf_archive_t *
ctf_open (const char *filename, const char *target, int *errp)
{
ctf_archive_t *arc;
int fd;
if ((fd = open (filename, O_RDONLY)) == -1)
{
if (errp != NULL)
*errp = errno;
return NULL;
}
arc = ctf_fdopen (fd, filename, target, errp);
(void) close (fd);
return arc;
}
/* Public entry point: open a CTF archive, or CTF file. Returns the archive, or
NULL and an error in *err. Despite the fact that this uses CTF archives, it
must be in this file to avoid dragging in BFD into non-BFD-using programs. */
ctf_archive_t *
ctf_arc_open (const char *filename, int *errp)
{
return ctf_open (filename, NULL, errp);
}
|