1da0c48c4Sopenharmony_ci/* Recover relocatibility for addresses computed from debug information. 2da0c48c4Sopenharmony_ci Copyright (C) 2005-2009, 2012 Red Hat, Inc. 3da0c48c4Sopenharmony_ci Copyright (C) 2022 Mark J. Wielaard <mark@klomp.org> 4da0c48c4Sopenharmony_ci Copyright (C) 2022 Google LLC 5da0c48c4Sopenharmony_ci This file is part of elfutils. 6da0c48c4Sopenharmony_ci 7da0c48c4Sopenharmony_ci This file is free software; you can redistribute it and/or modify 8da0c48c4Sopenharmony_ci it under the terms of either 9da0c48c4Sopenharmony_ci 10da0c48c4Sopenharmony_ci * the GNU Lesser General Public License as published by the Free 11da0c48c4Sopenharmony_ci Software Foundation; either version 3 of the License, or (at 12da0c48c4Sopenharmony_ci your option) any later version 13da0c48c4Sopenharmony_ci 14da0c48c4Sopenharmony_ci or 15da0c48c4Sopenharmony_ci 16da0c48c4Sopenharmony_ci * the GNU General Public License as published by the Free 17da0c48c4Sopenharmony_ci Software Foundation; either version 2 of the License, or (at 18da0c48c4Sopenharmony_ci your option) any later version 19da0c48c4Sopenharmony_ci 20da0c48c4Sopenharmony_ci or both in parallel, as here. 21da0c48c4Sopenharmony_ci 22da0c48c4Sopenharmony_ci elfutils is distributed in the hope that it will be useful, but 23da0c48c4Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 24da0c48c4Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 25da0c48c4Sopenharmony_ci General Public License for more details. 26da0c48c4Sopenharmony_ci 27da0c48c4Sopenharmony_ci You should have received copies of the GNU General Public License and 28da0c48c4Sopenharmony_ci the GNU Lesser General Public License along with this program. If 29da0c48c4Sopenharmony_ci not, see <http://www.gnu.org/licenses/>. */ 30da0c48c4Sopenharmony_ci 31da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H 32da0c48c4Sopenharmony_ci# include <config.h> 33da0c48c4Sopenharmony_ci#endif 34da0c48c4Sopenharmony_ci 35da0c48c4Sopenharmony_ci#include "libdwflP.h" 36da0c48c4Sopenharmony_ci#include <fcntl.h> 37da0c48c4Sopenharmony_ci 38da0c48c4Sopenharmony_ci/* Since dwfl_report_elf lays out the sections already, this will only be 39da0c48c4Sopenharmony_ci called when the section headers of the debuginfo file are being 40da0c48c4Sopenharmony_ci consulted instead, or for the section placed at 0. With binutils 41da0c48c4Sopenharmony_ci strip-to-debug, the symbol table is in the debuginfo file and relocation 42da0c48c4Sopenharmony_ci looks there. */ 43da0c48c4Sopenharmony_ciint 44da0c48c4Sopenharmony_cidwfl_offline_section_address (Dwfl_Module *mod, 45da0c48c4Sopenharmony_ci void **userdata __attribute__ ((unused)), 46da0c48c4Sopenharmony_ci const char *modname __attribute__ ((unused)), 47da0c48c4Sopenharmony_ci Dwarf_Addr base __attribute__ ((unused)), 48da0c48c4Sopenharmony_ci const char *secname __attribute__ ((unused)), 49da0c48c4Sopenharmony_ci Elf32_Word shndx, 50da0c48c4Sopenharmony_ci const GElf_Shdr *shdr __attribute__ ((unused)), 51da0c48c4Sopenharmony_ci Dwarf_Addr *addr) 52da0c48c4Sopenharmony_ci{ 53da0c48c4Sopenharmony_ci assert (mod->e_type == ET_REL); 54da0c48c4Sopenharmony_ci assert (shdr->sh_addr == 0); 55da0c48c4Sopenharmony_ci assert (shdr->sh_flags & SHF_ALLOC); 56da0c48c4Sopenharmony_ci assert (shndx != 0); 57da0c48c4Sopenharmony_ci 58da0c48c4Sopenharmony_ci if (mod->debug.elf == NULL) 59da0c48c4Sopenharmony_ci /* We are only here because sh_addr is zero even though layout is complete. 60da0c48c4Sopenharmony_ci The first section in the first file under -e is placed at 0. */ 61da0c48c4Sopenharmony_ci return 0; 62da0c48c4Sopenharmony_ci 63da0c48c4Sopenharmony_ci /* The section numbers might not match between the two files. 64da0c48c4Sopenharmony_ci The best we can rely on is the order of SHF_ALLOC sections. */ 65da0c48c4Sopenharmony_ci 66da0c48c4Sopenharmony_ci Elf_Scn *ourscn = elf_getscn (mod->debug.elf, shndx); 67da0c48c4Sopenharmony_ci Elf_Scn *scn = NULL; 68da0c48c4Sopenharmony_ci uint_fast32_t skip_alloc = 0; 69da0c48c4Sopenharmony_ci while ((scn = elf_nextscn (mod->debug.elf, scn)) != ourscn) 70da0c48c4Sopenharmony_ci { 71da0c48c4Sopenharmony_ci assert (scn != NULL); 72da0c48c4Sopenharmony_ci GElf_Shdr shdr_mem; 73da0c48c4Sopenharmony_ci GElf_Shdr *sh = gelf_getshdr (scn, &shdr_mem); 74da0c48c4Sopenharmony_ci if (unlikely (sh == NULL)) 75da0c48c4Sopenharmony_ci return -1; 76da0c48c4Sopenharmony_ci if (sh->sh_flags & SHF_ALLOC) 77da0c48c4Sopenharmony_ci ++skip_alloc; 78da0c48c4Sopenharmony_ci } 79da0c48c4Sopenharmony_ci 80da0c48c4Sopenharmony_ci scn = NULL; 81da0c48c4Sopenharmony_ci while ((scn = elf_nextscn (mod->main.elf, scn)) != NULL) 82da0c48c4Sopenharmony_ci { 83da0c48c4Sopenharmony_ci GElf_Shdr shdr_mem; 84da0c48c4Sopenharmony_ci GElf_Shdr *main_shdr = gelf_getshdr (scn, &shdr_mem); 85da0c48c4Sopenharmony_ci if (unlikely (main_shdr == NULL)) 86da0c48c4Sopenharmony_ci return -1; 87da0c48c4Sopenharmony_ci if ((main_shdr->sh_flags & SHF_ALLOC) && skip_alloc-- == 0) 88da0c48c4Sopenharmony_ci { 89da0c48c4Sopenharmony_ci assert (main_shdr->sh_flags == shdr->sh_flags); 90da0c48c4Sopenharmony_ci *addr = main_shdr->sh_addr; 91da0c48c4Sopenharmony_ci return 0; 92da0c48c4Sopenharmony_ci } 93da0c48c4Sopenharmony_ci } 94da0c48c4Sopenharmony_ci 95da0c48c4Sopenharmony_ci /* This should never happen. */ 96da0c48c4Sopenharmony_ci return -1; 97da0c48c4Sopenharmony_ci} 98da0c48c4Sopenharmony_ciINTDEF (dwfl_offline_section_address) 99da0c48c4Sopenharmony_ci 100da0c48c4Sopenharmony_ci/* Forward declarations. */ 101da0c48c4Sopenharmony_cistatic Dwfl_Module *process_elf (Dwfl *dwfl, const char *name, 102da0c48c4Sopenharmony_ci const char *file_name, int fd, Elf *elf); 103da0c48c4Sopenharmony_cistatic Dwfl_Module *process_archive (Dwfl *dwfl, const char *name, 104da0c48c4Sopenharmony_ci const char *file_name, int fd, Elf *elf, 105da0c48c4Sopenharmony_ci int (*predicate) (const char *module, 106da0c48c4Sopenharmony_ci const char *file)); 107da0c48c4Sopenharmony_ci 108da0c48c4Sopenharmony_ci/* Report one module for an ELF file, or many for an archive. 109da0c48c4Sopenharmony_ci Always consumes ELF and FD. */ 110da0c48c4Sopenharmony_cistatic Dwfl_Module * 111da0c48c4Sopenharmony_ciprocess_file (Dwfl *dwfl, const char *name, const char *file_name, int fd, 112da0c48c4Sopenharmony_ci Elf *elf, int (*predicate) (const char *module, 113da0c48c4Sopenharmony_ci const char *file)) 114da0c48c4Sopenharmony_ci{ 115da0c48c4Sopenharmony_ci switch (elf_kind (elf)) 116da0c48c4Sopenharmony_ci { 117da0c48c4Sopenharmony_ci default: 118da0c48c4Sopenharmony_ci case ELF_K_NONE: 119da0c48c4Sopenharmony_ci __libdwfl_seterrno (elf == NULL ? DWFL_E_LIBELF : DWFL_E_BADELF); 120da0c48c4Sopenharmony_ci return NULL; 121da0c48c4Sopenharmony_ci 122da0c48c4Sopenharmony_ci case ELF_K_ELF: 123da0c48c4Sopenharmony_ci return process_elf (dwfl, name, file_name, fd, elf); 124da0c48c4Sopenharmony_ci 125da0c48c4Sopenharmony_ci case ELF_K_AR: 126da0c48c4Sopenharmony_ci return process_archive (dwfl, name, file_name, fd, elf, predicate); 127da0c48c4Sopenharmony_ci } 128da0c48c4Sopenharmony_ci} 129da0c48c4Sopenharmony_ci 130da0c48c4Sopenharmony_ci/* Report the open ELF file as a module. Always consumes ELF and FD. */ 131da0c48c4Sopenharmony_cistatic Dwfl_Module * 132da0c48c4Sopenharmony_ciprocess_elf (Dwfl *dwfl, const char *name, const char *file_name, int fd, 133da0c48c4Sopenharmony_ci Elf *elf) 134da0c48c4Sopenharmony_ci{ 135da0c48c4Sopenharmony_ci Dwfl_Module *mod = __libdwfl_report_elf (dwfl, name, file_name, fd, elf, 136da0c48c4Sopenharmony_ci dwfl->offline_next_address, true, 137da0c48c4Sopenharmony_ci false); 138da0c48c4Sopenharmony_ci if (mod != NULL) 139da0c48c4Sopenharmony_ci { 140da0c48c4Sopenharmony_ci /* If this is an ET_EXEC file with fixed addresses, the address range 141da0c48c4Sopenharmony_ci it consumed may or may not intersect with the arbitrary range we 142da0c48c4Sopenharmony_ci will use for relocatable modules. Make sure we always use a free 143da0c48c4Sopenharmony_ci range for the offline allocations. If this module did use 144da0c48c4Sopenharmony_ci offline_next_address, it may have rounded it up for the module's 145da0c48c4Sopenharmony_ci alignment requirements. */ 146da0c48c4Sopenharmony_ci if ((dwfl->offline_next_address >= mod->low_addr 147da0c48c4Sopenharmony_ci || mod->low_addr - dwfl->offline_next_address < OFFLINE_REDZONE) 148da0c48c4Sopenharmony_ci && dwfl->offline_next_address < mod->high_addr + OFFLINE_REDZONE) 149da0c48c4Sopenharmony_ci dwfl->offline_next_address = mod->high_addr + OFFLINE_REDZONE; 150da0c48c4Sopenharmony_ci 151da0c48c4Sopenharmony_ci /* Don't keep the file descriptor around. */ 152da0c48c4Sopenharmony_ci if (mod->main.fd != -1 && elf_cntl (mod->main.elf, ELF_C_FDREAD) == 0) 153da0c48c4Sopenharmony_ci { 154da0c48c4Sopenharmony_ci /* Grab the dir path in case we want to report this file as 155da0c48c4Sopenharmony_ci Dwarf later. */ 156da0c48c4Sopenharmony_ci mod->elfdir = __libdw_debugdir (mod->main.fd); 157da0c48c4Sopenharmony_ci close (mod->main.fd); 158da0c48c4Sopenharmony_ci mod->main.fd = -1; 159da0c48c4Sopenharmony_ci } 160da0c48c4Sopenharmony_ci } 161da0c48c4Sopenharmony_ci 162da0c48c4Sopenharmony_ci return mod; 163da0c48c4Sopenharmony_ci} 164da0c48c4Sopenharmony_ci 165da0c48c4Sopenharmony_ci/* Always consumes MEMBER. Returns elf_next result on success. 166da0c48c4Sopenharmony_ci For errors returns ELF_C_NULL with *MOD set to null. */ 167da0c48c4Sopenharmony_cistatic Elf_Cmd 168da0c48c4Sopenharmony_ciprocess_archive_member (Dwfl *dwfl, const char *name, const char *file_name, 169da0c48c4Sopenharmony_ci int (*predicate) (const char *module, const char *file), 170da0c48c4Sopenharmony_ci int fd, Elf *member, Dwfl_Module **mod) 171da0c48c4Sopenharmony_ci{ 172da0c48c4Sopenharmony_ci const Elf_Arhdr *h = elf_getarhdr (member); 173da0c48c4Sopenharmony_ci if (unlikely (h == NULL)) 174da0c48c4Sopenharmony_ci { 175da0c48c4Sopenharmony_ci __libdwfl_seterrno (DWFL_E_LIBELF); 176da0c48c4Sopenharmony_ci fail: 177da0c48c4Sopenharmony_ci elf_end (member); 178da0c48c4Sopenharmony_ci *mod = NULL; 179da0c48c4Sopenharmony_ci return ELF_C_NULL; 180da0c48c4Sopenharmony_ci } 181da0c48c4Sopenharmony_ci 182da0c48c4Sopenharmony_ci if (!strcmp (h->ar_name, "/") || !strcmp (h->ar_name, "//") 183da0c48c4Sopenharmony_ci || !strcmp (h->ar_name, "/SYM64/")) 184da0c48c4Sopenharmony_ci { 185da0c48c4Sopenharmony_ci skip:; 186da0c48c4Sopenharmony_ci /* Skip this and go to the next. */ 187da0c48c4Sopenharmony_ci Elf_Cmd result = elf_next (member); 188da0c48c4Sopenharmony_ci elf_end (member); 189da0c48c4Sopenharmony_ci return result; 190da0c48c4Sopenharmony_ci } 191da0c48c4Sopenharmony_ci 192da0c48c4Sopenharmony_ci char *member_name; 193da0c48c4Sopenharmony_ci if (unlikely (asprintf (&member_name, "%s(%s)", file_name, h->ar_name) < 0)) 194da0c48c4Sopenharmony_ci { 195da0c48c4Sopenharmony_ci nomem: 196da0c48c4Sopenharmony_ci __libdwfl_seterrno (DWFL_E_NOMEM); 197da0c48c4Sopenharmony_ci elf_end (member); 198da0c48c4Sopenharmony_ci *mod = NULL; 199da0c48c4Sopenharmony_ci return ELF_C_NULL; 200da0c48c4Sopenharmony_ci } 201da0c48c4Sopenharmony_ci 202da0c48c4Sopenharmony_ci char *module_name = NULL; 203da0c48c4Sopenharmony_ci if (name == NULL || name[0] == '\0') 204da0c48c4Sopenharmony_ci name = h->ar_name; 205da0c48c4Sopenharmony_ci else if (unlikely (asprintf (&module_name, "%s:%s", name, h->ar_name) < 0)) 206da0c48c4Sopenharmony_ci { 207da0c48c4Sopenharmony_ci free (member_name); 208da0c48c4Sopenharmony_ci goto nomem; 209da0c48c4Sopenharmony_ci } 210da0c48c4Sopenharmony_ci else 211da0c48c4Sopenharmony_ci name = module_name; 212da0c48c4Sopenharmony_ci 213da0c48c4Sopenharmony_ci if (predicate != NULL) 214da0c48c4Sopenharmony_ci { 215da0c48c4Sopenharmony_ci /* Let the predicate decide whether to use this one. */ 216da0c48c4Sopenharmony_ci int want = (*predicate) (name, member_name); 217da0c48c4Sopenharmony_ci if (want <= 0) 218da0c48c4Sopenharmony_ci { 219da0c48c4Sopenharmony_ci free (member_name); 220da0c48c4Sopenharmony_ci free (module_name); 221da0c48c4Sopenharmony_ci if (unlikely (want < 0)) 222da0c48c4Sopenharmony_ci { 223da0c48c4Sopenharmony_ci __libdwfl_seterrno (DWFL_E_CB); 224da0c48c4Sopenharmony_ci goto fail; 225da0c48c4Sopenharmony_ci } 226da0c48c4Sopenharmony_ci goto skip; 227da0c48c4Sopenharmony_ci } 228da0c48c4Sopenharmony_ci } 229da0c48c4Sopenharmony_ci 230da0c48c4Sopenharmony_ci /* We let __libdwfl_report_elf cache the fd in mod->main.fd, 231da0c48c4Sopenharmony_ci though it's the same fd for all the members. 232da0c48c4Sopenharmony_ci On module teardown we will close it only on the last Elf reference. */ 233da0c48c4Sopenharmony_ci *mod = process_file (dwfl, name, member_name, fd, member, predicate); 234da0c48c4Sopenharmony_ci free (member_name); 235da0c48c4Sopenharmony_ci free (module_name); 236da0c48c4Sopenharmony_ci 237da0c48c4Sopenharmony_ci if (*mod == NULL) 238da0c48c4Sopenharmony_ci { 239da0c48c4Sopenharmony_ci elf_end (member); 240da0c48c4Sopenharmony_ci return ELF_C_NULL; 241da0c48c4Sopenharmony_ci } 242da0c48c4Sopenharmony_ci 243da0c48c4Sopenharmony_ci /* Advance the archive-reading offset for the next iteration. */ 244da0c48c4Sopenharmony_ci return elf_next (member); 245da0c48c4Sopenharmony_ci} 246da0c48c4Sopenharmony_ci 247da0c48c4Sopenharmony_ci/* Report each member of the archive as its own module. */ 248da0c48c4Sopenharmony_cistatic Dwfl_Module * 249da0c48c4Sopenharmony_ciprocess_archive (Dwfl *dwfl, const char *name, const char *file_name, int fd, 250da0c48c4Sopenharmony_ci Elf *archive, 251da0c48c4Sopenharmony_ci int (*predicate) (const char *module, const char *file)) 252da0c48c4Sopenharmony_ci 253da0c48c4Sopenharmony_ci{ 254da0c48c4Sopenharmony_ci Dwfl_Module *mod = NULL; 255da0c48c4Sopenharmony_ci /* elf_begin supports opening archives even with fd == -1 passed. */ 256da0c48c4Sopenharmony_ci Elf *member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive); 257da0c48c4Sopenharmony_ci if (unlikely (member == NULL)) /* Empty archive. */ 258da0c48c4Sopenharmony_ci { 259da0c48c4Sopenharmony_ci __libdwfl_seterrno (DWFL_E_BADELF); 260da0c48c4Sopenharmony_ci return NULL; 261da0c48c4Sopenharmony_ci } 262da0c48c4Sopenharmony_ci 263da0c48c4Sopenharmony_ci while (process_archive_member (dwfl, name, file_name, predicate, 264da0c48c4Sopenharmony_ci fd, member, &mod) != ELF_C_NULL) 265da0c48c4Sopenharmony_ci member = elf_begin (fd, ELF_C_READ_MMAP_PRIVATE, archive); 266da0c48c4Sopenharmony_ci 267da0c48c4Sopenharmony_ci /* We can drop the archive Elf handle even if we're still using members 268da0c48c4Sopenharmony_ci in live modules. When the last module's elf_end on a member returns 269da0c48c4Sopenharmony_ci zero, that module will close FD. If no modules survived the predicate, 270da0c48c4Sopenharmony_ci we are all done with the file right here. */ 271da0c48c4Sopenharmony_ci if (mod != NULL /* If no modules, caller will clean up. */ 272da0c48c4Sopenharmony_ci && elf_end (archive) == 0) 273da0c48c4Sopenharmony_ci close (fd); 274da0c48c4Sopenharmony_ci 275da0c48c4Sopenharmony_ci return mod; 276da0c48c4Sopenharmony_ci} 277da0c48c4Sopenharmony_ci 278da0c48c4Sopenharmony_ciDwfl_Module * 279da0c48c4Sopenharmony_ciinternal_function 280da0c48c4Sopenharmony_ci__libdwfl_report_offline (Dwfl *dwfl, const char *name, 281da0c48c4Sopenharmony_ci const char *file_name, int fd, bool closefd, 282da0c48c4Sopenharmony_ci int (*predicate) (const char *module, 283da0c48c4Sopenharmony_ci const char *file)) 284da0c48c4Sopenharmony_ci{ 285da0c48c4Sopenharmony_ci Elf *elf; 286da0c48c4Sopenharmony_ci Dwfl_Error error = __libdw_open_file (&fd, &elf, closefd, true); 287da0c48c4Sopenharmony_ci if (error != DWFL_E_NOERROR) 288da0c48c4Sopenharmony_ci { 289da0c48c4Sopenharmony_ci __libdwfl_seterrno (error); 290da0c48c4Sopenharmony_ci return NULL; 291da0c48c4Sopenharmony_ci } 292da0c48c4Sopenharmony_ci Dwfl_Module *mod = process_file (dwfl, name, file_name, fd, elf, predicate); 293da0c48c4Sopenharmony_ci if (mod == NULL) 294da0c48c4Sopenharmony_ci { 295da0c48c4Sopenharmony_ci elf_end (elf); 296da0c48c4Sopenharmony_ci if (closefd) 297da0c48c4Sopenharmony_ci close (fd); 298da0c48c4Sopenharmony_ci } 299da0c48c4Sopenharmony_ci return mod; 300da0c48c4Sopenharmony_ci} 301da0c48c4Sopenharmony_ci 302da0c48c4Sopenharmony_ciDwfl_Module * 303da0c48c4Sopenharmony_cidwfl_report_offline (Dwfl *dwfl, const char *name, 304da0c48c4Sopenharmony_ci const char *file_name, int fd) 305da0c48c4Sopenharmony_ci{ 306da0c48c4Sopenharmony_ci if (dwfl == NULL) 307da0c48c4Sopenharmony_ci return NULL; 308da0c48c4Sopenharmony_ci 309da0c48c4Sopenharmony_ci bool closefd = false; 310da0c48c4Sopenharmony_ci if (fd < 0) 311da0c48c4Sopenharmony_ci { 312da0c48c4Sopenharmony_ci closefd = true; 313da0c48c4Sopenharmony_ci fd = open (file_name, O_RDONLY); 314da0c48c4Sopenharmony_ci if (fd < 0) 315da0c48c4Sopenharmony_ci { 316da0c48c4Sopenharmony_ci __libdwfl_seterrno (DWFL_E_ERRNO); 317da0c48c4Sopenharmony_ci return NULL; 318da0c48c4Sopenharmony_ci } 319da0c48c4Sopenharmony_ci } 320da0c48c4Sopenharmony_ci 321da0c48c4Sopenharmony_ci return __libdwfl_report_offline (dwfl, name, file_name, fd, closefd, NULL); 322da0c48c4Sopenharmony_ci} 323da0c48c4Sopenharmony_ciINTDEF (dwfl_report_offline) 324da0c48c4Sopenharmony_ci 325da0c48c4Sopenharmony_ciDwfl_Module * 326da0c48c4Sopenharmony_cidwfl_report_offline_memory (Dwfl *dwfl, const char *name, 327da0c48c4Sopenharmony_ci const char *file_name, char *data, size_t size) 328da0c48c4Sopenharmony_ci{ 329da0c48c4Sopenharmony_ci if (dwfl == NULL) 330da0c48c4Sopenharmony_ci return NULL; 331da0c48c4Sopenharmony_ci 332da0c48c4Sopenharmony_ci Elf *elf; 333da0c48c4Sopenharmony_ci Dwfl_Error error = __libdw_open_elf_memory (data, size, &elf, true); 334da0c48c4Sopenharmony_ci if (error != DWFL_E_NOERROR) 335da0c48c4Sopenharmony_ci { 336da0c48c4Sopenharmony_ci __libdwfl_seterrno (error); 337da0c48c4Sopenharmony_ci return NULL; 338da0c48c4Sopenharmony_ci } 339da0c48c4Sopenharmony_ci /* It is ok to pass fd == -1 here, because libelf uses it as a value for 340da0c48c4Sopenharmony_ci "no file opened" and supports working with files without fd, thanks to 341da0c48c4Sopenharmony_ci the existence of the elf_memory function. */ 342da0c48c4Sopenharmony_ci Dwfl_Module *mod = process_file (dwfl, name, file_name, -1, elf, NULL); 343da0c48c4Sopenharmony_ci if (mod == NULL) 344da0c48c4Sopenharmony_ci elf_end (elf); 345da0c48c4Sopenharmony_ci return mod; 346da0c48c4Sopenharmony_ci} 347da0c48c4Sopenharmony_ciINTDEF (dwfl_report_offline_memory) 348