/* nto-tdep.c - general QNX Neutrino target functionality. Copyright (C) 2003-2024 Free Software Foundation, Inc. Contributed by QNX Software Systems Ltd. This file is part of GDB. This program 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 of the License, 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. If not, see . */ #include "defs.h" #include #include "nto-tdep.h" #include "top.h" #include "inferior.h" #include "infrun.h" #include "gdbarch.h" #include "bfd.h" #include "elf-bfd.h" #include "solib-svr4.h" #include "gdbcore.h" #include "objfiles.h" #include "source.h" #include "gdbsupport/pathstuff.h" #define QNX_NOTE_NAME "QNX" #define QNX_INFO_SECT_NAME "QNX_info" #ifdef __CYGWIN__ #include #endif #ifdef __CYGWIN__ static char default_nto_target[] = "C:\\QNXsdk\\target\\qnx6"; #elif defined(__sun__) || defined(linux) static char default_nto_target[] = "/opt/QNXsdk/target/qnx6"; #else static char default_nto_target[] = ""; #endif struct nto_target_ops current_nto_target; static const registry::key nto_inferior_data_reg; static char * nto_target (void) { char *p = getenv ("QNX_TARGET"); #ifdef __CYGWIN__ static char buf[PATH_MAX]; if (p) cygwin_conv_path (CCP_WIN_A_TO_POSIX, p, buf, PATH_MAX); else cygwin_conv_path (CCP_WIN_A_TO_POSIX, default_nto_target, buf, PATH_MAX); return buf; #else return p ? p : default_nto_target; #endif } /* Take a string such as i386, rs6000, etc. and map it onto CPUTYPE_X86, CPUTYPE_PPC, etc. as defined in nto-share/dsmsgs.h. */ int nto_map_arch_to_cputype (const char *arch) { if (!strcmp (arch, "i386") || !strcmp (arch, "x86")) return CPUTYPE_X86; if (!strcmp (arch, "rs6000") || !strcmp (arch, "powerpc")) return CPUTYPE_PPC; if (!strcmp (arch, "mips")) return CPUTYPE_MIPS; if (!strcmp (arch, "arm")) return CPUTYPE_ARM; if (!strcmp (arch, "sh")) return CPUTYPE_SH; return CPUTYPE_UNKNOWN; } int nto_find_and_open_solib (const char *solib, unsigned o_flags, gdb::unique_xmalloc_ptr *temp_pathname) { char *buf, *arch_path, *nto_root; const char *endian; const char *base; const char *arch; int arch_len, len, ret; #define PATH_FMT \ "%s/lib:%s/usr/lib:%s/usr/photon/lib:%s/usr/photon/dll:%s/lib/dll" nto_root = nto_target (); gdbarch *gdbarch = current_inferior ()->arch (); if (strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name, "i386") == 0) { arch = "x86"; endian = ""; } else if (strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name, "rs6000") == 0 || strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name, "powerpc") == 0) { arch = "ppc"; endian = "be"; } else { arch = gdbarch_bfd_arch_info (gdbarch)->arch_name; endian = gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG ? "be" : "le"; } /* In case nto_root is short, add strlen(solib) so we can reuse arch_path below. */ arch_len = (strlen (nto_root) + strlen (arch) + strlen (endian) + 2 + strlen (solib)); arch_path = (char *) alloca (arch_len); xsnprintf (arch_path, arch_len, "%s/%s%s", nto_root, arch, endian); len = strlen (PATH_FMT) + strlen (arch_path) * 5 + 1; buf = (char *) alloca (len); xsnprintf (buf, len, PATH_FMT, arch_path, arch_path, arch_path, arch_path, arch_path); base = lbasename (solib); ret = openp (buf, OPF_TRY_CWD_FIRST | OPF_RETURN_REALPATH, base, o_flags, temp_pathname); if (ret < 0 && base != solib) { xsnprintf (arch_path, arch_len, "/%s", solib); ret = open (arch_path, o_flags, 0); if (temp_pathname) { if (ret >= 0) *temp_pathname = gdb_realpath (arch_path); else temp_pathname->reset (NULL); } } return ret; } void nto_init_solib_absolute_prefix (void) { char buf[PATH_MAX * 2], arch_path[PATH_MAX]; char *nto_root; const char *endian; const char *arch; nto_root = nto_target (); gdbarch *gdbarch = current_inferior ()->arch (); if (strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name, "i386") == 0) { arch = "x86"; endian = ""; } else if (strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name, "rs6000") == 0 || strcmp (gdbarch_bfd_arch_info (gdbarch)->arch_name, "powerpc") == 0) { arch = "ppc"; endian = "be"; } else { arch = gdbarch_bfd_arch_info (gdbarch)->arch_name; endian = gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG ? "be" : "le"; } xsnprintf (arch_path, sizeof (arch_path), "%s/%s%s", nto_root, arch, endian); xsnprintf (buf, sizeof (buf), "set solib-absolute-prefix %s", arch_path); execute_command (buf, 0); } char ** nto_parse_redirection (char *pargv[], const char **pin, const char **pout, const char **perr) { char **argv; const char *in, *out, *err, *p; int argc, i, n; for (n = 0; pargv[n]; n++); if (n == 0) return NULL; in = ""; out = ""; err = ""; argv = XCNEWVEC (char *, n + 1); argc = n; for (i = 0, n = 0; n < argc; n++) { p = pargv[n]; if (*p == '>') { p++; if (*p) out = p; else out = pargv[++n]; } else if (*p == '<') { p++; if (*p) in = p; else in = pargv[++n]; } else if (*p++ == '2' && *p++ == '>') { if (*p == '&' && *(p + 1) == '1') err = out; else if (*p) err = p; else err = pargv[++n]; } else argv[i++] = pargv[n]; } *pin = in; *pout = out; *perr = err; return argv; } static CORE_ADDR lm_addr (const solib &so) { auto *li = gdb::checked_static_cast (so.lm_info.get ()); return li->l_addr; } static CORE_ADDR nto_truncate_ptr (CORE_ADDR addr) { gdbarch *gdbarch = current_inferior ()->arch (); if (gdbarch_ptr_bit (gdbarch) == sizeof (CORE_ADDR) * 8) /* We don't need to truncate anything, and the bit twiddling below will fail due to overflow problems. */ return addr; else return addr & (((CORE_ADDR) 1 << gdbarch_ptr_bit (gdbarch)) - 1); } static Elf_Internal_Phdr * find_load_phdr (bfd *abfd) { Elf_Internal_Phdr *phdr; unsigned int i; if (!elf_tdata (abfd)) return NULL; phdr = elf_tdata (abfd)->phdr; for (i = 0; i < elf_elfheader (abfd)->e_phnum; i++, phdr++) { if (phdr->p_type == PT_LOAD && (phdr->p_flags & PF_X)) return phdr; } return NULL; } void nto_relocate_section_addresses (solib &so, target_section *sec) { /* Neutrino treats the l_addr base address field in link.h as different than the base address in the System V ABI and so the offset needs to be calculated and applied to relocations. */ Elf_Internal_Phdr *phdr = find_load_phdr (sec->the_bfd_section->owner); unsigned vaddr = phdr ? phdr->p_vaddr : 0; sec->addr = nto_truncate_ptr (sec->addr + lm_addr (so) - vaddr); sec->endaddr = nto_truncate_ptr (sec->endaddr + lm_addr (so) - vaddr); } /* This is cheating a bit because our linker code is in libc.so. If we ever implement lazy linking, this may need to be re-examined. */ int nto_in_dynsym_resolve_code (CORE_ADDR pc) { if (in_plt_section (pc)) return 1; return 0; } void nto_dummy_supply_regset (struct regcache *regcache, char *regs) { /* Do nothing. */ } static void nto_sniff_abi_note_section (bfd *abfd, asection *sect, void *obj) { const char *sectname; unsigned int sectsize; /* Buffer holding the section contents. */ char *note; unsigned int namelen; const char *name; const unsigned sizeof_Elf_Nhdr = 12; sectname = bfd_section_name (sect); sectsize = bfd_section_size (sect); if (sectsize > 128) sectsize = 128; if (sectname != NULL && strstr (sectname, QNX_INFO_SECT_NAME) != NULL) *(enum gdb_osabi *) obj = GDB_OSABI_QNXNTO; else if (sectname != NULL && strstr (sectname, "note") != NULL && sectsize > sizeof_Elf_Nhdr) { note = XNEWVEC (char, sectsize); bfd_get_section_contents (abfd, sect, note, 0, sectsize); namelen = (unsigned int) bfd_h_get_32 (abfd, note); name = note + sizeof_Elf_Nhdr; if (sectsize >= namelen + sizeof_Elf_Nhdr && namelen == sizeof (QNX_NOTE_NAME) && 0 == strcmp (name, QNX_NOTE_NAME)) *(enum gdb_osabi *) obj = GDB_OSABI_QNXNTO; XDELETEVEC (note); } } enum gdb_osabi nto_elf_osabi_sniffer (bfd *abfd) { enum gdb_osabi osabi = GDB_OSABI_UNKNOWN; bfd_map_over_sections (abfd, nto_sniff_abi_note_section, &osabi); return osabi; } static const char * const nto_thread_state_str[] = { "DEAD", /* 0 0x00 */ "RUNNING", /* 1 0x01 */ "READY", /* 2 0x02 */ "STOPPED", /* 3 0x03 */ "SEND", /* 4 0x04 */ "RECEIVE", /* 5 0x05 */ "REPLY", /* 6 0x06 */ "STACK", /* 7 0x07 */ "WAITTHREAD", /* 8 0x08 */ "WAITPAGE", /* 9 0x09 */ "SIGSUSPEND", /* 10 0x0a */ "SIGWAITINFO", /* 11 0x0b */ "NANOSLEEP", /* 12 0x0c */ "MUTEX", /* 13 0x0d */ "CONDVAR", /* 14 0x0e */ "JOIN", /* 15 0x0f */ "INTR", /* 16 0x10 */ "SEM", /* 17 0x11 */ "WAITCTX", /* 18 0x12 */ "NET_SEND", /* 19 0x13 */ "NET_REPLY" /* 20 0x14 */ }; const char * nto_extra_thread_info (struct target_ops *self, struct thread_info *ti) { if (ti != NULL && ti->priv != NULL) { nto_thread_info *priv = get_nto_thread_info (ti); if (priv->state < ARRAY_SIZE (nto_thread_state_str)) return nto_thread_state_str [priv->state]; } return ""; } void nto_initialize_signals (void) { /* We use SIG45 for pulses, or something, so nostop, noprint and pass them. */ signal_stop_update (gdb_signal_from_name ("SIG45"), 0); signal_print_update (gdb_signal_from_name ("SIG45"), 0); signal_pass_update (gdb_signal_from_name ("SIG45"), 1); /* By default we don't want to stop on these two, but we do want to pass. */ #if defined(SIGSELECT) signal_stop_update (SIGSELECT, 0); signal_print_update (SIGSELECT, 0); signal_pass_update (SIGSELECT, 1); #endif #if defined(SIGPHOTON) signal_stop_update (SIGPHOTON, 0); signal_print_update (SIGPHOTON, 0); signal_pass_update (SIGPHOTON, 1); #endif } /* Read AUXV from initial_stack. */ LONGEST nto_read_auxv_from_initial_stack (CORE_ADDR initial_stack, gdb_byte *readbuf, LONGEST len, size_t sizeof_auxv_t) { gdb_byte targ32[4]; /* For 32 bit target values. */ gdb_byte targ64[8]; /* For 64 bit target values. */ CORE_ADDR data_ofs = 0; ULONGEST anint; LONGEST len_read = 0; gdb_byte *buff; enum bfd_endian byte_order; int ptr_size; if (sizeof_auxv_t == 16) ptr_size = 8; else ptr_size = 4; /* Skip over argc, argv and envp... Comment from ldd.c: The startup frame is set-up so that we have: auxv NULL ... envp2 envp1 <----- void *frame + (argc + 2) * sizeof(char *) NULL ... argv2 argv1 argc <------ void * frame On entry to ldd, frame gives the address of argc on the stack. */ /* Read argc. 4 bytes on both 64 and 32 bit arches and luckily little * endian. So we just read first 4 bytes. */ if (target_read_memory (initial_stack + data_ofs, targ32, 4) != 0) return 0; byte_order = gdbarch_byte_order (current_inferior ()->arch ()); anint = extract_unsigned_integer (targ32, sizeof (targ32), byte_order); /* Size of pointer is assumed to be 4 bytes (32 bit arch.) */ data_ofs += (anint + 2) * ptr_size; /* + 2 comes from argc itself and NULL terminating pointer in argv. */ /* Now loop over env table: */ anint = 0; while (target_read_memory (initial_stack + data_ofs, targ64, ptr_size) == 0) { if (extract_unsigned_integer (targ64, ptr_size, byte_order) == 0) anint = 1; /* Keep looping until non-null entry is found. */ else if (anint) break; data_ofs += ptr_size; } initial_stack += data_ofs; memset (readbuf, 0, len); buff = readbuf; while (len_read <= len-sizeof_auxv_t) { if (target_read_memory (initial_stack + len_read, buff, sizeof_auxv_t) == 0) { /* Both 32 and 64 bit structures have int as the first field. */ const ULONGEST a_type = extract_unsigned_integer (buff, sizeof (targ32), byte_order); if (a_type == AT_NULL) break; buff += sizeof_auxv_t; len_read += sizeof_auxv_t; } else break; } return len_read; } /* Return nto_inferior_data for the given INFERIOR. If not yet created, construct it. */ struct nto_inferior_data * nto_inferior_data (struct inferior *const inferior) { struct inferior *const inf = inferior ? inferior : current_inferior (); struct nto_inferior_data *inf_data; gdb_assert (inf != NULL); inf_data = nto_inferior_data_reg.get (inf); if (inf_data == NULL) inf_data = nto_inferior_data_reg.emplace (inf); return inf_data; }