Index: head/libexec/atrun/atrun.c =================================================================== --- head/libexec/atrun/atrun.c (revision 326273) +++ head/libexec/atrun/atrun.c (revision 326274) @@ -1,590 +1,593 @@ -/* +/*- * atrun.c - run jobs queued by at; run with root privileges. + * + * SPDX-License-Identifier: BSD-2-Clause + * * Copyright (C) 1993, 1994 Thomas Koenig * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ /* System Headers */ #include #include #include #include #ifdef __FreeBSD__ #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __FreeBSD__ #include #else #include #endif #ifdef LOGIN_CAP #include #endif #ifdef PAM #include #include #endif /* Local headers */ #include "gloadavg.h" #define MAIN #include "privs.h" /* Macros */ #ifndef ATJOB_DIR #define ATJOB_DIR "/usr/spool/atjobs/" #endif #ifndef ATSPOOL_DIR #define ATSPOOL_DIR "/usr/spool/atspool/" #endif #ifndef LOADAVG_MX #define LOADAVG_MX 1.5 #endif /* File scope variables */ static const char * const atrun = "atrun"; /* service name for syslog etc. */ static int debug = 0; void perr(const char *fmt, ...); void perrx(const char *fmt, ...); static void usage(void); /* Local functions */ static int write_string(int fd, const char* a) { return write(fd, a, strlen(a)); } #undef DEBUG_FORK #ifdef DEBUG_FORK static pid_t myfork(void) { pid_t res; res = fork(); if (res == 0) kill(getpid(),SIGSTOP); return res; } #define fork myfork #endif static void run_file(const char *filename, uid_t uid, gid_t gid) { /* Run a file by spawning off a process which redirects I/O, * spawns a subshell, then waits for it to complete and sends * mail to the user. */ pid_t pid; int fd_out, fd_in; int queue; char mailbuf[MAXLOGNAME], fmt[64]; char *mailname = NULL; FILE *stream; int send_mail = 0; struct stat buf, lbuf; off_t size; struct passwd *pentry; int fflags; long nuid; long ngid; #ifdef PAM pam_handle_t *pamh = NULL; int pam_err; struct pam_conv pamc = { .conv = openpam_nullconv, .appdata_ptr = NULL }; #endif PRIV_START if (chmod(filename, S_IRUSR) != 0) { perr("cannot change file permissions"); } PRIV_END pid = fork(); if (pid == -1) perr("cannot fork"); else if (pid != 0) return; /* Let's see who we mail to. Hopefully, we can read it from * the command file; if not, send it to the owner, or, failing that, * to root. */ pentry = getpwuid(uid); if (pentry == NULL) perrx("Userid %lu not found - aborting job %s", (unsigned long) uid, filename); #ifdef PAM PRIV_START pam_err = pam_start(atrun, pentry->pw_name, &pamc, &pamh); if (pam_err != PAM_SUCCESS) perrx("cannot start PAM: %s", pam_strerror(pamh, pam_err)); pam_err = pam_acct_mgmt(pamh, PAM_SILENT); /* Expired password shouldn't prevent the job from running. */ if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD) perrx("Account %s (userid %lu) unavailable for job %s: %s", pentry->pw_name, (unsigned long)uid, filename, pam_strerror(pamh, pam_err)); pam_end(pamh, pam_err); PRIV_END #endif /* PAM */ PRIV_START stream=fopen(filename, "r"); PRIV_END if (stream == NULL) perr("cannot open input file %s", filename); if ((fd_in = dup(fileno(stream))) <0) perr("error duplicating input file descriptor"); if (fstat(fd_in, &buf) == -1) perr("error in fstat of input file descriptor"); if (lstat(filename, &lbuf) == -1) perr("error in fstat of input file"); if (S_ISLNK(lbuf.st_mode)) perrx("Symbolic link encountered in job %s - aborting", filename); if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) || (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) || (lbuf.st_size!=buf.st_size)) perrx("Somebody changed files from under us for job %s - aborting", filename); if (buf.st_nlink > 1) perrx("Somebody is trying to run a linked script for job %s", filename); if ((fflags = fcntl(fd_in, F_GETFD)) <0) perr("error in fcntl"); fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC); snprintf(fmt, sizeof(fmt), "#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d", MAXLOGNAME - 1); if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4) perrx("File %s is in wrong format - aborting", filename); if (mailbuf[0] == '-') perrx("Illegal mail name %s in %s", mailbuf, filename); mailname = mailbuf; if (nuid != uid) perrx("Job %s - userid %ld does not match file uid %lu", filename, nuid, (unsigned long)uid); if (ngid != gid) perrx("Job %s - groupid %ld does not match file gid %lu", filename, ngid, (unsigned long)gid); fclose(stream); if (chdir(ATSPOOL_DIR) < 0) perr("cannot chdir to %s", ATSPOOL_DIR); /* Create a file to hold the output of the job we are about to run. * Write the mail header. */ if((fd_out=open(filename, O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0) perr("cannot create output file"); write_string(fd_out, "Subject: Output from your job "); write_string(fd_out, filename); write_string(fd_out, "\n\n"); fstat(fd_out, &buf); size = buf.st_size; close(STDIN_FILENO); close(STDOUT_FILENO); close(STDERR_FILENO); pid = fork(); if (pid < 0) perr("error in fork"); else if (pid == 0) { char *nul = NULL; char **nenvp = &nul; /* Set up things for the child; we want standard input from the input file, * and standard output and error sent to our output file. */ if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0) perr("error in lseek"); if (dup(fd_in) != STDIN_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDOUT_FILENO) perr("error in I/O redirection"); if (dup(fd_out) != STDERR_FILENO) perr("error in I/O redirection"); close(fd_in); close(fd_out); if (chdir(ATJOB_DIR) < 0) perr("cannot chdir to %s", ATJOB_DIR); queue = *filename; PRIV_START nice(tolower(queue) - 'a'); #ifdef LOGIN_CAP /* * For simplicity and safety, set all aspects of the user context * except for a selected subset: Don't set priority, which was * set based on the queue file name according to the tradition. * Don't bother to set environment, including path vars, either * because it will be discarded anyway. Although the job file * should set umask, preset it here just in case. */ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL & ~(LOGIN_SETPRIORITY | LOGIN_SETPATH | LOGIN_SETENV)) != 0) exit(EXIT_FAILURE); /* setusercontext() logged the error */ #else /* LOGIN_CAP */ if (initgroups(pentry->pw_name,pentry->pw_gid)) perr("cannot init group access list"); if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0) perr("cannot change group"); if (setlogin(pentry->pw_name)) perr("cannot set login name"); if (setuid(uid) < 0 || seteuid(uid) < 0) perr("cannot set user id"); #endif /* LOGIN_CAP */ if (chdir(pentry->pw_dir)) chdir("/"); if(execle("/bin/sh","sh",(char *) NULL, nenvp) != 0) perr("exec failed for /bin/sh"); PRIV_END } /* We're the parent. Let's wait. */ close(fd_in); close(fd_out); waitpid(pid, (int *) NULL, 0); /* Send mail. Unlink the output file first, so it is deleted after * the run. */ stat(filename, &buf); if (open(filename, O_RDONLY) != STDIN_FILENO) perr("open of jobfile failed"); unlink(filename); if ((buf.st_size != size) || send_mail) { PRIV_START #ifdef LOGIN_CAP /* * This time set full context to run the mailer. */ if (setusercontext(NULL, pentry, uid, LOGIN_SETALL) != 0) exit(EXIT_FAILURE); /* setusercontext() logged the error */ #else /* LOGIN_CAP */ if (initgroups(pentry->pw_name,pentry->pw_gid)) perr("cannot init group access list"); if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0) perr("cannot change group"); if (setlogin(pentry->pw_name)) perr("cannot set login name"); if (setuid(uid) < 0 || seteuid(uid) < 0) perr("cannot set user id"); #endif /* LOGIN_CAP */ if (chdir(pentry->pw_dir)) chdir("/"); #ifdef __FreeBSD__ execl(_PATH_SENDMAIL, "sendmail", "-F", "Atrun Service", "-odi", "-oem", mailname, (char *) NULL); #else execl(MAIL_CMD, MAIL_CMD, mailname, (char *) NULL); #endif perr("exec failed for mail command"); PRIV_END } exit(EXIT_SUCCESS); } /* Global functions */ /* Needed in gloadavg.c */ void perr(const char *fmt, ...) { const char * const fmtadd = ": %m"; char nfmt[strlen(fmt) + strlen(fmtadd) + 1]; va_list ap; va_start(ap, fmt); if (debug) { vwarn(fmt, ap); } else { snprintf(nfmt, sizeof(nfmt), "%s%s", fmt, fmtadd); vsyslog(LOG_ERR, nfmt, ap); } va_end(ap); exit(EXIT_FAILURE); } void perrx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); if (debug) vwarnx(fmt, ap); else vsyslog(LOG_ERR, fmt, ap); va_end(ap); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { /* Browse through ATJOB_DIR, checking all the jobfiles wether they should * be executed and or deleted. The queue is coded into the first byte of * the job filename, the date (in minutes since Eon) as a hex number in the * following eight bytes, followed by a dot and a serial number. A file * which has not been executed yet is denoted by its execute - bit set. * For those files which are to be executed, run_file() is called, which forks * off a child which takes care of I/O redirection, forks off another child * for execution and yet another one, optionally, for sending mail. * Files which already have run are removed during the next invocation. */ DIR *spool; struct dirent *dirent; struct stat buf; unsigned long ctm; unsigned long jobno; char queue; time_t now, run_time; char batch_name[] = "Z2345678901234"; uid_t batch_uid; gid_t batch_gid; int c; int run_batch; #ifdef __FreeBSD__ size_t ncpusz; double load_avg = -1; int ncpu; #else double load_avg = LOADAVG_MX; #endif /* We don't need root privileges all the time; running under uid and gid daemon * is fine. */ RELINQUISH_PRIVS_ROOT(DAEMON_UID, DAEMON_GID) openlog(atrun, LOG_PID, LOG_CRON); opterr = 0; while((c=getopt(argc, argv, "dl:"))!= -1) { switch (c) { case 'l': if (sscanf(optarg, "%lf", &load_avg) != 1) perr("garbled option -l"); #ifndef __FreeBSD__ if (load_avg <= 0.) load_avg = LOADAVG_MX; #endif break; case 'd': debug ++; break; case '?': default: usage(); } } if (chdir(ATJOB_DIR) != 0) perr("cannot change to %s", ATJOB_DIR); #ifdef __FreeBSD__ if (load_avg <= 0.) { ncpusz = sizeof(size_t); if (sysctlbyname("hw.ncpu", &ncpu, &ncpusz, NULL, 0) < 0) ncpu = 1; load_avg = LOADAVG_MX * ncpu; } #endif /* Main loop. Open spool directory for reading and look over all the * files in there. If the filename indicates that the job should be run * and the x bit is set, fork off a child which sets its user and group * id to that of the files and exec a /bin/sh which executes the shell * script. Unlink older files if they should no longer be run. For * deletion, their r bit has to be turned on. * * Also, pick the oldest batch job to run, at most one per invocation of * atrun. */ if ((spool = opendir(".")) == NULL) perr("cannot read %s", ATJOB_DIR); if (flock(dirfd(spool), LOCK_EX) == -1) perr("cannot lock %s", ATJOB_DIR); now = time(NULL); run_batch = 0; batch_uid = (uid_t) -1; batch_gid = (gid_t) -1; while ((dirent = readdir(spool)) != NULL) { if (stat(dirent->d_name,&buf) != 0) perr("cannot stat in %s", ATJOB_DIR); /* We don't want directories */ if (!S_ISREG(buf.st_mode)) continue; if (sscanf(dirent->d_name,"%c%5lx%8lx",&queue,&jobno,&ctm) != 3) continue; run_time = (time_t) ctm*60; if ((S_IXUSR & buf.st_mode) && (run_time <=now)) { if (isupper(queue) && (strcmp(batch_name,dirent->d_name) > 0)) { run_batch = 1; strlcpy(batch_name, dirent->d_name, sizeof(batch_name)); batch_uid = buf.st_uid; batch_gid = buf.st_gid; } /* The file is executable and old enough */ if (islower(queue)) run_file(dirent->d_name, buf.st_uid, buf.st_gid); } /* Delete older files */ if ((run_time < now) && !(S_IXUSR & buf.st_mode) && (S_IRUSR & buf.st_mode)) unlink(dirent->d_name); } /* run the single batch file, if any */ if (run_batch && (gloadavg() < load_avg)) run_file(batch_name, batch_uid, batch_gid); if (flock(dirfd(spool), LOCK_UN) == -1) perr("cannot unlock %s", ATJOB_DIR); if (closedir(spool) == -1) perr("cannot closedir %s", ATJOB_DIR); closelog(); exit(EXIT_SUCCESS); } static void usage(void) { if (debug) fprintf(stderr, "usage: atrun [-l load_avg] [-d]\n"); else syslog(LOG_ERR, "usage: atrun [-l load_avg] [-d]"); exit(EXIT_FAILURE); } Index: head/libexec/atrun/gloadavg.c =================================================================== --- head/libexec/atrun/gloadavg.c (revision 326273) +++ head/libexec/atrun/gloadavg.c (revision 326274) @@ -1,72 +1,74 @@ -/* +/*- * gloadavg.c - get load average for Linux * Copyright (C) 1993 Thomas Koenig + * + * SPDX-License-Identifier: BSD-2-Clause * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #ifndef __FreeBSD__ #define _POSIX_SOURCE 1 /* System Headers */ #include #else #include #endif /* Local headers */ #include "gloadavg.h" /* Global functions */ void perr(const char *fmt, ...); double gloadavg(void) /* return the current load average as a floating point number, or <0 for * error */ { double result; #ifndef __FreeBSD__ FILE *fp; if((fp=fopen(PROC_DIR "loadavg","r")) == NULL) result = -1.0; else { if(fscanf(fp,"%lf",&result) != 1) result = -1.0; fclose(fp); } #else if (getloadavg(&result, 1) != 1) perr("error in getloadavg"); #endif return result; } Index: head/libexec/atrun/gloadavg.h =================================================================== --- head/libexec/atrun/gloadavg.h (revision 326273) +++ head/libexec/atrun/gloadavg.h (revision 326274) @@ -1,29 +1,31 @@ -/* +/*- * gloadavg.h - header for atrun(8) * Copyright (C) 1993 Thomas Koenig + * + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author(s) may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ double gloadavg(void); #if 0 static char atrun_h_rcsid[] = "$FreeBSD$"; #endif Index: head/libexec/bootpd/tools/bootptest/print-bootp.c =================================================================== --- head/libexec/bootpd/tools/bootptest/print-bootp.c (revision 326273) +++ head/libexec/bootpd/tools/bootptest/print-bootp.c (revision 326274) @@ -1,487 +1,489 @@ -/* +/*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright (c) 1988-1990 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that: * 1. Source code distributions retain the above copyright * notice and this paragraph in its entirety * 2. Distributions including binary code include the above copyright * notice and this paragraph in its entirety in the documentation * or other materials provided with the distribution, and * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * Format and print bootp packets. * * This file was copied from tcpdump-2.1.1 and modified. * There is an e-mail list for tcpdump: * * $FreeBSD$ */ #include #include #include #include #include /* for struct timeval in net/if.h */ #include #include #include #include #include "bootp.h" #include "bootptest.h" /* These decode the vendor data. */ static void rfc1048_print(u_char *bp, int length); static void cmu_print(u_char *bp, int length); static void other_print(u_char *bp, int length); static void dump_hex(u_char *bp, int len); /* * Print bootp requests */ void bootp_print(bp, length, sport, dport) struct bootp *bp; int length; u_short sport, dport; { static char tstr[] = " [|bootp]"; static unsigned char vm_cmu[4] = VM_CMU; static unsigned char vm_rfc1048[4] = VM_RFC1048; u_char *ep; int vdlen; #define TCHECK(var, l) if ((u_char *)&(var) > ep - l) goto trunc /* Note funny sized packets */ if (length != sizeof(struct bootp)) (void) printf(" [len=%d]", length); /* 'ep' points to the end of avaible data. */ ep = (u_char *) snapend; switch (bp->bp_op) { case BOOTREQUEST: /* Usually, a request goes from a client to a server */ if (sport != IPPORT_BOOTPC || dport != IPPORT_BOOTPS) printf(" (request)"); break; case BOOTREPLY: /* Usually, a reply goes from a server to a client */ if (sport != IPPORT_BOOTPS || dport != IPPORT_BOOTPC) printf(" (reply)"); break; default: printf(" bootp-#%d", bp->bp_op); } /* The usual hardware address type is 1 (10Mb Ethernet) */ if (bp->bp_htype != 1) printf(" htype:%d", bp->bp_htype); /* The usual length for 10Mb Ethernet address is 6 bytes */ if (bp->bp_hlen != 6) printf(" hlen:%d", bp->bp_hlen); /* Client's Hardware address */ if (bp->bp_hlen) { struct ether_header *eh; char *e; TCHECK(bp->bp_chaddr[0], 6); eh = (struct ether_header *) packetp; if (bp->bp_op == BOOTREQUEST) e = (char *) ESRC(eh); else if (bp->bp_op == BOOTREPLY) e = (char *) EDST(eh); else e = NULL; if (e == NULL || bcmp((char *) bp->bp_chaddr, e, 6)) dump_hex(bp->bp_chaddr, bp->bp_hlen); } /* Only print interesting fields */ if (bp->bp_hops) printf(" hops:%d", bp->bp_hops); if (bp->bp_xid) printf(" xid:%ld", (long)ntohl(bp->bp_xid)); if (bp->bp_secs) printf(" secs:%d", ntohs(bp->bp_secs)); /* Client's ip address */ TCHECK(bp->bp_ciaddr, sizeof(bp->bp_ciaddr)); if (bp->bp_ciaddr.s_addr) printf(" C:%s", ipaddr_string(&bp->bp_ciaddr)); /* 'your' ip address (bootp client) */ TCHECK(bp->bp_yiaddr, sizeof(bp->bp_yiaddr)); if (bp->bp_yiaddr.s_addr) printf(" Y:%s", ipaddr_string(&bp->bp_yiaddr)); /* Server's ip address */ TCHECK(bp->bp_siaddr, sizeof(bp->bp_siaddr)); if (bp->bp_siaddr.s_addr) printf(" S:%s", ipaddr_string(&bp->bp_siaddr)); /* Gateway's ip address */ TCHECK(bp->bp_giaddr, sizeof(bp->bp_giaddr)); if (bp->bp_giaddr.s_addr) printf(" G:%s", ipaddr_string(&bp->bp_giaddr)); TCHECK(bp->bp_sname[0], sizeof(bp->bp_sname)); if (*bp->bp_sname) { printf(" sname:"); if (printfn(bp->bp_sname, ep)) { fputs(tstr + 1, stdout); return; } } TCHECK(bp->bp_file[0], sizeof(bp->bp_file)); if (*bp->bp_file) { printf(" file:"); if (printfn(bp->bp_file, ep)) { fputs(tstr + 1, stdout); return; } } /* Don't try to decode the vendor buffer unless we're verbose */ if (vflag <= 0) return; vdlen = sizeof(bp->bp_vend); /* Vendor data can extend to the end of the packet. */ if (vdlen < (ep - bp->bp_vend)) vdlen = (ep - bp->bp_vend); TCHECK(bp->bp_vend[0], vdlen); printf(" vend"); if (!bcmp(bp->bp_vend, vm_rfc1048, sizeof(u_int32))) rfc1048_print(bp->bp_vend, vdlen); else if (!bcmp(bp->bp_vend, vm_cmu, sizeof(u_int32))) cmu_print(bp->bp_vend, vdlen); else other_print(bp->bp_vend, vdlen); return; trunc: fputs(tstr, stdout); #undef TCHECK } /* * Option description data follows. * These are described in: RFC-1048, RFC-1395, RFC-1497, RFC-1533 * * The first char of each option string encodes the data format: * ?: unknown * a: ASCII * b: byte (8-bit) * i: inet address * l: int32 * s: short (16-bit) */ char * rfc1048_opts[] = { /* Originally from RFC-1048: */ "?PAD", /* 0: Padding - special, no data. */ "iSM", /* 1: subnet mask (RFC950)*/ "lTZ", /* 2: time offset, seconds from UTC */ "iGW", /* 3: gateways (or routers) */ "iTS", /* 4: time servers (RFC868) */ "iINS", /* 5: IEN name servers (IEN116) */ "iDNS", /* 6: domain name servers (RFC1035)(1034?) */ "iLOG", /* 7: MIT log servers */ "iCS", /* 8: cookie servers (RFC865) */ "iLPR", /* 9: lpr server (RFC1179) */ "iIPS", /* 10: impress servers (Imagen) */ "iRLP", /* 11: resource location servers (RFC887) */ "aHN", /* 12: host name (ASCII) */ "sBFS", /* 13: boot file size (in 512 byte blocks) */ /* Added by RFC-1395: */ "aDUMP", /* 14: Merit Dump File */ "aDNAM", /* 15: Domain Name (for DNS) */ "iSWAP", /* 16: Swap Server */ "aROOT", /* 17: Root Path */ /* Added by RFC-1497: */ "aEXTF", /* 18: Extensions Path (more options) */ /* Added by RFC-1533: (many, many options...) */ #if 1 /* These might not be worth recognizing by name. */ /* IP Layer Parameters, per-host (RFC-1533, sect. 4) */ "bIP-forward", /* 19: IP Forwarding flag */ "bIP-srcroute", /* 20: IP Source Routing Enable flag */ "iIP-filters", /* 21: IP Policy Filter (addr pairs) */ "sIP-maxudp", /* 22: IP Max-UDP reassembly size */ "bIP-ttlive", /* 23: IP Time to Live */ "lIP-pmtuage", /* 24: IP Path MTU aging timeout */ "sIP-pmtutab", /* 25: IP Path MTU plateau table */ /* IP parameters, per-interface (RFC-1533, sect. 5) */ "sIP-mtu-sz", /* 26: IP MTU size */ "bIP-mtu-sl", /* 27: IP MTU all subnets local */ "bIP-bcast1", /* 28: IP Broadcast Addr ones flag */ "bIP-mask-d", /* 29: IP do mask discovery */ "bIP-mask-s", /* 30: IP do mask supplier */ "bIP-rt-dsc", /* 31: IP do router discovery */ "iIP-rt-sa", /* 32: IP router solicitation addr */ "iIP-routes", /* 33: IP static routes (dst,router) */ /* Link Layer parameters, per-interface (RFC-1533, sect. 6) */ "bLL-trailer", /* 34: do tralier encapsulation */ "lLL-arp-tmo", /* 35: ARP cache timeout */ "bLL-ether2", /* 36: Ethernet version 2 (IEEE 802.3) */ /* TCP parameters (RFC-1533, sect. 7) */ "bTCP-def-ttl", /* 37: default time to live */ "lTCP-KA-tmo", /* 38: keepalive time interval */ "bTCP-KA-junk", /* 39: keepalive sends extra junk */ /* Application and Service Parameters (RFC-1533, sect. 8) */ "aNISDOM", /* 40: NIS Domain (Sun YP) */ "iNISSRV", /* 41: NIS Servers */ "iNTPSRV", /* 42: NTP (time) Servers (RFC 1129) */ "?VSINFO", /* 43: Vendor Specific Info (encapsulated) */ "iNBiosNS", /* 44: NetBIOS Name Server (RFC-1001,1..2) */ "iNBiosDD", /* 45: NetBIOS Datagram Dist. Server. */ "bNBiosNT", /* 46: NetBIOS Note Type */ "?NBiosS", /* 47: NetBIOS Scope */ "iXW-FS", /* 48: X Window System Font Servers */ "iXW-DM", /* 49: X Window System Display Managers */ /* DHCP extensions (RFC-1533, sect. 9) */ #endif }; #define KNOWN_OPTIONS (sizeof(rfc1048_opts) / sizeof(rfc1048_opts[0])) static void rfc1048_print(bp, length) u_char *bp; int length; { u_char tag; u_char *ep; int len; u_int32 ul; u_short us; struct in_addr ia; char *optstr; printf("-rfc1395"); /* Step over magic cookie */ bp += sizeof(int32); /* Setup end pointer */ ep = bp + length; while (bp < ep) { tag = *bp++; /* Check for tags with no data first. */ if (tag == TAG_PAD) continue; if (tag == TAG_END) return; if (tag < KNOWN_OPTIONS) { optstr = rfc1048_opts[tag]; printf(" %s:", optstr + 1); } else { printf(" T%d:", tag); optstr = "?"; } /* Now scan the length byte. */ len = *bp++; if (bp + len > ep) { /* truncated option */ printf(" |(%d>%td)", len, ep - bp); return; } /* Print the option value(s). */ switch (optstr[0]) { case 'a': /* ASCII string */ printfn(bp, bp + len); bp += len; len = 0; break; case 's': /* Word formats */ while (len >= 2) { bcopy((char *) bp, (char *) &us, 2); printf("%d", ntohs(us)); bp += 2; len -= 2; if (len) printf(","); } if (len) printf("(junk=%d)", len); break; case 'l': /* Long words */ while (len >= 4) { bcopy((char *) bp, (char *) &ul, 4); printf("%ld", (long)ntohl(ul)); bp += 4; len -= 4; if (len) printf(","); } if (len) printf("(junk=%d)", len); break; case 'i': /* INET addresses */ while (len >= 4) { bcopy((char *) bp, (char *) &ia, 4); printf("%s", ipaddr_string(&ia)); bp += 4; len -= 4; if (len) printf(","); } if (len) printf("(junk=%d)", len); break; case 'b': default: break; } /* switch */ /* Print as characters, if appropriate. */ if (len) { dump_hex(bp, len); if (isascii(*bp) && isprint(*bp)) { printf("("); printfn(bp, bp + len); printf(")"); } bp += len; len = 0; } } /* while bp < ep */ } static void cmu_print(bp, length) u_char *bp; int length; { struct cmu_vend *v; printf("-cmu"); v = (struct cmu_vend *) bp; if (length < sizeof(*v)) { printf(" |L=%d", length); return; } /* Subnet mask */ if (v->v_flags & VF_SMASK) { printf(" SM:%s", ipaddr_string(&v->v_smask)); } /* Default gateway */ if (v->v_dgate.s_addr) printf(" GW:%s", ipaddr_string(&v->v_dgate)); /* Domain name servers */ if (v->v_dns1.s_addr) printf(" DNS1:%s", ipaddr_string(&v->v_dns1)); if (v->v_dns2.s_addr) printf(" DNS2:%s", ipaddr_string(&v->v_dns2)); /* IEN-116 name servers */ if (v->v_ins1.s_addr) printf(" INS1:%s", ipaddr_string(&v->v_ins1)); if (v->v_ins2.s_addr) printf(" INS2:%s", ipaddr_string(&v->v_ins2)); /* Time servers */ if (v->v_ts1.s_addr) printf(" TS1:%s", ipaddr_string(&v->v_ts1)); if (v->v_ts2.s_addr) printf(" TS2:%s", ipaddr_string(&v->v_ts2)); } /* * Print out arbitrary, unknown vendor data. */ static void other_print(bp, length) u_char *bp; int length; { u_char *ep; /* end pointer */ u_char *zp; /* points one past last non-zero byte */ /* Setup end pointer */ ep = bp + length; /* Find the last non-zero byte. */ for (zp = ep; zp > bp; zp--) { if (zp[-1] != 0) break; } /* Print the all-zero case in a compact representation. */ if (zp == bp) { printf("-all-zero"); return; } printf("-unknown"); /* Are there enough trailing zeros to make "00..." worthwhile? */ if (zp + 2 > ep) zp = ep; /* print them all normally */ /* Now just print all the non-zero data. */ while (bp < zp) { printf(".%02X", *bp); bp++; } if (zp < ep) printf(".00..."); return; } static void dump_hex(bp, len) u_char *bp; int len; { while (len > 0) { printf("%02X", *bp); bp++; len--; if (len) printf("."); } } /* * Local Variables: * tab-width: 4 * c-indent-level: 4 * c-argdecl-indent: 4 * c-continued-statement-offset: 4 * c-continued-brace-offset: -4 * c-label-offset: -4 * c-brace-offset: 0 * End: */ Index: head/libexec/pppoed/pppoed.c =================================================================== --- head/libexec/pppoed/pppoed.c (revision 326273) +++ head/libexec/pppoed/pppoed.c (revision 326274) @@ -1,692 +1,694 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1999-2001 Brian Somers * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef NOKLDLOAD #include #include #endif #include #include #include #include #include #define DEFAULT_EXEC_PREFIX "exec /usr/sbin/ppp -direct " #define HISMACADDR "HISMACADDR" #define SESSION_ID "SESSION_ID" static void nglogx(const char *, ...) __printflike(1, 2); static int ReceivedSignal; static int usage(const char *prog) { fprintf(stderr, "usage: %s [-Fd] [-P pidfile] [-a name] [-e exec | -l label]" " [-n ngdebug] [-p provider] interface\n", prog); return EX_USAGE; } static void Farewell(int sig) { ReceivedSignal = sig; } static int ConfigureNode(const char *prog, const char *iface, const char *provider, int cs, int ds, int debug, struct ngm_connect *ngc) { /* * We're going to do this with the passed `ds' & `cs' descriptors: * * .---------. * | ether | * | | * `---------' * (orphan) ds cs * | | | * | | | * (ethernet) | | * .---------. .-----------. * | pppoe | | socket | * | |(pppoe-)<---->(pppoe-)| | * `--------- `-----------' * (exec-) * ^ .-----------. .-------------. * | | socket | | ppp -direct | * `--->(exec-)| |--fd--| provider | * `-----------' `-------------' * * where there are potentially many ppp processes running off of the * same PPPoE node. * The exec- hook isn't made 'till we Spawn(). */ char *epath, *spath; struct ngpppoe_init_data *data; const struct hooklist *hlist; const struct nodeinfo *ninfo; const struct linkinfo *nlink; struct ngm_mkpeer mkp; struct ng_mesg *resp; u_char rbuf[2048]; int f, plen; /* * Ask for a list of hooks attached to the "ether" node. This node should * magically exist as a way of hooking stuff onto an ethernet device */ epath = (char *)alloca(strlen(iface) + 2); sprintf(epath, "%s:", iface); if (debug) fprintf(stderr, "Sending NGM_LISTHOOKS to %s\n", epath); if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_LISTHOOKS, NULL, 0) < 0) { if (errno == ENOENT) fprintf(stderr, "%s Cannot send a netgraph message: Invalid interface\n", epath); else fprintf(stderr, "%s Cannot send a netgraph message: %s\n", epath, strerror(errno)); return EX_UNAVAILABLE; } /* Get our list back */ resp = (struct ng_mesg *)rbuf; if (NgRecvMsg(cs, resp, sizeof rbuf, NULL) <= 0) { perror("Cannot get netgraph response"); return EX_UNAVAILABLE; } hlist = (const struct hooklist *)resp->data; ninfo = &hlist->nodeinfo; if (debug) fprintf(stderr, "Got reply from id [%x]: Type %s with %d hooks\n", ninfo->id, ninfo->type, ninfo->hooks); /* Make sure we've got the right type of node */ if (strncmp(ninfo->type, NG_ETHER_NODE_TYPE, sizeof NG_ETHER_NODE_TYPE - 1)) { fprintf(stderr, "%s Unexpected node type ``%s'' (wanted ``" NG_ETHER_NODE_TYPE "'')\n", epath, ninfo->type); return EX_DATAERR; } /* look for a hook already attached. */ for (f = 0; f < ninfo->hooks; f++) { nlink = &hlist->link[f]; if (debug) fprintf(stderr, " Got [%x]:%s -> [%x]:%s\n", ninfo->id, nlink->ourhook, nlink->nodeinfo.id, nlink->peerhook); if (!strcmp(nlink->ourhook, NG_ETHER_HOOK_ORPHAN) || !strcmp(nlink->ourhook, NG_ETHER_HOOK_DIVERT)) { /* * Something is using the data coming out of this `ether' node. * If it's a PPPoE node, we use that node, otherwise we complain that * someone else is using the node. */ if (strcmp(nlink->nodeinfo.type, NG_PPPOE_NODE_TYPE)) { fprintf(stderr, "%s Node type %s is currently active\n", epath, nlink->nodeinfo.type); return EX_UNAVAILABLE; } break; } } if (f == ninfo->hooks) { /* * Create a new PPPoE node connected to the `ether' node using * the magic `orphan' and `ethernet' hooks */ snprintf(mkp.type, sizeof mkp.type, "%s", NG_PPPOE_NODE_TYPE); snprintf(mkp.ourhook, sizeof mkp.ourhook, "%s", NG_ETHER_HOOK_ORPHAN); snprintf(mkp.peerhook, sizeof mkp.peerhook, "%s", NG_PPPOE_HOOK_ETHERNET); if (debug) fprintf(stderr, "Send MKPEER: %s%s -> [type %s]:%s\n", epath, mkp.ourhook, mkp.type, mkp.peerhook); if (NgSendMsg(cs, epath, NGM_GENERIC_COOKIE, NGM_MKPEER, &mkp, sizeof mkp) < 0) { fprintf(stderr, "%s Cannot create a peer PPPoE node: %s\n", epath, strerror(errno)); return EX_OSERR; } } /* Connect the PPPoE node to our socket node. */ snprintf(ngc->path, sizeof ngc->path, "%s%s", epath, NG_ETHER_HOOK_ORPHAN); snprintf(ngc->ourhook, sizeof ngc->ourhook, "pppoe-%ld", (long)getpid()); memcpy(ngc->peerhook, ngc->ourhook, sizeof ngc->peerhook); if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, ngc, sizeof *ngc) < 0) { perror("Cannot CONNECT PPPoE and socket nodes"); return EX_OSERR; } plen = strlen(provider); data = (struct ngpppoe_init_data *)alloca(sizeof *data + plen); snprintf(data->hook, sizeof data->hook, "%s", ngc->peerhook); memcpy(data->data, provider, plen); data->data_len = plen; spath = (char *)alloca(strlen(ngc->peerhook) + 3); strcpy(spath, ".:"); strcpy(spath + 2, ngc->ourhook); if (debug) { if (provider) fprintf(stderr, "Sending PPPOE_LISTEN to %s, provider %s\n", spath, provider); else fprintf(stderr, "Sending PPPOE_LISTEN to %s\n", spath); } if (NgSendMsg(cs, spath, NGM_PPPOE_COOKIE, NGM_PPPOE_LISTEN, data, sizeof *data + plen) == -1) { fprintf(stderr, "%s: Cannot LISTEN on netgraph node: %s\n", spath, strerror(errno)); return EX_OSERR; } return 0; } static void Spawn(const char *prog, const char *acname, const char *provider, const char *exec, struct ngm_connect ngc, int cs, int ds, void *request, int sz, int debug) { char msgbuf[sizeof(struct ng_mesg) + sizeof(struct ngpppoe_sts)]; struct ng_mesg *rep = (struct ng_mesg *)msgbuf; struct ngpppoe_sts *sts = (struct ngpppoe_sts *)(msgbuf + sizeof *rep); struct ngpppoe_init_data *data; char env[18], unknown[14], sessionid[5], *path; unsigned char *macaddr; const char *msg; int ret, slen; switch ((ret = fork())) { case -1: syslog(LOG_ERR, "fork: %m"); break; case 0: switch (fork()) { case 0: break; case -1: _exit(errno); default: _exit(0); } close(cs); close(ds); /* Create a new socket node */ if (debug) syslog(LOG_INFO, "Creating a new socket node"); if (NgMkSockNode(NULL, &cs, &ds) == -1) { syslog(LOG_ERR, "Cannot create netgraph socket node: %m"); _exit(EX_CANTCREAT); } /* Connect the PPPoE node to our new socket node. */ snprintf(ngc.ourhook, sizeof ngc.ourhook, "exec-%ld", (long)getpid()); memcpy(ngc.peerhook, ngc.ourhook, sizeof ngc.peerhook); if (debug) syslog(LOG_INFO, "Sending CONNECT from .:%s -> %s.%s", ngc.ourhook, ngc.path, ngc.peerhook); if (NgSendMsg(cs, ".:", NGM_GENERIC_COOKIE, NGM_CONNECT, &ngc, sizeof ngc) < 0) { syslog(LOG_ERR, "Cannot CONNECT PPPoE and socket nodes: %m"); _exit(EX_OSERR); } /* * If we tell the socket node not to LINGER, it will go away when * the last hook is removed. */ if (debug) syslog(LOG_INFO, "Sending NGM_SOCK_CMD_NOLINGER to socket"); if (NgSendMsg(cs, ".:", NGM_SOCKET_COOKIE, NGM_SOCK_CMD_NOLINGER, NULL, 0) < 0) { syslog(LOG_ERR, "Cannot send NGM_SOCK_CMD_NOLINGER: %m"); _exit(EX_OSERR); } /* Put the PPPoE node into OFFER mode */ slen = strlen(acname); data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen); snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook); memcpy(data->data, acname, slen); data->data_len = slen; path = (char *)alloca(strlen(ngc.ourhook) + 3); strcpy(path, ".:"); strcpy(path + 2, ngc.ourhook); syslog(LOG_INFO, "Offering to %s as access concentrator %s", path, acname); if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_OFFER, data, sizeof *data + slen) == -1) { syslog(LOG_INFO, "%s: Cannot OFFER on netgraph node: %m", path); _exit(EX_OSERR); } /* If we have a provider code, set it */ if (provider) { slen = strlen(provider); data = (struct ngpppoe_init_data *)alloca(sizeof *data + slen); snprintf(data->hook, sizeof data->hook, "%s", ngc.ourhook); memcpy(data->data, provider, slen); data->data_len = slen; syslog(LOG_INFO, "adding to %s as offered service %s", path, acname); if (NgSendMsg(cs, path, NGM_PPPOE_COOKIE, NGM_PPPOE_SERVICE, data, sizeof *data + slen) == -1) { syslog(LOG_INFO, "%s: Cannot add service on netgraph node: %m", path); _exit(EX_OSERR); } } /* Put the peer's MAC address in the environment */ if (sz >= sizeof(struct ether_header)) { macaddr = ((struct ether_header *)request)->ether_shost; snprintf(env, sizeof(env), "%x:%x:%x:%x:%x:%x", macaddr[0], macaddr[1], macaddr[2], macaddr[3], macaddr[4], macaddr[5]); if (setenv(HISMACADDR, env, 1) != 0) syslog(LOG_INFO, "setenv: cannot set %s: %m", HISMACADDR); } /* And send our request data to the waiting node */ if (debug) syslog(LOG_INFO, "Sending original request to %s (%d bytes)", path, sz); if (NgSendData(ds, ngc.ourhook, request, sz) == -1) { syslog(LOG_ERR, "Cannot send original request to %s: %m", path); _exit(EX_OSERR); } /* Then wait for a success indication */ if (debug) syslog(LOG_INFO, "Waiting for a SUCCESS reply %s", path); do { if ((ret = NgRecvMsg(cs, rep, sizeof msgbuf, NULL)) < 0) { syslog(LOG_ERR, "%s: Cannot receive a message: %m", path); _exit(EX_OSERR); } if (ret == 0) { /* The socket has been closed */ syslog(LOG_INFO, "%s: Client timed out", path); _exit(EX_TEMPFAIL); } if (rep->header.version != NG_VERSION) { syslog(LOG_ERR, "%ld: Unexpected netgraph version, expected %ld", (long)rep->header.version, (long)NG_VERSION); _exit(EX_PROTOCOL); } if (rep->header.typecookie != NGM_PPPOE_COOKIE) { syslog(LOG_INFO, "%ld: Unexpected netgraph cookie, expected %ld", (long)rep->header.typecookie, (long)NGM_PPPOE_COOKIE); continue; } switch (rep->header.cmd) { case NGM_PPPOE_SET_FLAG: msg = "SET_FLAG"; break; case NGM_PPPOE_CONNECT: msg = "CONNECT"; break; case NGM_PPPOE_LISTEN: msg = "LISTEN"; break; case NGM_PPPOE_OFFER: msg = "OFFER"; break; case NGM_PPPOE_SUCCESS: msg = "SUCCESS"; break; case NGM_PPPOE_FAIL: msg = "FAIL"; break; case NGM_PPPOE_CLOSE: msg = "CLOSE"; break; case NGM_PPPOE_GET_STATUS: msg = "GET_STATUS"; break; case NGM_PPPOE_ACNAME: msg = "ACNAME"; if (setenv("ACNAME", sts->hook, 1) != 0) syslog(LOG_WARNING, "setenv: cannot set ACNAME=%s: %m", sts->hook); break; case NGM_PPPOE_SESSIONID: msg = "SESSIONID"; snprintf(sessionid, sizeof sessionid, "%04x", *(u_int16_t *)sts); if (setenv("SESSIONID", sessionid, 1) != 0) syslog(LOG_WARNING, "setenv: cannot set SESSIONID=%s: %m", sessionid); break; default: snprintf(unknown, sizeof unknown, "<%d>", (int)rep->header.cmd); msg = unknown; break; } switch (rep->header.cmd) { case NGM_PPPOE_FAIL: case NGM_PPPOE_CLOSE: syslog(LOG_ERR, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook); _exit(0); } syslog(LOG_INFO, "Received NGM_PPPOE_%s (hook \"%s\")", msg, sts->hook); } while (rep->header.cmd != NGM_PPPOE_SUCCESS); dup2(ds, STDIN_FILENO); dup2(ds, STDOUT_FILENO); close(ds); close(cs); setsid(); syslog(LOG_INFO, "Executing: %s", exec); execlp(_PATH_BSHELL, _PATH_BSHELL, "-c", exec, (char *)NULL); syslog(LOG_ERR, "execlp failed: %m"); _exit(EX_OSFILE); default: wait(&ret); errno = ret; if (errno) syslog(LOG_ERR, "Second fork failed: %m"); break; } } #ifndef NOKLDLOAD static int LoadModules(void) { const char *module[] = { "netgraph", "ng_socket", "ng_ether", "ng_pppoe" }; int f; for (f = 0; f < sizeof module / sizeof *module; f++) if (modfind(module[f]) == -1 && kldload(module[f]) == -1) { fprintf(stderr, "kldload: %s: %s\n", module[f], strerror(errno)); return 0; } return 1; } #endif static void nglog(const char *fmt, ...) { char nfmt[256]; va_list ap; snprintf(nfmt, sizeof nfmt, "%s: %s", fmt, strerror(errno)); va_start(ap, fmt); vsyslog(LOG_INFO, nfmt, ap); va_end(ap); } static void nglogx(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsyslog(LOG_INFO, fmt, ap); va_end(ap); } int main(int argc, char *argv[]) { char hostname[MAXHOSTNAMELEN], *exec, rhook[NG_HOOKSIZ]; unsigned char response[1024]; const char *label, *prog, *provider, *acname; struct ngm_connect ngc; struct sigaction act; int ch, cs, ds, ret, optF, optd, optn, sz, f; const char *pidfile; prog = strrchr(argv[0], '/'); prog = prog ? prog + 1 : argv[0]; pidfile = NULL; exec = NULL; label = NULL; acname = NULL; provider = ""; optF = optd = optn = 0; while ((ch = getopt(argc, argv, "FP:a:de:l:n:p:")) != -1) { switch (ch) { case 'F': optF = 1; break; case 'P': pidfile = optarg; break; case 'a': acname = optarg; break; case 'd': optd = 1; break; case 'e': exec = optarg; break; case 'l': label = optarg; break; case 'n': optn = 1; NgSetDebug(atoi(optarg)); break; case 'p': provider = optarg; break; default: return usage(prog); } } if (optind >= argc || optind + 2 < argc) return usage(prog); if (exec != NULL && label != NULL) return usage(prog); if (exec == NULL) { if (label == NULL) label = provider; if (label == NULL) { fprintf(stderr, "%s: Either a provider, a label or an exec command" " must be given\n", prog); return usage(prog); } exec = (char *)alloca(sizeof DEFAULT_EXEC_PREFIX + strlen(label)); if (exec == NULL) { fprintf(stderr, "%s: Cannot allocate %zu bytes\n", prog, sizeof DEFAULT_EXEC_PREFIX + strlen(label)); return EX_OSERR; } strcpy(exec, DEFAULT_EXEC_PREFIX); strcpy(exec + sizeof DEFAULT_EXEC_PREFIX - 1, label); } if (acname == NULL) { char *dot; if (gethostname(hostname, sizeof hostname)) strcpy(hostname, "localhost"); else if ((dot = strchr(hostname, '.'))) *dot = '\0'; acname = hostname; } #ifndef NOKLDLOAD if (!LoadModules()) return EX_UNAVAILABLE; #endif /* Create a socket node */ if (NgMkSockNode(NULL, &cs, &ds) == -1) { perror("Cannot create netgraph socket node"); return EX_CANTCREAT; } /* Connect it up (and fill in `ngc') */ if ((ret = ConfigureNode(prog, argv[optind], provider, cs, ds, optd, &ngc)) != 0) { close(cs); close(ds); return ret; } if (!optF && daemon(1, 0) == -1) { perror("daemon()"); close(cs); close(ds); return EX_OSERR; } if (pidfile != NULL) { FILE *fp; if ((fp = fopen(pidfile, "w")) == NULL) { perror(pidfile); close(cs); close(ds); return EX_CANTCREAT; } else { fprintf(fp, "%d\n", (int)getpid()); fclose(fp); } } openlog(prog, LOG_PID | (optF ? LOG_PERROR : 0), LOG_DAEMON); if (!optF && optn) NgSetErrLog(nglog, nglogx); memset(&act, '\0', sizeof act); act.sa_handler = Farewell; act.sa_flags = 0; sigemptyset(&act.sa_mask); sigaction(SIGHUP, &act, NULL); sigaction(SIGINT, &act, NULL); sigaction(SIGQUIT, &act, NULL); sigaction(SIGTERM, &act, NULL); while (!ReceivedSignal) { if (*provider) syslog(LOG_INFO, "Listening as provider %s", provider); else syslog(LOG_INFO, "Listening"); switch (sz = NgRecvData(ds, response, sizeof response, rhook)) { case -1: syslog(LOG_INFO, "NgRecvData: %m"); break; case 0: syslog(LOG_INFO, "NgRecvData: socket closed"); break; default: if (optd) { char *dbuf, *ptr; ptr = dbuf = alloca(sz * 2 + 1); for (f = 0; f < sz; f++, ptr += 2) sprintf(ptr, "%02x", (u_char)response[f]); *ptr = '\0'; syslog(LOG_INFO, "Got %d bytes of data: %s", sz, dbuf); } } if (sz <= 0) { ret = EX_UNAVAILABLE; break; } Spawn(prog, acname, provider, exec, ngc, cs, ds, response, sz, optd); } if (pidfile) remove(pidfile); if (ReceivedSignal) { syslog(LOG_INFO, "Received signal %d, exiting", ReceivedSignal); signal(ReceivedSignal, SIG_DFL); raise(ReceivedSignal); /* NOTREACHED */ ret = -ReceivedSignal; } return ret; } Index: head/libexec/rpc.rwalld/rwalld.c =================================================================== --- head/libexec/rpc.rwalld/rwalld.c (revision 326273) +++ head/libexec/rpc.rwalld/rwalld.c (revision 326274) @@ -1,204 +1,206 @@ -/* +/*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright (c) 1993 Christopher G. Demetriou * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef OSF #define WALL_CMD "/usr/sbin/wall" #else #define WALL_CMD "/usr/bin/wall -n" #endif void wallprog_1(struct svc_req *rqstp, SVCXPRT *transp); void possess(void); void killkids(int sig); static void usage(void); int nodaemon = 0; int from_inetd = 1; int main(int argc, char *argv[]) { SVCXPRT *transp; socklen_t salen; int ok; struct sockaddr_storage sa; if (argc == 2 && !strcmp(argv[1], "-n")) nodaemon = 1; if (argc != 1 && !nodaemon) usage(); if (geteuid() == 0) { struct passwd *pep = getpwnam("nobody"); if (pep) setuid(pep->pw_uid); else setuid(getuid()); } /* * See if inetd started us */ salen = sizeof(sa); if (getsockname(0, (struct sockaddr *)&sa, &salen) < 0) { from_inetd = 0; } if (!from_inetd) { if (!nodaemon) possess(); (void)rpcb_unset(WALLPROG, WALLVERS, NULL); } (void)signal(SIGCHLD, killkids); openlog("rpc.rwalld", LOG_CONS|LOG_PID, LOG_DAEMON); /* create and register the service */ if (from_inetd) { transp = svc_tli_create(0, NULL, NULL, 0, 0); if (transp == NULL) { syslog(LOG_ERR, "couldn't create udp service."); exit(1); } ok = svc_reg(transp, WALLPROG, WALLVERS, wallprog_1, NULL); } else ok = svc_create(wallprog_1, WALLPROG, WALLVERS, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (WALLPROG, WALLVERS, %s)", (!from_inetd)?"udp":"(inetd)"); exit(1); } svc_run(); syslog(LOG_ERR, "svc_run returned"); exit(1); } static void usage(void) { fprintf(stderr, "usage: rpc.rwalld [-n]\n"); exit(1); } void possess(void) { daemon(0, 0); } void killkids(int sig __unused) { while(wait4(-1, NULL, WNOHANG, NULL) > 0) ; } void * wallproc_wall_1_svc(wrapstring *s, struct svc_req *rqstp __unused) { static void *dummy = NULL; /* fork, popen wall with special option, and send the message */ if (fork() == 0) { FILE *pfp; pfp = popen(WALL_CMD, "w"); if (pfp != NULL) { fprintf(pfp, "\007\007%s", *s); pclose(pfp); exit(0); } } return(&dummy); } void wallprog_1(struct svc_req *rqstp, SVCXPRT *transp) { union { char *wallproc_wall_1_arg; } argument; char *result; bool_t (*xdr_argument)(), (*xdr_result)(); char *(*local)(); switch (rqstp->rq_proc) { case NULLPROC: (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); goto leave; case WALLPROC_WALL: xdr_argument = xdr_wrapstring; xdr_result = xdr_void; local = (char *(*)()) wallproc_wall_1_svc; break; default: svcerr_noproc(transp); goto leave; } bzero(&argument, sizeof(argument)); if (!svc_getargs(transp, (xdrproc_t)xdr_argument, &argument)) { svcerr_decode(transp); goto leave; } result = (*local)(&argument, rqstp); if (result != NULL && !svc_sendreply(transp, (xdrproc_t)xdr_result, result)) { svcerr_systemerr(transp); } if (!svc_freeargs(transp, (xdrproc_t)xdr_argument, &argument)) { syslog(LOG_ERR, "unable to free arguments"); exit(1); } leave: if (from_inetd) exit(0); } Index: head/libexec/rpc.sprayd/sprayd.c =================================================================== --- head/libexec/rpc.sprayd/sprayd.c (revision 326273) +++ head/libexec/rpc.sprayd/sprayd.c (revision 326274) @@ -1,166 +1,168 @@ -/* +/*- + * SPDX-License-Identifier: BSD-4-Clause + * * Copyright (c) 1994 Christos Zoulas * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Christos Zoulas. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef lint static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include static void spray_service(struct svc_req *, SVCXPRT *); static int from_inetd = 1; #define timersub(tvp, uvp, vvp) \ do { \ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \ if ((vvp)->tv_usec < 0) { \ (vvp)->tv_sec--; \ (vvp)->tv_usec += 1000000; \ } \ } while (0) #define TIMEOUT 120 void cleanup(int sig __unused) { (void) rpcb_unset(SPRAYPROG, SPRAYVERS, NULL); exit(0); } void die(int sig __unused) { exit(0); } int main(int argc, char *argv[]) { SVCXPRT *transp; int ok; struct sockaddr_storage from; socklen_t fromlen; /* * See if inetd started us */ fromlen = sizeof(from); if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) { from_inetd = 0; } if (!from_inetd) { daemon(0, 0); (void) rpcb_unset(SPRAYPROG, SPRAYVERS, NULL); (void) signal(SIGINT, cleanup); (void) signal(SIGTERM, cleanup); (void) signal(SIGHUP, cleanup); } else { (void) signal(SIGALRM, die); alarm(TIMEOUT); } openlog("rpc.sprayd", LOG_CONS|LOG_PID, LOG_DAEMON); if (from_inetd) { transp = svc_tli_create(0, NULL, NULL, 0, 0); if (transp == NULL) { syslog(LOG_ERR, "cannot create udp service."); exit(1); } ok = svc_reg(transp, SPRAYPROG, SPRAYVERS, spray_service, NULL); } else ok = svc_create(spray_service, SPRAYPROG, SPRAYVERS, "udp"); if (!ok) { syslog(LOG_ERR, "unable to register (SPRAYPROG, SPRAYVERS, %s)", (!from_inetd)?"udp":"(inetd)"); return 1; } svc_run(); syslog(LOG_ERR, "svc_run returned"); return 1; } static void spray_service(struct svc_req *rqstp, SVCXPRT *transp) { static spraycumul scum; static struct timeval clear, get; switch (rqstp->rq_proc) { case SPRAYPROC_CLEAR: scum.counter = 0; (void) gettimeofday(&clear, 0); /*FALLTHROUGH*/ case NULLPROC: (void)svc_sendreply(transp, (xdrproc_t)xdr_void, NULL); return; case SPRAYPROC_SPRAY: scum.counter++; return; case SPRAYPROC_GET: (void) gettimeofday(&get, 0); timersub(&get, &clear, &get); scum.clock.sec = get.tv_sec; scum.clock.usec = get.tv_usec; break; default: svcerr_noproc(transp); return; } if (!svc_sendreply(transp, (xdrproc_t)xdr_spraycumul, &scum)) { svcerr_systemerr(transp); syslog(LOG_ERR, "bad svc_sendreply"); } } Index: head/libexec/rtld-aout/shlib.c =================================================================== --- head/libexec/rtld-aout/shlib.c (revision 326273) +++ head/libexec/rtld-aout/shlib.c (revision 326274) @@ -1,325 +1,327 @@ -/* +/*- + * SPDX-License-Identifier: BSD-4-Clause + * * Copyright (c) 1993 Paul Kranenburg * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by Paul Kranenburg. * 4. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "shlib.h" #include "support.h" /* * Standard directories to search for files specified by -l. */ #ifndef STANDARD_SEARCH_DIRS #define STANDARD_SEARCH_DIRS "/usr/lib/aout" #endif /* * Actual vector of library search directories, * including `-L'ed and LD_LIBRARY_PATH spec'd ones. */ char **search_dirs; int n_search_dirs; static const char *standard_search_dirs[] = { STANDARD_SEARCH_DIRS }; void add_search_dir(const char *name) { int n; for (n = 0; n < n_search_dirs; n++) if (strcmp(search_dirs[n], name) == 0) return; n_search_dirs++; search_dirs = (char **) xrealloc(search_dirs, n_search_dirs * sizeof search_dirs[0]); search_dirs[n_search_dirs - 1] = strdup(name); } void add_search_path(char *path) { register char *cp, *dup; if (path == NULL) return; /* Add search directories from `path' */ path = dup = strdup(path); while ((cp = strsep(&path, ":")) != NULL) add_search_dir(cp); free(dup); } void std_search_path(void) { int i, n; /* Append standard search directories */ n = sizeof standard_search_dirs / sizeof standard_search_dirs[0]; for (i = 0; i < n; i++) add_search_dir(standard_search_dirs[i]); } /* * Return true if CP points to a valid dewey number. * Decode and leave the result in the array DEWEY. * Return the number of decoded entries in DEWEY. */ int getdewey(int dewey[], char *cp) { int i, n; for (n = 0, i = 0; i < MAXDEWEY; i++) { if (*cp == '\0') break; if (*cp == '.') cp++; if (!isdigit(*cp)) return 0; dewey[n++] = strtol(cp, &cp, 10); } return n; } /* * Compare two dewey arrays. * Return -1 if `d1' represents a smaller value than `d2'. * Return 1 if `d1' represents a greater value than `d2'. * Return 0 if equal. */ int cmpndewey(int d1[], int n1, int d2[], int n2) { register int i; for (i = 0; i < n1 && i < n2; i++) { if (d1[i] < d2[i]) return -1; if (d1[i] > d2[i]) return 1; } if (n1 == n2) return 0; if (i == n1) return -1; if (i == n2) return 1; errx(1, "cmpndewey: can't happen"); return 0; } /* * Search directories for a shared library matching the given * major and minor version numbers. See search_lib_dir() below for * the detailed matching rules. * * As soon as a directory with an acceptable match is found, the search * terminates. Subsequent directories are not searched for a better * match. This is in conformance with the SunOS searching rules. Also, * it avoids a lot of directory searches that are virtually guaranteed to * be fruitless. * * The return value is a full pathname to the matching library. The * string is dynamically allocated. If no matching library is found, the * function returns NULL. */ char * findshlib(char *name, int *majorp, int *minorp, int do_dot_a) { int i; for (i = 0; i < n_search_dirs; i++) { char *path; path = search_lib_dir(search_dirs[i], name, majorp, minorp, do_dot_a); if(path != NULL) return path; } return NULL; } /* * Search library directories for a file with the given name. The * return value is a full pathname to the matching file. The string * is dynamically allocated. If no matching file is found, the function * returns NULL. */ char * find_lib_file(const char *name) { int i; for (i = 0; i < n_search_dirs; i++) { char *path = concat(search_dirs[i], "/", name); struct stat sb; if (lstat(path, &sb) != -1) /* We found it */ return path; free(path); } return NULL; } /* * Search a given directory for a library (preferably shared) satisfying * the given criteria. * * The matching rules are as follows: * * if(*majorp == -1) * find the library with the highest major version; * else * insist on a major version identical to *majorp; * * Always find the library with the highest minor version; * if(*minorp != -1) * insist on a minor version >= *minorp; * * It is invalid to specify a specific minor number while wildcarding * the major number. * * The actual major and minor numbers found are returned via the pointer * arguments. * * A suitable shared library is always preferred over a static (.a) library. * If do_dot_a is false, then a static library will not be accepted in * any case. * * The return value is a full pathname to the matching library. The * string is dynamically allocated. If no matching library is found, the * function returns NULL. */ char * search_lib_dir(char *dir, char *name, int *majorp, int *minorp, int do_dot_a) { size_t namelen; DIR *dd; struct dirent *dp; int best_dewey[MAXDEWEY]; int best_ndewey; char dot_a_name[MAXNAMLEN+1]; char dot_so_name[MAXNAMLEN+1]; if((dd = opendir(dir)) == NULL) return NULL; namelen = strlen(name); best_ndewey = 0; dot_a_name[0] = '\0'; dot_so_name[0] = '\0'; while((dp = readdir(dd)) != NULL) { char *extension; if(strlen(dp->d_name) < 3 + namelen + 2 || /* lib+xxx+.a */ strncmp(dp->d_name, "lib", 3) != 0 || strncmp(dp->d_name + 3, name, namelen) != 0 || dp->d_name[3+namelen] != '.') continue; extension = dp->d_name + 3 + namelen + 1; /* a or so.* */ if(strncmp(extension, "so.", 3) == 0) { int cur_dewey[MAXDEWEY]; int cur_ndewey; cur_ndewey = getdewey(cur_dewey, extension+3); if(cur_ndewey < 2) /* Too few version numbers */ continue; if(*majorp != -1) { /* Need exact match on major */ if(cur_dewey[0] != *majorp) continue; if(*minorp != -1) { /* Need minor >= minimum */ if(cur_dewey[1] < *minorp) continue; } } if(cmpndewey(cur_dewey, cur_ndewey, best_dewey, best_ndewey) <= 0) /* No better than prior match */ continue; /* We found a better match */ strcpy(dot_so_name, dp->d_name); bcopy(cur_dewey, best_dewey, cur_ndewey * sizeof best_dewey[0]); best_ndewey = cur_ndewey; } else if(do_dot_a && strcmp(extension, "a") == 0) strcpy(dot_a_name, dp->d_name); } closedir(dd); if(dot_so_name[0] != '\0') { *majorp = best_dewey[0]; *minorp = best_dewey[1]; return concat(dir, "/", dot_so_name); } if(dot_a_name[0] != '\0') return concat(dir, "/", dot_a_name); return NULL; } Index: head/libexec/rtld-aout/shlib.h =================================================================== --- head/libexec/rtld-aout/shlib.h (revision 326273) +++ head/libexec/rtld-aout/shlib.h (revision 326274) @@ -1,43 +1,45 @@ /*- * Copyright (C) 1996 * Peter Wemm. All rights reserved. * + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *- * $FreeBSD$ */ /* * prototypes for shlib.c. Big deal. */ extern char **search_dirs; extern int n_search_dirs; void add_search_dir __P((const char *)); void add_search_path __P((char *)); void std_search_path __P((void)); int getdewey __P((int[], char *)); int cmpndewey __P((int[], int, int[], int)); char *findshlib __P((char *, int *, int *, int)); char *find_lib_file __P((const char *)); char *search_lib_dir __P((char *, char *, int *, int *, int)); Index: head/libexec/rtld-aout/support.c =================================================================== --- head/libexec/rtld-aout/support.c (revision 326273) +++ head/libexec/rtld-aout/support.c (revision 326274) @@ -1,82 +1,84 @@ -/* +/*- * Generic "support" routines to replace those obtained from libiberty for ld. * * I've collected these from random bits of (published) code I've written * over the years, not that they are a big deal. peter@freebsd.org - *- + * + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 1996 * Peter Wemm. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *- * $FreeBSD$ */ #include #include #include #include #include "support.h" char * concat(const char *s1, const char *s2, const char *s3) { int len = 1; char *s; if (s1) len += strlen(s1); if (s2) len += strlen(s2); if (s3) len += strlen(s3); s = xmalloc(len); s[0] = '\0'; if (s1) strcat(s, s1); if (s2) strcat(s, s2); if (s3) strcat(s, s3); return s; } void * xmalloc(size_t n) { char *p = malloc(n); if (p == NULL) errx(1, "Could not allocate memory"); return p; } void * xrealloc(void *p, size_t n) { p = realloc(p, n); if (p == NULL) errx(1, "Could not allocate memory"); return p; } Index: head/libexec/rtld-aout/support.h =================================================================== --- head/libexec/rtld-aout/support.h (revision 326273) +++ head/libexec/rtld-aout/support.h (revision 326274) @@ -1,35 +1,37 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 1996 * Peter Wemm. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. *- * $FreeBSD$ */ /* * prototypes for support.c. Big deal. */ void *xmalloc __P((size_t)); void *xrealloc __P((void *, size_t)); char *concat __P((const char *, const char *, const char *)); Index: head/libexec/rtld-elf/amd64/reloc.c =================================================================== --- head/libexec/rtld-elf/amd64/reloc.c (revision 326273) +++ head/libexec/rtld-elf/amd64/reloc.c (revision 326274) @@ -1,507 +1,509 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996, 1997, 1998, 1999 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * Dynamic linker for ELF. * * John Polstra . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "rtld.h" #include "rtld_tls.h" /* * Process the special R_X86_64_COPY relocations in the main program. These * copy data from a shared object into a region in the main program's BSS * segment. * * Returns 0 on success, -1 on failure. */ int do_copy_relocations(Obj_Entry *dstobj) { const Elf_Rela *relalim; const Elf_Rela *rela; assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela + dstobj->relasize); for (rela = dstobj->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_X86_64_COPY) { void *dstaddr; const Elf_Sym *dstsym; const char *name; size_t size; const void *srcaddr; const Elf_Sym *srcsym; const Obj_Entry *srcobj, *defobj; SymLook req; int res; dstaddr = (void *) (dstobj->relocbase + rela->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); name = dstobj->strtab + dstsym->st_name; size = dstsym->st_size; symlook_init(&req, name); req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); req.flags = SYMLOOK_EARLY; for (srcobj = globallist_next(dstobj); srcobj != NULL; srcobj = globallist_next(srcobj)) { res = symlook_obj(&req, srcobj); if (res == 0) { srcsym = req.sym_out; defobj = req.defobj_out; break; } } if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\" referenced from COPY" " relocation in %s", name, dstobj->path); return -1; } srcaddr = (const void *) (defobj->relocbase + srcsym->st_value); memcpy(dstaddr, srcaddr, size); } } return 0; } /* Initialize the special GOT entries. */ void init_pltgot(Obj_Entry *obj) { if (obj->pltgot != NULL) { obj->pltgot[1] = (Elf_Addr) obj; obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; } } /* Process the non-PLT relocations. */ int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; SymCache *cache; const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr *where, symval; Elf32_Addr *where32; int r; r = -1; /* * The dynamic loader may be called from a thread, we have * limited amounts of stack available so we cannot use alloca(). */ if (obj != obj_rtld) { cache = calloc(obj->dynsymcount, sizeof(SymCache)); /* No need to check for NULL here */ } else cache = NULL; relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); for (rela = obj->rela; rela < relalim; rela++) { /* * First, resolve symbol for relocations which * reference symbols. */ switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_64: case R_X86_64_PC32: case R_X86_64_GLOB_DAT: case R_X86_64_TPOFF64: case R_X86_64_TPOFF32: case R_X86_64_DTPMOD64: case R_X86_64_DTPOFF64: case R_X86_64_DTPOFF32: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) goto done; /* * If symbol is IFUNC, only perform relocation * when caller allowed it by passing * SYMLOOK_IFUNC flag. Skip the relocations * otherwise. * * Also error out in case IFUNC relocations * are specified for TLS, which cannot be * usefully interpreted. */ if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_64: case R_X86_64_PC32: case R_X86_64_GLOB_DAT: if ((flags & SYMLOOK_IFUNC) == 0) { obj->non_plt_gnu_ifunc = true; continue; } symval = (Elf_Addr)rtld_resolve_ifunc( defobj, def); break; case R_X86_64_TPOFF64: case R_X86_64_TPOFF32: case R_X86_64_DTPMOD64: case R_X86_64_DTPOFF64: case R_X86_64_DTPOFF32: _rtld_error("%s: IFUNC for TLS reloc", obj->path); goto done; } } else { if ((flags & SYMLOOK_IFUNC) != 0) continue; symval = (Elf_Addr)defobj->relocbase + def->st_value; } break; default: if ((flags & SYMLOOK_IFUNC) != 0) continue; break; } where = (Elf_Addr *)(obj->relocbase + rela->r_offset); where32 = (Elf32_Addr *)where; switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_NONE: break; case R_X86_64_64: *where = symval + rela->r_addend; break; case R_X86_64_PC32: /* * I don't think the dynamic linker should * ever see this type of relocation. But the * binutils-2.6 tools sometimes generate it. */ *where32 = (Elf32_Addr)(unsigned long)(symval + rela->r_addend - (Elf_Addr)where); break; /* missing: R_X86_64_GOT32 R_X86_64_PLT32 */ case R_X86_64_COPY: /* * These are deferred until all other relocations have * been done. All we do here is make sure that the COPY * relocation is not in a shared library. They are * allowed only in executable files. */ if (!obj->mainprog) { _rtld_error("%s: Unexpected R_X86_64_COPY " "relocation in shared library", obj->path); goto done; } break; case R_X86_64_GLOB_DAT: *where = symval; break; case R_X86_64_TPOFF64: /* * We lazily allocate offsets for static TLS * as we see the first relocation that * references the TLS block. This allows us to * support (small amounts of) static TLS in * dynamically loaded modules. If we run out * of space, we generate an error. */ if (!defobj->tls_done) { if (!allocate_tls_offset((Obj_Entry*) defobj)) { _rtld_error("%s: No space available " "for static Thread Local Storage", obj->path); goto done; } } *where = (Elf_Addr)(def->st_value - defobj->tlsoffset + rela->r_addend); break; case R_X86_64_TPOFF32: /* * We lazily allocate offsets for static TLS * as we see the first relocation that * references the TLS block. This allows us to * support (small amounts of) static TLS in * dynamically loaded modules. If we run out * of space, we generate an error. */ if (!defobj->tls_done) { if (!allocate_tls_offset((Obj_Entry*) defobj)) { _rtld_error("%s: No space available " "for static Thread Local Storage", obj->path); goto done; } } *where32 = (Elf32_Addr)(def->st_value - defobj->tlsoffset + rela->r_addend); break; case R_X86_64_DTPMOD64: *where += (Elf_Addr)defobj->tlsindex; break; case R_X86_64_DTPOFF64: *where += (Elf_Addr)(def->st_value + rela->r_addend); break; case R_X86_64_DTPOFF32: *where32 += (Elf32_Addr)(def->st_value + rela->r_addend); break; case R_X86_64_RELATIVE: *where = (Elf_Addr)(obj->relocbase + rela->r_addend); break; /* * missing: * R_X86_64_GOTPCREL, R_X86_64_32, R_X86_64_32S, R_X86_64_16, * R_X86_64_PC16, R_X86_64_8, R_X86_64_PC8 */ default: _rtld_error("%s: Unsupported relocation type %u" " in non-PLT relocations\n", obj->path, (unsigned int)ELF_R_TYPE(rela->r_info)); goto done; } } r = 0; done: free(cache); return (r); } /* Process the PLT relocations. */ int reloc_plt(Obj_Entry *obj) { const Elf_Rela *relalim; const Elf_Rela *rela; relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { Elf_Addr *where; switch(ELF_R_TYPE(rela->r_info)) { case R_X86_64_JMP_SLOT: /* Relocate the GOT slot pointing into the PLT. */ where = (Elf_Addr *)(obj->relocbase + rela->r_offset); *where += (Elf_Addr)obj->relocbase; break; case R_X86_64_IRELATIVE: obj->irelative = true; break; default: _rtld_error("Unknown relocation type %x in PLT", (unsigned int)ELF_R_TYPE(rela->r_info)); return (-1); } } return 0; } /* Relocate the jump slots in an object. */ int reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; if (obj->jmpslots_done) return 0; relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_JMP_SLOT: where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) return (-1); if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { obj->gnu_ifunc = true; continue; } target = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend); reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela); break; case R_X86_64_IRELATIVE: break; default: _rtld_error("Unknown relocation type %x in PLT", (unsigned int)ELF_R_TYPE(rela->r_info)); return (-1); } } obj->jmpslots_done = true; return 0; } /* Fixup the jump slot at "where" to transfer control to "target". */ Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj, const Elf_Rel *rel) { #ifdef dbg dbg("reloc_jmpslot: *%p = %p", where, (void *)target); #endif if (!ld_bind_not) *where = target; return (target); } int reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; if (!obj->irelative) return (0); relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { Elf_Addr *where, target, *ptr; switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_JMP_SLOT: break; case R_X86_64_IRELATIVE: ptr = (Elf_Addr *)(obj->relocbase + rela->r_addend); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); lock_release(rtld_bind_lock, lockstate); target = call_ifunc_resolver(ptr); wlock_acquire(rtld_bind_lock, lockstate); *where = target; break; } } obj->irelative = false; return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; if (!obj->gnu_ifunc) return (0); relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; switch (ELF_R_TYPE(rela->r_info)) { case R_X86_64_JMP_SLOT: where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) return (-1); if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) continue; lock_release(rtld_bind_lock, lockstate); target = (Elf_Addr)rtld_resolve_ifunc(defobj, def); wlock_acquire(rtld_bind_lock, lockstate); reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *)rela); break; } } obj->gnu_ifunc = false; return (0); } uint32_t cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2; void ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) { u_int p[4], cpu_high; do_cpuid(1, p); cpu_feature = p[3]; cpu_feature2 = p[2]; do_cpuid(0, p); cpu_high = p[0]; if (cpu_high >= 7) { cpuid_count(7, 0, p); cpu_stdext_feature = p[1]; cpu_stdext_feature2 = p[2]; } } void allocate_initial_tls(Obj_Entry *objs) { /* * Fix the size of the static TLS block by using the maximum * offset allocated so far and adding a bit for dynamic modules to * use. */ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA; amd64_set_fsbase(allocate_tls(objs, 0, 3*sizeof(Elf_Addr), sizeof(Elf_Addr))); } void *__tls_get_addr(tls_index *ti) { Elf_Addr** segbase; __asm __volatile("movq %%fs:0, %0" : "=r" (segbase)); return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset); } Index: head/libexec/rtld-elf/amd64/rtld_machdep.h =================================================================== --- head/libexec/rtld-elf/amd64/rtld_machdep.h (revision 326273) +++ head/libexec/rtld-elf/amd64/rtld_machdep.h (revision 326274) @@ -1,82 +1,84 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_MACHDEP_H #define RTLD_MACHDEP_H 1 #include #include struct Struct_Obj_Entry; /* Return the address of the .dynamic section in the dynamic linker. */ Elf_Dyn *rtld_dynamic_addr(void); #define rtld_dynamic(obj) rtld_dynamic_addr() Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj, const Elf_Rel *rel); #define make_function_pointer(def, defobj) \ ((defobj)->relocbase + (def)->st_value) #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) #define call_init_pointer(obj, target) \ (((InitArrFunc)(target))(main_argc, main_argv, environ)) extern uint32_t cpu_feature; extern uint32_t cpu_feature2; extern uint32_t cpu_stdext_feature; extern uint32_t cpu_stdext_feature2; #define call_ifunc_resolver(ptr) \ (((Elf_Addr (*)(uint32_t, uint32_t, uint32_t, uint32_t))ptr)( \ cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2)) #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) #define calculate_first_tls_offset(size, align) \ round(size, align) #define calculate_tls_offset(prev_offset, prev_size, size, align) \ round((prev_offset) + (size), align) #define calculate_tls_end(off, size) (off) typedef struct { unsigned long ti_module; unsigned long ti_offset; } tls_index; void *__tls_get_addr(tls_index *ti) __exported; #define RTLD_DEFAULT_STACK_PF_EXEC PF_X #define RTLD_DEFAULT_STACK_EXEC PROT_EXEC #define md_abi_variant_hook(x) #endif Index: head/libexec/rtld-elf/arm/rtld_machdep.h =================================================================== --- head/libexec/rtld-elf/arm/rtld_machdep.h (revision 326273) +++ head/libexec/rtld-elf/arm/rtld_machdep.h (revision 326274) @@ -1,85 +1,87 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_MACHDEP_H #define RTLD_MACHDEP_H 1 #include #include #include struct Struct_Obj_Entry; /* Return the address of the .dynamic section in the dynamic linker. */ #define rtld_dynamic(obj) (&_DYNAMIC) Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, const Elf_Rel *rel); #define make_function_pointer(def, defobj) \ ((defobj)->relocbase + (def)->st_value) #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) #define call_init_pointer(obj, target) \ (((InitArrFunc)(target))(main_argc, main_argv, environ)) #define call_ifunc_resolver(ptr) \ (((Elf_Addr (*)(void))ptr)()) #define TLS_TCB_SIZE 8 typedef struct { unsigned long ti_module; unsigned long ti_offset; } tls_index; #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) #define calculate_first_tls_offset(size, align) \ round(8, align) #define calculate_tls_offset(prev_offset, prev_size, size, align) \ round(prev_offset + prev_size, align) #define calculate_tls_end(off, size) ((off) + (size)) extern void *__tls_get_addr(tls_index *ti); #define RTLD_DEFAULT_STACK_PF_EXEC PF_X #define RTLD_DEFAULT_STACK_EXEC PROT_EXEC extern void arm_abi_variant_hook(Elf_Auxinfo **); #ifdef __ARM_FP #define md_abi_variant_hook(x) arm_abi_variant_hook(x) #define RTLD_VARIANT_ENV_NAMES #else #define md_abi_variant_hook(x) #endif #endif Index: head/libexec/rtld-elf/debug.c =================================================================== --- head/libexec/rtld-elf/debug.c (revision 326273) +++ head/libexec/rtld-elf/debug.c (revision 326274) @@ -1,144 +1,146 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * Support for printing debugging messages. */ #include #include #include "debug.h" #include "rtld.h" #include "rtld_printf.h" static const char rel_header[] = " symbol name r_info r_offset st_value st_size address value\n" " ------------------------------------------------------------------------------\n"; static const char rel_format[] = " %-25s %6lx %08lx %08lx %7d %10p %08lx\n"; int debug = 0; void debug_printf(const char *format, ...) { if (debug) { va_list ap; va_start(ap, format); rtld_vfdprintf(STDERR_FILENO, format, ap); rtld_fdputchar(STDERR_FILENO, '\n'); va_end(ap); } } void dump_relocations (Obj_Entry *obj0) { Obj_Entry *obj; for (obj = globallist_curr(obj0); obj != NULL; obj = globallist_next(obj)) { dump_obj_relocations(obj); } } void dump_obj_relocations (Obj_Entry *obj) { rtld_printf("Object \"%s\", relocbase %p\n", obj->path, obj->relocbase); if (obj->relsize) { rtld_printf("Non-PLT Relocations: %ld\n", (obj->relsize / sizeof(Elf_Rel))); dump_Elf_Rel(obj, obj->rel, obj->relsize); } if (obj->relasize) { rtld_printf("Non-PLT Relocations with Addend: %ld\n", (obj->relasize / sizeof(Elf_Rela))); dump_Elf_Rela(obj, obj->rela, obj->relasize); } if (obj->pltrelsize) { rtld_printf("PLT Relocations: %ld\n", (obj->pltrelsize / sizeof(Elf_Rel))); dump_Elf_Rel(obj, obj->pltrel, obj->pltrelsize); } if (obj->pltrelasize) { rtld_printf("PLT Relocations with Addend: %ld\n", (obj->pltrelasize / sizeof(Elf_Rela))); dump_Elf_Rela(obj, obj->pltrela, obj->pltrelasize); } } void dump_Elf_Rel (Obj_Entry *obj, const Elf_Rel *rel0, u_long relsize) { const Elf_Rel *rel; const Elf_Rel *rellim; const Elf_Sym *sym; Elf_Addr *dstaddr; rtld_putstr(rel_header); rellim = (const Elf_Rel *)((const char *)rel0 + relsize); for (rel = rel0; rel < rellim; rel++) { dstaddr = (Elf_Addr *)(obj->relocbase + rel->r_offset); sym = obj->symtab + ELF_R_SYM(rel->r_info); rtld_printf(rel_format, obj->strtab + sym->st_name, (u_long)rel->r_info, (u_long)rel->r_offset, (u_long)sym->st_value, (int)sym->st_size, dstaddr, (u_long)*dstaddr); } return; } void dump_Elf_Rela (Obj_Entry *obj, const Elf_Rela *rela0, u_long relasize) { const Elf_Rela *rela; const Elf_Rela *relalim; const Elf_Sym *sym; Elf_Addr *dstaddr; rtld_putstr(rel_header); relalim = (const Elf_Rela *)((const char *)rela0 + relasize); for (rela = rela0; rela < relalim; rela++) { dstaddr = (Elf_Addr *)(obj->relocbase + rela->r_offset); sym = obj->symtab + ELF_R_SYM(rela->r_info); rtld_printf(rel_format, obj->strtab + sym->st_name, (u_long)rela->r_info, (u_long)rela->r_offset, (u_long)sym->st_value, (int)sym->st_size, dstaddr, (u_long)*dstaddr); } return; } Index: head/libexec/rtld-elf/debug.h =================================================================== --- head/libexec/rtld-elf/debug.h (revision 326273) +++ head/libexec/rtld-elf/debug.h (revision 326274) @@ -1,62 +1,64 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * Support for printing debugging messages. */ #ifndef DEBUG_H #define DEBUG_H 1 #include #include #include extern void debug_printf(const char *, ...) __printflike(1, 2); extern int debug; #ifdef DEBUG #define dbg(...) debug_printf(__VA_ARGS__) #else #define dbg(...) ((void) 0) #endif #ifndef COMPAT_32BIT #define _MYNAME "ld-elf.so.1" #else #define _MYNAME "ld-elf32.so.1" #endif #define assert(cond) ((cond) ? (void) 0 : \ (msg(_MYNAME ": assert failed: " __FILE__ ":" \ __XSTRING(__LINE__) "\n"), abort())) #define msg(s) write(STDOUT_FILENO, s, strlen(s)) #define trace() msg(_MYNAME ": " __XSTRING(__LINE__) "\n") #endif /* DEBUG_H */ Index: head/libexec/rtld-elf/i386/reloc.c =================================================================== --- head/libexec/rtld-elf/i386/reloc.c (revision 326273) +++ head/libexec/rtld-elf/i386/reloc.c (revision 326274) @@ -1,507 +1,509 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996, 1997, 1998, 1999 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* * Dynamic linker for ELF. * * John Polstra . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "rtld.h" #include "rtld_tls.h" /* * Process the special R_386_COPY relocations in the main program. These * copy data from a shared object into a region in the main program's BSS * segment. * * Returns 0 on success, -1 on failure. */ int do_copy_relocations(Obj_Entry *dstobj) { const Elf_Rel *rellim; const Elf_Rel *rel; assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ rellim = (const Elf_Rel *) ((caddr_t) dstobj->rel + dstobj->relsize); for (rel = dstobj->rel; rel < rellim; rel++) { if (ELF_R_TYPE(rel->r_info) == R_386_COPY) { void *dstaddr; const Elf_Sym *dstsym; const char *name; size_t size; const void *srcaddr; const Elf_Sym *srcsym; const Obj_Entry *srcobj, *defobj; SymLook req; int res; dstaddr = (void *) (dstobj->relocbase + rel->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); name = dstobj->strtab + dstsym->st_name; size = dstsym->st_size; symlook_init(&req, name); req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info)); req.flags = SYMLOOK_EARLY; for (srcobj = globallist_next(dstobj); srcobj != NULL; srcobj = globallist_next(srcobj)) { res = symlook_obj(&req, srcobj); if (res == 0) { srcsym = req.sym_out; defobj = req.defobj_out; break; } } if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\" referenced from COPY" " relocation in %s", name, dstobj->path); return -1; } srcaddr = (const void *) (defobj->relocbase + srcsym->st_value); memcpy(dstaddr, srcaddr, size); } } return 0; } /* Initialize the special GOT entries. */ void init_pltgot(Obj_Entry *obj) { if (obj->pltgot != NULL) { obj->pltgot[1] = (Elf_Addr) obj; obj->pltgot[2] = (Elf_Addr) &_rtld_bind_start; } } /* Process the non-PLT relocations. */ int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Elf_Rel *rellim; const Elf_Rel *rel; SymCache *cache; const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr *where, symval, add; int r; r = -1; /* * The dynamic loader may be called from a thread, we have * limited amounts of stack available so we cannot use alloca(). */ if (obj != obj_rtld) { cache = calloc(obj->dynsymcount, sizeof(SymCache)); /* No need to check for NULL here */ } else cache = NULL; rellim = (const Elf_Rel *)((caddr_t) obj->rel + obj->relsize); for (rel = obj->rel; rel < rellim; rel++) { switch (ELF_R_TYPE(rel->r_info)) { case R_386_32: case R_386_PC32: case R_386_GLOB_DAT: case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: case R_386_TLS_DTPMOD32: case R_386_TLS_DTPOFF32: def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) goto done; if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { switch (ELF_R_TYPE(rel->r_info)) { case R_386_32: case R_386_PC32: case R_386_GLOB_DAT: if ((flags & SYMLOOK_IFUNC) == 0) { obj->non_plt_gnu_ifunc = true; continue; } symval = (Elf_Addr)rtld_resolve_ifunc( defobj, def); break; case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: case R_386_TLS_DTPMOD32: case R_386_TLS_DTPOFF32: _rtld_error("%s: IFUNC for TLS reloc", obj->path); goto done; } } else { if ((flags & SYMLOOK_IFUNC) != 0) continue; symval = (Elf_Addr)defobj->relocbase + def->st_value; } break; default: if ((flags & SYMLOOK_IFUNC) != 0) continue; break; } where = (Elf_Addr *)(obj->relocbase + rel->r_offset); switch (ELF_R_TYPE(rel->r_info)) { case R_386_NONE: break; case R_386_32: *where += symval; break; case R_386_PC32: /* * I don't think the dynamic linker should ever * see this type of relocation. But the * binutils-2.6 tools sometimes generate it. */ *where += symval - (Elf_Addr)where; break; case R_386_COPY: /* * These are deferred until all other * relocations have been done. All we do here * is make sure that the COPY relocation is * not in a shared library. They are allowed * only in executable files. */ if (!obj->mainprog) { _rtld_error("%s: Unexpected R_386_COPY " "relocation in shared library", obj->path); goto done; } break; case R_386_GLOB_DAT: *where = symval; break; case R_386_RELATIVE: *where += (Elf_Addr)obj->relocbase; break; case R_386_TLS_TPOFF: case R_386_TLS_TPOFF32: /* * We lazily allocate offsets for static TLS * as we see the first relocation that * references the TLS block. This allows us to * support (small amounts of) static TLS in * dynamically loaded modules. If we run out * of space, we generate an error. */ if (!defobj->tls_done) { if (!allocate_tls_offset((Obj_Entry*) defobj)) { _rtld_error("%s: No space available " "for static Thread Local Storage", obj->path); goto done; } } add = (Elf_Addr)(def->st_value - defobj->tlsoffset); if (ELF_R_TYPE(rel->r_info) == R_386_TLS_TPOFF) *where += add; else *where -= add; break; case R_386_TLS_DTPMOD32: *where += (Elf_Addr)defobj->tlsindex; break; case R_386_TLS_DTPOFF32: *where += (Elf_Addr) def->st_value; break; default: _rtld_error("%s: Unsupported relocation type %d" " in non-PLT relocations\n", obj->path, ELF_R_TYPE(rel->r_info)); goto done; } } r = 0; done: free(cache); return (r); } /* Process the PLT relocations. */ int reloc_plt(Obj_Entry *obj) { const Elf_Rel *rellim; const Elf_Rel *rel; rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { Elf_Addr *where/*, val*/; switch (ELF_R_TYPE(rel->r_info)) { case R_386_JMP_SLOT: /* Relocate the GOT slot pointing into the PLT. */ where = (Elf_Addr *)(obj->relocbase + rel->r_offset); *where += (Elf_Addr)obj->relocbase; break; case R_386_IRELATIVE: obj->irelative = true; break; default: _rtld_error("Unknown relocation type %x in PLT", ELF_R_TYPE(rel->r_info)); return (-1); } } return 0; } /* Relocate the jump slots in an object. */ int reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Elf_Rel *rellim; const Elf_Rel *rel; if (obj->jmpslots_done) return 0; rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; switch (ELF_R_TYPE(rel->r_info)) { case R_386_JMP_SLOT: where = (Elf_Addr *)(obj->relocbase + rel->r_offset); def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) return (-1); if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) { obj->gnu_ifunc = true; continue; } target = (Elf_Addr)(defobj->relocbase + def->st_value); reloc_jmpslot(where, target, defobj, obj, rel); break; case R_386_IRELATIVE: break; default: _rtld_error("Unknown relocation type %x in PLT", ELF_R_TYPE(rel->r_info)); return (-1); } } obj->jmpslots_done = true; return 0; } /* Fixup the jump slot at "where" to transfer control to "target". */ Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj, const Elf_Rel *rel) { #ifdef dbg dbg("reloc_jmpslot: *%p = %p", where, (void *)target); #endif if (!ld_bind_not) *where = target; return (target); } int reloc_iresolve(Obj_Entry *obj, RtldLockState *lockstate) { const Elf_Rel *rellim; const Elf_Rel *rel; Elf_Addr *where, target; if (!obj->irelative) return (0); rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { switch (ELF_R_TYPE(rel->r_info)) { case R_386_IRELATIVE: where = (Elf_Addr *)(obj->relocbase + rel->r_offset); lock_release(rtld_bind_lock, lockstate); target = call_ifunc_resolver(obj->relocbase + *where); wlock_acquire(rtld_bind_lock, lockstate); *where = target; break; } } obj->irelative = false; return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Elf_Rel *rellim; const Elf_Rel *rel; if (!obj->gnu_ifunc) return (0); rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { Elf_Addr *where, target; const Elf_Sym *def; const Obj_Entry *defobj; switch (ELF_R_TYPE(rel->r_info)) { case R_386_JMP_SLOT: where = (Elf_Addr *)(obj->relocbase + rel->r_offset); def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) return (-1); if (ELF_ST_TYPE(def->st_info) != STT_GNU_IFUNC) continue; lock_release(rtld_bind_lock, lockstate); target = (Elf_Addr)rtld_resolve_ifunc(defobj, def); wlock_acquire(rtld_bind_lock, lockstate); reloc_jmpslot(where, target, defobj, obj, rel); break; } } obj->gnu_ifunc = false; return (0); } uint32_t cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2; static void rtld_cpuid_count(int idx, int cnt, u_int *p) { __asm __volatile( " pushl %%ebx\n" " cpuid\n" " movl %%ebx,%1\n" " popl %%ebx\n" : "=a" (p[0]), "=r" (p[1]), "=c" (p[2]), "=d" (p[3]) : "0" (idx), "2" (cnt)); } void ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) { u_int p[4], cpu_high; int cpuid_supported; __asm __volatile( " pushfl\n" " popl %%eax\n" " movl %%eax,%%ecx\n" " xorl $0x200000,%%eax\n" " pushl %%eax\n" " popfl\n" " pushfl\n" " popl %%eax\n" " xorl %%eax,%%ecx\n" " je 1f\n" " movl $1,%0\n" " jmp 2f\n" "1: movl $0,%0\n" "2:\n" : "=r" (cpuid_supported) : : "eax", "ecx"); if (!cpuid_supported) return; rtld_cpuid_count(1, 0, p); cpu_feature = p[3]; cpu_feature2 = p[2]; rtld_cpuid_count(0, 0, p); cpu_high = p[0]; if (cpu_high >= 7) { rtld_cpuid_count(7, 0, p); cpu_stdext_feature = p[1]; cpu_stdext_feature2 = p[2]; } } void allocate_initial_tls(Obj_Entry *objs) { void* tls; /* * Fix the size of the static TLS block by using the maximum * offset allocated so far and adding a bit for dynamic modules to * use. */ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA; tls = allocate_tls(objs, NULL, 3*sizeof(Elf_Addr), sizeof(Elf_Addr)); i386_set_gsbase(tls); } /* GNU ABI */ __attribute__((__regparm__(1))) void *___tls_get_addr(tls_index *ti) { Elf_Addr** segbase; __asm __volatile("movl %%gs:0, %0" : "=r" (segbase)); return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset); } /* Sun ABI */ void *__tls_get_addr(tls_index *ti) { Elf_Addr** segbase; __asm __volatile("movl %%gs:0, %0" : "=r" (segbase)); return tls_get_addr_common(&segbase[1], ti->ti_module, ti->ti_offset); } Index: head/libexec/rtld-elf/i386/rtld_machdep.h =================================================================== --- head/libexec/rtld-elf/i386/rtld_machdep.h (revision 326273) +++ head/libexec/rtld-elf/i386/rtld_machdep.h (revision 326274) @@ -1,83 +1,85 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_MACHDEP_H #define RTLD_MACHDEP_H 1 #include #include struct Struct_Obj_Entry; /* Return the address of the .dynamic section in the dynamic linker. */ #define rtld_dynamic(obj) \ ((const Elf_Dyn *)((obj)->relocbase + (Elf_Addr)&_DYNAMIC)) Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const struct Struct_Obj_Entry *obj, const struct Struct_Obj_Entry *refobj, const Elf_Rel *rel); #define make_function_pointer(def, defobj) \ ((defobj)->relocbase + (def)->st_value) #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) #define call_init_pointer(obj, target) \ (((InitArrFunc)(target))(main_argc, main_argv, environ)) extern uint32_t cpu_feature; extern uint32_t cpu_feature2; extern uint32_t cpu_stdext_feature; extern uint32_t cpu_stdext_feature2; #define call_ifunc_resolver(ptr) \ (((Elf_Addr (*)(uint32_t, uint32_t, uint32_t, uint32_t))ptr)( \ cpu_feature, cpu_feature2, cpu_stdext_feature, cpu_stdext_feature2)) #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) #define calculate_first_tls_offset(size, align) \ round(size, align) #define calculate_tls_offset(prev_offset, prev_size, size, align) \ round((prev_offset) + (size), align) #define calculate_tls_end(off, size) (off) typedef struct { unsigned long ti_module; unsigned long ti_offset; } tls_index; void *___tls_get_addr(tls_index *ti) __attribute__((__regparm__(1))) __exported; void *__tls_get_addr(tls_index *ti) __exported; #define RTLD_DEFAULT_STACK_PF_EXEC PF_X #define RTLD_DEFAULT_STACK_EXEC PROT_EXEC #define md_abi_variant_hook(x) #endif Index: head/libexec/rtld-elf/map_object.c =================================================================== --- head/libexec/rtld-elf/map_object.c (revision 326273) +++ head/libexec/rtld-elf/map_object.c (revision 326274) @@ -1,477 +1,479 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include "debug.h" #include "rtld.h" static Elf_Ehdr *get_elf_header(int, const char *, const struct stat *); static int convert_flags(int); /* Elf flags -> mmap flags */ int __getosreldate(void); /* * Map a shared object into memory. The "fd" argument is a file descriptor, * which must be open on the object and positioned at its beginning. * The "path" argument is a pathname that is used only for error messages. * * The return value is a pointer to a newly-allocated Obj_Entry structure * for the shared object. Returns NULL on failure. */ Obj_Entry * map_object(int fd, const char *path, const struct stat *sb) { Obj_Entry *obj; Elf_Ehdr *hdr; int i; Elf_Phdr *phdr; Elf_Phdr *phlimit; Elf_Phdr **segs; int nsegs; Elf_Phdr *phdyn; Elf_Phdr *phinterp; Elf_Phdr *phtls; caddr_t mapbase; size_t mapsize; Elf_Addr base_vaddr; Elf_Addr base_vlimit; caddr_t base_addr; int base_flags; Elf_Off data_offset; Elf_Addr data_vaddr; Elf_Addr data_vlimit; caddr_t data_addr; int data_prot; int data_flags; Elf_Addr clear_vaddr; caddr_t clear_addr; caddr_t clear_page; Elf_Addr phdr_vaddr; size_t nclear, phsize; Elf_Addr bss_vaddr; Elf_Addr bss_vlimit; caddr_t bss_addr; Elf_Word stack_flags; Elf_Addr relro_page; size_t relro_size; Elf_Addr note_start; Elf_Addr note_end; char *note_map; size_t note_map_len; hdr = get_elf_header(fd, path, sb); if (hdr == NULL) return (NULL); /* * Scan the program header entries, and save key information. * * We expect that the loadable segments are ordered by load address. */ phdr = (Elf_Phdr *) ((char *)hdr + hdr->e_phoff); phsize = hdr->e_phnum * sizeof (phdr[0]); phlimit = phdr + hdr->e_phnum; nsegs = -1; phdyn = phinterp = phtls = NULL; phdr_vaddr = 0; relro_page = 0; relro_size = 0; note_start = 0; note_end = 0; note_map = NULL; segs = alloca(sizeof(segs[0]) * hdr->e_phnum); stack_flags = RTLD_DEFAULT_STACK_PF_EXEC | PF_R | PF_W; while (phdr < phlimit) { switch (phdr->p_type) { case PT_INTERP: phinterp = phdr; break; case PT_LOAD: segs[++nsegs] = phdr; if ((segs[nsegs]->p_align & (PAGE_SIZE - 1)) != 0) { _rtld_error("%s: PT_LOAD segment %d not page-aligned", path, nsegs); goto error; } break; case PT_PHDR: phdr_vaddr = phdr->p_vaddr; phsize = phdr->p_memsz; break; case PT_DYNAMIC: phdyn = phdr; break; case PT_TLS: phtls = phdr; break; case PT_GNU_STACK: stack_flags = phdr->p_flags; break; case PT_GNU_RELRO: relro_page = phdr->p_vaddr; relro_size = phdr->p_memsz; break; case PT_NOTE: if (phdr->p_offset > PAGE_SIZE || phdr->p_offset + phdr->p_filesz > PAGE_SIZE) { note_map_len = round_page(phdr->p_offset + phdr->p_filesz) - trunc_page(phdr->p_offset); note_map = mmap(NULL, note_map_len, PROT_READ, MAP_PRIVATE, fd, trunc_page(phdr->p_offset)); if (note_map == MAP_FAILED) { _rtld_error("%s: error mapping PT_NOTE (%d)", path, errno); goto error; } note_start = (Elf_Addr)(note_map + phdr->p_offset - trunc_page(phdr->p_offset)); } else { note_start = (Elf_Addr)(char *)hdr + phdr->p_offset; } note_end = note_start + phdr->p_filesz; break; } ++phdr; } if (phdyn == NULL) { _rtld_error("%s: object is not dynamically-linked", path); goto error; } if (nsegs < 0) { _rtld_error("%s: too few PT_LOAD segments", path); goto error; } /* * Map the entire address space of the object, to stake out our * contiguous region, and to establish the base address for relocation. */ base_vaddr = trunc_page(segs[0]->p_vaddr); base_vlimit = round_page(segs[nsegs]->p_vaddr + segs[nsegs]->p_memsz); mapsize = base_vlimit - base_vaddr; base_addr = (caddr_t) base_vaddr; base_flags = __getosreldate() >= P_OSREL_MAP_GUARD ? MAP_GUARD : MAP_PRIVATE | MAP_ANON | MAP_NOCORE; if (npagesizes > 1 && round_page(segs[0]->p_filesz) >= pagesizes[1]) base_flags |= MAP_ALIGNED_SUPER; if (base_vaddr != 0) base_flags |= MAP_FIXED | MAP_EXCL; mapbase = mmap(base_addr, mapsize, PROT_NONE, base_flags, -1, 0); if (mapbase == (caddr_t) -1) { _rtld_error("%s: mmap of entire address space failed: %s", path, rtld_strerror(errno)); goto error; } if (base_addr != NULL && mapbase != base_addr) { _rtld_error("%s: mmap returned wrong address: wanted %p, got %p", path, base_addr, mapbase); goto error1; } for (i = 0; i <= nsegs; i++) { /* Overlay the segment onto the proper region. */ data_offset = trunc_page(segs[i]->p_offset); data_vaddr = trunc_page(segs[i]->p_vaddr); data_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_filesz); data_addr = mapbase + (data_vaddr - base_vaddr); data_prot = convert_prot(segs[i]->p_flags); data_flags = convert_flags(segs[i]->p_flags) | MAP_FIXED; if (mmap(data_addr, data_vlimit - data_vaddr, data_prot, data_flags | MAP_PREFAULT_READ, fd, data_offset) == (caddr_t) -1) { _rtld_error("%s: mmap of data failed: %s", path, rtld_strerror(errno)); goto error1; } /* Do BSS setup */ if (segs[i]->p_filesz != segs[i]->p_memsz) { /* Clear any BSS in the last page of the segment. */ clear_vaddr = segs[i]->p_vaddr + segs[i]->p_filesz; clear_addr = mapbase + (clear_vaddr - base_vaddr); clear_page = mapbase + (trunc_page(clear_vaddr) - base_vaddr); if ((nclear = data_vlimit - clear_vaddr) > 0) { /* Make sure the end of the segment is writable */ if ((data_prot & PROT_WRITE) == 0 && -1 == mprotect(clear_page, PAGE_SIZE, data_prot|PROT_WRITE)) { _rtld_error("%s: mprotect failed: %s", path, rtld_strerror(errno)); goto error1; } memset(clear_addr, 0, nclear); /* Reset the data protection back */ if ((data_prot & PROT_WRITE) == 0) mprotect(clear_page, PAGE_SIZE, data_prot); } /* Overlay the BSS segment onto the proper region. */ bss_vaddr = data_vlimit; bss_vlimit = round_page(segs[i]->p_vaddr + segs[i]->p_memsz); bss_addr = mapbase + (bss_vaddr - base_vaddr); if (bss_vlimit > bss_vaddr) { /* There is something to do */ if (mmap(bss_addr, bss_vlimit - bss_vaddr, data_prot, data_flags | MAP_ANON, -1, 0) == (caddr_t)-1) { _rtld_error("%s: mmap of bss failed: %s", path, rtld_strerror(errno)); goto error1; } } } if (phdr_vaddr == 0 && data_offset <= hdr->e_phoff && (data_vlimit - data_vaddr + data_offset) >= (hdr->e_phoff + hdr->e_phnum * sizeof (Elf_Phdr))) { phdr_vaddr = data_vaddr + hdr->e_phoff - data_offset; } } obj = obj_new(); if (sb != NULL) { obj->dev = sb->st_dev; obj->ino = sb->st_ino; } obj->mapbase = mapbase; obj->mapsize = mapsize; obj->textsize = round_page(segs[0]->p_vaddr + segs[0]->p_memsz) - base_vaddr; obj->vaddrbase = base_vaddr; obj->relocbase = mapbase - base_vaddr; obj->dynamic = (const Elf_Dyn *) (obj->relocbase + phdyn->p_vaddr); if (hdr->e_entry != 0) obj->entry = (caddr_t) (obj->relocbase + hdr->e_entry); if (phdr_vaddr != 0) { obj->phdr = (const Elf_Phdr *) (obj->relocbase + phdr_vaddr); } else { obj->phdr = malloc(phsize); if (obj->phdr == NULL) { obj_free(obj); _rtld_error("%s: cannot allocate program header", path); goto error1; } memcpy((char *)obj->phdr, (char *)hdr + hdr->e_phoff, phsize); obj->phdr_alloc = true; } obj->phsize = phsize; if (phinterp != NULL) obj->interp = (const char *) (obj->relocbase + phinterp->p_vaddr); if (phtls != NULL) { tls_dtv_generation++; obj->tlsindex = ++tls_max_index; obj->tlssize = phtls->p_memsz; obj->tlsalign = phtls->p_align; obj->tlsinitsize = phtls->p_filesz; obj->tlsinit = mapbase + phtls->p_vaddr; } obj->stack_flags = stack_flags; obj->relro_page = obj->relocbase + trunc_page(relro_page); obj->relro_size = round_page(relro_size); if (note_start < note_end) digest_notes(obj, note_start, note_end); if (note_map != NULL) munmap(note_map, note_map_len); munmap(hdr, PAGE_SIZE); return (obj); error1: munmap(mapbase, mapsize); error: if (note_map != NULL && note_map != MAP_FAILED) munmap(note_map, note_map_len); munmap(hdr, PAGE_SIZE); return (NULL); } static Elf_Ehdr * get_elf_header(int fd, const char *path, const struct stat *sbp) { Elf_Ehdr *hdr; /* Make sure file has enough data for the ELF header */ if (sbp != NULL && sbp->st_size < sizeof(Elf_Ehdr)) { _rtld_error("%s: invalid file format", path); return (NULL); } hdr = mmap(NULL, PAGE_SIZE, PROT_READ, MAP_PRIVATE | MAP_PREFAULT_READ, fd, 0); if (hdr == (Elf_Ehdr *)MAP_FAILED) { _rtld_error("%s: read error: %s", path, rtld_strerror(errno)); return (NULL); } /* Make sure the file is valid */ if (!IS_ELF(*hdr)) { _rtld_error("%s: invalid file format", path); goto error; } if (hdr->e_ident[EI_CLASS] != ELF_TARG_CLASS || hdr->e_ident[EI_DATA] != ELF_TARG_DATA) { _rtld_error("%s: unsupported file layout", path); goto error; } if (hdr->e_ident[EI_VERSION] != EV_CURRENT || hdr->e_version != EV_CURRENT) { _rtld_error("%s: unsupported file version", path); goto error; } if (hdr->e_type != ET_EXEC && hdr->e_type != ET_DYN) { _rtld_error("%s: unsupported file type", path); goto error; } if (hdr->e_machine != ELF_TARG_MACH) { _rtld_error("%s: unsupported machine", path); goto error; } /* * We rely on the program header being in the first page. This is * not strictly required by the ABI specification, but it seems to * always true in practice. And, it simplifies things considerably. */ if (hdr->e_phentsize != sizeof(Elf_Phdr)) { _rtld_error( "%s: invalid shared object: e_phentsize != sizeof(Elf_Phdr)", path); goto error; } if (hdr->e_phoff + hdr->e_phnum * sizeof(Elf_Phdr) > (size_t)PAGE_SIZE) { _rtld_error("%s: program header too large", path); goto error; } return (hdr); error: munmap(hdr, PAGE_SIZE); return (NULL); } void obj_free(Obj_Entry *obj) { Objlist_Entry *elm; if (obj->tls_done) free_tls_offset(obj); while (obj->needed != NULL) { Needed_Entry *needed = obj->needed; obj->needed = needed->next; free(needed); } while (!STAILQ_EMPTY(&obj->names)) { Name_Entry *entry = STAILQ_FIRST(&obj->names); STAILQ_REMOVE_HEAD(&obj->names, link); free(entry); } while (!STAILQ_EMPTY(&obj->dldags)) { elm = STAILQ_FIRST(&obj->dldags); STAILQ_REMOVE_HEAD(&obj->dldags, link); free(elm); } while (!STAILQ_EMPTY(&obj->dagmembers)) { elm = STAILQ_FIRST(&obj->dagmembers); STAILQ_REMOVE_HEAD(&obj->dagmembers, link); free(elm); } if (obj->vertab) free(obj->vertab); if (obj->origin_path) free(obj->origin_path); if (obj->z_origin) free(obj->rpath); if (obj->priv) free(obj->priv); if (obj->path) free(obj->path); if (obj->phdr_alloc) free((void *)obj->phdr); free(obj); } Obj_Entry * obj_new(void) { Obj_Entry *obj; obj = CNEW(Obj_Entry); STAILQ_INIT(&obj->dldags); STAILQ_INIT(&obj->dagmembers); STAILQ_INIT(&obj->names); return obj; } /* * Given a set of ELF protection flags, return the corresponding protection * flags for MMAP. */ int convert_prot(int elfflags) { int prot = 0; if (elfflags & PF_R) prot |= PROT_READ; if (elfflags & PF_W) prot |= PROT_WRITE; if (elfflags & PF_X) prot |= PROT_EXEC; return prot; } static int convert_flags(int elfflags) { int flags = MAP_PRIVATE; /* All mappings are private */ /* * Readonly mappings are marked "MAP_NOCORE", because they can be * reconstructed by a debugger. */ if (!(elfflags & PF_W)) flags |= MAP_NOCORE; return flags; } Index: head/libexec/rtld-elf/mips/reloc.c =================================================================== --- head/libexec/rtld-elf/mips/reloc.c (revision 326273) +++ head/libexec/rtld-elf/mips/reloc.c (revision 326274) @@ -1,830 +1,832 @@ /* $NetBSD: mips_reloc.c,v 1.58 2010/01/14 11:57:06 skrll Exp $ */ -/* +/*- + * SPDX-License-Identifier: BSD-3-Clause + * * Copyright 1997 Michael L. Hitch * Portions copyright 2002 Charles M. Hannum * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include "debug.h" #include "rtld.h" #ifdef __mips_n64 #define GOT1_MASK 0x8000000000000000UL #else #define GOT1_MASK 0x80000000UL #endif /* * Determine if the second GOT entry is reserved for rtld or if it is * the first "real" GOT entry. * * This must be a macro rather than a function so that * _rtld_relocate_nonplt_self doesn't trigger a GOT invocation trying * to use it before the local GOT entries in rtld are adjusted. */ #ifdef __mips_n64 /* Old binutils uses the 32-bit GOT1 mask value for N64. */ #define GOT1_RESERVED_FOR_RTLD(got) \ (((got)[1] == 0x80000000) || (got)[1] & GOT1_MASK) #else #define GOT1_RESERVED_FOR_RTLD(got) ((got)[1] & GOT1_MASK) #endif #ifdef __mips_n64 /* * ELF64 MIPS encodes the relocs uniquely. The first 32-bits of info contain * the symbol index. The top 32-bits contain three relocation types encoded * in big-endian integer with first relocation in LSB. This means for little * endian we have to byte swap that integer (r_type). */ #define Elf_Sxword Elf64_Sxword #define ELF_R_NXTTYPE_64_P(r_type) ((((r_type) >> 8) & 0xff) == R_TYPE(64)) #if BYTE_ORDER == LITTLE_ENDIAN #undef ELF_R_SYM #undef ELF_R_TYPE #define ELF_R_SYM(r_info) ((r_info) & 0xffffffff) #define ELF_R_TYPE(r_info) bswap32((r_info) >> 32) #endif #else #define ELF_R_NXTTYPE_64_P(r_type) (0) #define Elf_Sxword Elf32_Sword #endif void _rtld_pltbind_start(void); void init_pltgot(Obj_Entry *obj) { if (obj->pltgot != NULL) { obj->pltgot[0] = (Elf_Addr) &_rtld_bind_start; if (GOT1_RESERVED_FOR_RTLD(obj->pltgot)) obj->pltgot[1] = (Elf_Addr) obj | GOT1_MASK; } if (obj->mips_pltgot != NULL) { obj->mips_pltgot[0] = (Elf_Addr) &_rtld_pltbind_start; obj->mips_pltgot[1] = (Elf_Addr) obj; } } int do_copy_relocations(Obj_Entry *dstobj) { const Obj_Entry *srcobj, *defobj; const Elf_Rel *rellim; const Elf_Rel *rel; const Elf_Sym *srcsym; const Elf_Sym *dstsym; const void *srcaddr; const char *name; void *dstaddr; SymLook req; size_t size; int res; /* * COPY relocs are invalid outside of the main program */ assert(dstobj->mainprog); rellim = (const Elf_Rel *)((caddr_t)dstobj->rel + dstobj->relsize); for (rel = dstobj->rel; rel < rellim; rel++) { if (ELF_R_TYPE(rel->r_info) != R_MIPS_COPY) continue; dstaddr = (void *)(dstobj->relocbase + rel->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rel->r_info); name = dstobj->strtab + dstsym->st_name; size = dstsym->st_size; symlook_init(&req, name); req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rel->r_info)); req.flags = SYMLOOK_EARLY; for (srcobj = globallist_next(dstobj); srcobj != NULL; srcobj = globallist_next(srcobj)) { res = symlook_obj(&req, srcobj); if (res == 0) { srcsym = req.sym_out; defobj = req.defobj_out; break; } } if (srcobj == NULL) { _rtld_error( "Undefined symbol \"%s\" referenced from COPY relocation in %s", name, dstobj->path); return (-1); } srcaddr = (const void *)(defobj->relocbase + srcsym->st_value); memcpy(dstaddr, srcaddr, size); } return (0); } void _rtld_relocate_nonplt_self(Elf_Dyn *, Elf_Addr); /* * It is possible for the compiler to emit relocations for unaligned data. * We handle this situation with these inlines. */ static __inline Elf_Sxword load_ptr(void *where, size_t len) { Elf_Sxword val; if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) { #ifdef __mips_n64 if (len == sizeof(Elf_Sxword)) return *(Elf_Sxword *)where; #endif return *(Elf_Sword *)where; } val = 0; #if BYTE_ORDER == LITTLE_ENDIAN (void)memcpy(&val, where, len); #endif #if BYTE_ORDER == BIG_ENDIAN (void)memcpy((uint8_t *)((&val)+1) - len, where, len); #endif return (len == sizeof(Elf_Sxword)) ? val : (Elf_Sword)val; } static __inline void store_ptr(void *where, Elf_Sxword val, size_t len) { if (__predict_true(((uintptr_t)where & (len - 1)) == 0)) { #ifdef __mips_n64 if (len == sizeof(Elf_Sxword)) { *(Elf_Sxword *)where = val; return; } #endif *(Elf_Sword *)where = val; return; } #if BYTE_ORDER == LITTLE_ENDIAN (void)memcpy(where, &val, len); #endif #if BYTE_ORDER == BIG_ENDIAN (void)memcpy(where, (const uint8_t *)((&val)+1) - len, len); #endif } void _rtld_relocate_nonplt_self(Elf_Dyn *dynp, Elf_Addr relocbase) { const Elf_Rel *rel = NULL, *rellim; Elf_Addr relsz = 0; const Elf_Sym *symtab = NULL, *sym; Elf_Addr *where; Elf_Addr *got = NULL; Elf_Word local_gotno = 0, symtabno = 0, gotsym = 0; size_t i; for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_REL: rel = (const Elf_Rel *)(relocbase + dynp->d_un.d_ptr); break; case DT_RELSZ: relsz = dynp->d_un.d_val; break; case DT_SYMTAB: symtab = (const Elf_Sym *)(relocbase + dynp->d_un.d_ptr); break; case DT_PLTGOT: got = (Elf_Addr *)(relocbase + dynp->d_un.d_ptr); break; case DT_MIPS_LOCAL_GOTNO: local_gotno = dynp->d_un.d_val; break; case DT_MIPS_SYMTABNO: symtabno = dynp->d_un.d_val; break; case DT_MIPS_GOTSYM: gotsym = dynp->d_un.d_val; break; } } i = GOT1_RESERVED_FOR_RTLD(got) ? 2 : 1; /* Relocate the local GOT entries */ got += i; for (; i < local_gotno; i++) { *got++ += relocbase; } sym = symtab + gotsym; /* Now do the global GOT entries */ for (i = gotsym; i < symtabno; i++) { *got = sym->st_value + relocbase; ++sym; ++got; } rellim = (const Elf_Rel *)((caddr_t)rel + relsz); for (; rel < rellim; rel++) { Elf_Word r_symndx, r_type; where = (void *)(relocbase + rel->r_offset); r_symndx = ELF_R_SYM(rel->r_info); r_type = ELF_R_TYPE(rel->r_info); switch (r_type & 0xff) { case R_TYPE(REL32): { const size_t rlen = ELF_R_NXTTYPE_64_P(r_type) ? sizeof(Elf_Sxword) : sizeof(Elf_Sword); Elf_Sxword old = load_ptr(where, rlen); Elf_Sxword val = old; #ifdef __mips_n64 assert(r_type == R_TYPE(REL32) || r_type == (R_TYPE(REL32)|(R_TYPE(64) << 8))); #endif assert(r_symndx < gotsym); sym = symtab + r_symndx; assert(ELF_ST_BIND(sym->st_info) == STB_LOCAL); val += relocbase; dbg("REL32/L(%p) %p -> %p in ", where, (void *)old, (void *)val); store_ptr(where, val, rlen); break; } case R_TYPE(GPREL32): case R_TYPE(NONE): break; default: abort(); break; } } } Elf_Addr _mips_rtld_bind(Obj_Entry *obj, Elf_Size reloff) { Elf_Addr *got = obj->pltgot; const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr *where; Elf_Addr target; RtldLockState lockstate; rlock_acquire(rtld_bind_lock, &lockstate); if (sigsetjmp(lockstate.env, 0) != 0) lock_upgrade(rtld_bind_lock, &lockstate); where = &got[obj->local_gotno + reloff - obj->gotsym]; def = find_symdef(reloff, obj, &defobj, SYMLOOK_IN_PLT, NULL, &lockstate); if (def == NULL) rtld_die(); target = (Elf_Addr)(defobj->relocbase + def->st_value); dbg("bind now/fixup at %s sym # %jd in %s --> was=%p new=%p", obj->path, (intmax_t)reloff, defobj->strtab + def->st_name, (void *)*where, (void *)target); if (!ld_bind_not) *where = target; lock_release(rtld_bind_lock, &lockstate); return (Elf_Addr)target; } int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Elf_Rel *rel; const Elf_Rel *rellim; Elf_Addr *got = obj->pltgot; const Elf_Sym *sym, *def; const Obj_Entry *defobj; Elf_Word i; #ifdef SUPPORT_OLD_BROKEN_LD int broken; #endif /* The relocation for the dynamic loader has already been done. */ if (obj == obj_rtld) return (0); if ((flags & SYMLOOK_IFUNC) != 0) /* XXX not implemented */ return (0); #ifdef SUPPORT_OLD_BROKEN_LD broken = 0; sym = obj->symtab; for (i = 1; i < 12; i++) if (sym[i].st_info == ELF_ST_INFO(STB_LOCAL, STT_NOTYPE)) broken = 1; dbg("%s: broken=%d", obj->path, broken); #endif i = GOT1_RESERVED_FOR_RTLD(got) ? 2 : 1; /* Relocate the local GOT entries */ got += i; dbg("got:%p for %d entries adding %p", got, obj->local_gotno, obj->relocbase); for (; i < obj->local_gotno; i++) { *got += (Elf_Addr)obj->relocbase; got++; } sym = obj->symtab + obj->gotsym; dbg("got:%p for %d entries", got, obj->symtabno); /* Now do the global GOT entries */ for (i = obj->gotsym; i < obj->symtabno; i++) { dbg(" doing got %d sym %p (%s, %lx)", i - obj->gotsym, sym, sym->st_name + obj->strtab, (u_long) *got); #ifdef SUPPORT_OLD_BROKEN_LD if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && broken && sym->st_shndx == SHN_UNDEF) { /* * XXX DANGER WILL ROBINSON! * You might think this is stupid, as it intentionally * defeats lazy binding -- and you'd be right. * Unfortunately, for lazy binding to work right, we * need to a way to force the GOT slots used for * function pointers to be resolved immediately. This * is supposed to be done automatically by the linker, * by not outputting a PLT slot and setting st_value * to 0 if there are non-PLT references, but older * versions of GNU ld do not do this. */ def = find_symdef(i, obj, &defobj, flags, NULL, lockstate); if (def == NULL) return -1; *got = def->st_value + (Elf_Addr)defobj->relocbase; } else #endif if (ELF_ST_TYPE(sym->st_info) == STT_FUNC && sym->st_value != 0 && sym->st_shndx == SHN_UNDEF) { /* * If there are non-PLT references to the function, * st_value should be 0, forcing us to resolve the * address immediately. * * XXX DANGER WILL ROBINSON! * The linker is not outputting PLT slots for calls to * functions that are defined in the same shared * library. This is a bug, because it can screw up * link ordering rules if the symbol is defined in * more than one module. For now, if there is a * definition, we fail the test above and force a full * symbol lookup. This means that all intra-module * calls are bound immediately. - mycroft, 2003/09/24 */ *got = sym->st_value + (Elf_Addr)obj->relocbase; if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { dbg("Warning2, i:%d maps to relocbase address:%p", i, obj->relocbase); } } else if (sym->st_info == ELF_ST_INFO(STB_GLOBAL, STT_SECTION)) { /* Symbols with index SHN_ABS are not relocated. */ if (sym->st_shndx != SHN_ABS) { *got = sym->st_value + (Elf_Addr)obj->relocbase; if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { dbg("Warning3, i:%d maps to relocbase address:%p", i, obj->relocbase); } } } else { /* TODO: add cache here */ def = find_symdef(i, obj, &defobj, flags, NULL, lockstate); if (def == NULL) { dbg("Warning4, can't find symbole %d", i); return -1; } *got = def->st_value + (Elf_Addr)defobj->relocbase; if ((Elf_Addr)(*got) == (Elf_Addr)obj->relocbase) { dbg("Warning4, i:%d maps to relocbase address:%p", i, obj->relocbase); dbg("via first obj symbol %s", obj->strtab + obj->symtab[i].st_name); dbg("found in obj %p:%s", defobj, defobj->path); } } dbg(" --> now %lx", (u_long) *got); ++sym; ++got; } got = obj->pltgot; rellim = (const Elf_Rel *)((caddr_t)obj->rel + obj->relsize); for (rel = obj->rel; rel < rellim; rel++) { Elf_Word r_symndx, r_type; void *where; where = obj->relocbase + rel->r_offset; r_symndx = ELF_R_SYM(rel->r_info); r_type = ELF_R_TYPE(rel->r_info); switch (r_type & 0xff) { case R_TYPE(NONE): break; case R_TYPE(REL32): { /* 32-bit PC-relative reference */ const size_t rlen = ELF_R_NXTTYPE_64_P(r_type) ? sizeof(Elf_Sxword) : sizeof(Elf_Sword); Elf_Sxword old = load_ptr(where, rlen); Elf_Sxword val = old; def = obj->symtab + r_symndx; if (r_symndx >= obj->gotsym) { val += got[obj->local_gotno + r_symndx - obj->gotsym]; dbg("REL32/G(%p) %p --> %p (%s) in %s", where, (void *)old, (void *)val, obj->strtab + def->st_name, obj->path); } else { /* * XXX: ABI DIFFERENCE! * * Old NetBSD binutils would generate shared * libs with section-relative relocations being * already adjusted for the start address of * the section. * * New binutils, OTOH, generate shared libs * with the same relocations being based at * zero, so we need to add in the start address * of the section. * * --rkb, Oct 6, 2001 */ if (def->st_info == ELF_ST_INFO(STB_LOCAL, STT_SECTION) #ifdef SUPPORT_OLD_BROKEN_LD && !broken #endif ) val += (Elf_Addr)def->st_value; val += (Elf_Addr)obj->relocbase; dbg("REL32/L(%p) %p -> %p (%s) in %s", where, (void *)old, (void *)val, obj->strtab + def->st_name, obj->path); } store_ptr(where, val, rlen); break; } case R_TYPE(COPY): /* * These are deferred until all other relocations have * been done. All we do here is make sure that the * COPY relocation is not in a shared library. They * are allowed only in executable files. */ if (!obj->mainprog) { _rtld_error("%s: Unexpected R_MIPS_COPY " "relocation in shared library", obj->path); return (-1); } break; #ifdef __mips_n64 case R_TYPE(TLS_DTPMOD64): #else case R_TYPE(TLS_DTPMOD32): #endif { const size_t rlen = sizeof(Elf_Addr); Elf_Addr old = load_ptr(where, rlen); Elf_Addr val = old; def = find_symdef(r_symndx, obj, &defobj, flags, NULL, lockstate); if (def == NULL) return -1; val += (Elf_Addr)defobj->tlsindex; store_ptr(where, val, rlen); dbg("DTPMOD %s in %s %p --> %p in %s", obj->strtab + obj->symtab[r_symndx].st_name, obj->path, (void *)old, (void*)val, defobj->path); break; } #ifdef __mips_n64 case R_TYPE(TLS_DTPREL64): #else case R_TYPE(TLS_DTPREL32): #endif { const size_t rlen = sizeof(Elf_Addr); Elf_Addr old = load_ptr(where, rlen); Elf_Addr val = old; def = find_symdef(r_symndx, obj, &defobj, flags, NULL, lockstate); if (def == NULL) return -1; if (!defobj->tls_done && allocate_tls_offset(obj)) return -1; val += (Elf_Addr)def->st_value - TLS_DTP_OFFSET; store_ptr(where, val, rlen); dbg("DTPREL %s in %s %p --> %p in %s", obj->strtab + obj->symtab[r_symndx].st_name, obj->path, (void*)old, (void *)val, defobj->path); break; } #ifdef __mips_n64 case R_TYPE(TLS_TPREL64): #else case R_TYPE(TLS_TPREL32): #endif { const size_t rlen = sizeof(Elf_Addr); Elf_Addr old = load_ptr(where, rlen); Elf_Addr val = old; def = find_symdef(r_symndx, obj, &defobj, flags, NULL, lockstate); if (def == NULL) return -1; if (!defobj->tls_done && allocate_tls_offset(obj)) return -1; val += (Elf_Addr)(def->st_value + defobj->tlsoffset - TLS_TP_OFFSET - TLS_TCB_SIZE); store_ptr(where, val, rlen); dbg("TPREL %s in %s %p --> %p in %s", obj->strtab + obj->symtab[r_symndx].st_name, obj->path, (void*)old, (void *)val, defobj->path); break; } default: dbg("sym = %lu, type = %lu, offset = %p, " "contents = %p, symbol = %s", (u_long)r_symndx, (u_long)ELF_R_TYPE(rel->r_info), (void *)rel->r_offset, (void *)load_ptr(where, sizeof(Elf_Sword)), obj->strtab + obj->symtab[r_symndx].st_name); _rtld_error("%s: Unsupported relocation type %ld " "in non-PLT relocations", obj->path, (u_long) ELF_R_TYPE(rel->r_info)); return -1; } } return 0; } /* * Process the PLT relocations. */ int reloc_plt(Obj_Entry *obj) { const Elf_Rel *rellim; const Elf_Rel *rel; rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { Elf_Addr *where; switch (ELF_R_TYPE(rel->r_info)) { case R_MIPS_JUMP_SLOT: where = (Elf_Addr *)(obj->relocbase + rel->r_offset); *where += (Elf_Addr )obj->relocbase; break; default: _rtld_error("Unknown relocation type %u in PLT", (unsigned int)ELF_R_TYPE(rel->r_info)); return (-1); } } return (0); } /* * LD_BIND_NOW was set - force relocation for all jump slots */ int reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Rel *rellim; const Elf_Rel *rel; const Elf_Sym *def; rellim = (const Elf_Rel *)((char *)obj->pltrel + obj->pltrelsize); for (rel = obj->pltrel; rel < rellim; rel++) { Elf_Addr *where; switch (ELF_R_TYPE(rel->r_info)) { case R_MIPS_JUMP_SLOT: def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) { dbg("reloc_jmpslots: sym not found"); return (-1); } where = (Elf_Addr *)(obj->relocbase + rel->r_offset); *where = (Elf_Addr)(defobj->relocbase + def->st_value); break; default: _rtld_error("Unknown relocation type %u in PLT", (unsigned int)ELF_R_TYPE(rel->r_info)); return (-1); } } return (0); } int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, int flags, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const Obj_Entry *defobj, const Obj_Entry *obj, const Elf_Rel *rel) { assert(ELF_R_TYPE(rel->r_info) == R_MIPS_JUMP_SLOT); if (*where != target && !ld_bind_not) *where = target; return (target); } void ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) { } void allocate_initial_tls(Obj_Entry *objs) { char *tls; /* * Fix the size of the static TLS block by using the maximum * offset allocated so far and adding a bit for dynamic modules to * use. */ tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; tls = (char *) allocate_tls(objs, NULL, TLS_TCB_SIZE, 8); sysarch(MIPS_SET_TLS, tls); } #ifdef __mips_n64 void * _mips_get_tls(void) { uint64_t _rv; __asm__ __volatile__ ( ".set\tpush\n\t" ".set\tmips64r2\n\t" "rdhwr\t%0, $29\n\t" ".set\tpop" : "=r" (_rv)); /* * XXXSS See 'git show c6be4f4d2d1b71c04de5d3bbb6933ce2dbcdb317' * * Remove the offset since this really a request to get the TLS * pointer via sysarch() (in theory). Of course, this may go away * once the TLS code is rewritten. */ _rv = _rv - TLS_TP_OFFSET - TLS_TCB_SIZE; return (void *)_rv; } #else /* mips 32 */ void * _mips_get_tls(void) { uint32_t _rv; __asm__ __volatile__ ( ".set\tpush\n\t" ".set\tmips32r2\n\t" "rdhwr\t%0, $29\n\t" ".set\tpop" : "=r" (_rv)); /* * XXXSS See 'git show c6be4f4d2d1b71c04de5d3bbb6933ce2dbcdb317' * * Remove the offset since this really a request to get the TLS * pointer via sysarch() (in theory). Of course, this may go away * once the TLS code is rewritten. */ _rv = _rv - TLS_TP_OFFSET - TLS_TCB_SIZE; return (void *)_rv; } #endif /* ! __mips_n64 */ void * __tls_get_addr(tls_index* ti) { Elf_Addr** tls; char *p; #ifdef TLS_USE_SYSARCH sysarch(MIPS_GET_TLS, &tls); #else tls = _mips_get_tls(); #endif p = tls_get_addr_common(tls, ti->ti_module, ti->ti_offset + TLS_DTP_OFFSET); return (p); } Index: head/libexec/rtld-elf/mips/rtld_machdep.h =================================================================== --- head/libexec/rtld-elf/mips/rtld_machdep.h (revision 326273) +++ head/libexec/rtld-elf/mips/rtld_machdep.h (revision 326274) @@ -1,77 +1,79 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_MACHDEP_H #define RTLD_MACHDEP_H 1 #include #include #include struct Struct_Obj_Entry; /* Return the address of the .dynamic section in the dynamic linker. */ #define rtld_dynamic(obj) (&_DYNAMIC) Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, const Elf_Rel *rel); #define make_function_pointer(def, defobj) \ ((defobj)->relocbase + (def)->st_value) #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) #define call_init_pointer(obj, target) \ (((InitArrFunc)(target))(main_argc, main_argv, environ)) #define call_ifunc_resolver(ptr) \ (((Elf_Addr (*)(void))ptr)()) typedef struct { unsigned long ti_module; unsigned long ti_offset; } tls_index; #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) #define calculate_first_tls_offset(size, align) \ round(TLS_TCB_SIZE, align) #define calculate_tls_offset(prev_offset, prev_size, size, align) \ round(prev_offset + prev_size, align) #define calculate_tls_end(off, size) ((off) + (size)) extern void *__tls_get_addr(tls_index *ti); #define RTLD_DEFAULT_STACK_PF_EXEC PF_X #define RTLD_DEFAULT_STACK_EXEC PROT_EXEC #define md_abi_variant_hook(x) #endif Index: head/libexec/rtld-elf/powerpc/reloc.c =================================================================== --- head/libexec/rtld-elf/powerpc/reloc.c (revision 326273) +++ head/libexec/rtld-elf/powerpc/reloc.c (revision 326274) @@ -1,665 +1,667 @@ /* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */ /*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD + * * Copyright (C) 1998 Tsubai Masanari * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "rtld.h" #define _ppc_ha(x) ((((u_int32_t)(x) & 0x8000) ? \ ((u_int32_t)(x) + 0x10000) : (u_int32_t)(x)) >> 16) #define _ppc_la(x) ((u_int32_t)(x) & 0xffff) #define min(a,b) (((a) < (b)) ? (a) : (b)) #define max(a,b) (((a) > (b)) ? (a) : (b)) #define PLT_EXTENDED_BEGIN (1 << 13) #define JMPTAB_BASE(N) (18 + N*2 + ((N > PLT_EXTENDED_BEGIN) ? \ (N - PLT_EXTENDED_BEGIN)*2 : 0)) /* * Process the R_PPC_COPY relocations */ int do_copy_relocations(Obj_Entry *dstobj) { const Elf_Rela *relalim; const Elf_Rela *rela; /* * COPY relocs are invalid outside of the main program */ assert(dstobj->mainprog); relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela + dstobj->relasize); for (rela = dstobj->rela; rela < relalim; rela++) { void *dstaddr; const Elf_Sym *dstsym; const char *name; size_t size; const void *srcaddr; const Elf_Sym *srcsym = NULL; const Obj_Entry *srcobj, *defobj; SymLook req; int res; if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) { continue; } dstaddr = (void *) (dstobj->relocbase + rela->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); name = dstobj->strtab + dstsym->st_name; size = dstsym->st_size; symlook_init(&req, name); req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); req.flags = SYMLOOK_EARLY; for (srcobj = globallist_next(dstobj); srcobj != NULL; srcobj = globallist_next(srcobj)) { res = symlook_obj(&req, srcobj); if (res == 0) { srcsym = req.sym_out; defobj = req.defobj_out; break; } } if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\" " " referenced from COPY" " relocation in %s", name, dstobj->path); return (-1); } srcaddr = (const void *) (defobj->relocbase+srcsym->st_value); memcpy(dstaddr, srcaddr, size); dbg("copy_reloc: src=%p,dst=%p,size=%d\n",srcaddr,dstaddr,size); } return (0); } /* * Perform early relocation of the run-time linker image */ void reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase) { const Elf_Rela *rela = NULL, *relalim; Elf_Addr relasz = 0; Elf_Addr *where; /* * Extract the rela/relasz values from the dynamic section */ for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_RELA: rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr); break; case DT_RELASZ: relasz = dynp->d_un.d_val; break; } } /* * Relocate these values */ relalim = (const Elf_Rela *)((caddr_t)rela + relasz); for (; rela < relalim; rela++) { where = (Elf_Addr *)(relocbase + rela->r_offset); *where = (Elf_Addr)(relocbase + rela->r_addend); } } /* * Relocate a non-PLT object with addend. */ static int reloc_nonplt_object(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate) { Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr tmp; switch (ELF_R_TYPE(rela->r_info)) { case R_PPC_NONE: break; case R_PPC_ADDR32: /* word32 S + A */ case R_PPC_GLOB_DAT: /* word32 S + A */ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) { return (-1); } tmp = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend); /* Don't issue write if unnecessary; avoid COW page fault */ if (*where != tmp) { *where = tmp; } break; case R_PPC_RELATIVE: /* word32 B + A */ tmp = (Elf_Addr)(obj->relocbase + rela->r_addend); /* As above, don't issue write unnecessarily */ if (*where != tmp) { *where = tmp; } break; case R_PPC_COPY: /* * These are deferred until all other relocations * have been done. All we do here is make sure * that the COPY relocation is not in a shared * library. They are allowed only in executable * files. */ if (!obj->mainprog) { _rtld_error("%s: Unexpected R_COPY " " relocation in shared library", obj->path); return (-1); } break; case R_PPC_JMP_SLOT: /* * These will be handled by the plt/jmpslot routines */ break; case R_PPC_DTPMOD32: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return (-1); *where = (Elf_Addr) defobj->tlsindex; break; case R_PPC_TPREL32: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return (-1); /* * We lazily allocate offsets for static TLS as we * see the first relocation that references the * TLS block. This allows us to support (small * amounts of) static TLS in dynamically loaded * modules. If we run out of space, we generate an * error. */ if (!defobj->tls_done) { if (!allocate_tls_offset((Obj_Entry*) defobj)) { _rtld_error("%s: No space available for static " "Thread Local Storage", obj->path); return (-1); } } *(Elf_Addr **)where = *where * sizeof(Elf_Addr) + (Elf_Addr *)(def->st_value + rela->r_addend + defobj->tlsoffset - TLS_TP_OFFSET); break; case R_PPC_DTPREL32: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return (-1); *where += (Elf_Addr)(def->st_value + rela->r_addend - TLS_DTV_OFFSET); break; default: _rtld_error("%s: Unsupported relocation type %d" " in non-PLT relocations\n", obj->path, ELF_R_TYPE(rela->r_info)); return (-1); } return (0); } /* * Process non-PLT relocations */ int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; SymCache *cache; int r = -1; if ((flags & SYMLOOK_IFUNC) != 0) /* XXX not implemented */ return (0); /* * The dynamic loader may be called from a thread, we have * limited amounts of stack available so we cannot use alloca(). */ if (obj != obj_rtld) { cache = calloc(obj->dynsymcount, sizeof(SymCache)); /* No need to check for NULL here */ } else cache = NULL; /* * From the SVR4 PPC ABI: * "The PowerPC family uses only the Elf32_Rela relocation * entries with explicit addends." */ relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); for (rela = obj->rela; rela < relalim; rela++) { if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags, lockstate) < 0) goto done; } r = 0; done: if (cache != NULL) free(cache); /* Synchronize icache for text seg in case we made any changes */ __syncicache(obj->mapbase, obj->textsize); return (r); } /* * Initialise a PLT slot to the resolving trampoline */ static int reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) { Elf_Word *where = (Elf_Word *)(obj->relocbase + rela->r_offset); Elf_Addr *pltresolve, *pltlongresolve, *jmptab; Elf_Addr distance; int N = obj->pltrelasize / sizeof(Elf_Rela); int reloff; reloff = rela - obj->pltrela; if (reloff < 0) return (-1); pltlongresolve = obj->pltgot + 5; pltresolve = pltlongresolve + 5; distance = (Elf_Addr)pltresolve - (Elf_Addr)(where + 1); dbg(" reloc_plt_object: where=%p,pltres=%p,reloff=%x,distance=%x", (void *)where, (void *)pltresolve, reloff, distance); if (reloff < PLT_EXTENDED_BEGIN) { /* li r11,reloff */ /* b pltresolve */ where[0] = 0x39600000 | reloff; where[1] = 0x48000000 | (distance & 0x03fffffc); } else { jmptab = obj->pltgot + JMPTAB_BASE(N); jmptab[reloff] = (u_int)pltlongresolve; /* lis r11,jmptab[reloff]@ha */ /* lwzu r12,jmptab[reloff]@l(r11) */ /* mtctr r12 */ /* bctr */ where[0] = 0x3d600000 | _ppc_ha(&jmptab[reloff]); where[1] = 0x858b0000 | _ppc_la(&jmptab[reloff]); where[2] = 0x7d8903a6; where[3] = 0x4e800420; } /* * The icache will be sync'd in reloc_plt, which is called * after all the slots have been updated */ return (0); } /* * Process the PLT relocations. */ int reloc_plt(Obj_Entry *obj) { const Elf_Rela *relalim; const Elf_Rela *rela; int N = obj->pltrelasize / sizeof(Elf_Rela); if (obj->pltrelasize != 0) { relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); if (reloc_plt_object(obj, rela) < 0) { return (-1); } } } /* * Sync the icache for the byte range represented by the * trampoline routines and call slots. */ if (obj->pltgot != NULL) __syncicache(obj->pltgot, JMPTAB_BASE(N)*4); return (0); } /* * LD_BIND_NOW was set - force relocation for all jump slots */ int reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; Elf_Addr *where; Elf_Addr target; relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) { dbg("reloc_jmpslots: sym not found"); return (-1); } target = (Elf_Addr)(defobj->relocbase + def->st_value); #if 0 /* PG XXX */ dbg("\"%s\" in \"%s\" --> %p in \"%s\"", defobj->strtab + def->st_name, basename(obj->path), (void *)target, basename(defobj->path)); #endif reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *) rela); } obj->jmpslots_done = true; return (0); } /* * Update the value of a PLT jump slot. Branch directly to the target if * it is within +/- 32Mb, otherwise go indirectly via the pltcall * trampoline call and jump table. */ Elf_Addr reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj, const Obj_Entry *obj, const Elf_Rel *rel) { Elf_Addr offset; const Elf_Rela *rela = (const Elf_Rela *) rel; dbg(" reloc_jmpslot: where=%p, target=%p", (void *)wherep, (void *)target); if (ld_bind_not) goto out; /* * At the PLT entry pointed at by `wherep', construct * a direct transfer to the now fully resolved function * address. */ offset = target - (Elf_Addr)wherep; if (abs((int)offset) < 32*1024*1024) { /* inside 32MB? */ /* b value # branch directly */ *wherep = 0x48000000 | (offset & 0x03fffffc); __syncicache(wherep, 4); } else { Elf_Addr *pltcall, *jmptab; int distance; int N = obj->pltrelasize / sizeof(Elf_Rela); int reloff = rela - obj->pltrela; if (reloff < 0) return (-1); pltcall = obj->pltgot; dbg(" reloc_jmpslot: indir, reloff=%x, N=%x\n", reloff, N); jmptab = obj->pltgot + JMPTAB_BASE(N); jmptab[reloff] = target; mb(); /* Order jmptab update before next changes */ if (reloff < PLT_EXTENDED_BEGIN) { /* for extended PLT entries, we keep the old code */ distance = (Elf_Addr)pltcall - (Elf_Addr)(wherep + 1); /* li r11,reloff */ /* b pltcall # use indirect pltcall routine */ /* first instruction same as before */ wherep[1] = 0x48000000 | (distance & 0x03fffffc); __syncicache(wherep, 8); } } out: return (target); } int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, int flags, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } /* * Setup the plt glue routines. */ #define PLTCALL_SIZE 20 #define PLTLONGRESOLVE_SIZE 20 #define PLTRESOLVE_SIZE 24 void init_pltgot(Obj_Entry *obj) { Elf_Word *pltcall, *pltresolve, *pltlongresolve; Elf_Word *jmptab; int N = obj->pltrelasize / sizeof(Elf_Rela); pltcall = obj->pltgot; if (pltcall == NULL) { return; } /* * From the SVR4 PPC ABI: * * 'The first 18 words (72 bytes) of the PLT are reserved for * use by the dynamic linker. * ... * 'If the executable or shared object requires N procedure * linkage table entries, the link editor shall reserve 3*N * words (12*N bytes) following the 18 reserved words. The * first 2*N of these words are the procedure linkage table * entries themselves. The static linker directs calls to bytes * (72 + (i-1)*8), for i between 1 and N inclusive. The remaining * N words (4*N bytes) are reserved for use by the dynamic linker.' */ /* * Copy the absolute-call assembler stub into the first part of * the reserved PLT area. */ memcpy(pltcall, _rtld_powerpc_pltcall, PLTCALL_SIZE); /* * Determine the address of the jumptable, which is the dyn-linker * reserved area after the call cells. Write the absolute address * of the jumptable into the absolute-call assembler code so it * can determine this address. */ jmptab = obj->pltgot + JMPTAB_BASE(N); pltcall[1] |= _ppc_ha(jmptab); /* addis 11,11,jmptab@ha */ pltcall[2] |= _ppc_la(jmptab); /* lwz 11,jmptab@l(11) */ /* * Skip down 20 bytes into the initial reserved area and copy * in the standard resolving assembler call. Into this assembler, * insert the absolute address of the _rtld_bind_start routine * and the address of the relocation object. * * We place pltlongresolve first, so it can fix up its arguments * and then fall through to the regular PLT resolver. */ pltlongresolve = obj->pltgot + 5; memcpy(pltlongresolve, _rtld_powerpc_pltlongresolve, PLTLONGRESOLVE_SIZE); pltlongresolve[0] |= _ppc_ha(jmptab); /* lis 12,jmptab@ha */ pltlongresolve[1] |= _ppc_la(jmptab); /* addi 12,12,jmptab@l */ pltresolve = pltlongresolve + PLTLONGRESOLVE_SIZE/sizeof(uint32_t); memcpy(pltresolve, _rtld_powerpc_pltresolve, PLTRESOLVE_SIZE); pltresolve[0] |= _ppc_ha(_rtld_bind_start); pltresolve[1] |= _ppc_la(_rtld_bind_start); pltresolve[3] |= _ppc_ha(obj); pltresolve[4] |= _ppc_la(obj); /* * The icache will be sync'd in reloc_plt, which is called * after all the slots have been updated */ } void ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) { } void allocate_initial_tls(Obj_Entry *list) { Elf_Addr **tp; /* * Fix the size of the static TLS block by using the maximum * offset allocated so far and adding a bit for dynamic modules to * use. */ tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; tp = (Elf_Addr **) ((char *) allocate_tls(list, NULL, TLS_TCB_SIZE, 8) + TLS_TP_OFFSET + TLS_TCB_SIZE); /* * XXX gcc seems to ignore 'tp = _tp;' */ __asm __volatile("mr 2,%0" :: "r"(tp)); } void* __tls_get_addr(tls_index* ti) { register Elf_Addr **tp; char *p; __asm __volatile("mr %0,2" : "=r"(tp)); p = tls_get_addr_common((Elf_Addr**)((Elf_Addr)tp - TLS_TP_OFFSET - TLS_TCB_SIZE), ti->ti_module, ti->ti_offset); return (p + TLS_DTV_OFFSET); } Index: head/libexec/rtld-elf/powerpc/rtld_machdep.h =================================================================== --- head/libexec/rtld-elf/powerpc/rtld_machdep.h (revision 326273) +++ head/libexec/rtld-elf/powerpc/rtld_machdep.h (revision 326274) @@ -1,92 +1,94 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_MACHDEP_H #define RTLD_MACHDEP_H 1 #include #include struct Struct_Obj_Entry; /* Return the address of the .dynamic section in the dynamic linker. */ #define rtld_dynamic(obj) (&_DYNAMIC) Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, const Elf_Rel *rel); #define make_function_pointer(def, defobj) \ ((defobj)->relocbase + (def)->st_value) #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) #define call_init_pointer(obj, target) \ (((InitArrFunc)(target))(main_argc, main_argv, environ)) #define call_ifunc_resolver(ptr) \ (((Elf_Addr (*)(void))ptr)()) /* * PLT functions. Not really correct prototypes, but the * symbol values are needed. */ void _rtld_powerpc_pltlongresolve(void); void _rtld_powerpc_pltresolve(void); void _rtld_powerpc_pltcall(void); /* * TLS */ #define TLS_TP_OFFSET 0x7000 #define TLS_DTV_OFFSET 0x8000 #define TLS_TCB_SIZE 8 #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) #define calculate_first_tls_offset(size, align) \ round(8, align) #define calculate_tls_offset(prev_offset, prev_size, size, align) \ round(prev_offset + prev_size, align) #define calculate_tls_end(off, size) ((off) + (size)) typedef struct { unsigned long ti_module; unsigned long ti_offset; } tls_index; extern void *__tls_get_addr(tls_index* ti); #define RTLD_DEFAULT_STACK_PF_EXEC PF_X #define RTLD_DEFAULT_STACK_EXEC PROT_EXEC #define md_abi_variant_hook(x) #endif Index: head/libexec/rtld-elf/powerpc64/reloc.c =================================================================== --- head/libexec/rtld-elf/powerpc64/reloc.c (revision 326273) +++ head/libexec/rtld-elf/powerpc64/reloc.c (revision 326274) @@ -1,566 +1,568 @@ /* $NetBSD: ppc_reloc.c,v 1.10 2001/09/10 06:09:41 mycroft Exp $ */ /*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD + * * Copyright (C) 1998 Tsubai Masanari * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "debug.h" #include "rtld.h" #if !defined(_CALL_ELF) || _CALL_ELF == 1 struct funcdesc { Elf_Addr addr; Elf_Addr toc; Elf_Addr env; }; #endif /* * Process the R_PPC_COPY relocations */ int do_copy_relocations(Obj_Entry *dstobj) { const Elf_Rela *relalim; const Elf_Rela *rela; /* * COPY relocs are invalid outside of the main program */ assert(dstobj->mainprog); relalim = (const Elf_Rela *) ((caddr_t) dstobj->rela + dstobj->relasize); for (rela = dstobj->rela; rela < relalim; rela++) { void *dstaddr; const Elf_Sym *dstsym; const char *name; size_t size; const void *srcaddr; const Elf_Sym *srcsym = NULL; const Obj_Entry *srcobj, *defobj; SymLook req; int res; if (ELF_R_TYPE(rela->r_info) != R_PPC_COPY) { continue; } dstaddr = (void *) (dstobj->relocbase + rela->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); name = dstobj->strtab + dstsym->st_name; size = dstsym->st_size; symlook_init(&req, name); req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); req.flags = SYMLOOK_EARLY; for (srcobj = globallist_next(dstobj); srcobj != NULL; srcobj = globallist_next(srcobj)) { res = symlook_obj(&req, srcobj); if (res == 0) { srcsym = req.sym_out; defobj = req.defobj_out; break; } } if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\" " " referenced from COPY" " relocation in %s", name, dstobj->path); return (-1); } srcaddr = (const void *) (defobj->relocbase+srcsym->st_value); memcpy(dstaddr, srcaddr, size); dbg("copy_reloc: src=%p,dst=%p,size=%zd\n",srcaddr,dstaddr,size); } return (0); } /* * Perform early relocation of the run-time linker image */ void reloc_non_plt_self(Elf_Dyn *dynp, Elf_Addr relocbase) { const Elf_Rela *rela = NULL, *relalim; Elf_Addr relasz = 0; Elf_Addr *where; /* * Extract the rela/relasz values from the dynamic section */ for (; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_RELA: rela = (const Elf_Rela *)(relocbase+dynp->d_un.d_ptr); break; case DT_RELASZ: relasz = dynp->d_un.d_val; break; } } /* * Relocate these values */ relalim = (const Elf_Rela *)((caddr_t)rela + relasz); for (; rela < relalim; rela++) { where = (Elf_Addr *)(relocbase + rela->r_offset); *where = (Elf_Addr)(relocbase + rela->r_addend); } } /* * Relocate a non-PLT object with addend. */ static int reloc_nonplt_object(Obj_Entry *obj_rtld, Obj_Entry *obj, const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate) { Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr tmp; switch (ELF_R_TYPE(rela->r_info)) { case R_PPC_NONE: break; case R_PPC64_UADDR64: /* doubleword64 S + A */ case R_PPC64_ADDR64: case R_PPC_GLOB_DAT: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) { return (-1); } tmp = (Elf_Addr)(defobj->relocbase + def->st_value + rela->r_addend); /* Don't issue write if unnecessary; avoid COW page fault */ if (*where != tmp) { *where = tmp; } break; case R_PPC_RELATIVE: /* doubleword64 B + A */ tmp = (Elf_Addr)(obj->relocbase + rela->r_addend); /* As above, don't issue write unnecessarily */ if (*where != tmp) { *where = tmp; } break; case R_PPC_COPY: /* * These are deferred until all other relocations * have been done. All we do here is make sure * that the COPY relocation is not in a shared * library. They are allowed only in executable * files. */ if (!obj->mainprog) { _rtld_error("%s: Unexpected R_COPY " " relocation in shared library", obj->path); return (-1); } break; case R_PPC_JMP_SLOT: /* * These will be handled by the plt/jmpslot routines */ break; case R_PPC64_DTPMOD64: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return (-1); *where = (Elf_Addr) defobj->tlsindex; break; case R_PPC64_TPREL64: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return (-1); /* * We lazily allocate offsets for static TLS as we * see the first relocation that references the * TLS block. This allows us to support (small * amounts of) static TLS in dynamically loaded * modules. If we run out of space, we generate an * error. */ if (!defobj->tls_done) { if (!allocate_tls_offset((Obj_Entry*) defobj)) { _rtld_error("%s: No space available for static " "Thread Local Storage", obj->path); return (-1); } } *(Elf_Addr **)where = *where * sizeof(Elf_Addr) + (Elf_Addr *)(def->st_value + rela->r_addend + defobj->tlsoffset - TLS_TP_OFFSET); break; case R_PPC64_DTPREL64: def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return (-1); *where += (Elf_Addr)(def->st_value + rela->r_addend - TLS_DTV_OFFSET); break; default: _rtld_error("%s: Unsupported relocation type %ld" " in non-PLT relocations\n", obj->path, ELF_R_TYPE(rela->r_info)); return (-1); } return (0); } /* * Process non-PLT relocations */ int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; SymCache *cache; int bytes = obj->dynsymcount * sizeof(SymCache); int r = -1; if ((flags & SYMLOOK_IFUNC) != 0) /* XXX not implemented */ return (0); /* * The dynamic loader may be called from a thread, we have * limited amounts of stack available so we cannot use alloca(). */ if (obj != obj_rtld) { cache = mmap(NULL, bytes, PROT_READ|PROT_WRITE, MAP_ANON, -1, 0); if (cache == MAP_FAILED) cache = NULL; } else cache = NULL; /* * From the SVR4 PPC ABI: * "The PowerPC family uses only the Elf32_Rela relocation * entries with explicit addends." */ relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); for (rela = obj->rela; rela < relalim; rela++) { if (reloc_nonplt_object(obj_rtld, obj, rela, cache, flags, lockstate) < 0) goto done; } r = 0; done: if (cache) munmap(cache, bytes); /* Synchronize icache for text seg in case we made any changes */ __syncicache(obj->mapbase, obj->textsize); return (r); } /* * Initialise a PLT slot to the resolving trampoline */ static int reloc_plt_object(Obj_Entry *obj, const Elf_Rela *rela) { Elf_Addr *where = (Elf_Addr *)(obj->relocbase + rela->r_offset); long reloff; reloff = rela - obj->pltrela; dbg(" reloc_plt_object: where=%p,reloff=%lx,glink=%#lx", (void *)where, reloff, obj->glink); #if !defined(_CALL_ELF) || _CALL_ELF == 1 /* Glink code is 3 instructions after the first 32k, 2 before */ *where = (Elf_Addr)obj->glink + 32 + 8*((reloff < 0x8000) ? reloff : 0x8000) + 12*((reloff < 0x8000) ? 0 : (reloff - 0x8000)); #else *where = (Elf_Addr)obj->glink + 4*reloff + 32; #endif return (0); } /* * Process the PLT relocations. */ int reloc_plt(Obj_Entry *obj) { const Elf_Rela *relalim; const Elf_Rela *rela; if (obj->pltrelasize != 0) { relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); if (reloc_plt_object(obj, rela) < 0) { return (-1); } } } return (0); } /* * LD_BIND_NOW was set - force relocation for all jump slots */ int reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; Elf_Addr *where; Elf_Addr target; relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { assert(ELF_R_TYPE(rela->r_info) == R_PPC_JMP_SLOT); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) { dbg("reloc_jmpslots: sym not found"); return (-1); } target = (Elf_Addr)(defobj->relocbase + def->st_value); if (def == &sym_zero) { /* Zero undefined weak symbols */ #if !defined(_CALL_ELF) || _CALL_ELF == 1 bzero(where, sizeof(struct funcdesc)); #else *where = 0; #endif } else { reloc_jmpslot(where, target, defobj, obj, (const Elf_Rel *) rela); } } obj->jmpslots_done = true; return (0); } /* * Update the value of a PLT jump slot. */ Elf_Addr reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *defobj, const Obj_Entry *obj, const Elf_Rel *rel) { /* * At the PLT entry pointed at by `wherep', construct * a direct transfer to the now fully resolved function * address. */ #if !defined(_CALL_ELF) || _CALL_ELF == 1 dbg(" reloc_jmpslot: where=%p, target=%p (%#lx + %#lx)", (void *)wherep, (void *)target, *(Elf_Addr *)target, (Elf_Addr)defobj->relocbase); if (ld_bind_not) goto out; /* * For the trampoline, the second two elements of the function * descriptor are unused, so we are fine replacing those at any time * with the real ones with no thread safety implications. However, we * need to make sure the main entry point pointer ([0]) is seen to be * modified *after* the second two elements. This can't be done in * general, since there are no barriers in the reading code, but put in * some isyncs to at least make it a little better. */ memcpy(wherep, (void *)target, sizeof(struct funcdesc)); wherep[2] = ((Elf_Addr *)target)[2]; wherep[1] = ((Elf_Addr *)target)[1]; __asm __volatile ("isync" : : : "memory"); wherep[0] = ((Elf_Addr *)target)[0]; __asm __volatile ("isync" : : : "memory"); if (((struct funcdesc *)(wherep))->addr < (Elf_Addr)defobj->relocbase) { /* * It is possible (LD_BIND_NOW) that the function * descriptor we are copying has not yet been relocated. * If this happens, fix it. Don't worry about threading in * this case since LD_BIND_NOW makes it irrelevant. */ ((struct funcdesc *)(wherep))->addr += (Elf_Addr)defobj->relocbase; ((struct funcdesc *)(wherep))->toc += (Elf_Addr)defobj->relocbase; } out: #else dbg(" reloc_jmpslot: where=%p, target=%p", (void *)wherep, (void *)target); if (!ld_bind_not) *wherep = target; #endif return (target); } int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, int flags, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } void init_pltgot(Obj_Entry *obj) { Elf_Addr *pltcall; pltcall = obj->pltgot; if (pltcall == NULL) { return; } #if defined(_CALL_ELF) && _CALL_ELF == 2 pltcall[0] = (Elf_Addr)&_rtld_bind_start; pltcall[1] = (Elf_Addr)obj; #else memcpy(pltcall, _rtld_bind_start, sizeof(struct funcdesc)); pltcall[2] = (Elf_Addr)obj; #endif } void ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) { } void allocate_initial_tls(Obj_Entry *list) { Elf_Addr **tp; /* * Fix the size of the static TLS block by using the maximum * offset allocated so far and adding a bit for dynamic modules to * use. */ tls_static_space = tls_last_offset + tls_last_size + RTLD_STATIC_TLS_EXTRA; tp = (Elf_Addr **) ((char *)allocate_tls(list, NULL, TLS_TCB_SIZE, 16) + TLS_TP_OFFSET + TLS_TCB_SIZE); __asm __volatile("mr 13,%0" :: "r"(tp)); } void* __tls_get_addr(tls_index* ti) { Elf_Addr **tp; char *p; __asm __volatile("mr %0,13" : "=r"(tp)); p = tls_get_addr_common((Elf_Addr**)((Elf_Addr)tp - TLS_TP_OFFSET - TLS_TCB_SIZE), ti->ti_module, ti->ti_offset); return (p + TLS_DTV_OFFSET); } Index: head/libexec/rtld-elf/powerpc64/rtld_machdep.h =================================================================== --- head/libexec/rtld-elf/powerpc64/rtld_machdep.h (revision 326273) +++ head/libexec/rtld-elf/powerpc64/rtld_machdep.h (revision 326274) @@ -1,84 +1,86 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_MACHDEP_H #define RTLD_MACHDEP_H 1 #include #include struct Struct_Obj_Entry; /* Return the address of the .dynamic section in the dynamic linker. */ #define rtld_dynamic(obj) (&_DYNAMIC) Elf_Addr reloc_jmpslot(Elf_Addr *where, Elf_Addr target, const struct Struct_Obj_Entry *defobj, const struct Struct_Obj_Entry *obj, const Elf_Rel *rel); #define make_function_pointer(def, defobj) \ ((defobj)->relocbase + (def)->st_value) #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) #define call_init_pointer(obj, target) \ (((InitArrFunc)(target))(main_argc, main_argv, environ)) #define call_ifunc_resolver(ptr) \ (((Elf_Addr (*)(void))ptr)()) /* * TLS */ #define TLS_TP_OFFSET 0x7000 #define TLS_DTV_OFFSET 0x8000 #define TLS_TCB_SIZE 16 #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) #define calculate_first_tls_offset(size, align) \ round(16, align) #define calculate_tls_offset(prev_offset, prev_size, size, align) \ round(prev_offset + prev_size, align) #define calculate_tls_end(off, size) ((off) + (size)) typedef struct { unsigned long ti_module; unsigned long ti_offset; } tls_index; extern void *__tls_get_addr(tls_index* ti); #define RTLD_DEFAULT_STACK_PF_EXEC PF_X #define RTLD_DEFAULT_STACK_EXEC PROT_EXEC #define md_abi_variant_hook(x) #endif Index: head/libexec/rtld-elf/rtld.c =================================================================== --- head/libexec/rtld-elf/rtld.c (revision 326273) +++ head/libexec/rtld-elf/rtld.c (revision 326274) @@ -1,5556 +1,5558 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra. * Copyright 2003 Alexander Kabaev . * Copyright 2009-2013 Konstantin Belousov . * Copyright 2012 John Marino . * Copyright 2014-2017 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * Dynamic linker for ELF. * * John Polstra . */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "debug.h" #include "rtld.h" #include "libmap.h" #include "paths.h" #include "rtld_tls.h" #include "rtld_printf.h" #include "rtld_utrace.h" #include "notes.h" /* Types. */ typedef void (*func_ptr_type)(); typedef void * (*path_enum_proc) (const char *path, size_t len, void *arg); /* * Function declarations. */ static const char *basename(const char *); static void digest_dynamic1(Obj_Entry *, int, const Elf_Dyn **, const Elf_Dyn **, const Elf_Dyn **); static void digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *, const Elf_Dyn *); static void digest_dynamic(Obj_Entry *, int); static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *); static Obj_Entry *dlcheck(void *); static int dlclose_locked(void *, RtldLockState *); static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags, int mode, RtldLockState *lockstate); static Obj_Entry *do_load_object(int, const char *, char *, struct stat *, int); static int do_search_info(const Obj_Entry *obj, int, struct dl_serinfo *); static bool donelist_check(DoneList *, const Obj_Entry *); static void errmsg_restore(char *); static char *errmsg_save(void); static void *fill_search_info(const char *, size_t, void *); static char *find_library(const char *, const Obj_Entry *, int *); static const char *gethints(bool); static void hold_object(Obj_Entry *); static void unhold_object(Obj_Entry *); static void init_dag(Obj_Entry *); static void init_marker(Obj_Entry *); static void init_pagesizes(Elf_Auxinfo **aux_info); static void init_rtld(caddr_t, Elf_Auxinfo **); static void initlist_add_neededs(Needed_Entry *, Objlist *); static void initlist_add_objects(Obj_Entry *, Obj_Entry *, Objlist *); static void linkmap_add(Obj_Entry *); static void linkmap_delete(Obj_Entry *); static void load_filtees(Obj_Entry *, int flags, RtldLockState *); static void unload_filtees(Obj_Entry *, RtldLockState *); static int load_needed_objects(Obj_Entry *, int); static int load_preload_objects(void); static Obj_Entry *load_object(const char *, int fd, const Obj_Entry *, int); static void map_stacks_exec(RtldLockState *); static int obj_enforce_relro(Obj_Entry *); static Obj_Entry *obj_from_addr(const void *); static void objlist_call_fini(Objlist *, Obj_Entry *, RtldLockState *); static void objlist_call_init(Objlist *, RtldLockState *); static void objlist_clear(Objlist *); static Objlist_Entry *objlist_find(Objlist *, const Obj_Entry *); static void objlist_init(Objlist *); static void objlist_push_head(Objlist *, Obj_Entry *); static void objlist_push_tail(Objlist *, Obj_Entry *); static void objlist_put_after(Objlist *, Obj_Entry *, Obj_Entry *); static void objlist_remove(Objlist *, Obj_Entry *); static int open_binary_fd(const char *argv0, bool search_in_path); static int parse_args(char* argv[], int argc, bool *use_pathp, int *fdp); static int parse_integer(const char *); static void *path_enumerate(const char *, path_enum_proc, void *); static void print_usage(const char *argv0); static void release_object(Obj_Entry *); static int relocate_object_dag(Obj_Entry *root, bool bind_now, Obj_Entry *rtldobj, int flags, RtldLockState *lockstate); static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, int flags, RtldLockState *lockstate); static int relocate_objects(Obj_Entry *, bool, Obj_Entry *, int, RtldLockState *); static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now, int flags, RtldLockState *lockstate); static int rtld_dirname(const char *, char *); static int rtld_dirname_abs(const char *, char *); static void *rtld_dlopen(const char *name, int fd, int mode); static void rtld_exit(void); static char *search_library_path(const char *, const char *, int *); static char *search_library_pathfds(const char *, const char *, int *); static const void **get_program_var_addr(const char *, RtldLockState *); static void set_program_var(const char *, const void *); static int symlook_default(SymLook *, const Obj_Entry *refobj); static int symlook_global(SymLook *, DoneList *); static void symlook_init_from_req(SymLook *, const SymLook *); static int symlook_list(SymLook *, const Objlist *, DoneList *); static int symlook_needed(SymLook *, const Needed_Entry *, DoneList *); static int symlook_obj1_sysv(SymLook *, const Obj_Entry *); static int symlook_obj1_gnu(SymLook *, const Obj_Entry *); static void trace_loaded_objects(Obj_Entry *); static void unlink_object(Obj_Entry *); static void unload_object(Obj_Entry *, RtldLockState *lockstate); static void unref_dag(Obj_Entry *); static void ref_dag(Obj_Entry *); static char *origin_subst_one(Obj_Entry *, char *, const char *, const char *, bool); static char *origin_subst(Obj_Entry *, char *); static bool obj_resolve_origin(Obj_Entry *obj); static void preinit_main(void); static int rtld_verify_versions(const Objlist *); static int rtld_verify_object_versions(Obj_Entry *); static void object_add_name(Obj_Entry *, const char *); static int object_match_name(const Obj_Entry *, const char *); static void ld_utrace_log(int, void *, void *, size_t, int, const char *); static void rtld_fill_dl_phdr_info(const Obj_Entry *obj, struct dl_phdr_info *phdr_info); static uint32_t gnu_hash(const char *); static bool matched_symbol(SymLook *, const Obj_Entry *, Sym_Match_Result *, const unsigned long); void r_debug_state(struct r_debug *, struct link_map *) __noinline __exported; void _r_debug_postinit(struct link_map *) __noinline __exported; int __sys_openat(int, const char *, int, ...); /* * Data declarations. */ static char *error_message; /* Message for dlerror(), or NULL */ struct r_debug r_debug __exported; /* for GDB; */ static bool libmap_disable; /* Disable libmap */ static bool ld_loadfltr; /* Immediate filters processing */ static char *libmap_override; /* Maps to use in addition to libmap.conf */ static bool trust; /* False for setuid and setgid programs */ static bool dangerous_ld_env; /* True if environment variables have been used to affect the libraries loaded */ bool ld_bind_not; /* Disable PLT update */ static char *ld_bind_now; /* Environment variable for immediate binding */ static char *ld_debug; /* Environment variable for debugging */ static char *ld_library_path; /* Environment variable for search path */ static char *ld_library_dirs; /* Environment variable for library descriptors */ static char *ld_preload; /* Environment variable for libraries to load first */ static char *ld_elf_hints_path; /* Environment variable for alternative hints path */ static char *ld_tracing; /* Called from ldd to print libs */ static char *ld_utrace; /* Use utrace() to log events. */ static struct obj_entry_q obj_list; /* Queue of all loaded objects */ static Obj_Entry *obj_main; /* The main program shared object */ static Obj_Entry obj_rtld; /* The dynamic linker shared object */ static unsigned int obj_count; /* Number of objects in obj_list */ static unsigned int obj_loads; /* Number of loads of objects (gen count) */ static Objlist list_global = /* Objects dlopened with RTLD_GLOBAL */ STAILQ_HEAD_INITIALIZER(list_global); static Objlist list_main = /* Objects loaded at program startup */ STAILQ_HEAD_INITIALIZER(list_main); static Objlist list_fini = /* Objects needing fini() calls */ STAILQ_HEAD_INITIALIZER(list_fini); Elf_Sym sym_zero; /* For resolving undefined weak refs. */ #define GDB_STATE(s,m) r_debug.r_state = s; r_debug_state(&r_debug,m); extern Elf_Dyn _DYNAMIC; #pragma weak _DYNAMIC int dlclose(void *) __exported; char *dlerror(void) __exported; void *dlopen(const char *, int) __exported; void *fdlopen(int, int) __exported; void *dlsym(void *, const char *) __exported; dlfunc_t dlfunc(void *, const char *) __exported; void *dlvsym(void *, const char *, const char *) __exported; int dladdr(const void *, Dl_info *) __exported; void dllockinit(void *, void *(*)(void *), void (*)(void *), void (*)(void *), void (*)(void *), void (*)(void *), void (*)(void *)) __exported; int dlinfo(void *, int , void *) __exported; int dl_iterate_phdr(__dl_iterate_hdr_callback, void *) __exported; int _rtld_addr_phdr(const void *, struct dl_phdr_info *) __exported; int _rtld_get_stack_prot(void) __exported; int _rtld_is_dlopened(void *) __exported; void _rtld_error(const char *, ...) __exported; int npagesizes, osreldate; size_t *pagesizes; long __stack_chk_guard[8] = {0, 0, 0, 0, 0, 0, 0, 0}; static int stack_prot = PROT_READ | PROT_WRITE | RTLD_DEFAULT_STACK_EXEC; static int max_stack_flags; /* * Global declarations normally provided by crt1. The dynamic linker is * not built with crt1, so we have to provide them ourselves. */ char *__progname; char **environ; /* * Used to pass argc, argv to init functions. */ int main_argc; char **main_argv; /* * Globals to control TLS allocation. */ size_t tls_last_offset; /* Static TLS offset of last module */ size_t tls_last_size; /* Static TLS size of last module */ size_t tls_static_space; /* Static TLS space allocated */ size_t tls_static_max_align; int tls_dtv_generation = 1; /* Used to detect when dtv size changes */ int tls_max_index = 1; /* Largest module index allocated */ bool ld_library_path_rpath = false; /* * Globals for path names, and such */ char *ld_elf_hints_default = _PATH_ELF_HINTS; char *ld_path_libmap_conf = _PATH_LIBMAP_CONF; char *ld_path_rtld = _PATH_RTLD; char *ld_standard_library_path = STANDARD_LIBRARY_PATH; char *ld_env_prefix = LD_; /* * Fill in a DoneList with an allocation large enough to hold all of * the currently-loaded objects. Keep this as a macro since it calls * alloca and we want that to occur within the scope of the caller. */ #define donelist_init(dlp) \ ((dlp)->objs = alloca(obj_count * sizeof (dlp)->objs[0]), \ assert((dlp)->objs != NULL), \ (dlp)->num_alloc = obj_count, \ (dlp)->num_used = 0) #define LD_UTRACE(e, h, mb, ms, r, n) do { \ if (ld_utrace != NULL) \ ld_utrace_log(e, h, mb, ms, r, n); \ } while (0) static void ld_utrace_log(int event, void *handle, void *mapbase, size_t mapsize, int refcnt, const char *name) { struct utrace_rtld ut; static const char rtld_utrace_sig[RTLD_UTRACE_SIG_SZ] = RTLD_UTRACE_SIG; memcpy(ut.sig, rtld_utrace_sig, sizeof(ut.sig)); ut.event = event; ut.handle = handle; ut.mapbase = mapbase; ut.mapsize = mapsize; ut.refcnt = refcnt; bzero(ut.name, sizeof(ut.name)); if (name) strlcpy(ut.name, name, sizeof(ut.name)); utrace(&ut, sizeof(ut)); } #ifdef RTLD_VARIANT_ENV_NAMES /* * construct the env variable based on the type of binary that's * running. */ static inline const char * _LD(const char *var) { static char buffer[128]; strlcpy(buffer, ld_env_prefix, sizeof(buffer)); strlcat(buffer, var, sizeof(buffer)); return (buffer); } #else #define _LD(x) LD_ x #endif /* * Main entry point for dynamic linking. The first argument is the * stack pointer. The stack is expected to be laid out as described * in the SVR4 ABI specification, Intel 386 Processor Supplement. * Specifically, the stack pointer points to a word containing * ARGC. Following that in the stack is a null-terminated sequence * of pointers to argument strings. Then comes a null-terminated * sequence of pointers to environment strings. Finally, there is a * sequence of "auxiliary vector" entries. * * The second argument points to a place to store the dynamic linker's * exit procedure pointer and the third to a place to store the main * program's object. * * The return value is the main program's entry point. */ func_ptr_type _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) { Elf_Auxinfo *aux, *auxp, *auxpf, *aux_info[AT_COUNT]; Objlist_Entry *entry; Obj_Entry *last_interposer, *obj, *preload_tail; const Elf_Phdr *phdr; Objlist initlist; RtldLockState lockstate; struct stat st; Elf_Addr *argcp; char **argv, *argv0, **env, **envp, *kexecpath, *library_path_rpath; caddr_t imgentry; char buf[MAXPATHLEN]; int argc, fd, i, mib[2], phnum, rtld_argc; size_t len; bool dir_enable, explicit_fd, search_in_path; /* * On entry, the dynamic linker itself has not been relocated yet. * Be very careful not to reference any global data until after * init_rtld has returned. It is OK to reference file-scope statics * and string constants, and to call static and global functions. */ /* Find the auxiliary vector on the stack. */ argcp = sp; argc = *sp++; argv = (char **) sp; sp += argc + 1; /* Skip over arguments and NULL terminator */ env = (char **) sp; while (*sp++ != 0) /* Skip over environment, and NULL terminator */ ; aux = (Elf_Auxinfo *) sp; /* Digest the auxiliary vector. */ for (i = 0; i < AT_COUNT; i++) aux_info[i] = NULL; for (auxp = aux; auxp->a_type != AT_NULL; auxp++) { if (auxp->a_type < AT_COUNT) aux_info[auxp->a_type] = auxp; } /* Initialize and relocate ourselves. */ assert(aux_info[AT_BASE] != NULL); init_rtld((caddr_t) aux_info[AT_BASE]->a_un.a_ptr, aux_info); __progname = obj_rtld.path; argv0 = argv[0] != NULL ? argv[0] : "(null)"; environ = env; main_argc = argc; main_argv = argv; if (aux_info[AT_CANARY] != NULL && aux_info[AT_CANARY]->a_un.a_ptr != NULL) { i = aux_info[AT_CANARYLEN]->a_un.a_val; if (i > sizeof(__stack_chk_guard)) i = sizeof(__stack_chk_guard); memcpy(__stack_chk_guard, aux_info[AT_CANARY]->a_un.a_ptr, i); } else { mib[0] = CTL_KERN; mib[1] = KERN_ARND; len = sizeof(__stack_chk_guard); if (sysctl(mib, 2, __stack_chk_guard, &len, NULL, 0) == -1 || len != sizeof(__stack_chk_guard)) { /* If sysctl was unsuccessful, use the "terminator canary". */ ((unsigned char *)(void *)__stack_chk_guard)[0] = 0; ((unsigned char *)(void *)__stack_chk_guard)[1] = 0; ((unsigned char *)(void *)__stack_chk_guard)[2] = '\n'; ((unsigned char *)(void *)__stack_chk_guard)[3] = 255; } } trust = !issetugid(); md_abi_variant_hook(aux_info); fd = -1; if (aux_info[AT_EXECFD] != NULL) { fd = aux_info[AT_EXECFD]->a_un.a_val; } else { assert(aux_info[AT_PHDR] != NULL); phdr = (const Elf_Phdr *)aux_info[AT_PHDR]->a_un.a_ptr; if (phdr == obj_rtld.phdr) { if (!trust) { rtld_printf("Tainted process refusing to run binary %s\n", argv0); rtld_die(); } dbg("opening main program in direct exec mode"); if (argc >= 2) { rtld_argc = parse_args(argv, argc, &search_in_path, &fd); argv0 = argv[rtld_argc]; explicit_fd = (fd != -1); if (!explicit_fd) fd = open_binary_fd(argv0, search_in_path); if (fstat(fd, &st) == -1) { _rtld_error("failed to fstat FD %d (%s): %s", fd, explicit_fd ? "user-provided descriptor" : argv0, rtld_strerror(errno)); rtld_die(); } /* * Rough emulation of the permission checks done by * execve(2), only Unix DACs are checked, ACLs are * ignored. Preserve the semantic of disabling owner * to execute if owner x bit is cleared, even if * others x bit is enabled. * mmap(2) does not allow to mmap with PROT_EXEC if * binary' file comes from noexec mount. We cannot * set VV_TEXT on the binary. */ dir_enable = false; if (st.st_uid == geteuid()) { if ((st.st_mode & S_IXUSR) != 0) dir_enable = true; } else if (st.st_gid == getegid()) { if ((st.st_mode & S_IXGRP) != 0) dir_enable = true; } else if ((st.st_mode & S_IXOTH) != 0) { dir_enable = true; } if (!dir_enable) { rtld_printf("No execute permission for binary %s\n", argv0); rtld_die(); } /* * For direct exec mode, argv[0] is the interpreter * name, we must remove it and shift arguments left * before invoking binary main. Since stack layout * places environment pointers and aux vectors right * after the terminating NULL, we must shift * environment and aux as well. */ main_argc = argc - rtld_argc; for (i = 0; i <= main_argc; i++) argv[i] = argv[i + rtld_argc]; *argcp -= rtld_argc; environ = env = envp = argv + main_argc + 1; do { *envp = *(envp + rtld_argc); envp++; } while (*envp != NULL); aux = auxp = (Elf_Auxinfo *)envp; auxpf = (Elf_Auxinfo *)(envp + rtld_argc); for (;; auxp++, auxpf++) { *auxp = *auxpf; if (auxp->a_type == AT_NULL) break; } } else { rtld_printf("no binary\n"); rtld_die(); } } } ld_bind_now = getenv(_LD("BIND_NOW")); /* * If the process is tainted, then we un-set the dangerous environment * variables. The process will be marked as tainted until setuid(2) * is called. If any child process calls setuid(2) we do not want any * future processes to honor the potentially un-safe variables. */ if (!trust) { if (unsetenv(_LD("PRELOAD")) || unsetenv(_LD("LIBMAP")) || unsetenv(_LD("LIBRARY_PATH")) || unsetenv(_LD("LIBRARY_PATH_FDS")) || unsetenv(_LD("LIBMAP_DISABLE")) || unsetenv(_LD("BIND_NOT")) || unsetenv(_LD("DEBUG")) || unsetenv(_LD("ELF_HINTS_PATH")) || unsetenv(_LD("LOADFLTR")) || unsetenv(_LD("LIBRARY_PATH_RPATH"))) { _rtld_error("environment corrupt; aborting"); rtld_die(); } } ld_debug = getenv(_LD("DEBUG")); if (ld_bind_now == NULL) ld_bind_not = getenv(_LD("BIND_NOT")) != NULL; libmap_disable = getenv(_LD("LIBMAP_DISABLE")) != NULL; libmap_override = getenv(_LD("LIBMAP")); ld_library_path = getenv(_LD("LIBRARY_PATH")); ld_library_dirs = getenv(_LD("LIBRARY_PATH_FDS")); ld_preload = getenv(_LD("PRELOAD")); ld_elf_hints_path = getenv(_LD("ELF_HINTS_PATH")); ld_loadfltr = getenv(_LD("LOADFLTR")) != NULL; library_path_rpath = getenv(_LD("LIBRARY_PATH_RPATH")); if (library_path_rpath != NULL) { if (library_path_rpath[0] == 'y' || library_path_rpath[0] == 'Y' || library_path_rpath[0] == '1') ld_library_path_rpath = true; else ld_library_path_rpath = false; } dangerous_ld_env = libmap_disable || (libmap_override != NULL) || (ld_library_path != NULL) || (ld_preload != NULL) || (ld_elf_hints_path != NULL) || ld_loadfltr; ld_tracing = getenv(_LD("TRACE_LOADED_OBJECTS")); ld_utrace = getenv(_LD("UTRACE")); if ((ld_elf_hints_path == NULL) || strlen(ld_elf_hints_path) == 0) ld_elf_hints_path = ld_elf_hints_default; if (ld_debug != NULL && *ld_debug != '\0') debug = 1; dbg("%s is initialized, base address = %p", __progname, (caddr_t) aux_info[AT_BASE]->a_un.a_ptr); dbg("RTLD dynamic = %p", obj_rtld.dynamic); dbg("RTLD pltgot = %p", obj_rtld.pltgot); dbg("initializing thread locks"); lockdflt_init(); /* * Load the main program, or process its program header if it is * already loaded. */ if (fd != -1) { /* Load the main program. */ dbg("loading main program"); obj_main = map_object(fd, argv0, NULL); close(fd); if (obj_main == NULL) rtld_die(); max_stack_flags = obj_main->stack_flags; } else { /* Main program already loaded. */ dbg("processing main program's program header"); assert(aux_info[AT_PHDR] != NULL); phdr = (const Elf_Phdr *) aux_info[AT_PHDR]->a_un.a_ptr; assert(aux_info[AT_PHNUM] != NULL); phnum = aux_info[AT_PHNUM]->a_un.a_val; assert(aux_info[AT_PHENT] != NULL); assert(aux_info[AT_PHENT]->a_un.a_val == sizeof(Elf_Phdr)); assert(aux_info[AT_ENTRY] != NULL); imgentry = (caddr_t) aux_info[AT_ENTRY]->a_un.a_ptr; if ((obj_main = digest_phdr(phdr, phnum, imgentry, argv0)) == NULL) rtld_die(); } if (aux_info[AT_EXECPATH] != NULL && fd == -1) { kexecpath = aux_info[AT_EXECPATH]->a_un.a_ptr; dbg("AT_EXECPATH %p %s", kexecpath, kexecpath); if (kexecpath[0] == '/') obj_main->path = kexecpath; else if (getcwd(buf, sizeof(buf)) == NULL || strlcat(buf, "/", sizeof(buf)) >= sizeof(buf) || strlcat(buf, kexecpath, sizeof(buf)) >= sizeof(buf)) obj_main->path = xstrdup(argv0); else obj_main->path = xstrdup(buf); } else { dbg("No AT_EXECPATH or direct exec"); obj_main->path = xstrdup(argv0); } dbg("obj_main path %s", obj_main->path); obj_main->mainprog = true; if (aux_info[AT_STACKPROT] != NULL && aux_info[AT_STACKPROT]->a_un.a_val != 0) stack_prot = aux_info[AT_STACKPROT]->a_un.a_val; #ifndef COMPAT_32BIT /* * Get the actual dynamic linker pathname from the executable if * possible. (It should always be possible.) That ensures that * gdb will find the right dynamic linker even if a non-standard * one is being used. */ if (obj_main->interp != NULL && strcmp(obj_main->interp, obj_rtld.path) != 0) { free(obj_rtld.path); obj_rtld.path = xstrdup(obj_main->interp); __progname = obj_rtld.path; } #endif digest_dynamic(obj_main, 0); dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d", obj_main->path, obj_main->valid_hash_sysv, obj_main->valid_hash_gnu, obj_main->dynsymcount); linkmap_add(obj_main); linkmap_add(&obj_rtld); /* Link the main program into the list of objects. */ TAILQ_INSERT_HEAD(&obj_list, obj_main, next); obj_count++; obj_loads++; /* Initialize a fake symbol for resolving undefined weak references. */ sym_zero.st_info = ELF_ST_INFO(STB_GLOBAL, STT_NOTYPE); sym_zero.st_shndx = SHN_UNDEF; sym_zero.st_value = -(uintptr_t)obj_main->relocbase; if (!libmap_disable) libmap_disable = (bool)lm_init(libmap_override); dbg("loading LD_PRELOAD libraries"); if (load_preload_objects() == -1) rtld_die(); preload_tail = globallist_curr(TAILQ_LAST(&obj_list, obj_entry_q)); dbg("loading needed objects"); if (load_needed_objects(obj_main, 0) == -1) rtld_die(); /* Make a list of all objects loaded at startup. */ last_interposer = obj_main; TAILQ_FOREACH(obj, &obj_list, next) { if (obj->marker) continue; if (obj->z_interpose && obj != obj_main) { objlist_put_after(&list_main, last_interposer, obj); last_interposer = obj; } else { objlist_push_tail(&list_main, obj); } obj->refcount++; } dbg("checking for required versions"); if (rtld_verify_versions(&list_main) == -1 && !ld_tracing) rtld_die(); if (ld_tracing) { /* We're done */ trace_loaded_objects(obj_main); exit(0); } if (getenv(_LD("DUMP_REL_PRE")) != NULL) { dump_relocations(obj_main); exit (0); } /* * Processing tls relocations requires having the tls offsets * initialized. Prepare offsets before starting initial * relocation processing. */ dbg("initializing initial thread local storage offsets"); STAILQ_FOREACH(entry, &list_main, link) { /* * Allocate all the initial objects out of the static TLS * block even if they didn't ask for it. */ allocate_tls_offset(entry->obj); } if (relocate_objects(obj_main, ld_bind_now != NULL && *ld_bind_now != '\0', &obj_rtld, SYMLOOK_EARLY, NULL) == -1) rtld_die(); dbg("doing copy relocations"); if (do_copy_relocations(obj_main) == -1) rtld_die(); dbg("enforcing main obj relro"); if (obj_enforce_relro(obj_main) == -1) rtld_die(); if (getenv(_LD("DUMP_REL_POST")) != NULL) { dump_relocations(obj_main); exit (0); } /* * Setup TLS for main thread. This must be done after the * relocations are processed, since tls initialization section * might be the subject for relocations. */ dbg("initializing initial thread local storage"); allocate_initial_tls(globallist_curr(TAILQ_FIRST(&obj_list))); dbg("initializing key program variables"); set_program_var("__progname", argv[0] != NULL ? basename(argv[0]) : ""); set_program_var("environ", env); set_program_var("__elf_aux_vector", aux); /* Make a list of init functions to call. */ objlist_init(&initlist); initlist_add_objects(globallist_curr(TAILQ_FIRST(&obj_list)), preload_tail, &initlist); r_debug_state(NULL, &obj_main->linkmap); /* say hello to gdb! */ map_stacks_exec(NULL); ifunc_init(aux); dbg("resolving ifuncs"); if (resolve_objects_ifunc(obj_main, ld_bind_now != NULL && *ld_bind_now != '\0', SYMLOOK_EARLY, NULL) == -1) rtld_die(); if (!obj_main->crt_no_init) { /* * Make sure we don't call the main program's init and fini * functions for binaries linked with old crt1 which calls * _init itself. */ obj_main->init = obj_main->fini = (Elf_Addr)NULL; obj_main->preinit_array = obj_main->init_array = obj_main->fini_array = (Elf_Addr)NULL; } wlock_acquire(rtld_bind_lock, &lockstate); if (obj_main->crt_no_init) preinit_main(); objlist_call_init(&initlist, &lockstate); _r_debug_postinit(&obj_main->linkmap); objlist_clear(&initlist); dbg("loading filtees"); TAILQ_FOREACH(obj, &obj_list, next) { if (obj->marker) continue; if (ld_loadfltr || obj->z_loadfltr) load_filtees(obj, 0, &lockstate); } lock_release(rtld_bind_lock, &lockstate); dbg("transferring control to program entry point = %p", obj_main->entry); /* Return the exit procedure and the program entry point. */ *exit_proc = rtld_exit; *objp = obj_main; return (func_ptr_type) obj_main->entry; } void * rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def) { void *ptr; Elf_Addr target; ptr = (void *)make_function_pointer(def, obj); target = call_ifunc_resolver(ptr); return ((void *)target); } /* * NB: MIPS uses a private version of this function (_mips_rtld_bind). * Changes to this function should be applied there as well. */ Elf_Addr _rtld_bind(Obj_Entry *obj, Elf_Size reloff) { const Elf_Rel *rel; const Elf_Sym *def; const Obj_Entry *defobj; Elf_Addr *where; Elf_Addr target; RtldLockState lockstate; rlock_acquire(rtld_bind_lock, &lockstate); if (sigsetjmp(lockstate.env, 0) != 0) lock_upgrade(rtld_bind_lock, &lockstate); if (obj->pltrel) rel = (const Elf_Rel *) ((caddr_t) obj->pltrel + reloff); else rel = (const Elf_Rel *) ((caddr_t) obj->pltrela + reloff); where = (Elf_Addr *) (obj->relocbase + rel->r_offset); def = find_symdef(ELF_R_SYM(rel->r_info), obj, &defobj, SYMLOOK_IN_PLT, NULL, &lockstate); if (def == NULL) rtld_die(); if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) target = (Elf_Addr)rtld_resolve_ifunc(defobj, def); else target = (Elf_Addr)(defobj->relocbase + def->st_value); dbg("\"%s\" in \"%s\" ==> %p in \"%s\"", defobj->strtab + def->st_name, basename(obj->path), (void *)target, basename(defobj->path)); /* * Write the new contents for the jmpslot. Note that depending on * architecture, the value which we need to return back to the * lazy binding trampoline may or may not be the target * address. The value returned from reloc_jmpslot() is the value * that the trampoline needs. */ target = reloc_jmpslot(where, target, defobj, obj, rel); lock_release(rtld_bind_lock, &lockstate); return target; } /* * Error reporting function. Use it like printf. If formats the message * into a buffer, and sets things up so that the next call to dlerror() * will return the message. */ void _rtld_error(const char *fmt, ...) { static char buf[512]; va_list ap; va_start(ap, fmt); rtld_vsnprintf(buf, sizeof buf, fmt, ap); error_message = buf; va_end(ap); LD_UTRACE(UTRACE_RTLD_ERROR, NULL, NULL, 0, 0, error_message); } /* * Return a dynamically-allocated copy of the current error message, if any. */ static char * errmsg_save(void) { return error_message == NULL ? NULL : xstrdup(error_message); } /* * Restore the current error message from a copy which was previously saved * by errmsg_save(). The copy is freed. */ static void errmsg_restore(char *saved_msg) { if (saved_msg == NULL) error_message = NULL; else { _rtld_error("%s", saved_msg); free(saved_msg); } } static const char * basename(const char *name) { const char *p = strrchr(name, '/'); return p != NULL ? p + 1 : name; } static struct utsname uts; static char * origin_subst_one(Obj_Entry *obj, char *real, const char *kw, const char *subst, bool may_free) { char *p, *p1, *res, *resp; int subst_len, kw_len, subst_count, old_len, new_len; kw_len = strlen(kw); /* * First, count the number of the keyword occurrences, to * preallocate the final string. */ for (p = real, subst_count = 0;; p = p1 + kw_len, subst_count++) { p1 = strstr(p, kw); if (p1 == NULL) break; } /* * If the keyword is not found, just return. * * Return non-substituted string if resolution failed. We * cannot do anything more reasonable, the failure mode of the * caller is unresolved library anyway. */ if (subst_count == 0 || (obj != NULL && !obj_resolve_origin(obj))) return (may_free ? real : xstrdup(real)); if (obj != NULL) subst = obj->origin_path; /* * There is indeed something to substitute. Calculate the * length of the resulting string, and allocate it. */ subst_len = strlen(subst); old_len = strlen(real); new_len = old_len + (subst_len - kw_len) * subst_count; res = xmalloc(new_len + 1); /* * Now, execute the substitution loop. */ for (p = real, resp = res, *resp = '\0';;) { p1 = strstr(p, kw); if (p1 != NULL) { /* Copy the prefix before keyword. */ memcpy(resp, p, p1 - p); resp += p1 - p; /* Keyword replacement. */ memcpy(resp, subst, subst_len); resp += subst_len; *resp = '\0'; p = p1 + kw_len; } else break; } /* Copy to the end of string and finish. */ strcat(resp, p); if (may_free) free(real); return (res); } static char * origin_subst(Obj_Entry *obj, char *real) { char *res1, *res2, *res3, *res4; if (obj == NULL || !trust) return (xstrdup(real)); if (uts.sysname[0] == '\0') { if (uname(&uts) != 0) { _rtld_error("utsname failed: %d", errno); return (NULL); } } res1 = origin_subst_one(obj, real, "$ORIGIN", NULL, false); res2 = origin_subst_one(NULL, res1, "$OSNAME", uts.sysname, true); res3 = origin_subst_one(NULL, res2, "$OSREL", uts.release, true); res4 = origin_subst_one(NULL, res3, "$PLATFORM", uts.machine, true); return (res4); } void rtld_die(void) { const char *msg = dlerror(); if (msg == NULL) msg = "Fatal error"; rtld_fdputstr(STDERR_FILENO, msg); rtld_fdputchar(STDERR_FILENO, '\n'); _exit(1); } /* * Process a shared object's DYNAMIC section, and save the important * information in its Obj_Entry structure. */ static void digest_dynamic1(Obj_Entry *obj, int early, const Elf_Dyn **dyn_rpath, const Elf_Dyn **dyn_soname, const Elf_Dyn **dyn_runpath) { const Elf_Dyn *dynp; Needed_Entry **needed_tail = &obj->needed; Needed_Entry **needed_filtees_tail = &obj->needed_filtees; Needed_Entry **needed_aux_filtees_tail = &obj->needed_aux_filtees; const Elf_Hashelt *hashtab; const Elf32_Word *hashval; Elf32_Word bkt, nmaskwords; int bloom_size32; int plttype = DT_REL; *dyn_rpath = NULL; *dyn_soname = NULL; *dyn_runpath = NULL; obj->bind_now = false; for (dynp = obj->dynamic; dynp->d_tag != DT_NULL; dynp++) { switch (dynp->d_tag) { case DT_REL: obj->rel = (const Elf_Rel *) (obj->relocbase + dynp->d_un.d_ptr); break; case DT_RELSZ: obj->relsize = dynp->d_un.d_val; break; case DT_RELENT: assert(dynp->d_un.d_val == sizeof(Elf_Rel)); break; case DT_JMPREL: obj->pltrel = (const Elf_Rel *) (obj->relocbase + dynp->d_un.d_ptr); break; case DT_PLTRELSZ: obj->pltrelsize = dynp->d_un.d_val; break; case DT_RELA: obj->rela = (const Elf_Rela *) (obj->relocbase + dynp->d_un.d_ptr); break; case DT_RELASZ: obj->relasize = dynp->d_un.d_val; break; case DT_RELAENT: assert(dynp->d_un.d_val == sizeof(Elf_Rela)); break; case DT_PLTREL: plttype = dynp->d_un.d_val; assert(dynp->d_un.d_val == DT_REL || plttype == DT_RELA); break; case DT_SYMTAB: obj->symtab = (const Elf_Sym *) (obj->relocbase + dynp->d_un.d_ptr); break; case DT_SYMENT: assert(dynp->d_un.d_val == sizeof(Elf_Sym)); break; case DT_STRTAB: obj->strtab = (const char *) (obj->relocbase + dynp->d_un.d_ptr); break; case DT_STRSZ: obj->strsize = dynp->d_un.d_val; break; case DT_VERNEED: obj->verneed = (const Elf_Verneed *) (obj->relocbase + dynp->d_un.d_val); break; case DT_VERNEEDNUM: obj->verneednum = dynp->d_un.d_val; break; case DT_VERDEF: obj->verdef = (const Elf_Verdef *) (obj->relocbase + dynp->d_un.d_val); break; case DT_VERDEFNUM: obj->verdefnum = dynp->d_un.d_val; break; case DT_VERSYM: obj->versyms = (const Elf_Versym *)(obj->relocbase + dynp->d_un.d_val); break; case DT_HASH: { hashtab = (const Elf_Hashelt *)(obj->relocbase + dynp->d_un.d_ptr); obj->nbuckets = hashtab[0]; obj->nchains = hashtab[1]; obj->buckets = hashtab + 2; obj->chains = obj->buckets + obj->nbuckets; obj->valid_hash_sysv = obj->nbuckets > 0 && obj->nchains > 0 && obj->buckets != NULL; } break; case DT_GNU_HASH: { hashtab = (const Elf_Hashelt *)(obj->relocbase + dynp->d_un.d_ptr); obj->nbuckets_gnu = hashtab[0]; obj->symndx_gnu = hashtab[1]; nmaskwords = hashtab[2]; bloom_size32 = (__ELF_WORD_SIZE / 32) * nmaskwords; obj->maskwords_bm_gnu = nmaskwords - 1; obj->shift2_gnu = hashtab[3]; obj->bloom_gnu = (Elf_Addr *) (hashtab + 4); obj->buckets_gnu = hashtab + 4 + bloom_size32; obj->chain_zero_gnu = obj->buckets_gnu + obj->nbuckets_gnu - obj->symndx_gnu; /* Number of bitmask words is required to be power of 2 */ obj->valid_hash_gnu = powerof2(nmaskwords) && obj->nbuckets_gnu > 0 && obj->buckets_gnu != NULL; } break; case DT_NEEDED: if (!obj->rtld) { Needed_Entry *nep = NEW(Needed_Entry); nep->name = dynp->d_un.d_val; nep->obj = NULL; nep->next = NULL; *needed_tail = nep; needed_tail = &nep->next; } break; case DT_FILTER: if (!obj->rtld) { Needed_Entry *nep = NEW(Needed_Entry); nep->name = dynp->d_un.d_val; nep->obj = NULL; nep->next = NULL; *needed_filtees_tail = nep; needed_filtees_tail = &nep->next; } break; case DT_AUXILIARY: if (!obj->rtld) { Needed_Entry *nep = NEW(Needed_Entry); nep->name = dynp->d_un.d_val; nep->obj = NULL; nep->next = NULL; *needed_aux_filtees_tail = nep; needed_aux_filtees_tail = &nep->next; } break; case DT_PLTGOT: obj->pltgot = (Elf_Addr *) (obj->relocbase + dynp->d_un.d_ptr); break; case DT_TEXTREL: obj->textrel = true; break; case DT_SYMBOLIC: obj->symbolic = true; break; case DT_RPATH: /* * We have to wait until later to process this, because we * might not have gotten the address of the string table yet. */ *dyn_rpath = dynp; break; case DT_SONAME: *dyn_soname = dynp; break; case DT_RUNPATH: *dyn_runpath = dynp; break; case DT_INIT: obj->init = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr); break; case DT_PREINIT_ARRAY: obj->preinit_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); break; case DT_PREINIT_ARRAYSZ: obj->preinit_array_num = dynp->d_un.d_val / sizeof(Elf_Addr); break; case DT_INIT_ARRAY: obj->init_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); break; case DT_INIT_ARRAYSZ: obj->init_array_num = dynp->d_un.d_val / sizeof(Elf_Addr); break; case DT_FINI: obj->fini = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr); break; case DT_FINI_ARRAY: obj->fini_array = (Elf_Addr)(obj->relocbase + dynp->d_un.d_ptr); break; case DT_FINI_ARRAYSZ: obj->fini_array_num = dynp->d_un.d_val / sizeof(Elf_Addr); break; /* * Don't process DT_DEBUG on MIPS as the dynamic section * is mapped read-only. DT_MIPS_RLD_MAP is used instead. */ #ifndef __mips__ case DT_DEBUG: if (!early) dbg("Filling in DT_DEBUG entry"); ((Elf_Dyn*)dynp)->d_un.d_ptr = (Elf_Addr) &r_debug; break; #endif case DT_FLAGS: if (dynp->d_un.d_val & DF_ORIGIN) obj->z_origin = true; if (dynp->d_un.d_val & DF_SYMBOLIC) obj->symbolic = true; if (dynp->d_un.d_val & DF_TEXTREL) obj->textrel = true; if (dynp->d_un.d_val & DF_BIND_NOW) obj->bind_now = true; /*if (dynp->d_un.d_val & DF_STATIC_TLS) ;*/ break; #ifdef __mips__ case DT_MIPS_LOCAL_GOTNO: obj->local_gotno = dynp->d_un.d_val; break; case DT_MIPS_SYMTABNO: obj->symtabno = dynp->d_un.d_val; break; case DT_MIPS_GOTSYM: obj->gotsym = dynp->d_un.d_val; break; case DT_MIPS_RLD_MAP: *((Elf_Addr *)(dynp->d_un.d_ptr)) = (Elf_Addr) &r_debug; break; case DT_MIPS_PLTGOT: obj->mips_pltgot = (Elf_Addr *) (obj->relocbase + dynp->d_un.d_ptr); break; #endif #ifdef __powerpc64__ case DT_PPC64_GLINK: obj->glink = (Elf_Addr) (obj->relocbase + dynp->d_un.d_ptr); break; #endif case DT_FLAGS_1: if (dynp->d_un.d_val & DF_1_NOOPEN) obj->z_noopen = true; if (dynp->d_un.d_val & DF_1_ORIGIN) obj->z_origin = true; if (dynp->d_un.d_val & DF_1_GLOBAL) obj->z_global = true; if (dynp->d_un.d_val & DF_1_BIND_NOW) obj->bind_now = true; if (dynp->d_un.d_val & DF_1_NODELETE) obj->z_nodelete = true; if (dynp->d_un.d_val & DF_1_LOADFLTR) obj->z_loadfltr = true; if (dynp->d_un.d_val & DF_1_INTERPOSE) obj->z_interpose = true; if (dynp->d_un.d_val & DF_1_NODEFLIB) obj->z_nodeflib = true; break; default: if (!early) { dbg("Ignoring d_tag %ld = %#lx", (long)dynp->d_tag, (long)dynp->d_tag); } break; } } obj->traced = false; if (plttype == DT_RELA) { obj->pltrela = (const Elf_Rela *) obj->pltrel; obj->pltrel = NULL; obj->pltrelasize = obj->pltrelsize; obj->pltrelsize = 0; } /* Determine size of dynsym table (equal to nchains of sysv hash) */ if (obj->valid_hash_sysv) obj->dynsymcount = obj->nchains; else if (obj->valid_hash_gnu) { obj->dynsymcount = 0; for (bkt = 0; bkt < obj->nbuckets_gnu; bkt++) { if (obj->buckets_gnu[bkt] == 0) continue; hashval = &obj->chain_zero_gnu[obj->buckets_gnu[bkt]]; do obj->dynsymcount++; while ((*hashval++ & 1u) == 0); } obj->dynsymcount += obj->symndx_gnu; } } static bool obj_resolve_origin(Obj_Entry *obj) { if (obj->origin_path != NULL) return (true); obj->origin_path = xmalloc(PATH_MAX); return (rtld_dirname_abs(obj->path, obj->origin_path) != -1); } static void digest_dynamic2(Obj_Entry *obj, const Elf_Dyn *dyn_rpath, const Elf_Dyn *dyn_soname, const Elf_Dyn *dyn_runpath) { if (obj->z_origin && !obj_resolve_origin(obj)) rtld_die(); if (dyn_runpath != NULL) { obj->runpath = (char *)obj->strtab + dyn_runpath->d_un.d_val; obj->runpath = origin_subst(obj, obj->runpath); } else if (dyn_rpath != NULL) { obj->rpath = (char *)obj->strtab + dyn_rpath->d_un.d_val; obj->rpath = origin_subst(obj, obj->rpath); } if (dyn_soname != NULL) object_add_name(obj, obj->strtab + dyn_soname->d_un.d_val); } static void digest_dynamic(Obj_Entry *obj, int early) { const Elf_Dyn *dyn_rpath; const Elf_Dyn *dyn_soname; const Elf_Dyn *dyn_runpath; digest_dynamic1(obj, early, &dyn_rpath, &dyn_soname, &dyn_runpath); digest_dynamic2(obj, dyn_rpath, dyn_soname, dyn_runpath); } /* * Process a shared object's program header. This is used only for the * main program, when the kernel has already loaded the main program * into memory before calling the dynamic linker. It creates and * returns an Obj_Entry structure. */ static Obj_Entry * digest_phdr(const Elf_Phdr *phdr, int phnum, caddr_t entry, const char *path) { Obj_Entry *obj; const Elf_Phdr *phlimit = phdr + phnum; const Elf_Phdr *ph; Elf_Addr note_start, note_end; int nsegs = 0; obj = obj_new(); for (ph = phdr; ph < phlimit; ph++) { if (ph->p_type != PT_PHDR) continue; obj->phdr = phdr; obj->phsize = ph->p_memsz; obj->relocbase = (caddr_t)phdr - ph->p_vaddr; break; } obj->stack_flags = PF_X | PF_R | PF_W; for (ph = phdr; ph < phlimit; ph++) { switch (ph->p_type) { case PT_INTERP: obj->interp = (const char *)(ph->p_vaddr + obj->relocbase); break; case PT_LOAD: if (nsegs == 0) { /* First load segment */ obj->vaddrbase = trunc_page(ph->p_vaddr); obj->mapbase = obj->vaddrbase + obj->relocbase; obj->textsize = round_page(ph->p_vaddr + ph->p_memsz) - obj->vaddrbase; } else { /* Last load segment */ obj->mapsize = round_page(ph->p_vaddr + ph->p_memsz) - obj->vaddrbase; } nsegs++; break; case PT_DYNAMIC: obj->dynamic = (const Elf_Dyn *)(ph->p_vaddr + obj->relocbase); break; case PT_TLS: obj->tlsindex = 1; obj->tlssize = ph->p_memsz; obj->tlsalign = ph->p_align; obj->tlsinitsize = ph->p_filesz; obj->tlsinit = (void*)(ph->p_vaddr + obj->relocbase); break; case PT_GNU_STACK: obj->stack_flags = ph->p_flags; break; case PT_GNU_RELRO: obj->relro_page = obj->relocbase + trunc_page(ph->p_vaddr); obj->relro_size = round_page(ph->p_memsz); break; case PT_NOTE: note_start = (Elf_Addr)obj->relocbase + ph->p_vaddr; note_end = note_start + ph->p_filesz; digest_notes(obj, note_start, note_end); break; } } if (nsegs < 1) { _rtld_error("%s: too few PT_LOAD segments", path); return NULL; } obj->entry = entry; return obj; } void digest_notes(Obj_Entry *obj, Elf_Addr note_start, Elf_Addr note_end) { const Elf_Note *note; const char *note_name; uintptr_t p; for (note = (const Elf_Note *)note_start; (Elf_Addr)note < note_end; note = (const Elf_Note *)((const char *)(note + 1) + roundup2(note->n_namesz, sizeof(Elf32_Addr)) + roundup2(note->n_descsz, sizeof(Elf32_Addr)))) { if (note->n_namesz != sizeof(NOTE_FREEBSD_VENDOR) || note->n_descsz != sizeof(int32_t)) continue; if (note->n_type != NT_FREEBSD_ABI_TAG && note->n_type != NT_FREEBSD_NOINIT_TAG) continue; note_name = (const char *)(note + 1); if (strncmp(NOTE_FREEBSD_VENDOR, note_name, sizeof(NOTE_FREEBSD_VENDOR)) != 0) continue; switch (note->n_type) { case NT_FREEBSD_ABI_TAG: /* FreeBSD osrel note */ p = (uintptr_t)(note + 1); p += roundup2(note->n_namesz, sizeof(Elf32_Addr)); obj->osrel = *(const int32_t *)(p); dbg("note osrel %d", obj->osrel); break; case NT_FREEBSD_NOINIT_TAG: /* FreeBSD 'crt does not call init' note */ obj->crt_no_init = true; dbg("note crt_no_init"); break; } } } static Obj_Entry * dlcheck(void *handle) { Obj_Entry *obj; TAILQ_FOREACH(obj, &obj_list, next) { if (obj == (Obj_Entry *) handle) break; } if (obj == NULL || obj->refcount == 0 || obj->dl_refcount == 0) { _rtld_error("Invalid shared object handle %p", handle); return NULL; } return obj; } /* * If the given object is already in the donelist, return true. Otherwise * add the object to the list and return false. */ static bool donelist_check(DoneList *dlp, const Obj_Entry *obj) { unsigned int i; for (i = 0; i < dlp->num_used; i++) if (dlp->objs[i] == obj) return true; /* * Our donelist allocation should always be sufficient. But if * our threads locking isn't working properly, more shared objects * could have been loaded since we allocated the list. That should * never happen, but we'll handle it properly just in case it does. */ if (dlp->num_used < dlp->num_alloc) dlp->objs[dlp->num_used++] = obj; return false; } /* * Hash function for symbol table lookup. Don't even think about changing * this. It is specified by the System V ABI. */ unsigned long elf_hash(const char *name) { const unsigned char *p = (const unsigned char *) name; unsigned long h = 0; unsigned long g; while (*p != '\0') { h = (h << 4) + *p++; if ((g = h & 0xf0000000) != 0) h ^= g >> 24; h &= ~g; } return h; } /* * The GNU hash function is the Daniel J. Bernstein hash clipped to 32 bits * unsigned in case it's implemented with a wider type. */ static uint32_t gnu_hash(const char *s) { uint32_t h; unsigned char c; h = 5381; for (c = *s; c != '\0'; c = *++s) h = h * 33 + c; return (h & 0xffffffff); } /* * Find the library with the given name, and return its full pathname. * The returned string is dynamically allocated. Generates an error * message and returns NULL if the library cannot be found. * * If the second argument is non-NULL, then it refers to an already- * loaded shared object, whose library search path will be searched. * * If a library is successfully located via LD_LIBRARY_PATH_FDS, its * descriptor (which is close-on-exec) will be passed out via the third * argument. * * The search order is: * DT_RPATH in the referencing file _unless_ DT_RUNPATH is present (1) * DT_RPATH of the main object if DSO without defined DT_RUNPATH (1) * LD_LIBRARY_PATH * DT_RUNPATH in the referencing file * ldconfig hints (if -z nodefaultlib, filter out default library directories * from list) * /lib:/usr/lib _unless_ the referencing file is linked with -z nodefaultlib * * (1) Handled in digest_dynamic2 - rpath left NULL if runpath defined. */ static char * find_library(const char *xname, const Obj_Entry *refobj, int *fdp) { char *pathname; char *name; bool nodeflib, objgiven; objgiven = refobj != NULL; if (libmap_disable || !objgiven || (name = lm_find(refobj->path, xname)) == NULL) name = (char *)xname; if (strchr(name, '/') != NULL) { /* Hard coded pathname */ if (name[0] != '/' && !trust) { _rtld_error("Absolute pathname required " "for shared object \"%s\"", name); return (NULL); } return (origin_subst(__DECONST(Obj_Entry *, refobj), __DECONST(char *, name))); } dbg(" Searching for \"%s\"", name); /* * If refobj->rpath != NULL, then refobj->runpath is NULL. Fall * back to pre-conforming behaviour if user requested so with * LD_LIBRARY_PATH_RPATH environment variable and ignore -z * nodeflib. */ if (objgiven && refobj->rpath != NULL && ld_library_path_rpath) { pathname = search_library_path(name, ld_library_path, fdp); if (pathname != NULL) return (pathname); if (refobj != NULL) { pathname = search_library_path(name, refobj->rpath, fdp); if (pathname != NULL) return (pathname); } pathname = search_library_pathfds(name, ld_library_dirs, fdp); if (pathname != NULL) return (pathname); pathname = search_library_path(name, gethints(false), fdp); if (pathname != NULL) return (pathname); pathname = search_library_path(name, ld_standard_library_path, fdp); if (pathname != NULL) return (pathname); } else { nodeflib = objgiven ? refobj->z_nodeflib : false; if (objgiven) { pathname = search_library_path(name, refobj->rpath, fdp); if (pathname != NULL) return (pathname); } if (objgiven && refobj->runpath == NULL && refobj != obj_main) { pathname = search_library_path(name, obj_main->rpath, fdp); if (pathname != NULL) return (pathname); } pathname = search_library_path(name, ld_library_path, fdp); if (pathname != NULL) return (pathname); if (objgiven) { pathname = search_library_path(name, refobj->runpath, fdp); if (pathname != NULL) return (pathname); } pathname = search_library_pathfds(name, ld_library_dirs, fdp); if (pathname != NULL) return (pathname); pathname = search_library_path(name, gethints(nodeflib), fdp); if (pathname != NULL) return (pathname); if (objgiven && !nodeflib) { pathname = search_library_path(name, ld_standard_library_path, fdp); if (pathname != NULL) return (pathname); } } if (objgiven && refobj->path != NULL) { _rtld_error("Shared object \"%s\" not found, " "required by \"%s\"", name, basename(refobj->path)); } else { _rtld_error("Shared object \"%s\" not found", name); } return (NULL); } /* * Given a symbol number in a referencing object, find the corresponding * definition of the symbol. Returns a pointer to the symbol, or NULL if * no definition was found. Returns a pointer to the Obj_Entry of the * defining object via the reference parameter DEFOBJ_OUT. */ const Elf_Sym * find_symdef(unsigned long symnum, const Obj_Entry *refobj, const Obj_Entry **defobj_out, int flags, SymCache *cache, RtldLockState *lockstate) { const Elf_Sym *ref; const Elf_Sym *def; const Obj_Entry *defobj; const Ver_Entry *ve; SymLook req; const char *name; int res; /* * If we have already found this symbol, get the information from * the cache. */ if (symnum >= refobj->dynsymcount) return NULL; /* Bad object */ if (cache != NULL && cache[symnum].sym != NULL) { *defobj_out = cache[symnum].obj; return cache[symnum].sym; } ref = refobj->symtab + symnum; name = refobj->strtab + ref->st_name; def = NULL; defobj = NULL; ve = NULL; /* * We don't have to do a full scale lookup if the symbol is local. * We know it will bind to the instance in this load module; to * which we already have a pointer (ie ref). By not doing a lookup, * we not only improve performance, but it also avoids unresolvable * symbols when local symbols are not in the hash table. This has * been seen with the ia64 toolchain. */ if (ELF_ST_BIND(ref->st_info) != STB_LOCAL) { if (ELF_ST_TYPE(ref->st_info) == STT_SECTION) { _rtld_error("%s: Bogus symbol table entry %lu", refobj->path, symnum); } symlook_init(&req, name); req.flags = flags; ve = req.ventry = fetch_ventry(refobj, symnum); req.lockstate = lockstate; res = symlook_default(&req, refobj); if (res == 0) { def = req.sym_out; defobj = req.defobj_out; } } else { def = ref; defobj = refobj; } /* * If we found no definition and the reference is weak, treat the * symbol as having the value zero. */ if (def == NULL && ELF_ST_BIND(ref->st_info) == STB_WEAK) { def = &sym_zero; defobj = obj_main; } if (def != NULL) { *defobj_out = defobj; /* Record the information in the cache to avoid subsequent lookups. */ if (cache != NULL) { cache[symnum].sym = def; cache[symnum].obj = defobj; } } else { if (refobj != &obj_rtld) _rtld_error("%s: Undefined symbol \"%s%s%s\"", refobj->path, name, ve != NULL ? "@" : "", ve != NULL ? ve->name : ""); } return def; } /* * Return the search path from the ldconfig hints file, reading it if * necessary. If nostdlib is true, then the default search paths are * not added to result. * * Returns NULL if there are problems with the hints file, * or if the search path there is empty. */ static const char * gethints(bool nostdlib) { static char *hints, *filtered_path; static struct elfhints_hdr hdr; struct fill_search_info_args sargs, hargs; struct dl_serinfo smeta, hmeta, *SLPinfo, *hintinfo; struct dl_serpath *SLPpath, *hintpath; char *p; struct stat hint_stat; unsigned int SLPndx, hintndx, fndx, fcount; int fd; size_t flen; uint32_t dl; bool skip; /* First call, read the hints file */ if (hints == NULL) { /* Keep from trying again in case the hints file is bad. */ hints = ""; if ((fd = open(ld_elf_hints_path, O_RDONLY | O_CLOEXEC)) == -1) return (NULL); /* * Check of hdr.dirlistlen value against type limit * intends to pacify static analyzers. Further * paranoia leads to checks that dirlist is fully * contained in the file range. */ if (read(fd, &hdr, sizeof hdr) != sizeof hdr || hdr.magic != ELFHINTS_MAGIC || hdr.version != 1 || hdr.dirlistlen > UINT_MAX / 2 || fstat(fd, &hint_stat) == -1) { cleanup1: close(fd); hdr.dirlistlen = 0; return (NULL); } dl = hdr.strtab; if (dl + hdr.dirlist < dl) goto cleanup1; dl += hdr.dirlist; if (dl + hdr.dirlistlen < dl) goto cleanup1; dl += hdr.dirlistlen; if (dl > hint_stat.st_size) goto cleanup1; p = xmalloc(hdr.dirlistlen + 1); if (pread(fd, p, hdr.dirlistlen + 1, hdr.strtab + hdr.dirlist) != (ssize_t)hdr.dirlistlen + 1 || p[hdr.dirlistlen] != '\0') { free(p); goto cleanup1; } hints = p; close(fd); } /* * If caller agreed to receive list which includes the default * paths, we are done. Otherwise, if we still did not * calculated filtered result, do it now. */ if (!nostdlib) return (hints[0] != '\0' ? hints : NULL); if (filtered_path != NULL) goto filt_ret; /* * Obtain the list of all configured search paths, and the * list of the default paths. * * First estimate the size of the results. */ smeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath); smeta.dls_cnt = 0; hmeta.dls_size = __offsetof(struct dl_serinfo, dls_serpath); hmeta.dls_cnt = 0; sargs.request = RTLD_DI_SERINFOSIZE; sargs.serinfo = &smeta; hargs.request = RTLD_DI_SERINFOSIZE; hargs.serinfo = &hmeta; path_enumerate(ld_standard_library_path, fill_search_info, &sargs); path_enumerate(hints, fill_search_info, &hargs); SLPinfo = xmalloc(smeta.dls_size); hintinfo = xmalloc(hmeta.dls_size); /* * Next fetch both sets of paths. */ sargs.request = RTLD_DI_SERINFO; sargs.serinfo = SLPinfo; sargs.serpath = &SLPinfo->dls_serpath[0]; sargs.strspace = (char *)&SLPinfo->dls_serpath[smeta.dls_cnt]; hargs.request = RTLD_DI_SERINFO; hargs.serinfo = hintinfo; hargs.serpath = &hintinfo->dls_serpath[0]; hargs.strspace = (char *)&hintinfo->dls_serpath[hmeta.dls_cnt]; path_enumerate(ld_standard_library_path, fill_search_info, &sargs); path_enumerate(hints, fill_search_info, &hargs); /* * Now calculate the difference between two sets, by excluding * standard paths from the full set. */ fndx = 0; fcount = 0; filtered_path = xmalloc(hdr.dirlistlen + 1); hintpath = &hintinfo->dls_serpath[0]; for (hintndx = 0; hintndx < hmeta.dls_cnt; hintndx++, hintpath++) { skip = false; SLPpath = &SLPinfo->dls_serpath[0]; /* * Check each standard path against current. */ for (SLPndx = 0; SLPndx < smeta.dls_cnt; SLPndx++, SLPpath++) { /* matched, skip the path */ if (!strcmp(hintpath->dls_name, SLPpath->dls_name)) { skip = true; break; } } if (skip) continue; /* * Not matched against any standard path, add the path * to result. Separate consequtive paths with ':'. */ if (fcount > 0) { filtered_path[fndx] = ':'; fndx++; } fcount++; flen = strlen(hintpath->dls_name); strncpy((filtered_path + fndx), hintpath->dls_name, flen); fndx += flen; } filtered_path[fndx] = '\0'; free(SLPinfo); free(hintinfo); filt_ret: return (filtered_path[0] != '\0' ? filtered_path : NULL); } static void init_dag(Obj_Entry *root) { const Needed_Entry *needed; const Objlist_Entry *elm; DoneList donelist; if (root->dag_inited) return; donelist_init(&donelist); /* Root object belongs to own DAG. */ objlist_push_tail(&root->dldags, root); objlist_push_tail(&root->dagmembers, root); donelist_check(&donelist, root); /* * Add dependencies of root object to DAG in breadth order * by exploiting the fact that each new object get added * to the tail of the dagmembers list. */ STAILQ_FOREACH(elm, &root->dagmembers, link) { for (needed = elm->obj->needed; needed != NULL; needed = needed->next) { if (needed->obj == NULL || donelist_check(&donelist, needed->obj)) continue; objlist_push_tail(&needed->obj->dldags, root); objlist_push_tail(&root->dagmembers, needed->obj); } } root->dag_inited = true; } static void init_marker(Obj_Entry *marker) { bzero(marker, sizeof(*marker)); marker->marker = true; } Obj_Entry * globallist_curr(const Obj_Entry *obj) { for (;;) { if (obj == NULL) return (NULL); if (!obj->marker) return (__DECONST(Obj_Entry *, obj)); obj = TAILQ_PREV(obj, obj_entry_q, next); } } Obj_Entry * globallist_next(const Obj_Entry *obj) { for (;;) { obj = TAILQ_NEXT(obj, next); if (obj == NULL) return (NULL); if (!obj->marker) return (__DECONST(Obj_Entry *, obj)); } } /* Prevent the object from being unmapped while the bind lock is dropped. */ static void hold_object(Obj_Entry *obj) { obj->holdcount++; } static void unhold_object(Obj_Entry *obj) { assert(obj->holdcount > 0); if (--obj->holdcount == 0 && obj->unholdfree) release_object(obj); } static void process_z(Obj_Entry *root) { const Objlist_Entry *elm; Obj_Entry *obj; /* * Walk over object DAG and process every dependent object * that is marked as DF_1_NODELETE or DF_1_GLOBAL. They need * to grow their own DAG. * * For DF_1_GLOBAL, DAG is required for symbol lookups in * symlook_global() to work. * * For DF_1_NODELETE, the DAG should have its reference upped. */ STAILQ_FOREACH(elm, &root->dagmembers, link) { obj = elm->obj; if (obj == NULL) continue; if (obj->z_nodelete && !obj->ref_nodel) { dbg("obj %s -z nodelete", obj->path); init_dag(obj); ref_dag(obj); obj->ref_nodel = true; } if (obj->z_global && objlist_find(&list_global, obj) == NULL) { dbg("obj %s -z global", obj->path); objlist_push_tail(&list_global, obj); init_dag(obj); } } } /* * Initialize the dynamic linker. The argument is the address at which * the dynamic linker has been mapped into memory. The primary task of * this function is to relocate the dynamic linker. */ static void init_rtld(caddr_t mapbase, Elf_Auxinfo **aux_info) { Obj_Entry objtmp; /* Temporary rtld object */ const Elf_Ehdr *ehdr; const Elf_Dyn *dyn_rpath; const Elf_Dyn *dyn_soname; const Elf_Dyn *dyn_runpath; #ifdef RTLD_INIT_PAGESIZES_EARLY /* The page size is required by the dynamic memory allocator. */ init_pagesizes(aux_info); #endif /* * Conjure up an Obj_Entry structure for the dynamic linker. * * The "path" member can't be initialized yet because string constants * cannot yet be accessed. Below we will set it correctly. */ memset(&objtmp, 0, sizeof(objtmp)); objtmp.path = NULL; objtmp.rtld = true; objtmp.mapbase = mapbase; #ifdef PIC objtmp.relocbase = mapbase; #endif objtmp.dynamic = rtld_dynamic(&objtmp); digest_dynamic1(&objtmp, 1, &dyn_rpath, &dyn_soname, &dyn_runpath); assert(objtmp.needed == NULL); #if !defined(__mips__) /* MIPS has a bogus DT_TEXTREL. */ assert(!objtmp.textrel); #endif /* * Temporarily put the dynamic linker entry into the object list, so * that symbols can be found. */ relocate_objects(&objtmp, true, &objtmp, 0, NULL); ehdr = (Elf_Ehdr *)mapbase; objtmp.phdr = (Elf_Phdr *)((char *)mapbase + ehdr->e_phoff); objtmp.phsize = ehdr->e_phnum * sizeof(objtmp.phdr[0]); /* Initialize the object list. */ TAILQ_INIT(&obj_list); /* Now that non-local variables can be accesses, copy out obj_rtld. */ memcpy(&obj_rtld, &objtmp, sizeof(obj_rtld)); #ifndef RTLD_INIT_PAGESIZES_EARLY /* The page size is required by the dynamic memory allocator. */ init_pagesizes(aux_info); #endif if (aux_info[AT_OSRELDATE] != NULL) osreldate = aux_info[AT_OSRELDATE]->a_un.a_val; digest_dynamic2(&obj_rtld, dyn_rpath, dyn_soname, dyn_runpath); /* Replace the path with a dynamically allocated copy. */ obj_rtld.path = xstrdup(ld_path_rtld); r_debug.r_brk = r_debug_state; r_debug.r_state = RT_CONSISTENT; } /* * Retrieve the array of supported page sizes. The kernel provides the page * sizes in increasing order. */ static void init_pagesizes(Elf_Auxinfo **aux_info) { static size_t psa[MAXPAGESIZES]; int mib[2]; size_t len, size; if (aux_info[AT_PAGESIZES] != NULL && aux_info[AT_PAGESIZESLEN] != NULL) { size = aux_info[AT_PAGESIZESLEN]->a_un.a_val; pagesizes = aux_info[AT_PAGESIZES]->a_un.a_ptr; } else { len = 2; if (sysctlnametomib("hw.pagesizes", mib, &len) == 0) size = sizeof(psa); else { /* As a fallback, retrieve the base page size. */ size = sizeof(psa[0]); if (aux_info[AT_PAGESZ] != NULL) { psa[0] = aux_info[AT_PAGESZ]->a_un.a_val; goto psa_filled; } else { mib[0] = CTL_HW; mib[1] = HW_PAGESIZE; len = 2; } } if (sysctl(mib, len, psa, &size, NULL, 0) == -1) { _rtld_error("sysctl for hw.pagesize(s) failed"); rtld_die(); } psa_filled: pagesizes = psa; } npagesizes = size / sizeof(pagesizes[0]); /* Discard any invalid entries at the end of the array. */ while (npagesizes > 0 && pagesizes[npagesizes - 1] == 0) npagesizes--; } /* * Add the init functions from a needed object list (and its recursive * needed objects) to "list". This is not used directly; it is a helper * function for initlist_add_objects(). The write lock must be held * when this function is called. */ static void initlist_add_neededs(Needed_Entry *needed, Objlist *list) { /* Recursively process the successor needed objects. */ if (needed->next != NULL) initlist_add_neededs(needed->next, list); /* Process the current needed object. */ if (needed->obj != NULL) initlist_add_objects(needed->obj, needed->obj, list); } /* * Scan all of the DAGs rooted in the range of objects from "obj" to * "tail" and add their init functions to "list". This recurses over * the DAGs and ensure the proper init ordering such that each object's * needed libraries are initialized before the object itself. At the * same time, this function adds the objects to the global finalization * list "list_fini" in the opposite order. The write lock must be * held when this function is called. */ static void initlist_add_objects(Obj_Entry *obj, Obj_Entry *tail, Objlist *list) { Obj_Entry *nobj; if (obj->init_scanned || obj->init_done) return; obj->init_scanned = true; /* Recursively process the successor objects. */ nobj = globallist_next(obj); if (nobj != NULL && obj != tail) initlist_add_objects(nobj, tail, list); /* Recursively process the needed objects. */ if (obj->needed != NULL) initlist_add_neededs(obj->needed, list); if (obj->needed_filtees != NULL) initlist_add_neededs(obj->needed_filtees, list); if (obj->needed_aux_filtees != NULL) initlist_add_neededs(obj->needed_aux_filtees, list); /* Add the object to the init list. */ if (obj->preinit_array != (Elf_Addr)NULL || obj->init != (Elf_Addr)NULL || obj->init_array != (Elf_Addr)NULL) objlist_push_tail(list, obj); /* Add the object to the global fini list in the reverse order. */ if ((obj->fini != (Elf_Addr)NULL || obj->fini_array != (Elf_Addr)NULL) && !obj->on_fini_list) { objlist_push_head(&list_fini, obj); obj->on_fini_list = true; } } #ifndef FPTR_TARGET #define FPTR_TARGET(f) ((Elf_Addr) (f)) #endif static void free_needed_filtees(Needed_Entry *n, RtldLockState *lockstate) { Needed_Entry *needed, *needed1; for (needed = n; needed != NULL; needed = needed->next) { if (needed->obj != NULL) { dlclose_locked(needed->obj, lockstate); needed->obj = NULL; } } for (needed = n; needed != NULL; needed = needed1) { needed1 = needed->next; free(needed); } } static void unload_filtees(Obj_Entry *obj, RtldLockState *lockstate) { free_needed_filtees(obj->needed_filtees, lockstate); obj->needed_filtees = NULL; free_needed_filtees(obj->needed_aux_filtees, lockstate); obj->needed_aux_filtees = NULL; obj->filtees_loaded = false; } static void load_filtee1(Obj_Entry *obj, Needed_Entry *needed, int flags, RtldLockState *lockstate) { for (; needed != NULL; needed = needed->next) { needed->obj = dlopen_object(obj->strtab + needed->name, -1, obj, flags, ((ld_loadfltr || obj->z_loadfltr) ? RTLD_NOW : RTLD_LAZY) | RTLD_LOCAL, lockstate); } } static void load_filtees(Obj_Entry *obj, int flags, RtldLockState *lockstate) { lock_restart_for_upgrade(lockstate); if (!obj->filtees_loaded) { load_filtee1(obj, obj->needed_filtees, flags, lockstate); load_filtee1(obj, obj->needed_aux_filtees, flags, lockstate); obj->filtees_loaded = true; } } static int process_needed(Obj_Entry *obj, Needed_Entry *needed, int flags) { Obj_Entry *obj1; for (; needed != NULL; needed = needed->next) { obj1 = needed->obj = load_object(obj->strtab + needed->name, -1, obj, flags & ~RTLD_LO_NOLOAD); if (obj1 == NULL && !ld_tracing && (flags & RTLD_LO_FILTEES) == 0) return (-1); } return (0); } /* * Given a shared object, traverse its list of needed objects, and load * each of them. Returns 0 on success. Generates an error message and * returns -1 on failure. */ static int load_needed_objects(Obj_Entry *first, int flags) { Obj_Entry *obj; for (obj = first; obj != NULL; obj = TAILQ_NEXT(obj, next)) { if (obj->marker) continue; if (process_needed(obj, obj->needed, flags) == -1) return (-1); } return (0); } static int load_preload_objects(void) { char *p = ld_preload; Obj_Entry *obj; static const char delim[] = " \t:;"; if (p == NULL) return 0; p += strspn(p, delim); while (*p != '\0') { size_t len = strcspn(p, delim); char savech; savech = p[len]; p[len] = '\0'; obj = load_object(p, -1, NULL, 0); if (obj == NULL) return -1; /* XXX - cleanup */ obj->z_interpose = true; p[len] = savech; p += len; p += strspn(p, delim); } LD_UTRACE(UTRACE_PRELOAD_FINISHED, NULL, NULL, 0, 0, NULL); return 0; } static const char * printable_path(const char *path) { return (path == NULL ? "" : path); } /* * Load a shared object into memory, if it is not already loaded. The * object may be specified by name or by user-supplied file descriptor * fd_u. In the later case, the fd_u descriptor is not closed, but its * duplicate is. * * Returns a pointer to the Obj_Entry for the object. Returns NULL * on failure. */ static Obj_Entry * load_object(const char *name, int fd_u, const Obj_Entry *refobj, int flags) { Obj_Entry *obj; int fd; struct stat sb; char *path; fd = -1; if (name != NULL) { TAILQ_FOREACH(obj, &obj_list, next) { if (obj->marker || obj->doomed) continue; if (object_match_name(obj, name)) return (obj); } path = find_library(name, refobj, &fd); if (path == NULL) return (NULL); } else path = NULL; if (fd >= 0) { /* * search_library_pathfds() opens a fresh file descriptor for the * library, so there is no need to dup(). */ } else if (fd_u == -1) { /* * If we didn't find a match by pathname, or the name is not * supplied, open the file and check again by device and inode. * This avoids false mismatches caused by multiple links or ".." * in pathnames. * * To avoid a race, we open the file and use fstat() rather than * using stat(). */ if ((fd = open(path, O_RDONLY | O_CLOEXEC | O_VERIFY)) == -1) { _rtld_error("Cannot open \"%s\"", path); free(path); return (NULL); } } else { fd = fcntl(fd_u, F_DUPFD_CLOEXEC, 0); if (fd == -1) { _rtld_error("Cannot dup fd"); free(path); return (NULL); } } if (fstat(fd, &sb) == -1) { _rtld_error("Cannot fstat \"%s\"", printable_path(path)); close(fd); free(path); return NULL; } TAILQ_FOREACH(obj, &obj_list, next) { if (obj->marker || obj->doomed) continue; if (obj->ino == sb.st_ino && obj->dev == sb.st_dev) break; } if (obj != NULL && name != NULL) { object_add_name(obj, name); free(path); close(fd); return obj; } if (flags & RTLD_LO_NOLOAD) { free(path); close(fd); return (NULL); } /* First use of this object, so we must map it in */ obj = do_load_object(fd, name, path, &sb, flags); if (obj == NULL) free(path); close(fd); return obj; } static Obj_Entry * do_load_object(int fd, const char *name, char *path, struct stat *sbp, int flags) { Obj_Entry *obj; struct statfs fs; /* * but first, make sure that environment variables haven't been * used to circumvent the noexec flag on a filesystem. */ if (dangerous_ld_env) { if (fstatfs(fd, &fs) != 0) { _rtld_error("Cannot fstatfs \"%s\"", printable_path(path)); return NULL; } if (fs.f_flags & MNT_NOEXEC) { _rtld_error("Cannot execute objects on %s\n", fs.f_mntonname); return NULL; } } dbg("loading \"%s\"", printable_path(path)); obj = map_object(fd, printable_path(path), sbp); if (obj == NULL) return NULL; /* * If DT_SONAME is present in the object, digest_dynamic2 already * added it to the object names. */ if (name != NULL) object_add_name(obj, name); obj->path = path; digest_dynamic(obj, 0); dbg("%s valid_hash_sysv %d valid_hash_gnu %d dynsymcount %d", obj->path, obj->valid_hash_sysv, obj->valid_hash_gnu, obj->dynsymcount); if (obj->z_noopen && (flags & (RTLD_LO_DLOPEN | RTLD_LO_TRACE)) == RTLD_LO_DLOPEN) { dbg("refusing to load non-loadable \"%s\"", obj->path); _rtld_error("Cannot dlopen non-loadable %s", obj->path); munmap(obj->mapbase, obj->mapsize); obj_free(obj); return (NULL); } obj->dlopened = (flags & RTLD_LO_DLOPEN) != 0; TAILQ_INSERT_TAIL(&obj_list, obj, next); obj_count++; obj_loads++; linkmap_add(obj); /* for GDB & dlinfo() */ max_stack_flags |= obj->stack_flags; dbg(" %p .. %p: %s", obj->mapbase, obj->mapbase + obj->mapsize - 1, obj->path); if (obj->textrel) dbg(" WARNING: %s has impure text", obj->path); LD_UTRACE(UTRACE_LOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0, obj->path); return obj; } static Obj_Entry * obj_from_addr(const void *addr) { Obj_Entry *obj; TAILQ_FOREACH(obj, &obj_list, next) { if (obj->marker) continue; if (addr < (void *) obj->mapbase) continue; if (addr < (void *) (obj->mapbase + obj->mapsize)) return obj; } return NULL; } static void preinit_main(void) { Elf_Addr *preinit_addr; int index; preinit_addr = (Elf_Addr *)obj_main->preinit_array; if (preinit_addr == NULL) return; for (index = 0; index < obj_main->preinit_array_num; index++) { if (preinit_addr[index] != 0 && preinit_addr[index] != 1) { dbg("calling preinit function for %s at %p", obj_main->path, (void *)preinit_addr[index]); LD_UTRACE(UTRACE_INIT_CALL, obj_main, (void *)preinit_addr[index], 0, 0, obj_main->path); call_init_pointer(obj_main, preinit_addr[index]); } } } /* * Call the finalization functions for each of the objects in "list" * belonging to the DAG of "root" and referenced once. If NULL "root" * is specified, every finalization function will be called regardless * of the reference count and the list elements won't be freed. All of * the objects are expected to have non-NULL fini functions. */ static void objlist_call_fini(Objlist *list, Obj_Entry *root, RtldLockState *lockstate) { Objlist_Entry *elm; char *saved_msg; Elf_Addr *fini_addr; int index; assert(root == NULL || root->refcount == 1); if (root != NULL) root->doomed = true; /* * Preserve the current error message since a fini function might * call into the dynamic linker and overwrite it. */ saved_msg = errmsg_save(); do { STAILQ_FOREACH(elm, list, link) { if (root != NULL && (elm->obj->refcount != 1 || objlist_find(&root->dagmembers, elm->obj) == NULL)) continue; /* Remove object from fini list to prevent recursive invocation. */ STAILQ_REMOVE(list, elm, Struct_Objlist_Entry, link); /* Ensure that new references cannot be acquired. */ elm->obj->doomed = true; hold_object(elm->obj); lock_release(rtld_bind_lock, lockstate); /* * It is legal to have both DT_FINI and DT_FINI_ARRAY defined. * When this happens, DT_FINI_ARRAY is processed first. */ fini_addr = (Elf_Addr *)elm->obj->fini_array; if (fini_addr != NULL && elm->obj->fini_array_num > 0) { for (index = elm->obj->fini_array_num - 1; index >= 0; index--) { if (fini_addr[index] != 0 && fini_addr[index] != 1) { dbg("calling fini function for %s at %p", elm->obj->path, (void *)fini_addr[index]); LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)fini_addr[index], 0, 0, elm->obj->path); call_initfini_pointer(elm->obj, fini_addr[index]); } } } if (elm->obj->fini != (Elf_Addr)NULL) { dbg("calling fini function for %s at %p", elm->obj->path, (void *)elm->obj->fini); LD_UTRACE(UTRACE_FINI_CALL, elm->obj, (void *)elm->obj->fini, 0, 0, elm->obj->path); call_initfini_pointer(elm->obj, elm->obj->fini); } wlock_acquire(rtld_bind_lock, lockstate); unhold_object(elm->obj); /* No need to free anything if process is going down. */ if (root != NULL) free(elm); /* * We must restart the list traversal after every fini call * because a dlclose() call from the fini function or from * another thread might have modified the reference counts. */ break; } } while (elm != NULL); errmsg_restore(saved_msg); } /* * Call the initialization functions for each of the objects in * "list". All of the objects are expected to have non-NULL init * functions. */ static void objlist_call_init(Objlist *list, RtldLockState *lockstate) { Objlist_Entry *elm; Obj_Entry *obj; char *saved_msg; Elf_Addr *init_addr; int index; /* * Clean init_scanned flag so that objects can be rechecked and * possibly initialized earlier if any of vectors called below * cause the change by using dlopen. */ TAILQ_FOREACH(obj, &obj_list, next) { if (obj->marker) continue; obj->init_scanned = false; } /* * Preserve the current error message since an init function might * call into the dynamic linker and overwrite it. */ saved_msg = errmsg_save(); STAILQ_FOREACH(elm, list, link) { if (elm->obj->init_done) /* Initialized early. */ continue; /* * Race: other thread might try to use this object before current * one completes the initialization. Not much can be done here * without better locking. */ elm->obj->init_done = true; hold_object(elm->obj); lock_release(rtld_bind_lock, lockstate); /* * It is legal to have both DT_INIT and DT_INIT_ARRAY defined. * When this happens, DT_INIT is processed first. */ if (elm->obj->init != (Elf_Addr)NULL) { dbg("calling init function for %s at %p", elm->obj->path, (void *)elm->obj->init); LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)elm->obj->init, 0, 0, elm->obj->path); call_initfini_pointer(elm->obj, elm->obj->init); } init_addr = (Elf_Addr *)elm->obj->init_array; if (init_addr != NULL) { for (index = 0; index < elm->obj->init_array_num; index++) { if (init_addr[index] != 0 && init_addr[index] != 1) { dbg("calling init function for %s at %p", elm->obj->path, (void *)init_addr[index]); LD_UTRACE(UTRACE_INIT_CALL, elm->obj, (void *)init_addr[index], 0, 0, elm->obj->path); call_init_pointer(elm->obj, init_addr[index]); } } } wlock_acquire(rtld_bind_lock, lockstate); unhold_object(elm->obj); } errmsg_restore(saved_msg); } static void objlist_clear(Objlist *list) { Objlist_Entry *elm; while (!STAILQ_EMPTY(list)) { elm = STAILQ_FIRST(list); STAILQ_REMOVE_HEAD(list, link); free(elm); } } static Objlist_Entry * objlist_find(Objlist *list, const Obj_Entry *obj) { Objlist_Entry *elm; STAILQ_FOREACH(elm, list, link) if (elm->obj == obj) return elm; return NULL; } static void objlist_init(Objlist *list) { STAILQ_INIT(list); } static void objlist_push_head(Objlist *list, Obj_Entry *obj) { Objlist_Entry *elm; elm = NEW(Objlist_Entry); elm->obj = obj; STAILQ_INSERT_HEAD(list, elm, link); } static void objlist_push_tail(Objlist *list, Obj_Entry *obj) { Objlist_Entry *elm; elm = NEW(Objlist_Entry); elm->obj = obj; STAILQ_INSERT_TAIL(list, elm, link); } static void objlist_put_after(Objlist *list, Obj_Entry *listobj, Obj_Entry *obj) { Objlist_Entry *elm, *listelm; STAILQ_FOREACH(listelm, list, link) { if (listelm->obj == listobj) break; } elm = NEW(Objlist_Entry); elm->obj = obj; if (listelm != NULL) STAILQ_INSERT_AFTER(list, listelm, elm, link); else STAILQ_INSERT_TAIL(list, elm, link); } static void objlist_remove(Objlist *list, Obj_Entry *obj) { Objlist_Entry *elm; if ((elm = objlist_find(list, obj)) != NULL) { STAILQ_REMOVE(list, elm, Struct_Objlist_Entry, link); free(elm); } } /* * Relocate dag rooted in the specified object. * Returns 0 on success, or -1 on failure. */ static int relocate_object_dag(Obj_Entry *root, bool bind_now, Obj_Entry *rtldobj, int flags, RtldLockState *lockstate) { Objlist_Entry *elm; int error; error = 0; STAILQ_FOREACH(elm, &root->dagmembers, link) { error = relocate_object(elm->obj, bind_now, rtldobj, flags, lockstate); if (error == -1) break; } return (error); } /* * Prepare for, or clean after, relocating an object marked with * DT_TEXTREL or DF_TEXTREL. Before relocating, all read-only * segments are remapped read-write. After relocations are done, the * segment's permissions are returned back to the modes specified in * the phdrs. If any relocation happened, or always for wired * program, COW is triggered. */ static int reloc_textrel_prot(Obj_Entry *obj, bool before) { const Elf_Phdr *ph; void *base; size_t l, sz; int prot; for (l = obj->phsize / sizeof(*ph), ph = obj->phdr; l > 0; l--, ph++) { if (ph->p_type != PT_LOAD || (ph->p_flags & PF_W) != 0) continue; base = obj->relocbase + trunc_page(ph->p_vaddr); sz = round_page(ph->p_vaddr + ph->p_filesz) - trunc_page(ph->p_vaddr); prot = convert_prot(ph->p_flags) | (before ? PROT_WRITE : 0); if (mprotect(base, sz, prot) == -1) { _rtld_error("%s: Cannot write-%sable text segment: %s", obj->path, before ? "en" : "dis", rtld_strerror(errno)); return (-1); } } return (0); } /* * Relocate single object. * Returns 0 on success, or -1 on failure. */ static int relocate_object(Obj_Entry *obj, bool bind_now, Obj_Entry *rtldobj, int flags, RtldLockState *lockstate) { if (obj->relocated) return (0); obj->relocated = true; if (obj != rtldobj) dbg("relocating \"%s\"", obj->path); if (obj->symtab == NULL || obj->strtab == NULL || !(obj->valid_hash_sysv || obj->valid_hash_gnu)) { _rtld_error("%s: Shared object has no run-time symbol table", obj->path); return (-1); } /* There are relocations to the write-protected text segment. */ if (obj->textrel && reloc_textrel_prot(obj, true) != 0) return (-1); /* Process the non-PLT non-IFUNC relocations. */ if (reloc_non_plt(obj, rtldobj, flags, lockstate)) return (-1); /* Re-protected the text segment. */ if (obj->textrel && reloc_textrel_prot(obj, false) != 0) return (-1); /* Set the special PLT or GOT entries. */ init_pltgot(obj); /* Process the PLT relocations. */ if (reloc_plt(obj) == -1) return (-1); /* Relocate the jump slots if we are doing immediate binding. */ if (obj->bind_now || bind_now) if (reloc_jmpslots(obj, flags, lockstate) == -1) return (-1); /* * Process the non-PLT IFUNC relocations. The relocations are * processed in two phases, because IFUNC resolvers may * reference other symbols, which must be readily processed * before resolvers are called. */ if (obj->non_plt_gnu_ifunc && reloc_non_plt(obj, rtldobj, flags | SYMLOOK_IFUNC, lockstate)) return (-1); if (!obj->mainprog && obj_enforce_relro(obj) == -1) return (-1); /* * Set up the magic number and version in the Obj_Entry. These * were checked in the crt1.o from the original ElfKit, so we * set them for backward compatibility. */ obj->magic = RTLD_MAGIC; obj->version = RTLD_VERSION; return (0); } /* * Relocate newly-loaded shared objects. The argument is a pointer to * the Obj_Entry for the first such object. All objects from the first * to the end of the list of objects are relocated. Returns 0 on success, * or -1 on failure. */ static int relocate_objects(Obj_Entry *first, bool bind_now, Obj_Entry *rtldobj, int flags, RtldLockState *lockstate) { Obj_Entry *obj; int error; for (error = 0, obj = first; obj != NULL; obj = TAILQ_NEXT(obj, next)) { if (obj->marker) continue; error = relocate_object(obj, bind_now, rtldobj, flags, lockstate); if (error == -1) break; } return (error); } /* * The handling of R_MACHINE_IRELATIVE relocations and jumpslots * referencing STT_GNU_IFUNC symbols is postponed till the other * relocations are done. The indirect functions specified as * ifunc are allowed to call other symbols, so we need to have * objects relocated before asking for resolution from indirects. * * The R_MACHINE_IRELATIVE slots are resolved in greedy fashion, * instead of the usual lazy handling of PLT slots. It is * consistent with how GNU does it. */ static int resolve_object_ifunc(Obj_Entry *obj, bool bind_now, int flags, RtldLockState *lockstate) { if (obj->irelative && reloc_iresolve(obj, lockstate) == -1) return (-1); if ((obj->bind_now || bind_now) && obj->gnu_ifunc && reloc_gnu_ifunc(obj, flags, lockstate) == -1) return (-1); return (0); } static int resolve_objects_ifunc(Obj_Entry *first, bool bind_now, int flags, RtldLockState *lockstate) { Obj_Entry *obj; for (obj = first; obj != NULL; obj = TAILQ_NEXT(obj, next)) { if (obj->marker) continue; if (resolve_object_ifunc(obj, bind_now, flags, lockstate) == -1) return (-1); } return (0); } static int initlist_objects_ifunc(Objlist *list, bool bind_now, int flags, RtldLockState *lockstate) { Objlist_Entry *elm; STAILQ_FOREACH(elm, list, link) { if (resolve_object_ifunc(elm->obj, bind_now, flags, lockstate) == -1) return (-1); } return (0); } /* * Cleanup procedure. It will be called (by the atexit mechanism) just * before the process exits. */ static void rtld_exit(void) { RtldLockState lockstate; wlock_acquire(rtld_bind_lock, &lockstate); dbg("rtld_exit()"); objlist_call_fini(&list_fini, NULL, &lockstate); /* No need to remove the items from the list, since we are exiting. */ if (!libmap_disable) lm_fini(); lock_release(rtld_bind_lock, &lockstate); } /* * Iterate over a search path, translate each element, and invoke the * callback on the result. */ static void * path_enumerate(const char *path, path_enum_proc callback, void *arg) { const char *trans; if (path == NULL) return (NULL); path += strspn(path, ":;"); while (*path != '\0') { size_t len; char *res; len = strcspn(path, ":;"); trans = lm_findn(NULL, path, len); if (trans) res = callback(trans, strlen(trans), arg); else res = callback(path, len, arg); if (res != NULL) return (res); path += len; path += strspn(path, ":;"); } return (NULL); } struct try_library_args { const char *name; size_t namelen; char *buffer; size_t buflen; int fd; }; static void * try_library_path(const char *dir, size_t dirlen, void *param) { struct try_library_args *arg; int fd; arg = param; if (*dir == '/' || trust) { char *pathname; if (dirlen + 1 + arg->namelen + 1 > arg->buflen) return (NULL); pathname = arg->buffer; strncpy(pathname, dir, dirlen); pathname[dirlen] = '/'; strcpy(pathname + dirlen + 1, arg->name); dbg(" Trying \"%s\"", pathname); fd = open(pathname, O_RDONLY | O_CLOEXEC | O_VERIFY); if (fd >= 0) { dbg(" Opened \"%s\", fd %d", pathname, fd); pathname = xmalloc(dirlen + 1 + arg->namelen + 1); strcpy(pathname, arg->buffer); arg->fd = fd; return (pathname); } else { dbg(" Failed to open \"%s\": %s", pathname, rtld_strerror(errno)); } } return (NULL); } static char * search_library_path(const char *name, const char *path, int *fdp) { char *p; struct try_library_args arg; if (path == NULL) return NULL; arg.name = name; arg.namelen = strlen(name); arg.buffer = xmalloc(PATH_MAX); arg.buflen = PATH_MAX; arg.fd = -1; p = path_enumerate(path, try_library_path, &arg); *fdp = arg.fd; free(arg.buffer); return (p); } /* * Finds the library with the given name using the directory descriptors * listed in the LD_LIBRARY_PATH_FDS environment variable. * * Returns a freshly-opened close-on-exec file descriptor for the library, * or -1 if the library cannot be found. */ static char * search_library_pathfds(const char *name, const char *path, int *fdp) { char *envcopy, *fdstr, *found, *last_token; size_t len; int dirfd, fd; dbg("%s('%s', '%s', fdp)", __func__, name, path); /* Don't load from user-specified libdirs into setuid binaries. */ if (!trust) return (NULL); /* We can't do anything if LD_LIBRARY_PATH_FDS isn't set. */ if (path == NULL) return (NULL); /* LD_LIBRARY_PATH_FDS only works with relative paths. */ if (name[0] == '/') { dbg("Absolute path (%s) passed to %s", name, __func__); return (NULL); } /* * Use strtok_r() to walk the FD:FD:FD list. This requires a local * copy of the path, as strtok_r rewrites separator tokens * with '\0'. */ found = NULL; envcopy = xstrdup(path); for (fdstr = strtok_r(envcopy, ":", &last_token); fdstr != NULL; fdstr = strtok_r(NULL, ":", &last_token)) { dirfd = parse_integer(fdstr); if (dirfd < 0) { _rtld_error("failed to parse directory FD: '%s'", fdstr); break; } fd = __sys_openat(dirfd, name, O_RDONLY | O_CLOEXEC | O_VERIFY); if (fd >= 0) { *fdp = fd; len = strlen(fdstr) + strlen(name) + 3; found = xmalloc(len); if (rtld_snprintf(found, len, "#%d/%s", dirfd, name) < 0) { _rtld_error("error generating '%d/%s'", dirfd, name); rtld_die(); } dbg("open('%s') => %d", found, fd); break; } } free(envcopy); return (found); } int dlclose(void *handle) { RtldLockState lockstate; int error; wlock_acquire(rtld_bind_lock, &lockstate); error = dlclose_locked(handle, &lockstate); lock_release(rtld_bind_lock, &lockstate); return (error); } static int dlclose_locked(void *handle, RtldLockState *lockstate) { Obj_Entry *root; root = dlcheck(handle); if (root == NULL) return -1; LD_UTRACE(UTRACE_DLCLOSE_START, handle, NULL, 0, root->dl_refcount, root->path); /* Unreference the object and its dependencies. */ root->dl_refcount--; if (root->refcount == 1) { /* * The object will be no longer referenced, so we must unload it. * First, call the fini functions. */ objlist_call_fini(&list_fini, root, lockstate); unref_dag(root); /* Finish cleaning up the newly-unreferenced objects. */ GDB_STATE(RT_DELETE,&root->linkmap); unload_object(root, lockstate); GDB_STATE(RT_CONSISTENT,NULL); } else unref_dag(root); LD_UTRACE(UTRACE_DLCLOSE_STOP, handle, NULL, 0, 0, NULL); return 0; } char * dlerror(void) { char *msg = error_message; error_message = NULL; return msg; } /* * This function is deprecated and has no effect. */ void dllockinit(void *context, void *(*lock_create)(void *context), void (*rlock_acquire)(void *lock), void (*wlock_acquire)(void *lock), void (*lock_release)(void *lock), void (*lock_destroy)(void *lock), void (*context_destroy)(void *context)) { static void *cur_context; static void (*cur_context_destroy)(void *); /* Just destroy the context from the previous call, if necessary. */ if (cur_context_destroy != NULL) cur_context_destroy(cur_context); cur_context = context; cur_context_destroy = context_destroy; } void * dlopen(const char *name, int mode) { return (rtld_dlopen(name, -1, mode)); } void * fdlopen(int fd, int mode) { return (rtld_dlopen(NULL, fd, mode)); } static void * rtld_dlopen(const char *name, int fd, int mode) { RtldLockState lockstate; int lo_flags; LD_UTRACE(UTRACE_DLOPEN_START, NULL, NULL, 0, mode, name); ld_tracing = (mode & RTLD_TRACE) == 0 ? NULL : "1"; if (ld_tracing != NULL) { rlock_acquire(rtld_bind_lock, &lockstate); if (sigsetjmp(lockstate.env, 0) != 0) lock_upgrade(rtld_bind_lock, &lockstate); environ = (char **)*get_program_var_addr("environ", &lockstate); lock_release(rtld_bind_lock, &lockstate); } lo_flags = RTLD_LO_DLOPEN; if (mode & RTLD_NODELETE) lo_flags |= RTLD_LO_NODELETE; if (mode & RTLD_NOLOAD) lo_flags |= RTLD_LO_NOLOAD; if (ld_tracing != NULL) lo_flags |= RTLD_LO_TRACE; return (dlopen_object(name, fd, obj_main, lo_flags, mode & (RTLD_MODEMASK | RTLD_GLOBAL), NULL)); } static void dlopen_cleanup(Obj_Entry *obj, RtldLockState *lockstate) { obj->dl_refcount--; unref_dag(obj); if (obj->refcount == 0) unload_object(obj, lockstate); } static Obj_Entry * dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags, int mode, RtldLockState *lockstate) { Obj_Entry *old_obj_tail; Obj_Entry *obj; Objlist initlist; RtldLockState mlockstate; int result; objlist_init(&initlist); if (lockstate == NULL && !(lo_flags & RTLD_LO_EARLY)) { wlock_acquire(rtld_bind_lock, &mlockstate); lockstate = &mlockstate; } GDB_STATE(RT_ADD,NULL); old_obj_tail = globallist_curr(TAILQ_LAST(&obj_list, obj_entry_q)); obj = NULL; if (name == NULL && fd == -1) { obj = obj_main; obj->refcount++; } else { obj = load_object(name, fd, refobj, lo_flags); } if (obj) { obj->dl_refcount++; if (mode & RTLD_GLOBAL && objlist_find(&list_global, obj) == NULL) objlist_push_tail(&list_global, obj); if (globallist_next(old_obj_tail) != NULL) { /* We loaded something new. */ assert(globallist_next(old_obj_tail) == obj); result = load_needed_objects(obj, lo_flags & (RTLD_LO_DLOPEN | RTLD_LO_EARLY)); init_dag(obj); ref_dag(obj); if (result != -1) result = rtld_verify_versions(&obj->dagmembers); if (result != -1 && ld_tracing) goto trace; if (result == -1 || relocate_object_dag(obj, (mode & RTLD_MODEMASK) == RTLD_NOW, &obj_rtld, (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0, lockstate) == -1) { dlopen_cleanup(obj, lockstate); obj = NULL; } else if (lo_flags & RTLD_LO_EARLY) { /* * Do not call the init functions for early loaded * filtees. The image is still not initialized enough * for them to work. * * Our object is found by the global object list and * will be ordered among all init calls done right * before transferring control to main. */ } else { /* Make list of init functions to call. */ initlist_add_objects(obj, obj, &initlist); } /* * Process all no_delete or global objects here, given * them own DAGs to prevent their dependencies from being * unloaded. This has to be done after we have loaded all * of the dependencies, so that we do not miss any. */ if (obj != NULL) process_z(obj); } else { /* * Bump the reference counts for objects on this DAG. If * this is the first dlopen() call for the object that was * already loaded as a dependency, initialize the dag * starting at it. */ init_dag(obj); ref_dag(obj); if ((lo_flags & RTLD_LO_TRACE) != 0) goto trace; } if (obj != NULL && ((lo_flags & RTLD_LO_NODELETE) != 0 || obj->z_nodelete) && !obj->ref_nodel) { dbg("obj %s nodelete", obj->path); ref_dag(obj); obj->z_nodelete = obj->ref_nodel = true; } } LD_UTRACE(UTRACE_DLOPEN_STOP, obj, NULL, 0, obj ? obj->dl_refcount : 0, name); GDB_STATE(RT_CONSISTENT,obj ? &obj->linkmap : NULL); if (!(lo_flags & RTLD_LO_EARLY)) { map_stacks_exec(lockstate); } if (initlist_objects_ifunc(&initlist, (mode & RTLD_MODEMASK) == RTLD_NOW, (lo_flags & RTLD_LO_EARLY) ? SYMLOOK_EARLY : 0, lockstate) == -1) { objlist_clear(&initlist); dlopen_cleanup(obj, lockstate); if (lockstate == &mlockstate) lock_release(rtld_bind_lock, lockstate); return (NULL); } if (!(lo_flags & RTLD_LO_EARLY)) { /* Call the init functions. */ objlist_call_init(&initlist, lockstate); } objlist_clear(&initlist); if (lockstate == &mlockstate) lock_release(rtld_bind_lock, lockstate); return obj; trace: trace_loaded_objects(obj); if (lockstate == &mlockstate) lock_release(rtld_bind_lock, lockstate); exit(0); } static void * do_dlsym(void *handle, const char *name, void *retaddr, const Ver_Entry *ve, int flags) { DoneList donelist; const Obj_Entry *obj, *defobj; const Elf_Sym *def; SymLook req; RtldLockState lockstate; tls_index ti; void *sym; int res; def = NULL; defobj = NULL; symlook_init(&req, name); req.ventry = ve; req.flags = flags | SYMLOOK_IN_PLT; req.lockstate = &lockstate; LD_UTRACE(UTRACE_DLSYM_START, handle, NULL, 0, 0, name); rlock_acquire(rtld_bind_lock, &lockstate); if (sigsetjmp(lockstate.env, 0) != 0) lock_upgrade(rtld_bind_lock, &lockstate); if (handle == NULL || handle == RTLD_NEXT || handle == RTLD_DEFAULT || handle == RTLD_SELF) { if ((obj = obj_from_addr(retaddr)) == NULL) { _rtld_error("Cannot determine caller's shared object"); lock_release(rtld_bind_lock, &lockstate); LD_UTRACE(UTRACE_DLSYM_STOP, handle, NULL, 0, 0, name); return NULL; } if (handle == NULL) { /* Just the caller's shared object. */ res = symlook_obj(&req, obj); if (res == 0) { def = req.sym_out; defobj = req.defobj_out; } } else if (handle == RTLD_NEXT || /* Objects after caller's */ handle == RTLD_SELF) { /* ... caller included */ if (handle == RTLD_NEXT) obj = globallist_next(obj); for (; obj != NULL; obj = TAILQ_NEXT(obj, next)) { if (obj->marker) continue; res = symlook_obj(&req, obj); if (res == 0) { if (def == NULL || ELF_ST_BIND(req.sym_out->st_info) != STB_WEAK) { def = req.sym_out; defobj = req.defobj_out; if (ELF_ST_BIND(def->st_info) != STB_WEAK) break; } } } /* * Search the dynamic linker itself, and possibly resolve the * symbol from there. This is how the application links to * dynamic linker services such as dlopen. */ if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { res = symlook_obj(&req, &obj_rtld); if (res == 0) { def = req.sym_out; defobj = req.defobj_out; } } } else { assert(handle == RTLD_DEFAULT); res = symlook_default(&req, obj); if (res == 0) { defobj = req.defobj_out; def = req.sym_out; } } } else { if ((obj = dlcheck(handle)) == NULL) { lock_release(rtld_bind_lock, &lockstate); LD_UTRACE(UTRACE_DLSYM_STOP, handle, NULL, 0, 0, name); return NULL; } donelist_init(&donelist); if (obj->mainprog) { /* Handle obtained by dlopen(NULL, ...) implies global scope. */ res = symlook_global(&req, &donelist); if (res == 0) { def = req.sym_out; defobj = req.defobj_out; } /* * Search the dynamic linker itself, and possibly resolve the * symbol from there. This is how the application links to * dynamic linker services such as dlopen. */ if (def == NULL || ELF_ST_BIND(def->st_info) == STB_WEAK) { res = symlook_obj(&req, &obj_rtld); if (res == 0) { def = req.sym_out; defobj = req.defobj_out; } } } else { /* Search the whole DAG rooted at the given object. */ res = symlook_list(&req, &obj->dagmembers, &donelist); if (res == 0) { def = req.sym_out; defobj = req.defobj_out; } } } if (def != NULL) { lock_release(rtld_bind_lock, &lockstate); /* * The value required by the caller is derived from the value * of the symbol. this is simply the relocated value of the * symbol. */ if (ELF_ST_TYPE(def->st_info) == STT_FUNC) sym = make_function_pointer(def, defobj); else if (ELF_ST_TYPE(def->st_info) == STT_GNU_IFUNC) sym = rtld_resolve_ifunc(defobj, def); else if (ELF_ST_TYPE(def->st_info) == STT_TLS) { ti.ti_module = defobj->tlsindex; ti.ti_offset = def->st_value; sym = __tls_get_addr(&ti); } else sym = defobj->relocbase + def->st_value; LD_UTRACE(UTRACE_DLSYM_STOP, handle, sym, 0, 0, name); return (sym); } _rtld_error("Undefined symbol \"%s%s%s\"", name, ve != NULL ? "@" : "", ve != NULL ? ve->name : ""); lock_release(rtld_bind_lock, &lockstate); LD_UTRACE(UTRACE_DLSYM_STOP, handle, NULL, 0, 0, name); return NULL; } void * dlsym(void *handle, const char *name) { return do_dlsym(handle, name, __builtin_return_address(0), NULL, SYMLOOK_DLSYM); } dlfunc_t dlfunc(void *handle, const char *name) { union { void *d; dlfunc_t f; } rv; rv.d = do_dlsym(handle, name, __builtin_return_address(0), NULL, SYMLOOK_DLSYM); return (rv.f); } void * dlvsym(void *handle, const char *name, const char *version) { Ver_Entry ventry; ventry.name = version; ventry.file = NULL; ventry.hash = elf_hash(version); ventry.flags= 0; return do_dlsym(handle, name, __builtin_return_address(0), &ventry, SYMLOOK_DLSYM); } int _rtld_addr_phdr(const void *addr, struct dl_phdr_info *phdr_info) { const Obj_Entry *obj; RtldLockState lockstate; rlock_acquire(rtld_bind_lock, &lockstate); obj = obj_from_addr(addr); if (obj == NULL) { _rtld_error("No shared object contains address"); lock_release(rtld_bind_lock, &lockstate); return (0); } rtld_fill_dl_phdr_info(obj, phdr_info); lock_release(rtld_bind_lock, &lockstate); return (1); } int dladdr(const void *addr, Dl_info *info) { const Obj_Entry *obj; const Elf_Sym *def; void *symbol_addr; unsigned long symoffset; RtldLockState lockstate; rlock_acquire(rtld_bind_lock, &lockstate); obj = obj_from_addr(addr); if (obj == NULL) { _rtld_error("No shared object contains address"); lock_release(rtld_bind_lock, &lockstate); return 0; } info->dli_fname = obj->path; info->dli_fbase = obj->mapbase; info->dli_saddr = (void *)0; info->dli_sname = NULL; /* * Walk the symbol list looking for the symbol whose address is * closest to the address sent in. */ for (symoffset = 0; symoffset < obj->dynsymcount; symoffset++) { def = obj->symtab + symoffset; /* * For skip the symbol if st_shndx is either SHN_UNDEF or * SHN_COMMON. */ if (def->st_shndx == SHN_UNDEF || def->st_shndx == SHN_COMMON) continue; /* * If the symbol is greater than the specified address, or if it * is further away from addr than the current nearest symbol, * then reject it. */ symbol_addr = obj->relocbase + def->st_value; if (symbol_addr > addr || symbol_addr < info->dli_saddr) continue; /* Update our idea of the nearest symbol. */ info->dli_sname = obj->strtab + def->st_name; info->dli_saddr = symbol_addr; /* Exact match? */ if (info->dli_saddr == addr) break; } lock_release(rtld_bind_lock, &lockstate); return 1; } int dlinfo(void *handle, int request, void *p) { const Obj_Entry *obj; RtldLockState lockstate; int error; rlock_acquire(rtld_bind_lock, &lockstate); if (handle == NULL || handle == RTLD_SELF) { void *retaddr; retaddr = __builtin_return_address(0); /* __GNUC__ only */ if ((obj = obj_from_addr(retaddr)) == NULL) _rtld_error("Cannot determine caller's shared object"); } else obj = dlcheck(handle); if (obj == NULL) { lock_release(rtld_bind_lock, &lockstate); return (-1); } error = 0; switch (request) { case RTLD_DI_LINKMAP: *((struct link_map const **)p) = &obj->linkmap; break; case RTLD_DI_ORIGIN: error = rtld_dirname(obj->path, p); break; case RTLD_DI_SERINFOSIZE: case RTLD_DI_SERINFO: error = do_search_info(obj, request, (struct dl_serinfo *)p); break; default: _rtld_error("Invalid request %d passed to dlinfo()", request); error = -1; } lock_release(rtld_bind_lock, &lockstate); return (error); } static void rtld_fill_dl_phdr_info(const Obj_Entry *obj, struct dl_phdr_info *phdr_info) { phdr_info->dlpi_addr = (Elf_Addr)obj->relocbase; phdr_info->dlpi_name = obj->path; phdr_info->dlpi_phdr = obj->phdr; phdr_info->dlpi_phnum = obj->phsize / sizeof(obj->phdr[0]); phdr_info->dlpi_tls_modid = obj->tlsindex; phdr_info->dlpi_tls_data = obj->tlsinit; phdr_info->dlpi_adds = obj_loads; phdr_info->dlpi_subs = obj_loads - obj_count; } int dl_iterate_phdr(__dl_iterate_hdr_callback callback, void *param) { struct dl_phdr_info phdr_info; Obj_Entry *obj, marker; RtldLockState bind_lockstate, phdr_lockstate; int error; init_marker(&marker); error = 0; wlock_acquire(rtld_phdr_lock, &phdr_lockstate); wlock_acquire(rtld_bind_lock, &bind_lockstate); for (obj = globallist_curr(TAILQ_FIRST(&obj_list)); obj != NULL;) { TAILQ_INSERT_AFTER(&obj_list, obj, &marker, next); rtld_fill_dl_phdr_info(obj, &phdr_info); hold_object(obj); lock_release(rtld_bind_lock, &bind_lockstate); error = callback(&phdr_info, sizeof phdr_info, param); wlock_acquire(rtld_bind_lock, &bind_lockstate); unhold_object(obj); obj = globallist_next(&marker); TAILQ_REMOVE(&obj_list, &marker, next); if (error != 0) { lock_release(rtld_bind_lock, &bind_lockstate); lock_release(rtld_phdr_lock, &phdr_lockstate); return (error); } } if (error == 0) { rtld_fill_dl_phdr_info(&obj_rtld, &phdr_info); lock_release(rtld_bind_lock, &bind_lockstate); error = callback(&phdr_info, sizeof(phdr_info), param); } lock_release(rtld_phdr_lock, &phdr_lockstate); return (error); } static void * fill_search_info(const char *dir, size_t dirlen, void *param) { struct fill_search_info_args *arg; arg = param; if (arg->request == RTLD_DI_SERINFOSIZE) { arg->serinfo->dls_cnt ++; arg->serinfo->dls_size += sizeof(struct dl_serpath) + dirlen + 1; } else { struct dl_serpath *s_entry; s_entry = arg->serpath; s_entry->dls_name = arg->strspace; s_entry->dls_flags = arg->flags; strncpy(arg->strspace, dir, dirlen); arg->strspace[dirlen] = '\0'; arg->strspace += dirlen + 1; arg->serpath++; } return (NULL); } static int do_search_info(const Obj_Entry *obj, int request, struct dl_serinfo *info) { struct dl_serinfo _info; struct fill_search_info_args args; args.request = RTLD_DI_SERINFOSIZE; args.serinfo = &_info; _info.dls_size = __offsetof(struct dl_serinfo, dls_serpath); _info.dls_cnt = 0; path_enumerate(obj->rpath, fill_search_info, &args); path_enumerate(ld_library_path, fill_search_info, &args); path_enumerate(obj->runpath, fill_search_info, &args); path_enumerate(gethints(obj->z_nodeflib), fill_search_info, &args); if (!obj->z_nodeflib) path_enumerate(ld_standard_library_path, fill_search_info, &args); if (request == RTLD_DI_SERINFOSIZE) { info->dls_size = _info.dls_size; info->dls_cnt = _info.dls_cnt; return (0); } if (info->dls_cnt != _info.dls_cnt || info->dls_size != _info.dls_size) { _rtld_error("Uninitialized Dl_serinfo struct passed to dlinfo()"); return (-1); } args.request = RTLD_DI_SERINFO; args.serinfo = info; args.serpath = &info->dls_serpath[0]; args.strspace = (char *)&info->dls_serpath[_info.dls_cnt]; args.flags = LA_SER_RUNPATH; if (path_enumerate(obj->rpath, fill_search_info, &args) != NULL) return (-1); args.flags = LA_SER_LIBPATH; if (path_enumerate(ld_library_path, fill_search_info, &args) != NULL) return (-1); args.flags = LA_SER_RUNPATH; if (path_enumerate(obj->runpath, fill_search_info, &args) != NULL) return (-1); args.flags = LA_SER_CONFIG; if (path_enumerate(gethints(obj->z_nodeflib), fill_search_info, &args) != NULL) return (-1); args.flags = LA_SER_DEFAULT; if (!obj->z_nodeflib && path_enumerate(ld_standard_library_path, fill_search_info, &args) != NULL) return (-1); return (0); } static int rtld_dirname(const char *path, char *bname) { const char *endp; /* Empty or NULL string gets treated as "." */ if (path == NULL || *path == '\0') { bname[0] = '.'; bname[1] = '\0'; return (0); } /* Strip trailing slashes */ endp = path + strlen(path) - 1; while (endp > path && *endp == '/') endp--; /* Find the start of the dir */ while (endp > path && *endp != '/') endp--; /* Either the dir is "/" or there are no slashes */ if (endp == path) { bname[0] = *endp == '/' ? '/' : '.'; bname[1] = '\0'; return (0); } else { do { endp--; } while (endp > path && *endp == '/'); } if (endp - path + 2 > PATH_MAX) { _rtld_error("Filename is too long: %s", path); return(-1); } strncpy(bname, path, endp - path + 1); bname[endp - path + 1] = '\0'; return (0); } static int rtld_dirname_abs(const char *path, char *base) { char *last; if (realpath(path, base) == NULL) return (-1); dbg("%s -> %s", path, base); last = strrchr(base, '/'); if (last == NULL) return (-1); if (last != base) *last = '\0'; return (0); } static void linkmap_add(Obj_Entry *obj) { struct link_map *l = &obj->linkmap; struct link_map *prev; obj->linkmap.l_name = obj->path; obj->linkmap.l_addr = obj->mapbase; obj->linkmap.l_ld = obj->dynamic; #ifdef __mips__ /* GDB needs load offset on MIPS to use the symbols */ obj->linkmap.l_offs = obj->relocbase; #endif if (r_debug.r_map == NULL) { r_debug.r_map = l; return; } /* * Scan to the end of the list, but not past the entry for the * dynamic linker, which we want to keep at the very end. */ for (prev = r_debug.r_map; prev->l_next != NULL && prev->l_next != &obj_rtld.linkmap; prev = prev->l_next) ; /* Link in the new entry. */ l->l_prev = prev; l->l_next = prev->l_next; if (l->l_next != NULL) l->l_next->l_prev = l; prev->l_next = l; } static void linkmap_delete(Obj_Entry *obj) { struct link_map *l = &obj->linkmap; if (l->l_prev == NULL) { if ((r_debug.r_map = l->l_next) != NULL) l->l_next->l_prev = NULL; return; } if ((l->l_prev->l_next = l->l_next) != NULL) l->l_next->l_prev = l->l_prev; } /* * Function for the debugger to set a breakpoint on to gain control. * * The two parameters allow the debugger to easily find and determine * what the runtime loader is doing and to whom it is doing it. * * When the loadhook trap is hit (r_debug_state, set at program * initialization), the arguments can be found on the stack: * * +8 struct link_map *m * +4 struct r_debug *rd * +0 RetAddr */ void r_debug_state(struct r_debug* rd, struct link_map *m) { /* * The following is a hack to force the compiler to emit calls to * this function, even when optimizing. If the function is empty, * the compiler is not obliged to emit any code for calls to it, * even when marked __noinline. However, gdb depends on those * calls being made. */ __compiler_membar(); } /* * A function called after init routines have completed. This can be used to * break before a program's entry routine is called, and can be used when * main is not available in the symbol table. */ void _r_debug_postinit(struct link_map *m) { /* See r_debug_state(). */ __compiler_membar(); } static void release_object(Obj_Entry *obj) { if (obj->holdcount > 0) { obj->unholdfree = true; return; } munmap(obj->mapbase, obj->mapsize); linkmap_delete(obj); obj_free(obj); } /* * Get address of the pointer variable in the main program. * Prefer non-weak symbol over the weak one. */ static const void ** get_program_var_addr(const char *name, RtldLockState *lockstate) { SymLook req; DoneList donelist; symlook_init(&req, name); req.lockstate = lockstate; donelist_init(&donelist); if (symlook_global(&req, &donelist) != 0) return (NULL); if (ELF_ST_TYPE(req.sym_out->st_info) == STT_FUNC) return ((const void **)make_function_pointer(req.sym_out, req.defobj_out)); else if (ELF_ST_TYPE(req.sym_out->st_info) == STT_GNU_IFUNC) return ((const void **)rtld_resolve_ifunc(req.defobj_out, req.sym_out)); else return ((const void **)(req.defobj_out->relocbase + req.sym_out->st_value)); } /* * Set a pointer variable in the main program to the given value. This * is used to set key variables such as "environ" before any of the * init functions are called. */ static void set_program_var(const char *name, const void *value) { const void **addr; if ((addr = get_program_var_addr(name, NULL)) != NULL) { dbg("\"%s\": *%p <-- %p", name, addr, value); *addr = value; } } /* * Search the global objects, including dependencies and main object, * for the given symbol. */ static int symlook_global(SymLook *req, DoneList *donelist) { SymLook req1; const Objlist_Entry *elm; int res; symlook_init_from_req(&req1, req); /* Search all objects loaded at program start up. */ if (req->defobj_out == NULL || ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK) { res = symlook_list(&req1, &list_main, donelist); if (res == 0 && (req->defobj_out == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) { req->sym_out = req1.sym_out; req->defobj_out = req1.defobj_out; assert(req->defobj_out != NULL); } } /* Search all DAGs whose roots are RTLD_GLOBAL objects. */ STAILQ_FOREACH(elm, &list_global, link) { if (req->defobj_out != NULL && ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK) break; res = symlook_list(&req1, &elm->obj->dagmembers, donelist); if (res == 0 && (req->defobj_out == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) { req->sym_out = req1.sym_out; req->defobj_out = req1.defobj_out; assert(req->defobj_out != NULL); } } return (req->sym_out != NULL ? 0 : ESRCH); } /* * Given a symbol name in a referencing object, find the corresponding * definition of the symbol. Returns a pointer to the symbol, or NULL if * no definition was found. Returns a pointer to the Obj_Entry of the * defining object via the reference parameter DEFOBJ_OUT. */ static int symlook_default(SymLook *req, const Obj_Entry *refobj) { DoneList donelist; const Objlist_Entry *elm; SymLook req1; int res; donelist_init(&donelist); symlook_init_from_req(&req1, req); /* * Look first in the referencing object if linked symbolically, * and similarly handle protected symbols. */ res = symlook_obj(&req1, refobj); if (res == 0 && (refobj->symbolic || ELF_ST_VISIBILITY(req1.sym_out->st_other) == STV_PROTECTED)) { req->sym_out = req1.sym_out; req->defobj_out = req1.defobj_out; assert(req->defobj_out != NULL); } if (refobj->symbolic || req->defobj_out != NULL) donelist_check(&donelist, refobj); symlook_global(req, &donelist); /* Search all dlopened DAGs containing the referencing object. */ STAILQ_FOREACH(elm, &refobj->dldags, link) { if (req->sym_out != NULL && ELF_ST_BIND(req->sym_out->st_info) != STB_WEAK) break; res = symlook_list(&req1, &elm->obj->dagmembers, &donelist); if (res == 0 && (req->sym_out == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK)) { req->sym_out = req1.sym_out; req->defobj_out = req1.defobj_out; assert(req->defobj_out != NULL); } } /* * Search the dynamic linker itself, and possibly resolve the * symbol from there. This is how the application links to * dynamic linker services such as dlopen. */ if (req->sym_out == NULL || ELF_ST_BIND(req->sym_out->st_info) == STB_WEAK) { res = symlook_obj(&req1, &obj_rtld); if (res == 0) { req->sym_out = req1.sym_out; req->defobj_out = req1.defobj_out; assert(req->defobj_out != NULL); } } return (req->sym_out != NULL ? 0 : ESRCH); } static int symlook_list(SymLook *req, const Objlist *objlist, DoneList *dlp) { const Elf_Sym *def; const Obj_Entry *defobj; const Objlist_Entry *elm; SymLook req1; int res; def = NULL; defobj = NULL; STAILQ_FOREACH(elm, objlist, link) { if (donelist_check(dlp, elm->obj)) continue; symlook_init_from_req(&req1, req); if ((res = symlook_obj(&req1, elm->obj)) == 0) { if (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK) { def = req1.sym_out; defobj = req1.defobj_out; if (ELF_ST_BIND(def->st_info) != STB_WEAK) break; } } } if (def != NULL) { req->sym_out = def; req->defobj_out = defobj; return (0); } return (ESRCH); } /* * Search the chain of DAGS cointed to by the given Needed_Entry * for a symbol of the given name. Each DAG is scanned completely * before advancing to the next one. Returns a pointer to the symbol, * or NULL if no definition was found. */ static int symlook_needed(SymLook *req, const Needed_Entry *needed, DoneList *dlp) { const Elf_Sym *def; const Needed_Entry *n; const Obj_Entry *defobj; SymLook req1; int res; def = NULL; defobj = NULL; symlook_init_from_req(&req1, req); for (n = needed; n != NULL; n = n->next) { if (n->obj == NULL || (res = symlook_list(&req1, &n->obj->dagmembers, dlp)) != 0) continue; if (def == NULL || ELF_ST_BIND(req1.sym_out->st_info) != STB_WEAK) { def = req1.sym_out; defobj = req1.defobj_out; if (ELF_ST_BIND(def->st_info) != STB_WEAK) break; } } if (def != NULL) { req->sym_out = def; req->defobj_out = defobj; return (0); } return (ESRCH); } /* * Search the symbol table of a single shared object for a symbol of * the given name and version, if requested. Returns a pointer to the * symbol, or NULL if no definition was found. If the object is * filter, return filtered symbol from filtee. * * The symbol's hash value is passed in for efficiency reasons; that * eliminates many recomputations of the hash value. */ int symlook_obj(SymLook *req, const Obj_Entry *obj) { DoneList donelist; SymLook req1; int flags, res, mres; /* * If there is at least one valid hash at this point, we prefer to * use the faster GNU version if available. */ if (obj->valid_hash_gnu) mres = symlook_obj1_gnu(req, obj); else if (obj->valid_hash_sysv) mres = symlook_obj1_sysv(req, obj); else return (EINVAL); if (mres == 0) { if (obj->needed_filtees != NULL) { flags = (req->flags & SYMLOOK_EARLY) ? RTLD_LO_EARLY : 0; load_filtees(__DECONST(Obj_Entry *, obj), flags, req->lockstate); donelist_init(&donelist); symlook_init_from_req(&req1, req); res = symlook_needed(&req1, obj->needed_filtees, &donelist); if (res == 0) { req->sym_out = req1.sym_out; req->defobj_out = req1.defobj_out; } return (res); } if (obj->needed_aux_filtees != NULL) { flags = (req->flags & SYMLOOK_EARLY) ? RTLD_LO_EARLY : 0; load_filtees(__DECONST(Obj_Entry *, obj), flags, req->lockstate); donelist_init(&donelist); symlook_init_from_req(&req1, req); res = symlook_needed(&req1, obj->needed_aux_filtees, &donelist); if (res == 0) { req->sym_out = req1.sym_out; req->defobj_out = req1.defobj_out; return (res); } } } return (mres); } /* Symbol match routine common to both hash functions */ static bool matched_symbol(SymLook *req, const Obj_Entry *obj, Sym_Match_Result *result, const unsigned long symnum) { Elf_Versym verndx; const Elf_Sym *symp; const char *strp; symp = obj->symtab + symnum; strp = obj->strtab + symp->st_name; switch (ELF_ST_TYPE(symp->st_info)) { case STT_FUNC: case STT_NOTYPE: case STT_OBJECT: case STT_COMMON: case STT_GNU_IFUNC: if (symp->st_value == 0) return (false); /* fallthrough */ case STT_TLS: if (symp->st_shndx != SHN_UNDEF) break; #ifndef __mips__ else if (((req->flags & SYMLOOK_IN_PLT) == 0) && (ELF_ST_TYPE(symp->st_info) == STT_FUNC)) break; /* fallthrough */ #endif default: return (false); } if (req->name[0] != strp[0] || strcmp(req->name, strp) != 0) return (false); if (req->ventry == NULL) { if (obj->versyms != NULL) { verndx = VER_NDX(obj->versyms[symnum]); if (verndx > obj->vernum) { _rtld_error( "%s: symbol %s references wrong version %d", obj->path, obj->strtab + symnum, verndx); return (false); } /* * If we are not called from dlsym (i.e. this * is a normal relocation from unversioned * binary), accept the symbol immediately if * it happens to have first version after this * shared object became versioned. Otherwise, * if symbol is versioned and not hidden, * remember it. If it is the only symbol with * this name exported by the shared object, it * will be returned as a match by the calling * function. If symbol is global (verndx < 2) * accept it unconditionally. */ if ((req->flags & SYMLOOK_DLSYM) == 0 && verndx == VER_NDX_GIVEN) { result->sym_out = symp; return (true); } else if (verndx >= VER_NDX_GIVEN) { if ((obj->versyms[symnum] & VER_NDX_HIDDEN) == 0) { if (result->vsymp == NULL) result->vsymp = symp; result->vcount++; } return (false); } } result->sym_out = symp; return (true); } if (obj->versyms == NULL) { if (object_match_name(obj, req->ventry->name)) { _rtld_error("%s: object %s should provide version %s " "for symbol %s", obj_rtld.path, obj->path, req->ventry->name, obj->strtab + symnum); return (false); } } else { verndx = VER_NDX(obj->versyms[symnum]); if (verndx > obj->vernum) { _rtld_error("%s: symbol %s references wrong version %d", obj->path, obj->strtab + symnum, verndx); return (false); } if (obj->vertab[verndx].hash != req->ventry->hash || strcmp(obj->vertab[verndx].name, req->ventry->name)) { /* * Version does not match. Look if this is a * global symbol and if it is not hidden. If * global symbol (verndx < 2) is available, * use it. Do not return symbol if we are * called by dlvsym, because dlvsym looks for * a specific version and default one is not * what dlvsym wants. */ if ((req->flags & SYMLOOK_DLSYM) || (verndx >= VER_NDX_GIVEN) || (obj->versyms[symnum] & VER_NDX_HIDDEN)) return (false); } } result->sym_out = symp; return (true); } /* * Search for symbol using SysV hash function. * obj->buckets is known not to be NULL at this point; the test for this was * performed with the obj->valid_hash_sysv assignment. */ static int symlook_obj1_sysv(SymLook *req, const Obj_Entry *obj) { unsigned long symnum; Sym_Match_Result matchres; matchres.sym_out = NULL; matchres.vsymp = NULL; matchres.vcount = 0; for (symnum = obj->buckets[req->hash % obj->nbuckets]; symnum != STN_UNDEF; symnum = obj->chains[symnum]) { if (symnum >= obj->nchains) return (ESRCH); /* Bad object */ if (matched_symbol(req, obj, &matchres, symnum)) { req->sym_out = matchres.sym_out; req->defobj_out = obj; return (0); } } if (matchres.vcount == 1) { req->sym_out = matchres.vsymp; req->defobj_out = obj; return (0); } return (ESRCH); } /* Search for symbol using GNU hash function */ static int symlook_obj1_gnu(SymLook *req, const Obj_Entry *obj) { Elf_Addr bloom_word; const Elf32_Word *hashval; Elf32_Word bucket; Sym_Match_Result matchres; unsigned int h1, h2; unsigned long symnum; matchres.sym_out = NULL; matchres.vsymp = NULL; matchres.vcount = 0; /* Pick right bitmask word from Bloom filter array */ bloom_word = obj->bloom_gnu[(req->hash_gnu / __ELF_WORD_SIZE) & obj->maskwords_bm_gnu]; /* Calculate modulus word size of gnu hash and its derivative */ h1 = req->hash_gnu & (__ELF_WORD_SIZE - 1); h2 = ((req->hash_gnu >> obj->shift2_gnu) & (__ELF_WORD_SIZE - 1)); /* Filter out the "definitely not in set" queries */ if (((bloom_word >> h1) & (bloom_word >> h2) & 1) == 0) return (ESRCH); /* Locate hash chain and corresponding value element*/ bucket = obj->buckets_gnu[req->hash_gnu % obj->nbuckets_gnu]; if (bucket == 0) return (ESRCH); hashval = &obj->chain_zero_gnu[bucket]; do { if (((*hashval ^ req->hash_gnu) >> 1) == 0) { symnum = hashval - obj->chain_zero_gnu; if (matched_symbol(req, obj, &matchres, symnum)) { req->sym_out = matchres.sym_out; req->defobj_out = obj; return (0); } } } while ((*hashval++ & 1) == 0); if (matchres.vcount == 1) { req->sym_out = matchres.vsymp; req->defobj_out = obj; return (0); } return (ESRCH); } static void trace_loaded_objects(Obj_Entry *obj) { char *fmt1, *fmt2, *fmt, *main_local, *list_containers; int c; if ((main_local = getenv(_LD("TRACE_LOADED_OBJECTS_PROGNAME"))) == NULL) main_local = ""; if ((fmt1 = getenv(_LD("TRACE_LOADED_OBJECTS_FMT1"))) == NULL) fmt1 = "\t%o => %p (%x)\n"; if ((fmt2 = getenv(_LD("TRACE_LOADED_OBJECTS_FMT2"))) == NULL) fmt2 = "\t%o (%x)\n"; list_containers = getenv(_LD("TRACE_LOADED_OBJECTS_ALL")); for (; obj != NULL; obj = TAILQ_NEXT(obj, next)) { Needed_Entry *needed; char *name, *path; bool is_lib; if (obj->marker) continue; if (list_containers && obj->needed != NULL) rtld_printf("%s:\n", obj->path); for (needed = obj->needed; needed; needed = needed->next) { if (needed->obj != NULL) { if (needed->obj->traced && !list_containers) continue; needed->obj->traced = true; path = needed->obj->path; } else path = "not found"; name = (char *)obj->strtab + needed->name; is_lib = strncmp(name, "lib", 3) == 0; /* XXX - bogus */ fmt = is_lib ? fmt1 : fmt2; while ((c = *fmt++) != '\0') { switch (c) { default: rtld_putchar(c); continue; case '\\': switch (c = *fmt) { case '\0': continue; case 'n': rtld_putchar('\n'); break; case 't': rtld_putchar('\t'); break; } break; case '%': switch (c = *fmt) { case '\0': continue; case '%': default: rtld_putchar(c); break; case 'A': rtld_putstr(main_local); break; case 'a': rtld_putstr(obj_main->path); break; case 'o': rtld_putstr(name); break; #if 0 case 'm': rtld_printf("%d", sodp->sod_major); break; case 'n': rtld_printf("%d", sodp->sod_minor); break; #endif case 'p': rtld_putstr(path); break; case 'x': rtld_printf("%p", needed->obj ? needed->obj->mapbase : 0); break; } break; } ++fmt; } } } } /* * Unload a dlopened object and its dependencies from memory and from * our data structures. It is assumed that the DAG rooted in the * object has already been unreferenced, and that the object has a * reference count of 0. */ static void unload_object(Obj_Entry *root, RtldLockState *lockstate) { Obj_Entry marker, *obj, *next; assert(root->refcount == 0); /* * Pass over the DAG removing unreferenced objects from * appropriate lists. */ unlink_object(root); /* Unmap all objects that are no longer referenced. */ for (obj = TAILQ_FIRST(&obj_list); obj != NULL; obj = next) { next = TAILQ_NEXT(obj, next); if (obj->marker || obj->refcount != 0) continue; LD_UTRACE(UTRACE_UNLOAD_OBJECT, obj, obj->mapbase, obj->mapsize, 0, obj->path); dbg("unloading \"%s\"", obj->path); /* * Unlink the object now to prevent new references from * being acquired while the bind lock is dropped in * recursive dlclose() invocations. */ TAILQ_REMOVE(&obj_list, obj, next); obj_count--; if (obj->filtees_loaded) { if (next != NULL) { init_marker(&marker); TAILQ_INSERT_BEFORE(next, &marker, next); unload_filtees(obj, lockstate); next = TAILQ_NEXT(&marker, next); TAILQ_REMOVE(&obj_list, &marker, next); } else unload_filtees(obj, lockstate); } release_object(obj); } } static void unlink_object(Obj_Entry *root) { Objlist_Entry *elm; if (root->refcount == 0) { /* Remove the object from the RTLD_GLOBAL list. */ objlist_remove(&list_global, root); /* Remove the object from all objects' DAG lists. */ STAILQ_FOREACH(elm, &root->dagmembers, link) { objlist_remove(&elm->obj->dldags, root); if (elm->obj != root) unlink_object(elm->obj); } } } static void ref_dag(Obj_Entry *root) { Objlist_Entry *elm; assert(root->dag_inited); STAILQ_FOREACH(elm, &root->dagmembers, link) elm->obj->refcount++; } static void unref_dag(Obj_Entry *root) { Objlist_Entry *elm; assert(root->dag_inited); STAILQ_FOREACH(elm, &root->dagmembers, link) elm->obj->refcount--; } /* * Common code for MD __tls_get_addr(). */ static void *tls_get_addr_slow(Elf_Addr **, int, size_t) __noinline; static void * tls_get_addr_slow(Elf_Addr **dtvp, int index, size_t offset) { Elf_Addr *newdtv, *dtv; RtldLockState lockstate; int to_copy; dtv = *dtvp; /* Check dtv generation in case new modules have arrived */ if (dtv[0] != tls_dtv_generation) { wlock_acquire(rtld_bind_lock, &lockstate); newdtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr)); to_copy = dtv[1]; if (to_copy > tls_max_index) to_copy = tls_max_index; memcpy(&newdtv[2], &dtv[2], to_copy * sizeof(Elf_Addr)); newdtv[0] = tls_dtv_generation; newdtv[1] = tls_max_index; free(dtv); lock_release(rtld_bind_lock, &lockstate); dtv = *dtvp = newdtv; } /* Dynamically allocate module TLS if necessary */ if (dtv[index + 1] == 0) { /* Signal safe, wlock will block out signals. */ wlock_acquire(rtld_bind_lock, &lockstate); if (!dtv[index + 1]) dtv[index + 1] = (Elf_Addr)allocate_module_tls(index); lock_release(rtld_bind_lock, &lockstate); } return ((void *)(dtv[index + 1] + offset)); } void * tls_get_addr_common(Elf_Addr **dtvp, int index, size_t offset) { Elf_Addr *dtv; dtv = *dtvp; /* Check dtv generation in case new modules have arrived */ if (__predict_true(dtv[0] == tls_dtv_generation && dtv[index + 1] != 0)) return ((void *)(dtv[index + 1] + offset)); return (tls_get_addr_slow(dtvp, index, offset)); } #if defined(__aarch64__) || defined(__arm__) || defined(__mips__) || \ defined(__powerpc__) || defined(__riscv) /* * Allocate Static TLS using the Variant I method. */ void * allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign) { Obj_Entry *obj; char *tcb; Elf_Addr **tls; Elf_Addr *dtv; Elf_Addr addr; int i; if (oldtcb != NULL && tcbsize == TLS_TCB_SIZE) return (oldtcb); assert(tcbsize >= TLS_TCB_SIZE); tcb = xcalloc(1, tls_static_space - TLS_TCB_SIZE + tcbsize); tls = (Elf_Addr **)(tcb + tcbsize - TLS_TCB_SIZE); if (oldtcb != NULL) { memcpy(tls, oldtcb, tls_static_space); free(oldtcb); /* Adjust the DTV. */ dtv = tls[0]; for (i = 0; i < dtv[1]; i++) { if (dtv[i+2] >= (Elf_Addr)oldtcb && dtv[i+2] < (Elf_Addr)oldtcb + tls_static_space) { dtv[i+2] = dtv[i+2] - (Elf_Addr)oldtcb + (Elf_Addr)tls; } } } else { dtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr)); tls[0] = dtv; dtv[0] = tls_dtv_generation; dtv[1] = tls_max_index; for (obj = globallist_curr(objs); obj != NULL; obj = globallist_next(obj)) { if (obj->tlsoffset > 0) { addr = (Elf_Addr)tls + obj->tlsoffset; if (obj->tlsinitsize > 0) memcpy((void*) addr, obj->tlsinit, obj->tlsinitsize); if (obj->tlssize > obj->tlsinitsize) memset((void*) (addr + obj->tlsinitsize), 0, obj->tlssize - obj->tlsinitsize); dtv[obj->tlsindex + 1] = addr; } } } return (tcb); } void free_tls(void *tcb, size_t tcbsize, size_t tcbalign) { Elf_Addr *dtv; Elf_Addr tlsstart, tlsend; int dtvsize, i; assert(tcbsize >= TLS_TCB_SIZE); tlsstart = (Elf_Addr)tcb + tcbsize - TLS_TCB_SIZE; tlsend = tlsstart + tls_static_space; dtv = *(Elf_Addr **)tlsstart; dtvsize = dtv[1]; for (i = 0; i < dtvsize; i++) { if (dtv[i+2] && (dtv[i+2] < tlsstart || dtv[i+2] >= tlsend)) { free((void*)dtv[i+2]); } } free(dtv); free(tcb); } #endif #if defined(__i386__) || defined(__amd64__) || defined(__sparc64__) /* * Allocate Static TLS using the Variant II method. */ void * allocate_tls(Obj_Entry *objs, void *oldtls, size_t tcbsize, size_t tcbalign) { Obj_Entry *obj; size_t size, ralign; char *tls; Elf_Addr *dtv, *olddtv; Elf_Addr segbase, oldsegbase, addr; int i; ralign = tcbalign; if (tls_static_max_align > ralign) ralign = tls_static_max_align; size = round(tls_static_space, ralign) + round(tcbsize, ralign); assert(tcbsize >= 2*sizeof(Elf_Addr)); tls = malloc_aligned(size, ralign); dtv = xcalloc(tls_max_index + 2, sizeof(Elf_Addr)); segbase = (Elf_Addr)(tls + round(tls_static_space, ralign)); ((Elf_Addr*)segbase)[0] = segbase; ((Elf_Addr*)segbase)[1] = (Elf_Addr) dtv; dtv[0] = tls_dtv_generation; dtv[1] = tls_max_index; if (oldtls) { /* * Copy the static TLS block over whole. */ oldsegbase = (Elf_Addr) oldtls; memcpy((void *)(segbase - tls_static_space), (const void *)(oldsegbase - tls_static_space), tls_static_space); /* * If any dynamic TLS blocks have been created tls_get_addr(), * move them over. */ olddtv = ((Elf_Addr**)oldsegbase)[1]; for (i = 0; i < olddtv[1]; i++) { if (olddtv[i+2] < oldsegbase - size || olddtv[i+2] > oldsegbase) { dtv[i+2] = olddtv[i+2]; olddtv[i+2] = 0; } } /* * We assume that this block was the one we created with * allocate_initial_tls(). */ free_tls(oldtls, 2*sizeof(Elf_Addr), sizeof(Elf_Addr)); } else { for (obj = objs; obj != NULL; obj = TAILQ_NEXT(obj, next)) { if (obj->marker || obj->tlsoffset == 0) continue; addr = segbase - obj->tlsoffset; memset((void*) (addr + obj->tlsinitsize), 0, obj->tlssize - obj->tlsinitsize); if (obj->tlsinit) memcpy((void*) addr, obj->tlsinit, obj->tlsinitsize); dtv[obj->tlsindex + 1] = addr; } } return (void*) segbase; } void free_tls(void *tls, size_t tcbsize, size_t tcbalign) { Elf_Addr* dtv; size_t size, ralign; int dtvsize, i; Elf_Addr tlsstart, tlsend; /* * Figure out the size of the initial TLS block so that we can * find stuff which ___tls_get_addr() allocated dynamically. */ ralign = tcbalign; if (tls_static_max_align > ralign) ralign = tls_static_max_align; size = round(tls_static_space, ralign); dtv = ((Elf_Addr**)tls)[1]; dtvsize = dtv[1]; tlsend = (Elf_Addr) tls; tlsstart = tlsend - size; for (i = 0; i < dtvsize; i++) { if (dtv[i + 2] != 0 && (dtv[i + 2] < tlsstart || dtv[i + 2] > tlsend)) { free_aligned((void *)dtv[i + 2]); } } free_aligned((void *)tlsstart); free((void*) dtv); } #endif /* * Allocate TLS block for module with given index. */ void * allocate_module_tls(int index) { Obj_Entry* obj; char* p; TAILQ_FOREACH(obj, &obj_list, next) { if (obj->marker) continue; if (obj->tlsindex == index) break; } if (!obj) { _rtld_error("Can't find module with TLS index %d", index); rtld_die(); } p = malloc_aligned(obj->tlssize, obj->tlsalign); memcpy(p, obj->tlsinit, obj->tlsinitsize); memset(p + obj->tlsinitsize, 0, obj->tlssize - obj->tlsinitsize); return p; } bool allocate_tls_offset(Obj_Entry *obj) { size_t off; if (obj->tls_done) return true; if (obj->tlssize == 0) { obj->tls_done = true; return true; } if (tls_last_offset == 0) off = calculate_first_tls_offset(obj->tlssize, obj->tlsalign); else off = calculate_tls_offset(tls_last_offset, tls_last_size, obj->tlssize, obj->tlsalign); /* * If we have already fixed the size of the static TLS block, we * must stay within that size. When allocating the static TLS, we * leave a small amount of space spare to be used for dynamically * loading modules which use static TLS. */ if (tls_static_space != 0) { if (calculate_tls_end(off, obj->tlssize) > tls_static_space) return false; } else if (obj->tlsalign > tls_static_max_align) { tls_static_max_align = obj->tlsalign; } tls_last_offset = obj->tlsoffset = off; tls_last_size = obj->tlssize; obj->tls_done = true; return true; } void free_tls_offset(Obj_Entry *obj) { /* * If we were the last thing to allocate out of the static TLS * block, we give our space back to the 'allocator'. This is a * simplistic workaround to allow libGL.so.1 to be loaded and * unloaded multiple times. */ if (calculate_tls_end(obj->tlsoffset, obj->tlssize) == calculate_tls_end(tls_last_offset, tls_last_size)) { tls_last_offset -= obj->tlssize; tls_last_size = 0; } } void * _rtld_allocate_tls(void *oldtls, size_t tcbsize, size_t tcbalign) { void *ret; RtldLockState lockstate; wlock_acquire(rtld_bind_lock, &lockstate); ret = allocate_tls(globallist_curr(TAILQ_FIRST(&obj_list)), oldtls, tcbsize, tcbalign); lock_release(rtld_bind_lock, &lockstate); return (ret); } void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign) { RtldLockState lockstate; wlock_acquire(rtld_bind_lock, &lockstate); free_tls(tcb, tcbsize, tcbalign); lock_release(rtld_bind_lock, &lockstate); } static void object_add_name(Obj_Entry *obj, const char *name) { Name_Entry *entry; size_t len; len = strlen(name); entry = malloc(sizeof(Name_Entry) + len); if (entry != NULL) { strcpy(entry->name, name); STAILQ_INSERT_TAIL(&obj->names, entry, link); } } static int object_match_name(const Obj_Entry *obj, const char *name) { Name_Entry *entry; STAILQ_FOREACH(entry, &obj->names, link) { if (strcmp(name, entry->name) == 0) return (1); } return (0); } static Obj_Entry * locate_dependency(const Obj_Entry *obj, const char *name) { const Objlist_Entry *entry; const Needed_Entry *needed; STAILQ_FOREACH(entry, &list_main, link) { if (object_match_name(entry->obj, name)) return entry->obj; } for (needed = obj->needed; needed != NULL; needed = needed->next) { if (strcmp(obj->strtab + needed->name, name) == 0 || (needed->obj != NULL && object_match_name(needed->obj, name))) { /* * If there is DT_NEEDED for the name we are looking for, * we are all set. Note that object might not be found if * dependency was not loaded yet, so the function can * return NULL here. This is expected and handled * properly by the caller. */ return (needed->obj); } } _rtld_error("%s: Unexpected inconsistency: dependency %s not found", obj->path, name); rtld_die(); } static int check_object_provided_version(Obj_Entry *refobj, const Obj_Entry *depobj, const Elf_Vernaux *vna) { const Elf_Verdef *vd; const char *vername; vername = refobj->strtab + vna->vna_name; vd = depobj->verdef; if (vd == NULL) { _rtld_error("%s: version %s required by %s not defined", depobj->path, vername, refobj->path); return (-1); } for (;;) { if (vd->vd_version != VER_DEF_CURRENT) { _rtld_error("%s: Unsupported version %d of Elf_Verdef entry", depobj->path, vd->vd_version); return (-1); } if (vna->vna_hash == vd->vd_hash) { const Elf_Verdaux *aux = (const Elf_Verdaux *) ((char *)vd + vd->vd_aux); if (strcmp(vername, depobj->strtab + aux->vda_name) == 0) return (0); } if (vd->vd_next == 0) break; vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next); } if (vna->vna_flags & VER_FLG_WEAK) return (0); _rtld_error("%s: version %s required by %s not found", depobj->path, vername, refobj->path); return (-1); } static int rtld_verify_object_versions(Obj_Entry *obj) { const Elf_Verneed *vn; const Elf_Verdef *vd; const Elf_Verdaux *vda; const Elf_Vernaux *vna; const Obj_Entry *depobj; int maxvernum, vernum; if (obj->ver_checked) return (0); obj->ver_checked = true; maxvernum = 0; /* * Walk over defined and required version records and figure out * max index used by any of them. Do very basic sanity checking * while there. */ vn = obj->verneed; while (vn != NULL) { if (vn->vn_version != VER_NEED_CURRENT) { _rtld_error("%s: Unsupported version %d of Elf_Verneed entry", obj->path, vn->vn_version); return (-1); } vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux); for (;;) { vernum = VER_NEED_IDX(vna->vna_other); if (vernum > maxvernum) maxvernum = vernum; if (vna->vna_next == 0) break; vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next); } if (vn->vn_next == 0) break; vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next); } vd = obj->verdef; while (vd != NULL) { if (vd->vd_version != VER_DEF_CURRENT) { _rtld_error("%s: Unsupported version %d of Elf_Verdef entry", obj->path, vd->vd_version); return (-1); } vernum = VER_DEF_IDX(vd->vd_ndx); if (vernum > maxvernum) maxvernum = vernum; if (vd->vd_next == 0) break; vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next); } if (maxvernum == 0) return (0); /* * Store version information in array indexable by version index. * Verify that object version requirements are satisfied along the * way. */ obj->vernum = maxvernum + 1; obj->vertab = xcalloc(obj->vernum, sizeof(Ver_Entry)); vd = obj->verdef; while (vd != NULL) { if ((vd->vd_flags & VER_FLG_BASE) == 0) { vernum = VER_DEF_IDX(vd->vd_ndx); assert(vernum <= maxvernum); vda = (const Elf_Verdaux *)((char *)vd + vd->vd_aux); obj->vertab[vernum].hash = vd->vd_hash; obj->vertab[vernum].name = obj->strtab + vda->vda_name; obj->vertab[vernum].file = NULL; obj->vertab[vernum].flags = 0; } if (vd->vd_next == 0) break; vd = (const Elf_Verdef *) ((char *)vd + vd->vd_next); } vn = obj->verneed; while (vn != NULL) { depobj = locate_dependency(obj, obj->strtab + vn->vn_file); if (depobj == NULL) return (-1); vna = (const Elf_Vernaux *) ((char *)vn + vn->vn_aux); for (;;) { if (check_object_provided_version(obj, depobj, vna)) return (-1); vernum = VER_NEED_IDX(vna->vna_other); assert(vernum <= maxvernum); obj->vertab[vernum].hash = vna->vna_hash; obj->vertab[vernum].name = obj->strtab + vna->vna_name; obj->vertab[vernum].file = obj->strtab + vn->vn_file; obj->vertab[vernum].flags = (vna->vna_other & VER_NEED_HIDDEN) ? VER_INFO_HIDDEN : 0; if (vna->vna_next == 0) break; vna = (const Elf_Vernaux *) ((char *)vna + vna->vna_next); } if (vn->vn_next == 0) break; vn = (const Elf_Verneed *) ((char *)vn + vn->vn_next); } return 0; } static int rtld_verify_versions(const Objlist *objlist) { Objlist_Entry *entry; int rc; rc = 0; STAILQ_FOREACH(entry, objlist, link) { /* * Skip dummy objects or objects that have their version requirements * already checked. */ if (entry->obj->strtab == NULL || entry->obj->vertab != NULL) continue; if (rtld_verify_object_versions(entry->obj) == -1) { rc = -1; if (ld_tracing == NULL) break; } } if (rc == 0 || ld_tracing != NULL) rc = rtld_verify_object_versions(&obj_rtld); return rc; } const Ver_Entry * fetch_ventry(const Obj_Entry *obj, unsigned long symnum) { Elf_Versym vernum; if (obj->vertab) { vernum = VER_NDX(obj->versyms[symnum]); if (vernum >= obj->vernum) { _rtld_error("%s: symbol %s has wrong verneed value %d", obj->path, obj->strtab + symnum, vernum); } else if (obj->vertab[vernum].hash != 0) { return &obj->vertab[vernum]; } } return NULL; } int _rtld_get_stack_prot(void) { return (stack_prot); } int _rtld_is_dlopened(void *arg) { Obj_Entry *obj; RtldLockState lockstate; int res; rlock_acquire(rtld_bind_lock, &lockstate); obj = dlcheck(arg); if (obj == NULL) obj = obj_from_addr(arg); if (obj == NULL) { _rtld_error("No shared object contains address"); lock_release(rtld_bind_lock, &lockstate); return (-1); } res = obj->dlopened ? 1 : 0; lock_release(rtld_bind_lock, &lockstate); return (res); } int obj_enforce_relro(Obj_Entry *obj) { if (obj->relro_size > 0 && mprotect(obj->relro_page, obj->relro_size, PROT_READ) == -1) { _rtld_error("%s: Cannot enforce relro protection: %s", obj->path, rtld_strerror(errno)); return (-1); } return (0); } static void map_stacks_exec(RtldLockState *lockstate) { void (*thr_map_stacks_exec)(void); if ((max_stack_flags & PF_X) == 0 || (stack_prot & PROT_EXEC) != 0) return; thr_map_stacks_exec = (void (*)(void))(uintptr_t) get_program_var_addr("__pthread_map_stacks_exec", lockstate); if (thr_map_stacks_exec != NULL) { stack_prot |= PROT_EXEC; thr_map_stacks_exec(); } } void symlook_init(SymLook *dst, const char *name) { bzero(dst, sizeof(*dst)); dst->name = name; dst->hash = elf_hash(name); dst->hash_gnu = gnu_hash(name); } static void symlook_init_from_req(SymLook *dst, const SymLook *src) { dst->name = src->name; dst->hash = src->hash; dst->hash_gnu = src->hash_gnu; dst->ventry = src->ventry; dst->flags = src->flags; dst->defobj_out = NULL; dst->sym_out = NULL; dst->lockstate = src->lockstate; } static int open_binary_fd(const char *argv0, bool search_in_path) { char *pathenv, *pe, binpath[PATH_MAX]; int fd; if (search_in_path && strchr(argv0, '/') == NULL) { pathenv = getenv("PATH"); if (pathenv == NULL) { rtld_printf("-p and no PATH environment variable\n"); rtld_die(); } pathenv = strdup(pathenv); if (pathenv == NULL) { rtld_printf("Cannot allocate memory\n"); rtld_die(); } fd = -1; errno = ENOENT; while ((pe = strsep(&pathenv, ":")) != NULL) { if (strlcpy(binpath, pe, sizeof(binpath)) >= sizeof(binpath)) continue; if (binpath[0] != '\0' && strlcat(binpath, "/", sizeof(binpath)) >= sizeof(binpath)) continue; if (strlcat(binpath, argv0, sizeof(binpath)) >= sizeof(binpath)) continue; fd = open(binpath, O_RDONLY | O_CLOEXEC | O_VERIFY); if (fd != -1 || errno != ENOENT) break; } free(pathenv); } else { fd = open(argv0, O_RDONLY | O_CLOEXEC | O_VERIFY); } if (fd == -1) { rtld_printf("Opening %s: %s\n", argv0, rtld_strerror(errno)); rtld_die(); } return (fd); } /* * Parse a set of command-line arguments. */ static int parse_args(char* argv[], int argc, bool *use_pathp, int *fdp) { const char *arg; int fd, i, j, arglen; char opt; dbg("Parsing command-line arguments"); *use_pathp = false; *fdp = -1; for (i = 1; i < argc; i++ ) { arg = argv[i]; dbg("argv[%d]: '%s'", i, arg); /* * rtld arguments end with an explicit "--" or with the first * non-prefixed argument. */ if (strcmp(arg, "--") == 0) { i++; break; } if (arg[0] != '-') break; /* * All other arguments are single-character options that can * be combined, so we need to search through `arg` for them. */ arglen = strlen(arg); for (j = 1; j < arglen; j++) { opt = arg[j]; if (opt == 'h') { print_usage(argv[0]); rtld_die(); } else if (opt == 'f') { /* * -f XX can be used to specify a descriptor for the * binary named at the command line (i.e., the later * argument will specify the process name but the * descriptor is what will actually be executed) */ if (j != arglen - 1) { /* -f must be the last option in, e.g., -abcf */ _rtld_error("invalid options: %s", arg); rtld_die(); } i++; fd = parse_integer(argv[i]); if (fd == -1) { _rtld_error("invalid file descriptor: '%s'", argv[i]); rtld_die(); } *fdp = fd; break; } else if (opt == 'p') { *use_pathp = true; } else { rtld_printf("invalid argument: '%s'\n", arg); print_usage(argv[0]); rtld_die(); } } } return (i); } /* * Parse a file descriptor number without pulling in more of libc (e.g. atoi). */ static int parse_integer(const char *str) { static const int RADIX = 10; /* XXXJA: possibly support hex? */ const char *orig; int n; char c; orig = str; n = 0; for (c = *str; c != '\0'; c = *++str) { if (c < '0' || c > '9') return (-1); n *= RADIX; n += c - '0'; } /* Make sure we actually parsed something. */ if (str == orig) return (-1); return (n); } static void print_usage(const char *argv0) { rtld_printf("Usage: %s [-h] [-f ] [--] []\n" "\n" "Options:\n" " -h Display this help message\n" " -p Search in PATH for named binary\n" " -f Execute instead of searching for \n" " -- End of RTLD options\n" " Name of process to execute\n" " Arguments to the executed process\n", argv0); } /* * Overrides for libc_pic-provided functions. */ int __getosreldate(void) { size_t len; int oid[2]; int error, osrel; if (osreldate != 0) return (osreldate); oid[0] = CTL_KERN; oid[1] = KERN_OSRELDATE; osrel = 0; len = sizeof(osrel); error = sysctl(oid, 2, &osrel, &len, NULL, 0); if (error == 0 && osrel > 0 && len == sizeof(osrel)) osreldate = osrel; return (osreldate); } void exit(int status) { _exit(status); } void (*__cleanup)(void); int __isthreaded = 0; int _thread_autoinit_dummy_decl = 1; /* * No unresolved symbols for rtld. */ void __pthread_cxa_finalize(struct dl_phdr_info *a) { } void __stack_chk_fail(void) { _rtld_error("stack overflow detected; terminated"); rtld_die(); } __weak_reference(__stack_chk_fail, __stack_chk_fail_local); void __chk_fail(void) { _rtld_error("buffer overflow detected; terminated"); rtld_die(); } const char * rtld_strerror(int errnum) { if (errnum < 0 || errnum >= sys_nerr) return ("Unknown error"); return (sys_errlist[errnum]); } Index: head/libexec/rtld-elf/rtld.h =================================================================== --- head/libexec/rtld-elf/rtld.h (revision 326273) +++ head/libexec/rtld-elf/rtld.h (revision 326274) @@ -1,408 +1,410 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996, 1997, 1998, 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_H /* { */ #define RTLD_H 1 #include #include #include #include #include #include #include #include #include "rtld_lock.h" #include "rtld_machdep.h" #define NEW(type) ((type *) xmalloc(sizeof(type))) #define CNEW(type) ((type *) xcalloc(1, sizeof(type))) /* We might as well do booleans like C++. */ typedef unsigned char bool; #define false 0 #define true 1 extern size_t tls_last_offset; extern size_t tls_last_size; extern size_t tls_static_space; extern int tls_dtv_generation; extern int tls_max_index; extern int npagesizes; extern size_t *pagesizes; extern int main_argc; extern char **main_argv; extern char **environ; struct stat; struct Struct_Obj_Entry; /* Lists of shared objects */ typedef struct Struct_Objlist_Entry { STAILQ_ENTRY(Struct_Objlist_Entry) link; struct Struct_Obj_Entry *obj; } Objlist_Entry; typedef STAILQ_HEAD(Struct_Objlist, Struct_Objlist_Entry) Objlist; /* Types of init and fini functions */ typedef void (*InitFunc)(void); typedef void (*InitArrFunc)(int, char **, char **); /* Lists of shared object dependencies */ typedef struct Struct_Needed_Entry { struct Struct_Needed_Entry *next; struct Struct_Obj_Entry *obj; unsigned long name; /* Offset of name in string table */ } Needed_Entry; typedef struct Struct_Name_Entry { STAILQ_ENTRY(Struct_Name_Entry) link; char name[1]; } Name_Entry; /* Lock object */ typedef struct Struct_LockInfo { void *context; /* Client context for creating locks */ void *thelock; /* The one big lock */ /* Debugging aids. */ volatile int rcount; /* Number of readers holding lock */ volatile int wcount; /* Number of writers holding lock */ /* Methods */ void *(*lock_create)(void *context); void (*rlock_acquire)(void *lock); void (*wlock_acquire)(void *lock); void (*rlock_release)(void *lock); void (*wlock_release)(void *lock); void (*lock_destroy)(void *lock); void (*context_destroy)(void *context); } LockInfo; typedef struct Struct_Ver_Entry { Elf_Word hash; unsigned int flags; const char *name; const char *file; } Ver_Entry; typedef struct Struct_Sym_Match_Result { const Elf_Sym *sym_out; const Elf_Sym *vsymp; int vcount; } Sym_Match_Result; #define VER_INFO_HIDDEN 0x01 /* * Shared object descriptor. * * Items marked with "(%)" are dynamically allocated, and must be freed * when the structure is destroyed. * * CAUTION: It appears that the JDK port peeks into these structures. * It looks at "next" and "mapbase" at least. Don't add new members * near the front, until this can be straightened out. */ typedef struct Struct_Obj_Entry { /* * These two items have to be set right for compatibility with the * original ElfKit crt1.o. */ Elf_Size magic; /* Magic number (sanity check) */ Elf_Size version; /* Version number of struct format */ TAILQ_ENTRY(Struct_Obj_Entry) next; char *path; /* Pathname of underlying file (%) */ char *origin_path; /* Directory path of origin file */ int refcount; /* DAG references */ int holdcount; /* Count of transient references */ int dl_refcount; /* Number of times loaded by dlopen */ /* These items are computed by map_object() or by digest_phdr(). */ caddr_t mapbase; /* Base address of mapped region */ size_t mapsize; /* Size of mapped region in bytes */ size_t textsize; /* Size of text segment in bytes */ Elf_Addr vaddrbase; /* Base address in shared object file */ caddr_t relocbase; /* Relocation constant = mapbase - vaddrbase */ const Elf_Dyn *dynamic; /* Dynamic section */ caddr_t entry; /* Entry point */ const Elf_Phdr *phdr; /* Program header if it is mapped, else NULL */ size_t phsize; /* Size of program header in bytes */ const char *interp; /* Pathname of the interpreter, if any */ Elf_Word stack_flags; /* TLS information */ int tlsindex; /* Index in DTV for this module */ void *tlsinit; /* Base address of TLS init block */ size_t tlsinitsize; /* Size of TLS init block for this module */ size_t tlssize; /* Size of TLS block for this module */ size_t tlsoffset; /* Offset of static TLS block for this module */ size_t tlsalign; /* Alignment of static TLS block */ caddr_t relro_page; size_t relro_size; /* Items from the dynamic section. */ Elf_Addr *pltgot; /* PLT or GOT, depending on architecture */ const Elf_Rel *rel; /* Relocation entries */ unsigned long relsize; /* Size in bytes of relocation info */ const Elf_Rela *rela; /* Relocation entries with addend */ unsigned long relasize; /* Size in bytes of addend relocation info */ const Elf_Rel *pltrel; /* PLT relocation entries */ unsigned long pltrelsize; /* Size in bytes of PLT relocation info */ const Elf_Rela *pltrela; /* PLT relocation entries with addend */ unsigned long pltrelasize; /* Size in bytes of PLT addend reloc info */ const Elf_Sym *symtab; /* Symbol table */ const char *strtab; /* String table */ unsigned long strsize; /* Size in bytes of string table */ #ifdef __mips__ Elf_Word local_gotno; /* Number of local GOT entries */ Elf_Word symtabno; /* Number of dynamic symbols */ Elf_Word gotsym; /* First dynamic symbol in GOT */ Elf_Addr *mips_pltgot; /* Second PLT GOT */ #endif #ifdef __powerpc64__ Elf_Addr glink; /* GLINK PLT call stub section */ #endif const Elf_Verneed *verneed; /* Required versions. */ Elf_Word verneednum; /* Number of entries in verneed table */ const Elf_Verdef *verdef; /* Provided versions. */ Elf_Word verdefnum; /* Number of entries in verdef table */ const Elf_Versym *versyms; /* Symbol versions table */ const Elf_Hashelt *buckets; /* Hash table buckets array */ unsigned long nbuckets; /* Number of buckets */ const Elf_Hashelt *chains; /* Hash table chain array */ unsigned long nchains; /* Number of entries in chain array */ Elf32_Word nbuckets_gnu; /* Number of GNU hash buckets*/ Elf32_Word symndx_gnu; /* 1st accessible symbol on dynsym table */ Elf32_Word maskwords_bm_gnu; /* Bloom filter words - 1 (bitmask) */ Elf32_Word shift2_gnu; /* Bloom filter shift count */ Elf32_Word dynsymcount; /* Total entries in dynsym table */ Elf_Addr *bloom_gnu; /* Bloom filter used by GNU hash func */ const Elf_Hashelt *buckets_gnu; /* GNU hash table bucket array */ const Elf_Hashelt *chain_zero_gnu; /* GNU hash table value array (Zeroed) */ char *rpath; /* Search path specified in object */ char *runpath; /* Search path with different priority */ Needed_Entry *needed; /* Shared objects needed by this one (%) */ Needed_Entry *needed_filtees; Needed_Entry *needed_aux_filtees; STAILQ_HEAD(, Struct_Name_Entry) names; /* List of names for this object we know about. */ Ver_Entry *vertab; /* Versions required /defined by this object */ int vernum; /* Number of entries in vertab */ Elf_Addr init; /* Initialization function to call */ Elf_Addr fini; /* Termination function to call */ Elf_Addr preinit_array; /* Pre-initialization array of functions */ Elf_Addr init_array; /* Initialization array of functions */ Elf_Addr fini_array; /* Termination array of functions */ int preinit_array_num; /* Number of entries in preinit_array */ int init_array_num; /* Number of entries in init_array */ int fini_array_num; /* Number of entries in fini_array */ int32_t osrel; /* OSREL note value */ bool mainprog : 1; /* True if this is the main program */ bool rtld : 1; /* True if this is the dynamic linker */ bool relocated : 1; /* True if processed by relocate_objects() */ bool ver_checked : 1; /* True if processed by rtld_verify_object_versions */ bool textrel : 1; /* True if there are relocations to text seg */ bool symbolic : 1; /* True if generated with "-Bsymbolic" */ bool bind_now : 1; /* True if all relocations should be made first */ bool traced : 1; /* Already printed in ldd trace output */ bool jmpslots_done : 1; /* Already have relocated the jump slots */ bool init_done : 1; /* Already have added object to init list */ bool tls_done : 1; /* Already allocated offset for static TLS */ bool phdr_alloc : 1; /* Phdr is allocated and needs to be freed. */ bool z_origin : 1; /* Process rpath and soname tokens */ bool z_nodelete : 1; /* Do not unload the object and dependencies */ bool z_noopen : 1; /* Do not load on dlopen */ bool z_loadfltr : 1; /* Immediately load filtees */ bool z_interpose : 1; /* Interpose all objects but main */ bool z_nodeflib : 1; /* Don't search default library path */ bool z_global : 1; /* Make the object global */ bool ref_nodel : 1; /* Refcount increased to prevent dlclose */ bool init_scanned: 1; /* Object is already on init list. */ bool on_fini_list: 1; /* Object is already on fini list. */ bool dag_inited : 1; /* Object has its DAG initialized. */ bool filtees_loaded : 1; /* Filtees loaded */ bool irelative : 1; /* Object has R_MACHDEP_IRELATIVE relocs */ bool gnu_ifunc : 1; /* Object has references to STT_GNU_IFUNC */ bool non_plt_gnu_ifunc : 1; /* Object has non-plt IFUNC references */ bool crt_no_init : 1; /* Object' crt does not call _init/_fini */ bool valid_hash_sysv : 1; /* A valid System V hash hash tag is available */ bool valid_hash_gnu : 1; /* A valid GNU hash tag is available */ bool dlopened : 1; /* dlopen()-ed (vs. load statically) */ bool marker : 1; /* marker on the global obj list */ bool unholdfree : 1; /* unmap upon last unhold */ bool doomed : 1; /* Object cannot be referenced */ struct link_map linkmap; /* For GDB and dlinfo() */ Objlist dldags; /* Object belongs to these dlopened DAGs (%) */ Objlist dagmembers; /* DAG has these members (%) */ dev_t dev; /* Object's filesystem's device */ ino_t ino; /* Object's inode number */ void *priv; /* Platform-dependent */ } Obj_Entry; #define RTLD_MAGIC 0xd550b87a #define RTLD_VERSION 1 TAILQ_HEAD(obj_entry_q, Struct_Obj_Entry); #define RTLD_STATIC_TLS_EXTRA 128 /* Flags to be passed into symlook_ family of functions. */ #define SYMLOOK_IN_PLT 0x01 /* Lookup for PLT symbol */ #define SYMLOOK_DLSYM 0x02 /* Return newest versioned symbol. Used by dlsym. */ #define SYMLOOK_EARLY 0x04 /* Symlook is done during initialization. */ #define SYMLOOK_IFUNC 0x08 /* Allow IFUNC processing in reloc_non_plt(). */ /* Flags for load_object(). */ #define RTLD_LO_NOLOAD 0x01 /* dlopen() specified RTLD_NOLOAD. */ #define RTLD_LO_DLOPEN 0x02 /* Load_object() called from dlopen(). */ #define RTLD_LO_TRACE 0x04 /* Only tracing. */ #define RTLD_LO_NODELETE 0x08 /* Loaded object cannot be closed. */ #define RTLD_LO_FILTEES 0x10 /* Loading filtee. */ #define RTLD_LO_EARLY 0x20 /* Do not call ctors, postpone it to the initialization during the image start. */ /* * Symbol cache entry used during relocation to avoid multiple lookups * of the same symbol. */ typedef struct Struct_SymCache { const Elf_Sym *sym; /* Symbol table entry */ const Obj_Entry *obj; /* Shared object which defines it */ } SymCache; /* * This structure provides a reentrant way to keep a list of objects and * check which ones have already been processed in some way. */ typedef struct Struct_DoneList { const Obj_Entry **objs; /* Array of object pointers */ unsigned int num_alloc; /* Allocated size of the array */ unsigned int num_used; /* Number of array slots used */ } DoneList; struct Struct_RtldLockState { int lockstate; sigjmp_buf env; }; struct fill_search_info_args { int request; unsigned int flags; struct dl_serinfo *serinfo; struct dl_serpath *serpath; char *strspace; }; /* * The pack of arguments and results for the symbol lookup functions. */ typedef struct Struct_SymLook { const char *name; unsigned long hash; uint32_t hash_gnu; const Ver_Entry *ventry; int flags; const Obj_Entry *defobj_out; const Elf_Sym *sym_out; struct Struct_RtldLockState *lockstate; } SymLook; void _rtld_error(const char *, ...) __printflike(1, 2) __exported; void rtld_die(void) __dead2; const char *rtld_strerror(int); Obj_Entry *map_object(int, const char *, const struct stat *); void *xcalloc(size_t, size_t); void *xmalloc(size_t); char *xstrdup(const char *); void *malloc_aligned(size_t size, size_t align); void free_aligned(void *ptr); extern Elf_Addr _GLOBAL_OFFSET_TABLE_[]; extern Elf_Sym sym_zero; /* For resolving undefined weak refs. */ extern bool ld_bind_not; void dump_relocations(Obj_Entry *); void dump_obj_relocations(Obj_Entry *); void dump_Elf_Rel(Obj_Entry *, const Elf_Rel *, u_long); void dump_Elf_Rela(Obj_Entry *, const Elf_Rela *, u_long); /* * Function declarations. */ unsigned long elf_hash(const char *); const Elf_Sym *find_symdef(unsigned long, const Obj_Entry *, const Obj_Entry **, int, SymCache *, struct Struct_RtldLockState *); void ifunc_init(Elf_Auxinfo[__min_size(AT_COUNT)]); void init_pltgot(Obj_Entry *); void lockdflt_init(void); void digest_notes(Obj_Entry *, Elf_Addr, Elf_Addr); Obj_Entry *globallist_curr(const Obj_Entry *obj); Obj_Entry *globallist_next(const Obj_Entry *obj); void obj_free(Obj_Entry *); Obj_Entry *obj_new(void); void _rtld_bind_start(void); void *rtld_resolve_ifunc(const Obj_Entry *obj, const Elf_Sym *def); void symlook_init(SymLook *, const char *); int symlook_obj(SymLook *, const Obj_Entry *); void *tls_get_addr_common(Elf_Addr** dtvp, int index, size_t offset); void *allocate_tls(Obj_Entry *, void *, size_t, size_t); void free_tls(void *, size_t, size_t); void *allocate_module_tls(int index); bool allocate_tls_offset(Obj_Entry *obj); void free_tls_offset(Obj_Entry *obj); const Ver_Entry *fetch_ventry(const Obj_Entry *obj, unsigned long); int convert_prot(int elfflags); /* * MD function declarations. */ int do_copy_relocations(Obj_Entry *); int reloc_non_plt(Obj_Entry *, Obj_Entry *, int flags, struct Struct_RtldLockState *); int reloc_plt(Obj_Entry *); int reloc_jmpslots(Obj_Entry *, int flags, struct Struct_RtldLockState *); int reloc_iresolve(Obj_Entry *, struct Struct_RtldLockState *); int reloc_gnu_ifunc(Obj_Entry *, int flags, struct Struct_RtldLockState *); void allocate_initial_tls(Obj_Entry *); #endif /* } */ Index: head/libexec/rtld-elf/rtld_lock.c =================================================================== --- head/libexec/rtld-elf/rtld_lock.c (revision 326273) +++ head/libexec/rtld-elf/rtld_lock.c (revision 326274) @@ -1,409 +1,411 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * from: FreeBSD: src/libexec/rtld-elf/sparc64/lockdflt.c,v 1.3 2002/10/09 * $FreeBSD$ */ /* * Thread locking implementation for the dynamic linker. * * We use the "simple, non-scalable reader-preference lock" from: * * J. M. Mellor-Crummey and M. L. Scott. "Scalable Reader-Writer * Synchronization for Shared-Memory Multiprocessors." 3rd ACM Symp. on * Principles and Practice of Parallel Programming, April 1991. * * In this algorithm the lock is a single word. Its low-order bit is * set when a writer holds the lock. The remaining high-order bits * contain a count of readers desiring the lock. The algorithm requires * atomic "compare_and_store" and "add" operations, which we take * from machine/atomic.h. */ #include #include #include #include #include "debug.h" #include "rtld.h" #include "rtld_machdep.h" void _rtld_thread_init(struct RtldLockInfo *) __exported; void _rtld_atfork_pre(int *) __exported; void _rtld_atfork_post(int *) __exported; #define WAFLAG 0x1 /* A writer holds the lock */ #define RC_INCR 0x2 /* Adjusts count of readers desiring lock */ typedef struct Struct_Lock { volatile u_int lock; void *base; } Lock; static sigset_t fullsigmask, oldsigmask; static int thread_flag, wnested; static void * def_lock_create(void) { void *base; char *p; uintptr_t r; Lock *l; /* * Arrange for the lock to occupy its own cache line. First, we * optimistically allocate just a cache line, hoping that malloc * will give us a well-aligned block of memory. If that doesn't * work, we allocate a larger block and take a well-aligned cache * line from it. */ base = xmalloc(CACHE_LINE_SIZE); p = (char *)base; if ((uintptr_t)p % CACHE_LINE_SIZE != 0) { free(base); base = xmalloc(2 * CACHE_LINE_SIZE); p = (char *)base; if ((r = (uintptr_t)p % CACHE_LINE_SIZE) != 0) p += CACHE_LINE_SIZE - r; } l = (Lock *)p; l->base = base; l->lock = 0; return l; } static void def_lock_destroy(void *lock) { Lock *l = (Lock *)lock; free(l->base); } static void def_rlock_acquire(void *lock) { Lock *l = (Lock *)lock; atomic_add_acq_int(&l->lock, RC_INCR); while (l->lock & WAFLAG) ; /* Spin */ } static void def_wlock_acquire(void *lock) { Lock *l; sigset_t tmp_oldsigmask; l = (Lock *)lock; for (;;) { sigprocmask(SIG_BLOCK, &fullsigmask, &tmp_oldsigmask); if (atomic_cmpset_acq_int(&l->lock, 0, WAFLAG)) break; sigprocmask(SIG_SETMASK, &tmp_oldsigmask, NULL); } if (atomic_fetchadd_int(&wnested, 1) == 0) oldsigmask = tmp_oldsigmask; } static void def_lock_release(void *lock) { Lock *l; l = (Lock *)lock; if ((l->lock & WAFLAG) == 0) atomic_add_rel_int(&l->lock, -RC_INCR); else { assert(wnested > 0); atomic_add_rel_int(&l->lock, -WAFLAG); if (atomic_fetchadd_int(&wnested, -1) == 1) sigprocmask(SIG_SETMASK, &oldsigmask, NULL); } } static int def_thread_set_flag(int mask) { int old_val = thread_flag; thread_flag |= mask; return (old_val); } static int def_thread_clr_flag(int mask) { int old_val = thread_flag; thread_flag &= ~mask; return (old_val); } /* * Public interface exposed to the rest of the dynamic linker. */ static struct RtldLockInfo lockinfo; static struct RtldLockInfo deflockinfo; static __inline int thread_mask_set(int mask) { return lockinfo.thread_set_flag(mask); } static __inline void thread_mask_clear(int mask) { lockinfo.thread_clr_flag(mask); } #define RTLD_LOCK_CNT 3 struct rtld_lock { void *handle; int mask; } rtld_locks[RTLD_LOCK_CNT]; rtld_lock_t rtld_bind_lock = &rtld_locks[0]; rtld_lock_t rtld_libc_lock = &rtld_locks[1]; rtld_lock_t rtld_phdr_lock = &rtld_locks[2]; void rlock_acquire(rtld_lock_t lock, RtldLockState *lockstate) { if (lockstate == NULL) return; if (thread_mask_set(lock->mask) & lock->mask) { dbg("rlock_acquire: recursed"); lockstate->lockstate = RTLD_LOCK_UNLOCKED; return; } lockinfo.rlock_acquire(lock->handle); lockstate->lockstate = RTLD_LOCK_RLOCKED; } void wlock_acquire(rtld_lock_t lock, RtldLockState *lockstate) { if (lockstate == NULL) return; if (thread_mask_set(lock->mask) & lock->mask) { dbg("wlock_acquire: recursed"); lockstate->lockstate = RTLD_LOCK_UNLOCKED; return; } lockinfo.wlock_acquire(lock->handle); lockstate->lockstate = RTLD_LOCK_WLOCKED; } void lock_release(rtld_lock_t lock, RtldLockState *lockstate) { if (lockstate == NULL) return; switch (lockstate->lockstate) { case RTLD_LOCK_UNLOCKED: break; case RTLD_LOCK_RLOCKED: case RTLD_LOCK_WLOCKED: thread_mask_clear(lock->mask); lockinfo.lock_release(lock->handle); break; default: assert(0); } } void lock_upgrade(rtld_lock_t lock, RtldLockState *lockstate) { if (lockstate == NULL) return; lock_release(lock, lockstate); wlock_acquire(lock, lockstate); } void lock_restart_for_upgrade(RtldLockState *lockstate) { if (lockstate == NULL) return; switch (lockstate->lockstate) { case RTLD_LOCK_UNLOCKED: case RTLD_LOCK_WLOCKED: break; case RTLD_LOCK_RLOCKED: siglongjmp(lockstate->env, 1); break; default: assert(0); } } void lockdflt_init(void) { int i; deflockinfo.rtli_version = RTLI_VERSION; deflockinfo.lock_create = def_lock_create; deflockinfo.lock_destroy = def_lock_destroy; deflockinfo.rlock_acquire = def_rlock_acquire; deflockinfo.wlock_acquire = def_wlock_acquire; deflockinfo.lock_release = def_lock_release; deflockinfo.thread_set_flag = def_thread_set_flag; deflockinfo.thread_clr_flag = def_thread_clr_flag; deflockinfo.at_fork = NULL; for (i = 0; i < RTLD_LOCK_CNT; i++) { rtld_locks[i].mask = (1 << i); rtld_locks[i].handle = NULL; } memcpy(&lockinfo, &deflockinfo, sizeof(lockinfo)); _rtld_thread_init(NULL); /* * Construct a mask to block all signals except traps which might * conceivably be generated within the dynamic linker itself. */ sigfillset(&fullsigmask); sigdelset(&fullsigmask, SIGILL); sigdelset(&fullsigmask, SIGTRAP); sigdelset(&fullsigmask, SIGABRT); sigdelset(&fullsigmask, SIGEMT); sigdelset(&fullsigmask, SIGFPE); sigdelset(&fullsigmask, SIGBUS); sigdelset(&fullsigmask, SIGSEGV); sigdelset(&fullsigmask, SIGSYS); } /* * Callback function to allow threads implementation to * register their own locking primitives if the default * one is not suitable. * The current context should be the only context * executing at the invocation time. */ void _rtld_thread_init(struct RtldLockInfo *pli) { int flags, i; void *locks[RTLD_LOCK_CNT]; /* disable all locking while this function is running */ flags = thread_mask_set(~0); if (pli == NULL) pli = &deflockinfo; for (i = 0; i < RTLD_LOCK_CNT; i++) if ((locks[i] = pli->lock_create()) == NULL) break; if (i < RTLD_LOCK_CNT) { while (--i >= 0) pli->lock_destroy(locks[i]); abort(); } for (i = 0; i < RTLD_LOCK_CNT; i++) { if (rtld_locks[i].handle == NULL) continue; if (flags & rtld_locks[i].mask) lockinfo.lock_release(rtld_locks[i].handle); lockinfo.lock_destroy(rtld_locks[i].handle); } for (i = 0; i < RTLD_LOCK_CNT; i++) { rtld_locks[i].handle = locks[i]; if (flags & rtld_locks[i].mask) pli->wlock_acquire(rtld_locks[i].handle); } lockinfo.lock_create = pli->lock_create; lockinfo.lock_destroy = pli->lock_destroy; lockinfo.rlock_acquire = pli->rlock_acquire; lockinfo.wlock_acquire = pli->wlock_acquire; lockinfo.lock_release = pli->lock_release; lockinfo.thread_set_flag = pli->thread_set_flag; lockinfo.thread_clr_flag = pli->thread_clr_flag; lockinfo.at_fork = pli->at_fork; /* restore thread locking state, this time with new locks */ thread_mask_clear(~0); thread_mask_set(flags); dbg("_rtld_thread_init: done"); } void _rtld_atfork_pre(int *locks) { RtldLockState ls[2]; if (locks == NULL) return; /* * Warning: this did not worked well with the rtld compat * locks above, when the thread signal mask was corrupted (set * to all signals blocked) if two locks were taken * simultaneously in the write mode. The caller of the * _rtld_atfork_pre() must provide the working implementation * of the locks anyway, and libthr locks are fine. */ wlock_acquire(rtld_phdr_lock, &ls[0]); wlock_acquire(rtld_bind_lock, &ls[1]); /* XXXKIB: I am really sorry for this. */ locks[0] = ls[1].lockstate; locks[2] = ls[0].lockstate; } void _rtld_atfork_post(int *locks) { RtldLockState ls[2]; if (locks == NULL) return; bzero(ls, sizeof(ls)); ls[0].lockstate = locks[2]; ls[1].lockstate = locks[0]; lock_release(rtld_bind_lock, &ls[1]); lock_release(rtld_phdr_lock, &ls[0]); } Index: head/libexec/rtld-elf/rtld_lock.h =================================================================== --- head/libexec/rtld-elf/rtld_lock.h (revision 326273) +++ head/libexec/rtld-elf/rtld_lock.h (revision 326274) @@ -1,75 +1,77 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2003 Alexander Kabaev. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _RTLD_LOCK_H_ #define _RTLD_LOCK_H_ #define RTLI_VERSION 0x01 #define MAX_RTLD_LOCKS 8 struct RtldLockInfo { unsigned int rtli_version; void *(*lock_create)(void); void (*lock_destroy)(void *); void (*rlock_acquire)(void *); void (*wlock_acquire)(void *); void (*lock_release)(void *); int (*thread_set_flag)(int); int (*thread_clr_flag)(int); void (*at_fork)(void); }; extern void _rtld_thread_init(struct RtldLockInfo *) __exported; extern void _rtld_atfork_pre(int *) __exported; extern void _rtld_atfork_post(int *) __exported; #ifdef IN_RTLD struct rtld_lock; typedef struct rtld_lock *rtld_lock_t; extern rtld_lock_t rtld_bind_lock; extern rtld_lock_t rtld_libc_lock; extern rtld_lock_t rtld_phdr_lock; #define RTLD_LOCK_UNLOCKED 0 #define RTLD_LOCK_RLOCKED 1 #define RTLD_LOCK_WLOCKED 2 struct Struct_RtldLockState; typedef struct Struct_RtldLockState RtldLockState; void rlock_acquire(rtld_lock_t, RtldLockState *); void wlock_acquire(rtld_lock_t, RtldLockState *); void lock_release(rtld_lock_t, RtldLockState *); void lock_upgrade(rtld_lock_t, RtldLockState *); void lock_restart_for_upgrade(RtldLockState *); #endif /* IN_RTLD */ #endif Index: head/libexec/rtld-elf/rtld_printf.h =================================================================== --- head/libexec/rtld-elf/rtld_printf.h (revision 326273) +++ head/libexec/rtld-elf/rtld_printf.h (revision 326274) @@ -1,46 +1,48 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 2011 Konstantin Belousov . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_PRINTF_H #define RTLD_PRINTF_H 1 #include #include int rtld_snprintf(char *buf, size_t bufsize, const char *fmt, ...) __printflike(3, 4); int rtld_vsnprintf(char *buf, size_t bufsize, const char *fmt, va_list ap); int rtld_vfdprintf(int fd, const char *fmt, va_list ap); int rtld_fdprintf(int fd, const char *fmt, ...) __printflike(2, 3); void rtld_fdputstr(int fd, const char *str); void rtld_fdputchar(int fd, int c); #define rtld_printf(...) rtld_fdprintf(STDOUT_FILENO, __VA_ARGS__) #define rtld_putstr(str) rtld_fdputstr(STDOUT_FILENO, (str)) #define rtld_putchar(c) rtld_fdputchar(STDOUT_FILENO, (c)) #endif Index: head/libexec/rtld-elf/rtld_tls.h =================================================================== --- head/libexec/rtld-elf/rtld_tls.h (revision 326273) +++ head/libexec/rtld-elf/rtld_tls.h (revision 326274) @@ -1,70 +1,72 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2004 Doug Rabson * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * Semi-public interface from thread libraries to rtld for managing * TLS. */ #ifndef _RTLD_TLS_H_ #define _RTLD_TLS_H_ /* * Allocate a TLS block for a new thread. The memory allocated will * include 'tcbsize' bytes aligned to a 'tcbalign' boundary (in bytes) * for the thread library's private purposes. The location of the TCB * block is returned by this function. For architectures using * 'Variant I' TLS, the thread local storage follows the TCB, and for * 'Variant II', the thread local storage precedes it. For * architectures using the 'Variant II' model (e.g. i386, amd64, * sparc64), the TCB must begin with two pointer fields which are used * by rtld for its TLS implementation. For the 'Variant I' model, the * TCB must begin with a single pointer field for rtld's * implementation. * * If the value of 'oldtls' is non-NULL, the new TLS block will be * initialised using the values contained in 'oldtls' and 'oldtls' * will be freed. This is typically used when initialising a thread * library to migrate from using the initial bootstrap TLS block * created by rtld to one which contains suitable thread library * private data. * * The value returned from this function is suitable for installing * directly into the thread pointer register. */ void *_rtld_allocate_tls(void* oldtls, size_t tcbsize, size_t tcbalign) __exported; /* * Free a TLS block allocated using _rtld_allocate_tls(). The tcbsize * and tcbalign parameters must be the same as those used to allocate * the block. */ void _rtld_free_tls(void *tcb, size_t tcbsize, size_t tcbalign) __exported; #endif Index: head/libexec/rtld-elf/sparc64/reloc.c =================================================================== --- head/libexec/rtld-elf/sparc64/reloc.c (revision 326273) +++ head/libexec/rtld-elf/sparc64/reloc.c (revision 326274) @@ -1,862 +1,864 @@ /* $NetBSD: mdreloc.c,v 1.42 2008/04/28 20:23:04 martin Exp $ */ /*- + * SPDX-License-Identifier: BSD-2-Clause-NetBSD + * * Copyright (c) 2000 Eduardo Horvath. * Copyright (c) 1999 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Paul Kranenburg. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include "debug.h" #include "rtld.h" /* * The following table holds for each relocation type: * - the width in bits of the memory location the relocation * applies to (not currently used) * - the number of bits the relocation value must be shifted to the * right (i.e. discard least significant bits) to fit into * the appropriate field in the instruction word. * - flags indicating whether * * the relocation involves a symbol * * the relocation is relative to the current position * * the relocation is for a GOT entry * * the relocation is relative to the load address * */ #define _RF_S 0x80000000 /* Resolve symbol */ #define _RF_A 0x40000000 /* Use addend */ #define _RF_P 0x20000000 /* Location relative */ #define _RF_G 0x10000000 /* GOT offset */ #define _RF_B 0x08000000 /* Load address relative */ #define _RF_U 0x04000000 /* Unaligned */ #define _RF_X 0x02000000 /* Bare symbols, needs proc */ #define _RF_D 0x01000000 /* Use dynamic TLS offset */ #define _RF_O 0x00800000 /* Use static TLS offset */ #define _RF_I 0x00400000 /* Use TLS object ID */ #define _RF_SZ(s) (((s) & 0xff) << 8) /* memory target size */ #define _RF_RS(s) ( (s) & 0xff) /* right shift */ static const int reloc_target_flags[] = { 0, /* NONE */ _RF_S|_RF_A| _RF_SZ(8) | _RF_RS(0), /* 8 */ _RF_S|_RF_A| _RF_SZ(16) | _RF_RS(0), /* 16 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* 32 */ _RF_S|_RF_A|_RF_P| _RF_SZ(8) | _RF_RS(0), /* DISP_8 */ _RF_S|_RF_A|_RF_P| _RF_SZ(16) | _RF_RS(0), /* DISP_16 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* DISP_32 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_30 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP_22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HI22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 13 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LO10 */ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT10 */ _RF_G| _RF_SZ(32) | _RF_RS(0), /* GOT13 */ _RF_G| _RF_SZ(32) | _RF_RS(10), /* GOT22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PC10 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC22 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WPLT30 */ _RF_SZ(32) | _RF_RS(0), /* COPY */ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* GLOB_DAT */ _RF_SZ(32) | _RF_RS(0), /* JMP_SLOT */ _RF_A| _RF_B| _RF_SZ(64) | _RF_RS(0), /* RELATIVE */ _RF_S|_RF_A| _RF_U| _RF_SZ(32) | _RF_RS(0), /* UA_32 */ _RF_A| _RF_SZ(32) | _RF_RS(0), /* PLT32 */ _RF_A| _RF_SZ(32) | _RF_RS(10), /* HIPLT22 */ _RF_A| _RF_SZ(32) | _RF_RS(0), /* LOPLT10 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT32 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PCPLT22 */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(0), /* PCPLT10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 11 */ _RF_S|_RF_A|_RF_X| _RF_SZ(64) | _RF_RS(0), /* 64 */ _RF_S|_RF_A|/*extra*/ _RF_SZ(32) | _RF_RS(0), /* OLO10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(42), /* HH22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(32), /* HM10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* LM22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(42), /* PC_HH22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(32), /* PC_HM10 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(10), /* PC_LM22 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP16 */ _RF_S|_RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* WDISP19 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GLOB_JMP */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 7 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 5 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* 6 */ _RF_S|_RF_A|_RF_P| _RF_SZ(64) | _RF_RS(0), /* DISP64 */ _RF_A| _RF_SZ(64) | _RF_RS(0), /* PLT64 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(10), /* HIX22 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* LOX10 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(22), /* H44 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(12), /* M44 */ _RF_S|_RF_A|_RF_X| _RF_SZ(32) | _RF_RS(0), /* L44 */ _RF_S|_RF_A| _RF_SZ(64) | _RF_RS(0), /* REGISTER */ _RF_S|_RF_A| _RF_U| _RF_SZ(64) | _RF_RS(0), /* UA64 */ _RF_S|_RF_A| _RF_U| _RF_SZ(16) | _RF_RS(0), /* UA16 */ /* TLS */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* GD_HI22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* GD_LO10 */ 0, /* GD_ADD */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* GD_CALL */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDM_HI22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDM_LO10 */ 0, /* LDM_ADD */ _RF_A|_RF_P| _RF_SZ(32) | _RF_RS(2), /* LDM_CALL */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* LDO_HIX22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* LDO_LOX10 */ 0, /* LDO_ADD */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(10), /* IE_HI22 */ _RF_S|_RF_A| _RF_SZ(32) | _RF_RS(0), /* IE_LO10 */ 0, /* IE_LD */ 0, /* IE_LDX */ 0, /* IE_ADD */ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(10), /* LE_HIX22 */ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* LE_LOX10 */ _RF_S| _RF_I| _RF_SZ(32) | _RF_RS(0), /* DTPMOD32 */ _RF_S| _RF_I| _RF_SZ(64) | _RF_RS(0), /* DTPMOD64 */ _RF_S|_RF_A| _RF_D| _RF_SZ(32) | _RF_RS(0), /* DTPOFF32 */ _RF_S|_RF_A| _RF_D| _RF_SZ(64) | _RF_RS(0), /* DTPOFF64 */ _RF_S|_RF_A| _RF_O| _RF_SZ(32) | _RF_RS(0), /* TPOFF32 */ _RF_S|_RF_A| _RF_O| _RF_SZ(64) | _RF_RS(0) /* TPOFF64 */ }; #if 0 static const char *const reloc_names[] = { "NONE", "8", "16", "32", "DISP_8", "DISP_16", "DISP_32", "WDISP_30", "WDISP_22", "HI22", "22", "13", "LO10", "GOT10", "GOT13", "GOT22", "PC10", "PC22", "WPLT30", "COPY", "GLOB_DAT", "JMP_SLOT", "RELATIVE", "UA_32", "PLT32", "HIPLT22", "LOPLT10", "LOPLT10", "PCPLT22", "PCPLT32", "10", "11", "64", "OLO10", "HH22", "HM10", "LM22", "PC_HH22", "PC_HM10", "PC_LM22", "WDISP16", "WDISP19", "GLOB_JMP", "7", "5", "6", "DISP64", "PLT64", "HIX22", "LOX10", "H44", "M44", "L44", "REGISTER", "UA64", "UA16", "GD_HI22", "GD_LO10", "GD_ADD", "GD_CALL", "LDM_HI22", "LDMO10", "LDM_ADD", "LDM_CALL", "LDO_HIX22", "LDO_LOX10", "LDO_ADD", "IE_HI22", "IE_LO10", "IE_LD", "IE_LDX", "IE_ADD", "LE_HIX22", "LE_LOX10", "DTPMOD32", "DTPMOD64", "DTPOFF32", "DTPOFF64", "TPOFF32", "TPOFF64" }; #endif #define RELOC_RESOLVE_SYMBOL(t) ((reloc_target_flags[t] & _RF_S) != 0) #define RELOC_PC_RELATIVE(t) ((reloc_target_flags[t] & _RF_P) != 0) #define RELOC_BASE_RELATIVE(t) ((reloc_target_flags[t] & _RF_B) != 0) #define RELOC_UNALIGNED(t) ((reloc_target_flags[t] & _RF_U) != 0) #define RELOC_USE_ADDEND(t) ((reloc_target_flags[t] & _RF_A) != 0) #define RELOC_BARE_SYMBOL(t) ((reloc_target_flags[t] & _RF_X) != 0) #define RELOC_USE_TLS_DOFF(t) ((reloc_target_flags[t] & _RF_D) != 0) #define RELOC_USE_TLS_OFF(t) ((reloc_target_flags[t] & _RF_O) != 0) #define RELOC_USE_TLS_ID(t) ((reloc_target_flags[t] & _RF_I) != 0) #define RELOC_TARGET_SIZE(t) ((reloc_target_flags[t] >> 8) & 0xff) #define RELOC_VALUE_RIGHTSHIFT(t) (reloc_target_flags[t] & 0xff) static const long reloc_target_bitmask[] = { #define _BM(x) (~(-(1ULL << (x)))) 0, /* NONE */ _BM(8), _BM(16), _BM(32), /* 8, 16, 32 */ _BM(8), _BM(16), _BM(32), /* DISP8, DISP16, DISP32 */ _BM(30), _BM(22), /* WDISP30, WDISP22 */ _BM(22), _BM(22), /* HI22, 22 */ _BM(13), _BM(10), /* 13, LO10 */ _BM(10), _BM(13), _BM(22), /* GOT10, GOT13, GOT22 */ _BM(10), _BM(22), /* PC10, PC22 */ _BM(30), 0, /* WPLT30, COPY */ _BM(32), _BM(32), _BM(32), /* GLOB_DAT, JMP_SLOT, RELATIVE */ _BM(32), _BM(32), /* UA32, PLT32 */ _BM(22), _BM(10), /* HIPLT22, LOPLT10 */ _BM(32), _BM(22), _BM(10), /* PCPLT32, PCPLT22, PCPLT10 */ _BM(10), _BM(11), -1, /* 10, 11, 64 */ _BM(13), _BM(22), /* OLO10, HH22 */ _BM(10), _BM(22), /* HM10, LM22 */ _BM(22), _BM(10), _BM(22), /* PC_HH22, PC_HM10, PC_LM22 */ _BM(16), _BM(19), /* WDISP16, WDISP19 */ -1, /* GLOB_JMP */ _BM(7), _BM(5), _BM(6), /* 7, 5, 6 */ -1, -1, /* DISP64, PLT64 */ _BM(22), _BM(13), /* HIX22, LOX10 */ _BM(22), _BM(10), _BM(13), /* H44, M44, L44 */ -1, -1, _BM(16), /* REGISTER, UA64, UA16 */ _BM(22), _BM(10), 0, _BM(30), /* GD_HI22, GD_LO10, GD_ADD, GD_CALL */ _BM(22), _BM(10), 0, /* LDM_HI22, LDMO10, LDM_ADD */ _BM(30), /* LDM_CALL */ _BM(22), _BM(10), 0, /* LDO_HIX22, LDO_LOX10, LDO_ADD */ _BM(22), _BM(10), 0, 0, /* IE_HI22, IE_LO10, IE_LD, IE_LDX */ 0, /* IE_ADD */ _BM(22), _BM(13), /* LE_HIX22, LE_LOX10 */ _BM(32), -1, /* DTPMOD32, DTPMOD64 */ _BM(32), -1, /* DTPOFF32, DTPOFF64 */ _BM(32), -1 /* TPOFF32, TPOFF64 */ #undef _BM }; #define RELOC_VALUE_BITMASK(t) (reloc_target_bitmask[t]) #undef flush #define flush(va, offs) \ __asm __volatile("flush %0 + %1" : : "r" (va), "I" (offs)); static int reloc_nonplt_object(Obj_Entry *obj, const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate); static void install_plt(Elf_Word *pltgot, Elf_Addr proc); extern char _rtld_bind_start_0[]; extern char _rtld_bind_start_1[]; int do_copy_relocations(Obj_Entry *dstobj) { const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *dstsym; const Elf_Sym *srcsym; void *dstaddr; const void *srcaddr; const Obj_Entry *srcobj, *defobj; SymLook req; const char *name; size_t size; int res; assert(dstobj->mainprog); /* COPY relocations are invalid elsewhere */ relalim = (const Elf_Rela *)((caddr_t)dstobj->rela + dstobj->relasize); for (rela = dstobj->rela; rela < relalim; rela++) { if (ELF_R_TYPE(rela->r_info) == R_SPARC_COPY) { dstaddr = (void *)(dstobj->relocbase + rela->r_offset); dstsym = dstobj->symtab + ELF_R_SYM(rela->r_info); name = dstobj->strtab + dstsym->st_name; size = dstsym->st_size; symlook_init(&req, name); req.ventry = fetch_ventry(dstobj, ELF_R_SYM(rela->r_info)); req.flags = SYMLOOK_EARLY; for (srcobj = globallist_next(dstobj); srcobj != NULL; srcobj = globallist_next(srcobj)) { res = symlook_obj(&req, srcobj); if (res == 0) { srcsym = req.sym_out; defobj = req.defobj_out; break; } } if (srcobj == NULL) { _rtld_error("Undefined symbol \"%s\"" "referenced from COPY relocation" "in %s", name, dstobj->path); return (-1); } srcaddr = (const void *)(defobj->relocbase + srcsym->st_value); memcpy(dstaddr, srcaddr, size); } } return (0); } int reloc_non_plt(Obj_Entry *obj, Obj_Entry *obj_rtld, int flags, RtldLockState *lockstate) { const Elf_Rela *relalim; const Elf_Rela *rela; SymCache *cache; int r = -1; if ((flags & SYMLOOK_IFUNC) != 0) /* XXX not implemented */ return (0); /* * The dynamic loader may be called from a thread, we have * limited amounts of stack available so we cannot use alloca(). */ if (obj != obj_rtld) { cache = calloc(obj->dynsymcount, sizeof(SymCache)); /* No need to check for NULL here */ } else cache = NULL; relalim = (const Elf_Rela *)((caddr_t)obj->rela + obj->relasize); for (rela = obj->rela; rela < relalim; rela++) { if (reloc_nonplt_object(obj, rela, cache, flags, lockstate) < 0) goto done; } r = 0; done: if (cache != NULL) free(cache); return (r); } static int reloc_nonplt_object(Obj_Entry *obj, const Elf_Rela *rela, SymCache *cache, int flags, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Sym *def; Elf_Addr *where; Elf_Word *where32; Elf_Word type; Elf_Addr value; Elf_Addr mask; where = (Elf_Addr *)(obj->relocbase + rela->r_offset); where32 = (Elf_Word *)where; defobj = NULL; def = NULL; type = ELF64_R_TYPE_ID(rela->r_info); if (type == R_SPARC_NONE) return (0); /* We do JMP_SLOTs below. */ if (type == R_SPARC_JMP_SLOT) return (0); /* COPY relocs are also handled elsewhere. */ if (type == R_SPARC_COPY) return (0); /* Ignore ADD and CALL relocations for dynamic TLS references. */ if (type == R_SPARC_TLS_GD_ADD || type == R_SPARC_TLS_GD_CALL || type == R_SPARC_TLS_LDM_ADD || type == R_SPARC_TLS_LDM_CALL || type == R_SPARC_TLS_LDO_ADD) return (0); /* * Note: R_SPARC_TLS_TPOFF64 must be the numerically largest * relocation type. */ if (type >= sizeof(reloc_target_bitmask) / sizeof(*reloc_target_bitmask)) { _rtld_error("%s: Unsupported relocation type %d in non-PLT " "object\n", obj->path, type); return (-1); } value = rela->r_addend; /* * Handle relative relocs here, because we might not be able to access * globals yet. */ if (type == R_SPARC_RELATIVE) { /* XXXX -- apparently we ignore the preexisting value. */ *where = (Elf_Addr)(obj->relocbase + value); return (0); } /* * If we get here while relocating rtld itself, we will crash because * a non-local variable is accessed. */ if (RELOC_RESOLVE_SYMBOL(type)) { /* Find the symbol. */ def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, flags, cache, lockstate); if (def == NULL) return (-1); if (RELOC_USE_TLS_ID(type)) value = (Elf_Addr)defobj->tlsindex; else if (RELOC_USE_TLS_DOFF(type)) value += (Elf_Addr)def->st_value; else if (RELOC_USE_TLS_OFF(type)) { /* * We lazily allocate offsets for static TLS as we * see the first relocation that references the TLS * block. This allows us to support (small amounts * of) static TLS in dynamically loaded modules. If * we run out of space, we generate an error. */ if (!defobj->tls_done && !allocate_tls_offset((Obj_Entry*)defobj)) { _rtld_error("%s: No space available for " "static Thread Local Storage", obj->path); return (-1); } value += (Elf_Addr)(def->st_value - defobj->tlsoffset); } else { /* Add in the symbol's absolute address. */ value += (Elf_Addr)(def->st_value + defobj->relocbase); } } if (type == R_SPARC_OLO10) value = (value & 0x3ff) + ELF64_R_TYPE_DATA(rela->r_info); if (type == R_SPARC_HIX22 || type == R_SPARC_TLS_LE_HIX22) value ^= 0xffffffffffffffff; if (RELOC_PC_RELATIVE(type)) value -= (Elf_Addr)where; if (RELOC_BASE_RELATIVE(type)) { /* * Note that even though sparcs use `Elf_rela' exclusively * we still need the implicit memory addend in relocations * referring to GOT entries. Undoubtedly, someone f*cked * this up in the distant past, and now we're stuck with * it in the name of compatibility for all eternity ... * * In any case, the implicit and explicit should be mutually * exclusive. We provide a check for that here. */ /* XXXX -- apparently we ignore the preexisting value */ value += (Elf_Addr)(obj->relocbase); } mask = RELOC_VALUE_BITMASK(type); value >>= RELOC_VALUE_RIGHTSHIFT(type); value &= mask; if (type == R_SPARC_LOX10 || type == R_SPARC_TLS_LE_LOX10) value |= 0x1c00; if (RELOC_UNALIGNED(type)) { /* Handle unaligned relocations. */ Elf_Addr tmp; char *ptr; int size; int i; size = RELOC_TARGET_SIZE(type) / 8; ptr = (char *)where; tmp = 0; /* Read it in one byte at a time. */ for (i = 0; i < size; i++) tmp = (tmp << 8) | ptr[i]; tmp &= ~mask; tmp |= value; /* Write it back out. */ for (i = 0; i < size; i++) ptr[i] = ((tmp >> ((size - i - 1) * 8)) & 0xff); } else if (RELOC_TARGET_SIZE(type) > 32) { *where &= ~mask; *where |= value; } else { *where32 &= ~mask; *where32 |= value; } return (0); } int reloc_plt(Obj_Entry *obj) { #if 0 const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; Elf_Addr *where; Elf_Addr value; relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { if (rela->r_addend == 0) continue; assert(ELF64_R_TYPE_ID(rela->r_info) == R_SPARC_JMP_SLOT); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT, NULL, lockstate); value = (Elf_Addr)(defobj->relocbase + def->st_value); *where = value; } #endif return (0); } /* * Instruction templates: */ #define BAA 0x10400000 /* ba,a %xcc, 0 */ #define SETHI 0x03000000 /* sethi %hi(0), %g1 */ #define JMP 0x81c06000 /* jmpl %g1+%lo(0), %g0 */ #define NOP 0x01000000 /* sethi %hi(0), %g0 */ #define OR 0x82806000 /* or %g1, 0, %g1 */ #define XOR 0x82c06000 /* xor %g1, 0, %g1 */ #define MOV71 0x8283a000 /* or %o7, 0, %g1 */ #define MOV17 0x9c806000 /* or %g1, 0, %o7 */ #define CALL 0x40000000 /* call 0 */ #define SLLX 0x8b407000 /* sllx %g1, 0, %g1 */ #define SETHIG5 0x0b000000 /* sethi %hi(0), %g5 */ #define ORG5 0x82804005 /* or %g1, %g5, %g1 */ /* %hi(v) with variable shift */ #define HIVAL(v, s) (((v) >> (s)) & 0x003fffff) #define LOVAL(v) ((v) & 0x000003ff) int reloc_jmpslots(Obj_Entry *obj, int flags, RtldLockState *lockstate) { const Obj_Entry *defobj; const Elf_Rela *relalim; const Elf_Rela *rela; const Elf_Sym *def; Elf_Addr *where; Elf_Addr target; relalim = (const Elf_Rela *)((char *)obj->pltrela + obj->pltrelasize); for (rela = obj->pltrela; rela < relalim; rela++) { assert(ELF64_R_TYPE_ID(rela->r_info) == R_SPARC_JMP_SLOT); where = (Elf_Addr *)(obj->relocbase + rela->r_offset); def = find_symdef(ELF_R_SYM(rela->r_info), obj, &defobj, SYMLOOK_IN_PLT | flags, NULL, lockstate); if (def == NULL) return -1; target = (Elf_Addr)(defobj->relocbase + def->st_value); reloc_jmpslot(where, target, defobj, obj, (Elf_Rel *)rela); } obj->jmpslots_done = true; return (0); } int reloc_iresolve(Obj_Entry *obj, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } int reloc_gnu_ifunc(Obj_Entry *obj, int flags, struct Struct_RtldLockState *lockstate) { /* XXX not implemented */ return (0); } Elf_Addr reloc_jmpslot(Elf_Addr *wherep, Elf_Addr target, const Obj_Entry *obj, const Obj_Entry *refobj, const Elf_Rel *rel) { const Elf_Rela *rela = (const Elf_Rela *)rel; Elf_Addr offset; Elf_Word *where; if (ld_bind_not) { /* Skip any PLT modifications */ } else if (rela - refobj->pltrela < 32764) { /* * At the PLT entry pointed at by `where', we now construct * a direct transfer to the now fully resolved function * address. * * A PLT entry is supposed to start by looking like this: * * sethi (. - .PLT0), %g1 * ba,a %xcc, .PLT1 * nop * nop * nop * nop * nop * nop * * When we replace these entries we start from the second * entry and do it in reverse order so the last thing we * do is replace the branch. That allows us to change this * atomically. * * We now need to find out how far we need to jump. We * have a choice of several different relocation techniques * which are increasingly expensive. */ where = (Elf_Word *)wherep; offset = ((Elf_Addr)where) - target; if (offset <= (1L<<20) && offset >= -(1L<<20)) { /* * We're within 1MB -- we can use a direct branch * instruction. * * We can generate this pattern: * * sethi %hi(. - .PLT0), %g1 * ba,a %xcc, addr * nop * nop * nop * nop * nop * nop * */ where[1] = BAA | ((offset >> 2) &0x3fffff); flush(where, 4); } else if (target >= 0 && target < (1L<<32)) { /* * We're within 32-bits of address zero. * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %hi(addr), %g1 * jmp %g1+%lo(addr) * nop * nop * nop * nop * nop * */ where[2] = JMP | LOVAL(target); flush(where, 8); where[1] = SETHI | HIVAL(target, 10); flush(where, 4); } else if (target <= 0 && target > -(1L<<32)) { /* * We're within 32-bits of address -1. * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %hix(addr), %g1 * xor %g1, %lox(addr), %g1 * jmp %g1 * nop * nop * nop * nop * */ where[3] = JMP; flush(where, 12); where[2] = XOR | ((~target) & 0x00001fff); flush(where, 8); where[1] = SETHI | HIVAL(~target, 10); flush(where, 4); } else if (offset <= (1L<<32) && offset >= -((1L<<32) - 4)) { /* * We're within 32-bits -- we can use a direct call * insn * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * mov %o7, %g1 * call (.+offset) * mov %g1, %o7 * nop * nop * nop * nop * */ where[3] = MOV17; flush(where, 12); where[2] = CALL | ((offset >> 4) & 0x3fffffff); flush(where, 8); where[1] = MOV71; flush(where, 4); } else if (offset >= 0 && offset < (1L<<44)) { /* * We're within 44 bits. We can generate this * pattern: * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %h44(addr), %g1 * or %g1, %m44(addr), %g1 * sllx %g1, 12, %g1 * jmp %g1+%l44(addr) * nop * nop * nop * */ where[4] = JMP | LOVAL(offset); flush(where, 16); where[3] = SLLX | 12; flush(where, 12); where[2] = OR | (((offset) >> 12) & 0x00001fff); flush(where, 8); where[1] = SETHI | HIVAL(offset, 22); flush(where, 4); } else if (offset < 0 && offset > -(1L<<44)) { /* * We're within 44 bits. We can generate this * pattern: * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %h44(-addr), %g1 * xor %g1, %m44(-addr), %g1 * sllx %g1, 12, %g1 * jmp %g1+%l44(addr) * nop * nop * nop * */ where[4] = JMP | LOVAL(offset); flush(where, 16); where[3] = SLLX | 12; flush(where, 12); where[2] = XOR | (((~offset) >> 12) & 0x00001fff); flush(where, 8); where[1] = SETHI | HIVAL(~offset, 22); flush(where, 4); } else { /* * We need to load all 64-bits * * The resulting code in the jump slot is: * * sethi %hi(. - .PLT0), %g1 * sethi %hh(addr), %g1 * sethi %lm(addr), %g5 * or %g1, %hm(addr), %g1 * sllx %g1, 32, %g1 * or %g1, %g5, %g1 * jmp %g1+%lo(addr) * nop * */ where[6] = JMP | LOVAL(target); flush(where, 24); where[5] = ORG5; flush(where, 20); where[4] = SLLX | 32; flush(where, 16); where[3] = OR | LOVAL((target) >> 32); flush(where, 12); where[2] = SETHIG5 | HIVAL(target, 10); flush(where, 8); where[1] = SETHI | HIVAL(target, 42); flush(where, 4); } } else { /* * This is a high PLT slot; the relocation offset specifies a * pointer that needs to be frobbed; no actual code needs to * be modified. The pointer to be calculated needs the addend * added and the reference object relocation base subtraced. */ *wherep = target + rela->r_addend - (Elf_Addr)refobj->relocbase; } return (target); } void ifunc_init(Elf_Auxinfo aux_info[__min_size(AT_COUNT)] __unused) { } /* * Install rtld function call into this PLT slot. */ #define SAVE 0x9de3bf50 #define SETHI_l0 0x21000000 #define SETHI_l1 0x23000000 #define OR_l0_l0 0xa0142000 #define SLLX_l0_32_l0 0xa12c3020 #define OR_l0_l1_l0 0xa0140011 #define JMPL_l0_o1 0x93c42000 #define MOV_g1_o0 0x90100001 void init_pltgot(Obj_Entry *obj) { Elf_Word *entry; if (obj->pltgot != NULL) { entry = (Elf_Word *)obj->pltgot; install_plt(&entry[0], (Elf_Addr)_rtld_bind_start_0); install_plt(&entry[8], (Elf_Addr)_rtld_bind_start_1); obj->pltgot[8] = (Elf_Addr)obj; } } static void install_plt(Elf_Word *pltgot, Elf_Addr proc) { pltgot[0] = SAVE; flush(pltgot, 0); pltgot[1] = SETHI_l0 | HIVAL(proc, 42); flush(pltgot, 4); pltgot[2] = SETHI_l1 | HIVAL(proc, 10); flush(pltgot, 8); pltgot[3] = OR_l0_l0 | LOVAL((proc) >> 32); flush(pltgot, 12); pltgot[4] = SLLX_l0_32_l0; flush(pltgot, 16); pltgot[5] = OR_l0_l1_l0; flush(pltgot, 20); pltgot[6] = JMPL_l0_o1 | LOVAL(proc); flush(pltgot, 24); pltgot[7] = MOV_g1_o0; flush(pltgot, 28); } void allocate_initial_tls(Obj_Entry *objs) { Elf_Addr* tpval; /* * Fix the size of the static TLS block by using the maximum offset * allocated so far and adding a bit for dynamic modules to use. */ tls_static_space = tls_last_offset + RTLD_STATIC_TLS_EXTRA; tpval = allocate_tls(objs, NULL, 3 * sizeof(Elf_Addr), sizeof(Elf_Addr)); __asm __volatile("mov %0, %%g7" : : "r" (tpval)); } void *__tls_get_addr(tls_index *ti) { register Elf_Addr** tp __asm__("%g7"); return (tls_get_addr_common(tp, ti->ti_module, ti->ti_offset)); } Index: head/libexec/rtld-elf/sparc64/rtld_machdep.h =================================================================== --- head/libexec/rtld-elf/sparc64/rtld_machdep.h (revision 326273) +++ head/libexec/rtld-elf/sparc64/rtld_machdep.h (revision 326274) @@ -1,77 +1,79 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 1999, 2000 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef RTLD_MACHDEP_H #define RTLD_MACHDEP_H 1 #include #include struct Struct_Obj_Entry; /* Return the address of the .dynamic section in the dynamic linker. */ Elf_Dyn *rtld_dynamic_addr(void); #define rtld_dynamic(obj) rtld_dynamic_addr() Elf_Addr reloc_jmpslot(Elf_Addr *, Elf_Addr, const struct Struct_Obj_Entry *, const struct Struct_Obj_Entry *, const Elf_Rel *); #define make_function_pointer(def, defobj) \ ((defobj)->relocbase + (def)->st_value) #define call_initfini_pointer(obj, target) \ (((InitFunc)(target))()) #define call_init_pointer(obj, target) \ (((InitArrFunc)(target))(main_argc, main_argv, environ)) #define call_ifunc_resolver(ptr) \ (((Elf_Addr (*)(void))ptr)()) #define round(size, align) \ (((size) + (align) - 1) & ~((align) - 1)) #define calculate_first_tls_offset(size, align) \ round(size, align) #define calculate_tls_offset(prev_offset, prev_size, size, align) \ round((prev_offset) + (size), align) #define calculate_tls_end(off, size) ((off) + (size)) typedef struct { unsigned long ti_module; unsigned long ti_offset; } tls_index; extern void *__tls_get_addr(tls_index *ti); #define RTLD_DEFAULT_STACK_PF_EXEC 0 #define RTLD_DEFAULT_STACK_EXEC 0 #define md_abi_variant_hook(x) #endif Index: head/libexec/rtld-elf/xmalloc.c =================================================================== --- head/libexec/rtld-elf/xmalloc.c (revision 326273) +++ head/libexec/rtld-elf/xmalloc.c (revision 326274) @@ -1,97 +1,99 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright 1996-1998 John D. Polstra. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include "rtld.h" #include "rtld_printf.h" void * xcalloc(size_t number, size_t size) { void *p; p = calloc(number, size); if (p == NULL) { rtld_fdputstr(STDERR_FILENO, "Out of memory\n"); _exit(1); } return (p); } void * xmalloc(size_t size) { void *p = malloc(size); if (p == NULL) { rtld_fdputstr(STDERR_FILENO, "Out of memory\n"); _exit(1); } return p; } char * xstrdup(const char *str) { char *copy; size_t len; len = strlen(str) + 1; copy = xmalloc(len); memcpy(copy, str, len); return (copy); } void * malloc_aligned(size_t size, size_t align) { void *mem, *res; if (align < sizeof(void *)) align = sizeof(void *); mem = xmalloc(size + sizeof(void *) + align - 1); res = (void *)round((uintptr_t)mem + sizeof(void *), align); *(void **)((uintptr_t)res - sizeof(void *)) = mem; return (res); } void free_aligned(void *ptr) { void *mem; uintptr_t x; if (ptr == NULL) return; x = (uintptr_t)ptr; x -= sizeof(void *); mem = *(void **)x; free(mem); } Index: head/libexec/save-entropy/save-entropy.sh =================================================================== --- head/libexec/save-entropy/save-entropy.sh (revision 326273) +++ head/libexec/save-entropy/save-entropy.sh (revision 326274) @@ -1,92 +1,94 @@ #!/bin/sh # +# SPDX-License-Identifier: BSD-2-Clause-FreeBSD +# # Copyright (c) 2001-2006,2012 Douglas Barton, dougb@FreeBSD.org # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # $FreeBSD$ # This script is called by cron to store bits of randomness which are # then used to seed /dev/random on boot. # Originally developed by Doug Barton, dougb@FreeBSD.org PATH=/bin:/usr/bin # If there is a global system configuration file, suck it in. # if [ -r /etc/defaults/rc.conf ]; then . /etc/defaults/rc.conf source_rc_confs 2>/dev/null elif [ -r /etc/rc.conf ]; then . /etc/rc.conf 2>/dev/null fi [ $(/sbin/sysctl -n security.jail.jailed) = 0 ] || exit 0 case ${entropy_dir} in [Nn][Oo]) exit 0 ;; *) entropy_dir=${entropy_dir:-/var/db/entropy} ;; esac entropy_save_sz=${entropy_save_sz:-4096} entropy_save_num=${entropy_save_num:-8} if [ ! -d "${entropy_dir}" ]; then install -d -o operator -g operator -m 0700 "${entropy_dir}" || { logger -is -t "$0" The entropy directory "${entropy_dir}" does \ not exist, and cannot be created. Therefore no entropy can \ be saved.; exit 1; } fi cd "${entropy_dir}" || { logger -is -t "$0" Cannot cd to the entropy directory: "${entropy_dir}". \ Entropy file rotation is aborted.; exit 1; } for f in saved-entropy.*; do case "${f}" in saved-entropy.\*) continue ;; esac # No files match [ ${f#saved-entropy\.} -ge ${entropy_save_num} ] && unlink ${f} done umask 377 n=$(( ${entropy_save_num} - 1 )) while [ ${n} -ge 1 ]; do if [ -f "saved-entropy.${n}" ]; then mv "saved-entropy.${n}" "saved-entropy.$(( ${n} + 1 ))" elif [ -e "saved-entropy.${n}" -o -L "saved-entropy.${n}" ]; then logger -is -t "$0" \ "${entropy_dir}/saved-entropy.${n}" is not a regular file, and so \ it will not be rotated. Entropy file rotation is aborted. exit 1 fi n=$(( ${n} - 1 )) done dd if=/dev/random of=saved-entropy.1 bs=${entropy_save_sz} count=1 2>/dev/null exit 0 Index: head/libexec/talkd/extern.h =================================================================== --- head/libexec/talkd/extern.h (revision 326273) +++ head/libexec/talkd/extern.h (revision 326274) @@ -1,43 +1,45 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2002 M. Warner Losh. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ extern int debug; extern char hostname[]; int announce(CTL_MSG *, const char *); int delete_invite(u_int32_t); void do_announce(CTL_MSG *, CTL_RESPONSE *); CTL_MSG *find_match(CTL_MSG *request); CTL_MSG *find_request(CTL_MSG *request); int find_user(const char *name, char *tty); void insert_table(CTL_MSG *, CTL_RESPONSE *); int new_id(void); int print_mesg(const char *, CTL_MSG *, const char *); void print_request(const char *, CTL_MSG *); void print_response(const char *, CTL_RESPONSE *); void process_request(CTL_MSG *mp, CTL_RESPONSE *rp); void timeout(int sig); Index: head/libexec/tftpd/tftp-file.c =================================================================== --- head/libexec/tftpd/tftp-file.c (revision 326273) +++ head/libexec/tftpd/tftp-file.c (revision 326274) @@ -1,284 +1,286 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include "tftp-file.h" #include "tftp-utils.h" static FILE *file; static int convert; static char convbuffer[66000]; static int gotcr = 0; static size_t convert_from_net(char *buffer, size_t count) { size_t i, n; /* * Convert all CR/LF to LF and all CR,NUL to CR */ n = 0; for (i = 0; i < count; i++) { if (gotcr == 0) { convbuffer[n++] = buffer[i]; gotcr = (buffer[i] == '\r'); continue; } /* CR, NULL -> CR */ if (buffer[i] == '\0') { gotcr = 0; continue; } /* CR, LF -> LF */ if (buffer[i] == '\n') { if (n == 0) { if (ftell(file) != 0) { fseek(file, -1, SEEK_END); convbuffer[n++] = '\n'; } else { /* This shouldn't happen */ tftp_log(LOG_ERR, "Received LF as first character"); abort(); } } else convbuffer[n-1] = '\n'; gotcr = 0; continue; } /* Everything else just accept as is */ convbuffer[n++] = buffer[i]; gotcr = (buffer[i] == '\r'); continue; } return fwrite(convbuffer, 1, n, file); } static size_t convert_to_net(char *buffer, size_t count, int init) { size_t i; static size_t n = 0, in = 0; static int newline = 0; if (init) { newline = 0; n = 0; in = 0; return 0 ; } /* * Convert all LF to CR,LF and all CR to CR,NUL */ i = 0; if (newline) { buffer[i++] = newline; newline = 0; } while (i < count) { if (n == in) { /* When done we're done */ if (feof(file)) break; /* Otherwise read another bunch */ in = fread(convbuffer, 1, count, file); if (in == 0) break; n = 0; } /* CR -> CR,NULL */ if (convbuffer[n] == '\r') { buffer[i++] = '\r'; buffer[i++] = '\0'; n++; continue; } /* LF -> CR,LF */ if (convbuffer[n] == '\n') { buffer[i++] = '\r'; buffer[i++] = '\n'; n++; continue; } buffer[i++] = convbuffer[n++]; } if (i > count) { /* * Whoops... that isn't alllowed (but it will happen * when there is a CR or LF at the end of the buffer) */ newline = buffer[i-1]; } if (i < count) { /* We are done! */ return i; } else return count; } int write_init(int fd, FILE *f, const char *mode) { if (f == NULL) { file = fdopen(fd, "w"); if (file == NULL) { int en = errno; tftp_log(LOG_ERR, "fdopen() failed: %s", strerror(errno)); return en; } } else file = f; convert = !strcmp(mode, "netascii"); return 0; } size_t write_file(char *buffer, int count) { if (convert == 0) return fwrite(buffer, 1, count, file); return convert_from_net(buffer, count); } int write_close(void) { if (fclose(file) != 0) { tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); return 1; } return 0; } int read_init(int fd, FILE *f, const char *mode) { convert_to_net(NULL, 0, 1); if (f == NULL) { file = fdopen(fd, "r"); if (file == NULL) { int en = errno; tftp_log(LOG_ERR, "fdopen() failed: %s", strerror(errno)); return en; } } else file = f; convert = !strcmp(mode, "netascii"); return 0; } size_t read_file(char *buffer, int count) { if (convert == 0) return fread(buffer, 1, count, file); return convert_to_net(buffer, count, 0); } int read_close(void) { if (fclose(file) != 0) { tftp_log(LOG_ERR, "fclose() failed: %s", strerror(errno)); return 1; } return 0; } /* When an error has occurred, it is possible that the two sides * are out of synch. Ie: that what I think is the other side's * response to packet N is really their response to packet N-1. * * So, to try to prevent that, we flush all the input queued up * for us on the network connection on our host. * * We return the number of packets we flushed (mostly for reporting * when trace is active). */ int synchnet(int peer) /* socket to flush */ { int i, j = 0; char rbuf[MAXPKTSIZE]; struct sockaddr_storage from; socklen_t fromlen; while (1) { (void) ioctl(peer, FIONREAD, &i); if (i) { j++; fromlen = sizeof from; (void) recvfrom(peer, rbuf, sizeof (rbuf), 0, (struct sockaddr *)&from, &fromlen); } else { return(j); } } } Index: head/libexec/tftpd/tftp-file.h =================================================================== --- head/libexec/tftpd/tftp-file.h (revision 326273) +++ head/libexec/tftpd/tftp-file.h (revision 326274) @@ -1,37 +1,39 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); int write_init(int fd, FILE *f, const char *mode); size_t write_file(char *buffer, int count); int write_close(void); int read_init(int fd, FILE *f, const char *mode); size_t read_file(char *buffer, int count); int read_close(void); int synchnet(int peer); Index: head/libexec/tftpd/tftp-io.c =================================================================== --- head/libexec/tftpd/tftp-io.c (revision 326273) +++ head/libexec/tftpd/tftp-io.c (revision 326274) @@ -1,475 +1,477 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tftp-file.h" #include "tftp-io.h" #include "tftp-utils.h" #include "tftp-options.h" struct sockaddr_storage peer_sock; struct sockaddr_storage me_sock; static int send_packet(int peer, uint16_t block, char *pkt, int size); static struct errmsg { int e_code; const char *e_msg; } errmsgs[] = { { EUNDEF, "Undefined error code" }, { ENOTFOUND, "File not found" }, { EACCESS, "Access violation" }, { ENOSPACE, "Disk full or allocation exceeded" }, { EBADOP, "Illegal TFTP operation" }, { EBADID, "Unknown transfer ID" }, { EEXISTS, "File already exists" }, { ENOUSER, "No such user" }, { EOPTNEG, "Option negotiation" }, { -1, NULL } }; #define DROPPACKET(s) \ if (packetdroppercentage != 0 && \ random()%100 < packetdroppercentage) { \ tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ return; \ } #define DROPPACKETn(s,n) \ if (packetdroppercentage != 0 && \ random()%100 < packetdroppercentage) { \ tftp_log(LOG_DEBUG, "Artificial packet drop in %s", s); \ return (n); \ } const char * errtomsg(int error) { static char ebuf[40]; struct errmsg *pe; if (error == 0) return ("success"); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) return (pe->e_msg); snprintf(ebuf, sizeof(ebuf), "error %d", error); return (ebuf); } static int send_packet(int peer, uint16_t block, char *pkt, int size) { int i; int t = 1; for (i = 0; i < 12 ; i++) { DROPPACKETn("send_packet", 0); if (sendto(peer, pkt, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) == size) { if (i) tftp_log(LOG_ERR, "%s block %d, attempt %d successful", packettype(ntohs(((struct tftphdr *) (pkt))->th_opcode)), block, i); return (0); } tftp_log(LOG_ERR, "%s block %d, attempt %d failed (Error %d: %s)", packettype(ntohs(((struct tftphdr *)(pkt))->th_opcode)), block, i, errno, strerror(errno)); sleep(t); if (t < 32) t <<= 1; } tftp_log(LOG_ERR, "send_packet: %s", strerror(errno)); return (1); } /* * Send an ERROR packet (error message). * Error code passed in is one of the * standard TFTP codes, or a UNIX errno * offset by 100. */ void send_error(int peer, int error) { struct tftphdr *tp; int length; struct errmsg *pe; char buf[MAXPKTSIZE]; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ERROR %d", error); DROPPACKET("send_error"); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)ERROR); tp->th_code = htons((u_short)error); for (pe = errmsgs; pe->e_code >= 0; pe++) if (pe->e_code == error) break; if (pe->e_code < 0) { pe->e_msg = strerror(error - 100); tp->th_code = EUNDEF; /* set 'undef' errorcode */ } strcpy(tp->th_msg, pe->e_msg); length = strlen(pe->e_msg); tp->th_msg[length] = '\0'; length += 5; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ERROR %d: %s", error, tp->th_msg); if (sendto(peer, buf, length, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) != length) tftp_log(LOG_ERR, "send_error: %s", strerror(errno)); } /* * Send an WRQ packet (write request). */ int send_wrq(int peer, char *filename, char *mode) { int n; struct tftphdr *tp; char *bp; char buf[MAXPKTSIZE]; int size; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending WRQ: filename: '%s', mode '%s'", filename, mode ); DROPPACKETn("send_wrq", 1); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)WRQ); size = 2; bp = tp->th_stuff; strcpy(bp, filename); bp += strlen(filename); *bp = 0; bp++; size += strlen(filename) + 1; strcpy(bp, mode); bp += strlen(mode); *bp = 0; bp++; size += strlen(mode) + 1; if (options_rfc_enabled) size += make_options(peer, bp, sizeof(buf) - size); n = sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len); if (n != size) { tftp_log(LOG_ERR, "send_wrq: %s", strerror(errno)); return (1); } return (0); } /* * Send an RRQ packet (write request). */ int send_rrq(int peer, char *filename, char *mode) { int n; struct tftphdr *tp; char *bp; char buf[MAXPKTSIZE]; int size; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending RRQ: filename: '%s', mode '%s'", filename, mode ); DROPPACKETn("send_rrq", 1); tp = (struct tftphdr *)buf; tp->th_opcode = htons((u_short)RRQ); size = 2; bp = tp->th_stuff; strcpy(bp, filename); bp += strlen(filename); *bp = 0; bp++; size += strlen(filename) + 1; strcpy(bp, mode); bp += strlen(mode); *bp = 0; bp++; size += strlen(mode) + 1; if (options_rfc_enabled) { options[OPT_TSIZE].o_request = strdup("0"); size += make_options(peer, bp, sizeof(buf) - size); } n = sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len); if (n != size) { tftp_log(LOG_ERR, "send_rrq: %d %s", n, strerror(errno)); return (1); } return (0); } /* * Send an OACK packet (option acknowledgement). */ int send_oack(int peer) { struct tftphdr *tp; int size, i, n; char *bp; char buf[MAXPKTSIZE]; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending OACK"); DROPPACKETn("send_oack", 0); /* * Send back an options acknowledgement (only the ones with * a reply for) */ tp = (struct tftphdr *)buf; bp = buf + 2; size = sizeof(buf) - 2; tp->th_opcode = htons((u_short)OACK); for (i = 0; options[i].o_type != NULL; i++) { if (options[i].o_reply != NULL) { n = snprintf(bp, size, "%s%c%s", options[i].o_type, 0, options[i].o_reply); bp += n+1; size -= n+1; if (size < 0) { tftp_log(LOG_ERR, "oack: buffer overflow"); exit(1); } } } size = bp - buf; if (sendto(peer, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { tftp_log(LOG_INFO, "send_oack: %s", strerror(errno)); return (1); } return (0); } /* * Send an ACK packet (acknowledgement). */ int send_ack(int fp, uint16_t block) { struct tftphdr *tp; int size; char buf[MAXPKTSIZE]; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending ACK for block %d", block); DROPPACKETn("send_ack", 0); tp = (struct tftphdr *)buf; size = sizeof(buf) - 2; tp->th_opcode = htons((u_short)ACK); tp->th_block = htons((u_short)block); size = 4; if (sendto(fp, buf, size, 0, (struct sockaddr *)&peer_sock, peer_sock.ss_len) != size) { tftp_log(LOG_INFO, "send_ack: %s", strerror(errno)); return (1); } return (0); } /* * Send a DATA packet */ int send_data(int peer, uint16_t block, char *data, int size) { char buf[MAXPKTSIZE]; struct tftphdr *pkt; int n; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Sending DATA packet %d of %d bytes", block, size); DROPPACKETn("send_data", 0); pkt = (struct tftphdr *)buf; pkt->th_opcode = htons((u_short)DATA); pkt->th_block = htons((u_short)block); memcpy(pkt->th_data, data, size); n = send_packet(peer, block, (char *)pkt, size + 4); return (n); } /* * Receive a packet */ static jmp_buf timeoutbuf; static void timeout(int sig __unused) { /* tftp_log(LOG_DEBUG, "Timeout\n"); Inside a signal handler... */ longjmp(timeoutbuf, 1); } int receive_packet(int peer, char *data, int size, struct sockaddr_storage *from, int thistimeout) { struct tftphdr *pkt; struct sockaddr_storage from_local; struct sockaddr_storage *pfrom; socklen_t fromlen; int n; static int waiting; if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Waiting %d seconds for packet", timeoutpacket); pkt = (struct tftphdr *)data; waiting = 0; signal(SIGALRM, timeout); setjmp(timeoutbuf); alarm(thistimeout); if (waiting > 0) { alarm(0); return (RP_TIMEOUT); } if (waiting > 0) { tftp_log(LOG_ERR, "receive_packet: timeout"); alarm(0); return (RP_TIMEOUT); } waiting++; pfrom = (from == NULL) ? &from_local : from; fromlen = sizeof(*pfrom); n = recvfrom(peer, data, size, 0, (struct sockaddr *)pfrom, &fromlen); alarm(0); DROPPACKETn("receive_packet", RP_TIMEOUT); if (n < 0) { tftp_log(LOG_ERR, "receive_packet: timeout"); return (RP_TIMEOUT); } alarm(0); if (n < 0) { /* No idea what could have happened if it isn't a timeout */ tftp_log(LOG_ERR, "receive_packet: %s", strerror(errno)); return (RP_RECVFROM); } if (n < 4) { tftp_log(LOG_ERR, "receive_packet: packet too small (%d bytes)", n); return (RP_TOOSMALL); } pkt->th_opcode = ntohs((u_short)pkt->th_opcode); if (pkt->th_opcode == DATA || pkt->th_opcode == ACK) pkt->th_block = ntohs((u_short)pkt->th_block); if (pkt->th_opcode == DATA && n > pktsize) { tftp_log(LOG_ERR, "receive_packet: packet too big"); return (RP_TOOBIG); } if (((struct sockaddr_in *)(pfrom))->sin_addr.s_addr != ((struct sockaddr_in *)(&peer_sock))->sin_addr.s_addr) { tftp_log(LOG_ERR, "receive_packet: received packet from wrong source"); return (RP_WRONGSOURCE); } if (pkt->th_opcode == ERROR) { tftp_log(pkt->th_code == EUNDEF ? LOG_DEBUG : LOG_ERR, "Got ERROR packet: %s", pkt->th_msg); return (RP_ERROR); } if (debug&DEBUG_PACKETS) tftp_log(LOG_DEBUG, "Received %d bytes in a %s packet", n, packettype(pkt->th_opcode)); return n - 4; } Index: head/libexec/tftpd/tftp-io.h =================================================================== --- head/libexec/tftpd/tftp-io.h (revision 326273) +++ head/libexec/tftpd/tftp-io.h (revision 326274) @@ -1,47 +1,49 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #define RP_NONE 0 #define RP_RECVFROM -1 #define RP_TOOSMALL -2 #define RP_ERROR -3 #define RP_WRONGSOURCE -4 #define RP_TIMEOUT -5 #define RP_TOOBIG -6 const char *errtomsg(int); void send_error(int peer, int); int send_wrq(int peer, char *, char *); int send_rrq(int peer, char *, char *); int send_oack(int peer); int send_ack(int peer, unsigned short); int send_data(int peer, uint16_t, char *, int); int receive_packet(int peer, char *, int, struct sockaddr_storage *, int); extern struct sockaddr_storage peer_sock; extern struct sockaddr_storage me_sock; Index: head/libexec/tftpd/tftp-options.c =================================================================== --- head/libexec/tftpd/tftp-options.c (revision 326273) +++ head/libexec/tftpd/tftp-options.c (revision 326274) @@ -1,388 +1,390 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "tftp-utils.h" #include "tftp-io.h" #include "tftp-options.h" /* * Option handlers */ struct options options[] = { { "tsize", NULL, NULL, NULL /* option_tsize */, 1 }, { "timeout", NULL, NULL, option_timeout, 1 }, { "blksize", NULL, NULL, option_blksize, 1 }, { "blksize2", NULL, NULL, option_blksize2, 0 }, { "rollover", NULL, NULL, option_rollover, 0 }, { NULL, NULL, NULL, NULL, 0 } }; /* By default allow them */ int options_rfc_enabled = 1; int options_extra_enabled = 1; /* * Rules for the option handlers: * - If there is no o_request, there will be no processing. * * For servers * - Logging is done as warnings. * - The handler exit()s if there is a serious problem with the * values submitted in the option. * * For clients * - Logging is done as errors. After all, the server shouldn't * return rubbish. * - The handler returns if there is a serious problem with the * values submitted in the option. * - Sending the EBADOP packets is done by the handler. */ int option_tsize(int peer __unused, struct tftphdr *tp __unused, int mode, struct stat *stbuf) { if (options[OPT_TSIZE].o_request == NULL) return (0); if (mode == RRQ) asprintf(&options[OPT_TSIZE].o_reply, "%ju", stbuf->st_size); else /* XXX Allows writes of all sizes. */ options[OPT_TSIZE].o_reply = strdup(options[OPT_TSIZE].o_request); return (0); } int option_timeout(int peer) { int to; if (options[OPT_TIMEOUT].o_request == NULL) return (0); to = atoi(options[OPT_TIMEOUT].o_request); if (to < TIMEOUT_MIN || to > TIMEOUT_MAX) { tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING, "Received bad value for timeout. " "Should be between %d and %d, received %d", TIMEOUT_MIN, TIMEOUT_MAX, to); send_error(peer, EBADOP); if (acting_as_client) return (1); exit(1); } else { timeoutpacket = to; options[OPT_TIMEOUT].o_reply = strdup(options[OPT_TIMEOUT].o_request); } settimeouts(timeoutpacket, timeoutnetwork, maxtimeouts); if (debug&DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "Setting timeout to '%s'", options[OPT_TIMEOUT].o_reply); return (0); } int option_rollover(int peer) { if (options[OPT_ROLLOVER].o_request == NULL) return (0); if (strcmp(options[OPT_ROLLOVER].o_request, "0") != 0 && strcmp(options[OPT_ROLLOVER].o_request, "1") != 0) { tftp_log(acting_as_client ? LOG_ERR : LOG_WARNING, "Bad value for rollover, " "should be either 0 or 1, received '%s', " "ignoring request", options[OPT_ROLLOVER].o_request); if (acting_as_client) { send_error(peer, EBADOP); return (1); } return (0); } options[OPT_ROLLOVER].o_reply = strdup(options[OPT_ROLLOVER].o_request); if (debug&DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "Setting rollover to '%s'", options[OPT_ROLLOVER].o_reply); return (0); } int option_blksize(int peer) { u_long maxdgram; size_t len; if (options[OPT_BLKSIZE].o_request == NULL) return (0); /* maximum size of an UDP packet according to the system */ len = sizeof(maxdgram); if (sysctlbyname("net.inet.udp.maxdgram", &maxdgram, &len, NULL, 0) < 0) { tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram"); return (acting_as_client ? 1 : 0); } int size = atoi(options[OPT_BLKSIZE].o_request); if (size < BLKSIZE_MIN || size > BLKSIZE_MAX) { if (acting_as_client) { tftp_log(LOG_ERR, "Invalid blocksize (%d bytes), aborting", size); send_error(peer, EBADOP); return (1); } else { tftp_log(LOG_WARNING, "Invalid blocksize (%d bytes), ignoring request", size); return (0); } } if (size > (int)maxdgram) { if (acting_as_client) { tftp_log(LOG_ERR, "Invalid blocksize (%d bytes), " "net.inet.udp.maxdgram sysctl limits it to " "%ld bytes.\n", size, maxdgram); send_error(peer, EBADOP); return (1); } else { tftp_log(LOG_WARNING, "Invalid blocksize (%d bytes), " "net.inet.udp.maxdgram sysctl limits it to " "%ld bytes.\n", size, maxdgram); size = maxdgram; /* No reason to return */ } } asprintf(&options[OPT_BLKSIZE].o_reply, "%d", size); segsize = size; pktsize = size + 4; if (debug&DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "Setting blksize to '%s'", options[OPT_BLKSIZE].o_reply); return (0); } int option_blksize2(int peer __unused) { u_long maxdgram; int size, i; size_t len; int sizes[] = { 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 0 }; if (options[OPT_BLKSIZE2].o_request == NULL) return (0); /* maximum size of an UDP packet according to the system */ len = sizeof(maxdgram); if (sysctlbyname("net.inet.udp.maxdgram", &maxdgram, &len, NULL, 0) < 0) { tftp_log(LOG_ERR, "sysctl: net.inet.udp.maxdgram"); return (acting_as_client ? 1 : 0); } size = atoi(options[OPT_BLKSIZE2].o_request); for (i = 0; sizes[i] != 0; i++) { if (size == sizes[i]) break; } if (sizes[i] == 0) { tftp_log(LOG_INFO, "Invalid blocksize2 (%d bytes), ignoring request", size); return (acting_as_client ? 1 : 0); } if (size > (int)maxdgram) { for (i = 0; sizes[i+1] != 0; i++) { if ((int)maxdgram < sizes[i+1]) break; } tftp_log(LOG_INFO, "Invalid blocksize2 (%d bytes), net.inet.udp.maxdgram " "sysctl limits it to %ld bytes.\n", size, maxdgram); size = sizes[i]; /* No need to return */ } asprintf(&options[OPT_BLKSIZE2].o_reply, "%d", size); segsize = size; pktsize = size + 4; if (debug&DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "Setting blksize2 to '%s'", options[OPT_BLKSIZE2].o_reply); return (0); } /* * Append the available options to the header */ uint16_t make_options(int peer __unused, char *buffer, uint16_t size) { int i; char *value; const char *option; uint16_t length; uint16_t returnsize = 0; if (!options_rfc_enabled) return (0); for (i = 0; options[i].o_type != NULL; i++) { if (options[i].rfc == 0 && !options_extra_enabled) continue; option = options[i].o_type; if (acting_as_client) value = options[i].o_request; else value = options[i].o_reply; if (value == NULL) continue; length = strlen(value) + strlen(option) + 2; if (size <= length) { tftp_log(LOG_ERR, "Running out of option space for " "option '%s' with value '%s': " "needed %d bytes, got %d bytes", option, value, size, length); continue; } sprintf(buffer, "%s%c%s%c", option, '\000', value, '\000'); size -= length; buffer += length; returnsize += length; } return (returnsize); } /* * Parse the received options in the header */ int parse_options(int peer, char *buffer, uint16_t size) { int i, options_failed; char *c, *cp, *option, *value; if (!options_rfc_enabled) return (0); /* Parse the options */ cp = buffer; options_failed = 0; while (size > 0) { option = cp; i = get_field(peer, cp, size); cp += i; value = cp; i = get_field(peer, cp, size); cp += i; /* We are at the end */ if (*option == '\0') break; if (debug&DEBUG_OPTIONS) tftp_log(LOG_DEBUG, "option: '%s' value: '%s'", option, value); for (c = option; *c; c++) if (isupper(*c)) *c = tolower(*c); for (i = 0; options[i].o_type != NULL; i++) { if (strcmp(option, options[i].o_type) == 0) { if (!acting_as_client) options[i].o_request = value; if (!options_extra_enabled && !options[i].rfc) { tftp_log(LOG_INFO, "Option '%s' with value '%s' found " "but it is not an RFC option", option, value); continue; } if (options[i].o_handler) options_failed += (options[i].o_handler)(peer); break; } } if (options[i].o_type == NULL) tftp_log(LOG_WARNING, "Unknown option: '%s'", option); size -= strlen(option) + strlen(value) + 2; } return (options_failed); } /* * Set some default values in the options */ void init_options(void) { options[OPT_ROLLOVER].o_request = strdup("0"); } Index: head/libexec/tftpd/tftp-options.h =================================================================== --- head/libexec/tftpd/tftp-options.h (revision 326273) +++ head/libexec/tftpd/tftp-options.h (revision 326274) @@ -1,62 +1,64 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* * Options */ void init_options(void); uint16_t make_options(int peer, char *buffer, uint16_t size); int parse_options(int peer, char *buffer, uint16_t size); /* Call back functions */ int option_tsize(int peer, struct tftphdr *, int, struct stat *); int option_timeout(int peer); int option_blksize(int peer); int option_blksize2(int peer); int option_rollover(int peer); extern int options_extra_enabled; extern int options_rfc_enabled; struct options { const char *o_type; char *o_request; char *o_reply; int (*o_handler)(int peer); int rfc; }; extern struct options options[]; enum opt_enum { OPT_TSIZE = 0, OPT_TIMEOUT, OPT_BLKSIZE, OPT_BLKSIZE2, OPT_ROLLOVER, }; Index: head/libexec/tftpd/tftp-transfer.c =================================================================== --- head/libexec/tftpd/tftp-transfer.c (revision 326273) +++ head/libexec/tftpd/tftp-transfer.c (revision 326274) @@ -1,322 +1,324 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include "tftp-file.h" #include "tftp-io.h" #include "tftp-utils.h" #include "tftp-options.h" #include "tftp-transfer.h" /* * Send a file via the TFTP data session. */ void tftp_send(int peer, uint16_t *block, struct tftp_stats *ts) { struct tftphdr *rp; int size, n_data, n_ack, try; uint16_t oldblock; char sendbuffer[MAXPKTSIZE]; char recvbuffer[MAXPKTSIZE]; rp = (struct tftphdr *)recvbuffer; *block = 1; ts->amount = 0; do { if (debug&DEBUG_SIMPLE) tftp_log(LOG_DEBUG, "Sending block %d", *block); size = read_file(sendbuffer, segsize); if (size < 0) { tftp_log(LOG_ERR, "read_file returned %d", size); send_error(peer, errno + 100); goto abort; } for (try = 0; ; try++) { n_data = send_data(peer, *block, sendbuffer, size); if (n_data > 0) { if (try == maxtimeouts) { tftp_log(LOG_ERR, "Cannot send DATA packet #%d, " "giving up", *block); return; } tftp_log(LOG_ERR, "Cannot send DATA packet #%d, trying again", *block); continue; } n_ack = receive_packet(peer, recvbuffer, MAXPKTSIZE, NULL, timeoutpacket); if (n_ack < 0) { if (n_ack == RP_TIMEOUT) { if (try == maxtimeouts) { tftp_log(LOG_ERR, "Timeout #%d send ACK %d " "giving up", try, *block); return; } tftp_log(LOG_WARNING, "Timeout #%d on ACK %d", try, *block); continue; } /* Either read failure or ERROR packet */ if (debug&DEBUG_SIMPLE) tftp_log(LOG_ERR, "Aborting: %s", rp_strerror(n_ack)); goto abort; } if (rp->th_opcode == ACK) { ts->blocks++; if (rp->th_block == *block) { ts->amount += size; break; } /* Re-synchronize with the other side */ (void) synchnet(peer); if (rp->th_block == (*block - 1)) { ts->retries++; continue; } } } oldblock = *block; (*block)++; if (oldblock > *block) { if (options[OPT_ROLLOVER].o_request == NULL) { /* * "rollover" option not specified in * tftp client. Default to rolling block * counter to 0. */ *block = 0; } else { *block = atoi(options[OPT_ROLLOVER].o_request); } ts->rollovers++; } gettimeofday(&(ts->tstop), NULL); } while (size == segsize); abort: return; } /* * Receive a file via the TFTP data session. * * - It could be that the first block has already arrived while * trying to figure out if we were receiving options or not. In * that case it is passed to this function. */ void tftp_receive(int peer, uint16_t *block, struct tftp_stats *ts, struct tftphdr *firstblock, size_t fb_size) { struct tftphdr *rp; uint16_t oldblock; int n_data, n_ack, writesize, i, retry; char recvbuffer[MAXPKTSIZE]; ts->amount = 0; if (firstblock != NULL) { writesize = write_file(firstblock->th_data, fb_size); ts->amount += writesize; for (i = 0; ; i++) { n_ack = send_ack(peer, *block); if (n_ack > 0) { if (i == maxtimeouts) { tftp_log(LOG_ERR, "Cannot send ACK packet #%d, " "giving up", *block); return; } tftp_log(LOG_ERR, "Cannot send ACK packet #%d, trying again", *block); continue; } break; } if (fb_size != segsize) { gettimeofday(&(ts->tstop), NULL); return; } } rp = (struct tftphdr *)recvbuffer; do { oldblock = *block; (*block)++; if (oldblock > *block) { if (options[OPT_ROLLOVER].o_request == NULL) { /* * "rollover" option not specified in * tftp client. Default to rolling block * counter to 0. */ *block = 0; } else { *block = atoi(options[OPT_ROLLOVER].o_request); } ts->rollovers++; } for (retry = 0; ; retry++) { if (debug&DEBUG_SIMPLE) tftp_log(LOG_DEBUG, "Receiving DATA block %d", *block); n_data = receive_packet(peer, recvbuffer, MAXPKTSIZE, NULL, timeoutpacket); if (n_data < 0) { if (retry == maxtimeouts) { tftp_log(LOG_ERR, "Timeout #%d on DATA block %d, " "giving up", retry, *block); return; } if (n_data == RP_TIMEOUT) { tftp_log(LOG_WARNING, "Timeout #%d on DATA block %d", retry, *block); send_ack(peer, oldblock); continue; } /* Either read failure or ERROR packet */ if (debug&DEBUG_SIMPLE) tftp_log(LOG_DEBUG, "Aborting: %s", rp_strerror(n_data)); goto abort; } if (rp->th_opcode == DATA) { ts->blocks++; if (rp->th_block == *block) break; tftp_log(LOG_WARNING, "Expected DATA block %d, got block %d", *block, rp->th_block); /* Re-synchronize with the other side */ (void) synchnet(peer); if (rp->th_block == (*block-1)) { tftp_log(LOG_INFO, "Trying to sync"); *block = oldblock; ts->retries++; goto send_ack; /* rexmit */ } } else { tftp_log(LOG_WARNING, "Expected DATA block, got %s block", packettype(rp->th_opcode)); } } if (n_data > 0) { writesize = write_file(rp->th_data, n_data); ts->amount += writesize; if (writesize <= 0) { tftp_log(LOG_ERR, "write_file returned %d", writesize); if (writesize < 0) send_error(peer, errno + 100); else send_error(peer, ENOSPACE); goto abort; } } send_ack: for (i = 0; ; i++) { n_ack = send_ack(peer, *block); if (n_ack > 0) { if (i == maxtimeouts) { tftp_log(LOG_ERR, "Cannot send ACK packet #%d, " "giving up", *block); return; } tftp_log(LOG_ERR, "Cannot send ACK packet #%d, trying again", *block); continue; } break; } gettimeofday(&(ts->tstop), NULL); } while (n_data == segsize); /* Don't do late packet management for the client implementation */ if (acting_as_client) return; for (i = 0; ; i++) { n_data = receive_packet(peer, (char *)rp, pktsize, NULL, timeoutpacket); if (n_data <= 0) break; if (n_data > 0 && rp->th_opcode == DATA && /* and got a data block */ *block == rp->th_block) /* then my last ack was lost */ send_ack(peer, *block); /* resend final ack */ } abort: return; } Index: head/libexec/tftpd/tftp-transfer.h =================================================================== --- head/libexec/tftpd/tftp-transfer.h (revision 326273) +++ head/libexec/tftpd/tftp-transfer.h (revision 326274) @@ -1,31 +1,33 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); void tftp_send(int peer, uint16_t *block, struct tftp_stats *tp); void tftp_receive(int peer, uint16_t *block, struct tftp_stats *tp, struct tftphdr *firstblock, size_t fb_size); Index: head/libexec/tftpd/tftp-utils.c =================================================================== --- head/libexec/tftpd/tftp-utils.c (revision 326273) +++ head/libexec/tftpd/tftp-utils.c (revision 326274) @@ -1,319 +1,321 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include "tftp-utils.h" #include "tftp-io.h" /* * Default values, can be changed later via the TFTP Options */ int timeoutpacket = TIMEOUT; int timeoutnetwork = MAX_TIMEOUTS * TIMEOUT; int maxtimeouts = MAX_TIMEOUTS; uint16_t segsize = SEGSIZE; uint16_t pktsize = SEGSIZE + 4; int acting_as_client; /* * Set timeout values for packet reception. The idea is that you * get 'maxtimeouts' of 5 seconds between 'timeoutpacket' (i.e. the * first timeout) to 'timeoutnetwork' (i.e. the last timeout) */ int settimeouts(int _timeoutpacket, int _timeoutnetwork, int _maxtimeouts __unused) { int i; /* We cannot do impossible things */ if (_timeoutpacket >= _timeoutnetwork) return (0); maxtimeouts = 0; i = _timeoutpacket; while (i < _timeoutnetwork || maxtimeouts < MIN_TIMEOUTS) { maxtimeouts++; i += 5; } timeoutpacket = _timeoutpacket; timeoutnetwork = i; return (1); } /* translate IPv4 mapped IPv6 address to IPv4 address */ void unmappedaddr(struct sockaddr_in6 *sin6) { struct sockaddr_in *sin4; u_int32_t addr; int port; if (sin6->sin6_family != AF_INET6 || !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) return; sin4 = (struct sockaddr_in *)sin6; memcpy(&addr, &sin6->sin6_addr.s6_addr[12], sizeof(addr)); port = sin6->sin6_port; memset(sin4, 0, sizeof(struct sockaddr_in)); sin4->sin_addr.s_addr = addr; sin4->sin_port = port; sin4->sin_family = AF_INET; sin4->sin_len = sizeof(struct sockaddr_in); } /* Get a field from a \0 separated string */ ssize_t get_field(int peer, char *buffer, ssize_t size) { char *cp = buffer; while (cp < buffer + size) { if (*cp == '\0') break; cp++; } if (*cp != '\0') { tftp_log(LOG_ERR, "Bad option - no trailing \\0 found"); send_error(peer, EBADOP); exit(1); } return (cp - buffer + 1); } /* * Logging functions */ static int _tftp_logtostdout = 1; void tftp_openlog(const char *ident, int logopt, int facility) { _tftp_logtostdout = (ident == NULL); if (_tftp_logtostdout == 0) openlog(ident, logopt, facility); } void tftp_closelog(void) { if (_tftp_logtostdout == 0) closelog(); } void tftp_log(int priority, const char *message, ...) { va_list ap; char *s; va_start(ap, message); if (_tftp_logtostdout == 0) { vasprintf(&s, message, ap); syslog(priority, "%s", s); } else { vprintf(message, ap); printf("\n"); } va_end(ap); } /* * Packet types */ struct packettypes packettypes[] = { { RRQ, "RRQ" }, { WRQ, "WRQ" }, { DATA, "DATA" }, { ACK, "ACK" }, { ERROR, "ERROR" }, { OACK, "OACK" }, { 0, NULL }, }; const char * packettype(int type) { static char failed[100]; int i = 0; while (packettypes[i].name != NULL) { if (packettypes[i].value == type) break; i++; } if (packettypes[i].name != NULL) return packettypes[i].name; sprintf(failed, "unknown (type: %d)", type); return (failed); } /* * Debugs */ int debug = DEBUG_NONE; struct debugs debugs[] = { { DEBUG_PACKETS, "packet", "Packet debugging" }, { DEBUG_SIMPLE, "simple", "Simple debugging" }, { DEBUG_OPTIONS, "options", "Options debugging" }, { DEBUG_ACCESS, "access", "TCPd access debugging" }, { DEBUG_NONE, NULL, "No debugging" }, }; int packetdroppercentage = 0; int debug_find(char *s) { int i = 0; while (debugs[i].name != NULL) { if (strcasecmp(debugs[i].name, s) == 0) break; i++; } return (debugs[i].value); } int debug_finds(char *s) { int i = 0; char *ps = s; while (s != NULL) { ps = strchr(s, ' '); if (ps != NULL) *ps = '\0'; i += debug_find(s); if (ps != NULL) *ps = ' '; s = ps; } return (i); } const char * debug_show(int d) { static char s[100]; int i = 0; s[0] = '\0'; while (debugs[i].name != NULL) { if (d&debugs[i].value) { if (s[0] != '\0') strcat(s, " "); strcat(s, debugs[i].name); } i++; } if (s[0] != '\0') return (s); return ("none"); } /* * RP_ */ struct rp_errors rp_errors[] = { { RP_TIMEOUT, "Network timeout" }, { RP_TOOSMALL, "Not enough data bytes" }, { RP_WRONGSOURCE, "Invalid IP address of UDP port" }, { RP_ERROR, "Error packet" }, { RP_RECVFROM, "recvfrom() complained" }, { RP_TOOBIG, "Too many data bytes" }, { RP_NONE, NULL } }; char * rp_strerror(int error) { static char s[100]; int i = 0; while (rp_errors[i].desc != NULL) { if (rp_errors[i].error == error) { strcpy(s, rp_errors[i].desc); } i++; } if (s[0] == '\0') sprintf(s, "unknown (error=%d)", error); return (s); } /* * Performance figures */ void stats_init(struct tftp_stats *ts) { ts->amount = 0; ts->rollovers = 0; ts->retries = 0; ts->blocks = 0; ts->amount = 0; gettimeofday(&(ts->tstart), NULL); } void printstats(const char *direction, int verbose, struct tftp_stats *ts) { double delta; /* compute delta in 1/10's second units */ delta = ((ts->tstop.tv_sec*10.)+(ts->tstop.tv_usec/100000)) - ((ts->tstart.tv_sec*10.)+(ts->tstart.tv_usec/100000)); delta = delta/10.; /* back to seconds */ printf("%s %zu bytes during %.1f seconds in %u blocks", direction, ts->amount, delta, ts->blocks); if (ts->rollovers != 0) printf(" with %d rollover%s", ts->rollovers, ts->rollovers != 1 ? "s" : ""); if (verbose) printf(" [%.0f bits/sec]", (ts->amount*8.)/delta); putchar('\n'); } Index: head/libexec/tftpd/tftp-utils.h =================================================================== --- head/libexec/tftpd/tftp-utils.h (revision 326273) +++ head/libexec/tftpd/tftp-utils.h (revision 326274) @@ -1,124 +1,126 @@ -/* +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (C) 2008 Edwin Groothuis. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); /* */ #define TIMEOUT 5 #define MAX_TIMEOUTS 5 /* Generic values */ #define MAXSEGSIZE 65464 /* Maximum size of the data segment */ #define MAXPKTSIZE (MAXSEGSIZE + 4) /* Maximum size of the packet */ /* For the blksize option */ #define BLKSIZE_MIN 8 /* Minimum size of the data segment */ #define BLKSIZE_MAX MAXSEGSIZE /* Maximum size of the data segment */ /* For the timeout option */ #define TIMEOUT_MIN 0 /* Minimum timeout value */ #define TIMEOUT_MAX 255 /* Maximum timeout value */ #define MIN_TIMEOUTS 3 extern int timeoutpacket; extern int timeoutnetwork; extern int maxtimeouts; int settimeouts(int timeoutpacket, int timeoutnetwork, int maxtimeouts); extern uint16_t segsize; extern uint16_t pktsize; extern int acting_as_client; /* */ void unmappedaddr(struct sockaddr_in6 *sin6); ssize_t get_field(int peer, char *buffer, ssize_t size); /* * Packet types */ struct packettypes { int value; const char *const name; }; extern struct packettypes packettypes[]; const char *packettype(int); /* * RP_ */ struct rp_errors { int error; const char *const desc; }; extern struct rp_errors rp_errors[]; char *rp_strerror(int error); /* * Debug features */ #define DEBUG_NONE 0x0000 #define DEBUG_PACKETS 0x0001 #define DEBUG_SIMPLE 0x0002 #define DEBUG_OPTIONS 0x0004 #define DEBUG_ACCESS 0x0008 struct debugs { int value; const char *const name; const char *const desc; }; extern int debug; extern struct debugs debugs[]; extern int packetdroppercentage; int debug_find(char *s); int debug_finds(char *s); const char *debug_show(int d); /* * Log routines */ #define DEBUG(s) tftp_log(LOG_DEBUG, "%s", s) extern int tftp_logtostdout; void tftp_openlog(const char *ident, int logopt, int facility); void tftp_closelog(void); void tftp_log(int priority, const char *message, ...) __printflike(2, 3); /* * Performance figures */ struct tftp_stats { size_t amount; int rollovers; uint32_t blocks; int retries; struct timeval tstart; struct timeval tstop; }; void stats_init(struct tftp_stats *ts); void printstats(const char *direction, int verbose, struct tftp_stats *ts); Index: head/libexec/ulog-helper/ulog-helper.c =================================================================== --- head/libexec/ulog-helper/ulog-helper.c (revision 326273) +++ head/libexec/ulog-helper/ulog-helper.c (revision 326274) @@ -1,97 +1,99 @@ /*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * * Copyright (c) 2009 Ed Schouten * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include /* * This setuid helper utility writes user login records to disk. * Unprivileged processes are not capable of writing records to utmpx, * but we do want to allow this for pseudo-terminals. Because a file * descriptor to a pseudo-terminal master device can only be obtained by * processes using the pseudo-terminal, we expect such a descriptor on * stdin. * * It uses the real user ID of the calling process to determine the * username. It does allow users to log arbitrary hostnames. */ static const char * get_username(void) { const struct passwd *pw; const char *login; uid_t uid; /* * Attempt to determine the username corresponding to this login * session. First, validate the results of getlogin() against * the password database. If getlogin() returns invalid data, * return an arbitrary username corresponding to this uid. */ uid = getuid(); if ((login = getlogin()) != NULL && (pw = getpwnam(login)) != NULL && pw->pw_uid == uid) return (login); if ((pw = getpwuid(uid)) != NULL) return (pw->pw_name); return (NULL); } int main(int argc, char *argv[]) { const char *line, *user, *host; /* Device line name. */ if ((line = ptsname(STDIN_FILENO)) == NULL) return (EX_USAGE); if ((argc == 2 || argc == 3) && strcmp(argv[1], "login") == 0) { /* Username. */ user = get_username(); if (user == NULL) return (EX_OSERR); /* Hostname. */ host = argc == 3 ? argv[2] : NULL; ulog_login(line, user, host); return (EX_OK); } else if (argc == 2 && strcmp(argv[1], "logout") == 0) { ulog_logout(line); return (EX_OK); } return (EX_USAGE); }