Index: projects/sendfile/bin/sh/eval.c =================================================================== --- projects/sendfile/bin/sh/eval.c (revision 275355) +++ projects/sendfile/bin/sh/eval.c (revision 275356) @@ -1,1390 +1,1382 @@ /*- * Copyright (c) 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. * 4. 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 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include /* For WIFSIGNALED(status) */ #include /* * Evaluate a command. */ #include "shell.h" #include "nodes.h" #include "syntax.h" #include "expand.h" #include "parser.h" #include "jobs.h" #include "eval.h" #include "builtins.h" #include "options.h" #include "exec.h" #include "redir.h" #include "input.h" #include "output.h" #include "trap.h" #include "var.h" #include "memalloc.h" #include "error.h" #include "show.h" #include "mystring.h" #ifndef NO_HISTORY #include "myhistedit.h" #endif int evalskip; /* set if we are skipping commands */ int skipcount; /* number of levels to skip */ static int loopnest; /* current loop nesting level */ int funcnest; /* depth of function calls */ static int builtin_flags; /* evalcommand flags for builtins */ char *commandname; struct strlist *cmdenviron; int exitstatus; /* exit status of last command */ int oexitstatus; /* saved exit status */ static void evalloop(union node *, int); static void evalfor(union node *, int); static union node *evalcase(union node *); static void evalsubshell(union node *, int); static void evalredir(union node *, int); static void exphere(union node *, struct arglist *); static void expredir(union node *); static void evalpipe(union node *); static int is_valid_fast_cmdsubst(union node *n); static void evalcommand(union node *, int, struct backcmd *); static void prehash(union node *); /* * Called to reset things after an exception. */ void reseteval(void) { evalskip = 0; loopnest = 0; } /* * The eval command. */ int evalcmd(int argc, char **argv) { char *p; char *concat; char **ap; if (argc > 1) { p = argv[1]; if (argc > 2) { STARTSTACKSTR(concat); ap = argv + 2; for (;;) { STPUTS(p, concat); if ((p = *ap++) == NULL) break; STPUTC(' ', concat); } STPUTC('\0', concat); p = grabstackstr(concat); } evalstring(p, builtin_flags); } else exitstatus = 0; return exitstatus; } /* * Execute a command or commands contained in a string. */ void evalstring(char *s, int flags) { union node *n; struct stackmark smark; int flags_exit; int any; flags_exit = flags & EV_EXIT; flags &= ~EV_EXIT; any = 0; setstackmark(&smark); setinputstring(s, 1); while ((n = parsecmd(0)) != NEOF) { if (n != NULL && !nflag) { if (flags_exit && preadateof()) evaltree(n, flags | EV_EXIT); else evaltree(n, flags); any = 1; if (evalskip) break; } popstackmark(&smark); setstackmark(&smark); } popfile(); popstackmark(&smark); if (!any) exitstatus = 0; if (flags_exit) exraise(EXEXIT); } /* * Evaluate a parse tree. The value is left in the global variable * exitstatus. */ void evaltree(union node *n, int flags) { int do_etest; union node *next; struct stackmark smark; setstackmark(&smark); do_etest = 0; if (n == NULL) { TRACE(("evaltree(NULL) called\n")); exitstatus = 0; goto out; } do { next = NULL; #ifndef NO_HISTORY displayhist = 1; /* show history substitutions done with fc */ #endif TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type)); switch (n->type) { case NSEMI: evaltree(n->nbinary.ch1, flags & ~EV_EXIT); if (evalskip) goto out; next = n->nbinary.ch2; break; case NAND: evaltree(n->nbinary.ch1, EV_TESTED); if (evalskip || exitstatus != 0) { goto out; } next = n->nbinary.ch2; break; case NOR: evaltree(n->nbinary.ch1, EV_TESTED); if (evalskip || exitstatus == 0) goto out; next = n->nbinary.ch2; break; case NREDIR: evalredir(n, flags); break; case NSUBSHELL: evalsubshell(n, flags); do_etest = !(flags & EV_TESTED); break; case NBACKGND: evalsubshell(n, flags); break; case NIF: { evaltree(n->nif.test, EV_TESTED); if (evalskip) goto out; if (exitstatus == 0) next = n->nif.ifpart; else if (n->nif.elsepart) next = n->nif.elsepart; else exitstatus = 0; break; } case NWHILE: case NUNTIL: evalloop(n, flags & ~EV_EXIT); break; case NFOR: evalfor(n, flags & ~EV_EXIT); break; case NCASE: next = evalcase(n); break; case NCLIST: next = n->nclist.body; break; case NCLISTFALLTHRU: if (n->nclist.body) { evaltree(n->nclist.body, flags & ~EV_EXIT); if (evalskip) goto out; } next = n->nclist.next; break; case NDEFUN: defun(n->narg.text, n->narg.next); exitstatus = 0; break; case NNOT: evaltree(n->nnot.com, EV_TESTED); if (evalskip) goto out; exitstatus = !exitstatus; break; case NPIPE: evalpipe(n); do_etest = !(flags & EV_TESTED); break; case NCMD: evalcommand(n, flags, (struct backcmd *)NULL); do_etest = !(flags & EV_TESTED); break; default: out1fmt("Node type = %d\n", n->type); flushout(&output); break; } n = next; popstackmark(&smark); setstackmark(&smark); } while (n != NULL); out: popstackmark(&smark); if (pendingsig) dotrap(); if (eflag && exitstatus != 0 && do_etest) exitshell(exitstatus); if (flags & EV_EXIT) exraise(EXEXIT); } static void evalloop(union node *n, int flags) { int status; loopnest++; status = 0; for (;;) { if (!evalskip) evaltree(n->nbinary.ch1, EV_TESTED); if (evalskip) { if (evalskip == SKIPCONT && --skipcount <= 0) { evalskip = 0; continue; } if (evalskip == SKIPBREAK && --skipcount <= 0) evalskip = 0; if (evalskip == SKIPRETURN) status = exitstatus; break; } if (n->type == NWHILE) { if (exitstatus != 0) break; } else { if (exitstatus == 0) break; } evaltree(n->nbinary.ch2, flags); status = exitstatus; } loopnest--; exitstatus = status; } static void evalfor(union node *n, int flags) { struct arglist arglist; union node *argp; struct strlist *sp; int status; arglist.lastp = &arglist.list; for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { oexitstatus = exitstatus; expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); } *arglist.lastp = NULL; loopnest++; status = 0; for (sp = arglist.list ; sp ; sp = sp->next) { setvar(n->nfor.var, sp->text, 0); evaltree(n->nfor.body, flags); status = exitstatus; if (evalskip) { if (evalskip == SKIPCONT && --skipcount <= 0) { evalskip = 0; continue; } if (evalskip == SKIPBREAK && --skipcount <= 0) evalskip = 0; break; } } loopnest--; exitstatus = status; } /* * Evaluate a case statement, returning the selected tree. * * The exit status needs care to get right. */ static union node * evalcase(union node *n) { union node *cp; union node *patp; struct arglist arglist; arglist.lastp = &arglist.list; oexitstatus = exitstatus; expandarg(n->ncase.expr, &arglist, EXP_TILDE); for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) { for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { if (casematch(patp, arglist.list->text)) { while (cp->nclist.next && cp->type == NCLISTFALLTHRU && cp->nclist.body == NULL) cp = cp->nclist.next; if (cp->nclist.next && cp->type == NCLISTFALLTHRU) return (cp); if (cp->nclist.body == NULL) exitstatus = 0; return (cp->nclist.body); } } } exitstatus = 0; return (NULL); } /* * Kick off a subshell to evaluate a tree. */ static void evalsubshell(union node *n, int flags) { struct job *jp; int backgnd = (n->type == NBACKGND); oexitstatus = exitstatus; expredir(n->nredir.redirect); if ((!backgnd && flags & EV_EXIT && !have_traps()) || forkshell(jp = makejob(n, 1), n, backgnd) == 0) { if (backgnd) flags &=~ EV_TESTED; redirect(n->nredir.redirect, 0); evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ } else if (! backgnd) { INTOFF; exitstatus = waitforjob(jp, (int *)NULL); INTON; } else exitstatus = 0; } /* * Evaluate a redirected compound command. */ static void evalredir(union node *n, int flags) { struct jmploc jmploc; struct jmploc *savehandler; volatile int in_redirect = 1; oexitstatus = exitstatus; expredir(n->nredir.redirect); savehandler = handler; if (setjmp(jmploc.loc)) { int e; handler = savehandler; e = exception; popredir(); if (e == EXERROR || e == EXEXEC) { if (in_redirect) { exitstatus = 2; return; } } longjmp(handler->loc, 1); } else { INTOFF; handler = &jmploc; redirect(n->nredir.redirect, REDIR_PUSH); in_redirect = 0; INTON; evaltree(n->nredir.n, flags); } INTOFF; handler = savehandler; popredir(); INTON; } static void exphere(union node *redir, struct arglist *fn) { struct jmploc jmploc; struct jmploc *savehandler; struct localvar *savelocalvars; int need_longjmp = 0; redir->nhere.expdoc = nullstr; savelocalvars = localvars; localvars = NULL; forcelocal++; savehandler = handler; if (setjmp(jmploc.loc)) need_longjmp = exception != EXERROR && exception != EXEXEC; else { handler = &jmploc; expandarg(redir->nhere.doc, fn, 0); redir->nhere.expdoc = fn->list->text; INTOFF; } handler = savehandler; forcelocal--; poplocalvars(); localvars = savelocalvars; if (need_longjmp) longjmp(handler->loc, 1); INTON; } /* * Compute the names of the files in a redirection list. */ static void expredir(union node *n) { union node *redir; for (redir = n ; redir ; redir = redir->nfile.next) { struct arglist fn; fn.lastp = &fn.list; switch (redir->type) { case NFROM: case NTO: case NFROMTO: case NAPPEND: case NCLOBBER: expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); redir->nfile.expfname = fn.list->text; break; case NFROMFD: case NTOFD: if (redir->ndup.vname) { expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); fixredir(redir, fn.list->text, 1); } break; case NXHERE: exphere(redir, &fn); break; } } } /* * Evaluate a pipeline. All the processes in the pipeline are children * of the process creating the pipeline. (This differs from some versions * of the shell, which make the last process in a pipeline the parent * of all the rest.) */ static void evalpipe(union node *n) { struct job *jp; struct nodelist *lp; int pipelen; int prevfd; int pip[2]; TRACE(("evalpipe(%p) called\n", (void *)n)); pipelen = 0; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) pipelen++; INTOFF; jp = makejob(n, pipelen); prevfd = -1; for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { prehash(lp->n); pip[1] = -1; if (lp->next) { if (pipe(pip) < 0) { if (prevfd >= 0) close(prevfd); error("Pipe call failed: %s", strerror(errno)); } } if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { INTON; if (prevfd > 0) { dup2(prevfd, 0); close(prevfd); } if (pip[1] >= 0) { if (!(prevfd >= 0 && pip[0] == 0)) close(pip[0]); if (pip[1] != 1) { dup2(pip[1], 1); close(pip[1]); } } evaltree(lp->n, EV_EXIT); } if (prevfd >= 0) close(prevfd); prevfd = pip[0]; if (pip[1] != -1) close(pip[1]); } INTON; if (n->npipe.backgnd == 0) { INTOFF; exitstatus = waitforjob(jp, (int *)NULL); TRACE(("evalpipe: job done exit status %d\n", exitstatus)); INTON; } else exitstatus = 0; } static int is_valid_fast_cmdsubst(union node *n) { return (n->type == NCMD); } /* * Execute a command inside back quotes. If it's a builtin command, we * want to save its output in a block obtained from malloc. Otherwise * we fork off a subprocess and get the output of the command via a pipe. * Should be called with interrupts off. */ void evalbackcmd(union node *n, struct backcmd *result) { int pip[2]; struct job *jp; struct stackmark smark; struct jmploc jmploc; struct jmploc *savehandler; struct localvar *savelocalvars; result->fd = -1; result->buf = NULL; result->nleft = 0; result->jp = NULL; if (n == NULL) { exitstatus = 0; return; } setstackmark(&smark); exitstatus = oexitstatus; if (is_valid_fast_cmdsubst(n)) { savelocalvars = localvars; localvars = NULL; forcelocal++; savehandler = handler; if (setjmp(jmploc.loc)) { if (exception == EXERROR || exception == EXEXEC) exitstatus = 2; else if (exception != 0) { handler = savehandler; forcelocal--; poplocalvars(); localvars = savelocalvars; longjmp(handler->loc, 1); } } else { handler = &jmploc; evalcommand(n, EV_BACKCMD, result); } handler = savehandler; forcelocal--; poplocalvars(); localvars = savelocalvars; } else { if (pipe(pip) < 0) error("Pipe call failed: %s", strerror(errno)); jp = makejob(n, 1); if (forkshell(jp, n, FORK_NOJOB) == 0) { FORCEINTON; close(pip[0]); if (pip[1] != 1) { dup2(pip[1], 1); close(pip[1]); } evaltree(n, EV_EXIT); } close(pip[1]); result->fd = pip[0]; result->jp = jp; } popstackmark(&smark); TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n", result->fd, result->buf, result->nleft, result->jp)); } static int mustexpandto(const char *argtext, const char *mask) { for (;;) { if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) { argtext++; continue; } if (*argtext == CTLESC) argtext++; else if (BASESYNTAX[(int)*argtext] == CCTL) return (0); if (*argtext != *mask) return (0); if (*argtext == '\0') return (1); argtext++; mask++; } } static int isdeclarationcmd(struct narg *arg) { int have_command = 0; if (arg == NULL) return (0); while (mustexpandto(arg->text, "command")) { have_command = 1; arg = &arg->next->narg; if (arg == NULL) return (0); /* * To also allow "command -p" and "command --" as part of * a declaration command, add code here. * We do not do this, as ksh does not do it either and it * is not required by POSIX. */ } return (mustexpandto(arg->text, "export") || mustexpandto(arg->text, "readonly") || (mustexpandto(arg->text, "local") && (have_command || !isfunc("local")))); } static void xtracecommand(struct arglist *varlist, struct arglist *arglist) { struct strlist *sp; char sep = 0; const char *p, *ps4; ps4 = expandstr(ps4val()); out2str(ps4 != NULL ? ps4 : ps4val()); for (sp = varlist->list ; sp ; sp = sp->next) { if (sep != 0) out2c(' '); p = strchr(sp->text, '='); if (p != NULL) { p++; outbin(sp->text, p - sp->text, out2); out2qstr(p); } else out2qstr(sp->text); sep = ' '; } for (sp = arglist->list ; sp ; sp = sp->next) { if (sep != 0) out2c(' '); - /* Disambiguate command looking like assignment. */ - if (sp == arglist->list && - strchr(sp->text, '=') != NULL && - strchr(sp->text, '\'') == NULL) { - out2c('\''); - out2str(sp->text); - out2c('\''); - } else - out2qstr(sp->text); + out2qstr(sp->text); sep = ' '; } out2c('\n'); flushout(&errout); } /* * Check if a builtin can safely be executed in the same process, * even though it should be in a subshell (command substitution). * Note that jobid, jobs, times and trap can show information not * available in a child process; this is deliberate. * The arguments should already have been expanded. */ static int safe_builtin(int idx, int argc, char **argv) { if (idx == BLTINCMD || idx == COMMANDCMD || idx == ECHOCMD || idx == FALSECMD || idx == JOBIDCMD || idx == JOBSCMD || idx == KILLCMD || idx == PRINTFCMD || idx == PWDCMD || idx == TESTCMD || idx == TIMESCMD || idx == TRUECMD || idx == TYPECMD) return (1); if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD || idx == UMASKCMD) return (argc <= 1 || (argc == 2 && argv[1][0] == '-')); if (idx == SETCMD) return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' || argv[1][0] == '+') && argv[1][1] == 'o' && argv[1][2] == '\0')); return (0); } /* * Execute a simple command. * Note: This may or may not return if (flags & EV_EXIT). */ static void evalcommand(union node *cmd, int flags, struct backcmd *backcmd) { union node *argp; struct arglist arglist; struct arglist varlist; char **argv; int argc; char **envp; int varflag; struct strlist *sp; int mode; int pip[2]; struct cmdentry cmdentry; struct job *jp; struct jmploc jmploc; struct jmploc *savehandler; char *savecmdname; struct shparam saveparam; struct localvar *savelocalvars; struct parsefile *savetopfile; volatile int e; char *lastarg; int realstatus; int do_clearcmdentry; const char *path = pathval(); /* First expand the arguments. */ TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); arglist.lastp = &arglist.list; varlist.lastp = &varlist.list; varflag = 1; jp = NULL; do_clearcmdentry = 0; oexitstatus = exitstatus; exitstatus = 0; for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { if (varflag && isassignment(argp->narg.text)) { expandarg(argp, varflag == 1 ? &varlist : &arglist, EXP_VARTILDE); continue; } else if (varflag == 1) varflag = isdeclarationcmd(&argp->narg) ? 2 : 0; expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); } *arglist.lastp = NULL; *varlist.lastp = NULL; expredir(cmd->ncmd.redirect); argc = 0; for (sp = arglist.list ; sp ; sp = sp->next) argc++; /* Add one slot at the beginning for tryexec(). */ argv = stalloc(sizeof (char *) * (argc + 2)); argv++; for (sp = arglist.list ; sp ; sp = sp->next) { TRACE(("evalcommand arg: %s\n", sp->text)); *argv++ = sp->text; } *argv = NULL; lastarg = NULL; if (iflag && funcnest == 0 && argc > 0) lastarg = argv[-1]; argv -= argc; /* Print the command if xflag is set. */ if (xflag) xtracecommand(&varlist, &arglist); /* Now locate the command. */ if (argc == 0) { /* Variable assignment(s) without command */ cmdentry.cmdtype = CMDBUILTIN; cmdentry.u.index = BLTINCMD; cmdentry.special = 0; } else { static const char PATH[] = "PATH="; int cmd_flags = 0, bltinonly = 0; /* * Modify the command lookup path, if a PATH= assignment * is present */ for (sp = varlist.list ; sp ; sp = sp->next) if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) { path = sp->text + sizeof(PATH) - 1; /* * On `PATH=... command`, we need to make * sure that the command isn't using the * non-updated hash table of the outer PATH * setting and we need to make sure that * the hash table isn't filled with items * from the temporary setting. * * It would be better to forbit using and * updating the table while this command * runs, by the command finding mechanism * is heavily integrated with hash handling, * so we just delete the hash before and after * the command runs. Partly deleting like * changepatch() does doesn't seem worth the * bookinging effort, since most such runs add * directories in front of the new PATH. */ clearcmdentry(); do_clearcmdentry = 1; } for (;;) { if (bltinonly) { cmdentry.u.index = find_builtin(*argv, &cmdentry.special); if (cmdentry.u.index < 0) { cmdentry.u.index = BLTINCMD; argv--; argc++; break; } } else find_command(argv[0], &cmdentry, cmd_flags, path); /* implement the bltin and command builtins here */ if (cmdentry.cmdtype != CMDBUILTIN) break; if (cmdentry.u.index == BLTINCMD) { if (argc == 1) break; argv++; argc--; bltinonly = 1; } else if (cmdentry.u.index == COMMANDCMD) { if (argc == 1) break; if (!strcmp(argv[1], "-p")) { if (argc == 2) break; if (argv[2][0] == '-') { if (strcmp(argv[2], "--")) break; if (argc == 3) break; argv += 3; argc -= 3; } else { argv += 2; argc -= 2; } path = _PATH_STDPATH; clearcmdentry(); do_clearcmdentry = 1; } else if (!strcmp(argv[1], "--")) { if (argc == 2) break; argv += 2; argc -= 2; } else if (argv[1][0] == '-') break; else { argv++; argc--; } cmd_flags |= DO_NOFUNC; bltinonly = 0; } else break; } /* * Special builtins lose their special properties when * called via 'command'. */ if (cmd_flags & DO_NOFUNC) cmdentry.special = 0; } /* Fork off a child process if necessary. */ if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) && ((flags & EV_EXIT) == 0 || have_traps())) || ((flags & EV_BACKCMD) != 0 && (cmdentry.cmdtype != CMDBUILTIN || !safe_builtin(cmdentry.u.index, argc, argv)))) { jp = makejob(cmd, 1); mode = FORK_FG; if (flags & EV_BACKCMD) { mode = FORK_NOJOB; if (pipe(pip) < 0) error("Pipe call failed: %s", strerror(errno)); } if (cmdentry.cmdtype == CMDNORMAL && cmd->ncmd.redirect == NULL && varlist.list == NULL && (mode == FORK_FG || mode == FORK_NOJOB) && !disvforkset() && !iflag && !mflag) { vforkexecshell(jp, argv, environment(), path, cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL); goto parent; } if (forkshell(jp, cmd, mode) != 0) goto parent; /* at end of routine */ if (flags & EV_BACKCMD) { FORCEINTON; close(pip[0]); if (pip[1] != 1) { dup2(pip[1], 1); close(pip[1]); } flags &= ~EV_BACKCMD; } flags |= EV_EXIT; } /* This is the child process if a fork occurred. */ /* Execute the command. */ if (cmdentry.cmdtype == CMDFUNCTION) { #ifdef DEBUG trputs("Shell function: "); trargs(argv); #endif saveparam = shellparam; shellparam.malloc = 0; shellparam.reset = 1; shellparam.nparam = argc - 1; shellparam.p = argv + 1; shellparam.optp = NULL; shellparam.optnext = NULL; INTOFF; savelocalvars = localvars; localvars = NULL; reffunc(cmdentry.u.func); savehandler = handler; if (setjmp(jmploc.loc)) { freeparam(&shellparam); shellparam = saveparam; popredir(); unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; funcnest--; handler = savehandler; longjmp(handler->loc, 1); } handler = &jmploc; funcnest++; redirect(cmd->ncmd.redirect, REDIR_PUSH); INTON; for (sp = varlist.list ; sp ; sp = sp->next) mklocal(sp->text); exitstatus = oexitstatus; evaltree(getfuncnode(cmdentry.u.func), flags & (EV_TESTED | EV_EXIT)); INTOFF; unreffunc(cmdentry.u.func); poplocalvars(); localvars = savelocalvars; freeparam(&shellparam); shellparam = saveparam; handler = savehandler; funcnest--; popredir(); INTON; if (evalskip == SKIPRETURN) { evalskip = 0; skipcount = 0; } if (jp) exitshell(exitstatus); } else if (cmdentry.cmdtype == CMDBUILTIN) { #ifdef DEBUG trputs("builtin command: "); trargs(argv); #endif mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; if (flags == EV_BACKCMD) { memout.nleft = 0; memout.nextc = memout.buf; memout.bufsize = 64; mode |= REDIR_BACKQ; } savecmdname = commandname; savetopfile = getcurrentfile(); cmdenviron = varlist.list; e = -1; savehandler = handler; if (setjmp(jmploc.loc)) { e = exception; if (e == EXINT) exitstatus = SIGINT+128; else if (e != EXEXIT) exitstatus = 2; goto cmddone; } handler = &jmploc; redirect(cmd->ncmd.redirect, mode); outclearerror(out1); /* * If there is no command word, redirection errors should * not be fatal but assignment errors should. */ if (argc == 0) cmdentry.special = 1; listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET); if (argc > 0) bltinsetlocale(); commandname = argv[0]; argptr = argv + 1; nextopt_optptr = NULL; /* initialize nextopt */ builtin_flags = flags; exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); flushall(); if (outiserror(out1)) { warning("write error on stdout"); if (exitstatus == 0 || exitstatus == 1) exitstatus = 2; } cmddone: if (argc > 0) bltinunsetlocale(); cmdenviron = NULL; out1 = &output; out2 = &errout; freestdout(); handler = savehandler; commandname = savecmdname; if (jp) exitshell(exitstatus); if (flags == EV_BACKCMD) { backcmd->buf = memout.buf; backcmd->nleft = memout.nextc - memout.buf; memout.buf = NULL; } if (cmdentry.u.index != EXECCMD) popredir(); if (e != -1) { if ((e != EXERROR && e != EXEXEC) || cmdentry.special) exraise(e); popfilesupto(savetopfile); if (flags != EV_BACKCMD) FORCEINTON; } } else { #ifdef DEBUG trputs("normal command: "); trargs(argv); #endif redirect(cmd->ncmd.redirect, 0); for (sp = varlist.list ; sp ; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); envp = environment(); shellexec(argv, envp, path, cmdentry.u.index); /*NOTREACHED*/ } goto out; parent: /* parent process gets here (if we forked) */ if (mode == FORK_FG) { /* argument to fork */ INTOFF; exitstatus = waitforjob(jp, &realstatus); INTON; if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { evalskip = SKIPBREAK; skipcount = loopnest; } } else if (mode == FORK_NOJOB) { backcmd->fd = pip[0]; close(pip[1]); backcmd->jp = jp; } out: if (lastarg) setvar("_", lastarg, 0); if (do_clearcmdentry) clearcmdentry(); } /* * Search for a command. This is called before we fork so that the * location of the command will be available in the parent as well as * the child. The check for "goodname" is an overly conservative * check that the name will not be subject to expansion. */ static void prehash(union node *n) { struct cmdentry entry; if (n && n->type == NCMD && n->ncmd.args) if (goodname(n->ncmd.args->narg.text)) find_command(n->ncmd.args->narg.text, &entry, 0, pathval()); } /* * Builtin commands. Builtin commands whose functions are closely * tied to evaluation are implemented here. */ /* * No command given, a bltin command with no arguments, or a bltin command * with an invalid name. */ int bltincmd(int argc, char **argv) { if (argc > 1) { out2fmt_flush("%s: not found\n", argv[1]); return 127; } /* * Preserve exitstatus of a previous possible redirection * as POSIX mandates */ return exitstatus; } /* * Handle break and continue commands. Break, continue, and return are * all handled by setting the evalskip flag. The evaluation routines * above all check this flag, and if it is set they start skipping * commands rather than executing them. The variable skipcount is * the number of loops to break/continue, or the number of function * levels to return. (The latter is always 1.) It should probably * be an error to break out of more loops than exist, but it isn't * in the standard shell so we don't make it one here. */ int breakcmd(int argc, char **argv) { long n; char *end; if (argc > 1) { /* Allow arbitrarily large numbers. */ n = strtol(argv[1], &end, 10); if (!is_digit(argv[1][0]) || *end != '\0') error("Illegal number: %s", argv[1]); } else n = 1; if (n > loopnest) n = loopnest; if (n > 0) { evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; skipcount = n; } return 0; } /* * The `command' command. */ int commandcmd(int argc __unused, char **argv __unused) { const char *path; int ch; int cmd = -1; path = bltinlookup("PATH", 1); while ((ch = nextopt("pvV")) != '\0') { switch (ch) { case 'p': path = _PATH_STDPATH; break; case 'v': cmd = TYPECMD_SMALLV; break; case 'V': cmd = TYPECMD_BIGV; break; } } if (cmd != -1) { if (*argptr == NULL || argptr[1] != NULL) error("wrong number of arguments"); return typecmd_impl(2, argptr - 1, cmd, path); } if (*argptr != NULL) error("commandcmd bad call"); /* * Do nothing successfully if no command was specified; * ksh also does this. */ return 0; } /* * The return command. */ int returncmd(int argc, char **argv) { int ret = argc > 1 ? number(argv[1]) : oexitstatus; evalskip = SKIPRETURN; skipcount = 1; return ret; } int falsecmd(int argc __unused, char **argv __unused) { return 1; } int truecmd(int argc __unused, char **argv __unused) { return 0; } int execcmd(int argc, char **argv) { /* * Because we have historically not supported any options, * only treat "--" specially. */ if (argc > 1 && strcmp(argv[1], "--") == 0) argc--, argv++; if (argc > 1) { struct strlist *sp; iflag = 0; /* exit on error */ mflag = 0; optschanged(); for (sp = cmdenviron; sp ; sp = sp->next) setvareq(sp->text, VEXPORT|VSTACK); shellexec(argv + 1, environment(), pathval(), 0); } return 0; } int timescmd(int argc __unused, char **argv __unused) { struct rusage ru; long shumins, shsmins, chumins, chsmins; double shusecs, shssecs, chusecs, chssecs; if (getrusage(RUSAGE_SELF, &ru) < 0) return 1; shumins = ru.ru_utime.tv_sec / 60; shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; shsmins = ru.ru_stime.tv_sec / 60; shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; if (getrusage(RUSAGE_CHILDREN, &ru) < 0) return 1; chumins = ru.ru_utime.tv_sec / 60; chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; chsmins = ru.ru_stime.tv_sec / 60; chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins, shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs); return 0; } Index: projects/sendfile/bin/sh/output.c =================================================================== --- projects/sendfile/bin/sh/output.c (revision 275355) +++ projects/sendfile/bin/sh/output.c (revision 275356) @@ -1,330 +1,329 @@ /*- * Copyright (c) 1991, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Kenneth Almquist. * * 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. * 4. 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 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. */ #ifndef lint #if 0 static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; #endif #endif /* not lint */ #include __FBSDID("$FreeBSD$"); /* * Shell output routines. We use our own output routines because: * When a builtin command is interrupted we have to discard * any pending output. * When a builtin command appears in back quotes, we want to * save the output of the command in a region obtained * via malloc, rather than doing a fork and reading the * output of the command via a pipe. */ #include /* defines BUFSIZ */ #include #include #include #include #include #include "shell.h" #include "syntax.h" #include "output.h" #include "memalloc.h" #include "error.h" #include "var.h" #define OUTBUFSIZ BUFSIZ #define MEM_OUT -2 /* output to dynamically allocated memory */ #define OUTPUT_ERR 01 /* error occurred on output */ static int doformat_wr(void *, const char *, int); struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; struct output errout = {NULL, 0, NULL, 256, 2, 0}; struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; struct output *out1 = &output; struct output *out2 = &errout; void outcslow(int c, struct output *file) { outc(c, file); } void out1str(const char *p) { outstr(p, out1); } void out1qstr(const char *p) { outqstr(p, out1); } void out2str(const char *p) { outstr(p, out2); } void out2qstr(const char *p) { outqstr(p, out2); } void outstr(const char *p, struct output *file) { outbin(p, strlen(p), file); } /* Like outstr(), but quote for re-input into the shell. */ void outqstr(const char *p, struct output *file) { char ch; int inquotes; if (p[0] == '\0') { outstr("''", file); return; } - /* Caller will handle '=' if necessary */ - if (p[strcspn(p, "|&;<>()$`\\\"' \t\n*?[~#")] == '\0' || + if (p[strcspn(p, "|&;<>()$`\\\"' \t\n*?[~#=")] == '\0' || strcmp(p, "[") == 0) { outstr(p, file); return; } inquotes = 0; while ((ch = *p++) != '\0') { switch (ch) { case '\'': /* Can't quote single quotes inside single quotes. */ if (inquotes) outcslow('\'', file); inquotes = 0; outstr("\\'", file); break; default: if (!inquotes) outcslow('\'', file); inquotes = 1; outc(ch, file); } } if (inquotes) outcslow('\'', file); } void outbin(const void *data, size_t len, struct output *file) { const char *p; p = data; while (len-- > 0) outc(*p++, file); } void emptyoutbuf(struct output *dest) { int offset; if (dest->buf == NULL) { INTOFF; dest->buf = ckmalloc(dest->bufsize); dest->nextc = dest->buf; dest->nleft = dest->bufsize; INTON; } else if (dest->fd == MEM_OUT) { offset = dest->bufsize; INTOFF; dest->bufsize <<= 1; dest->buf = ckrealloc(dest->buf, dest->bufsize); dest->nleft = dest->bufsize - offset; dest->nextc = dest->buf + offset; INTON; } else { flushout(dest); } dest->nleft--; } void flushall(void) { flushout(&output); flushout(&errout); } void flushout(struct output *dest) { if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) return; if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) dest->flags |= OUTPUT_ERR; dest->nextc = dest->buf; dest->nleft = dest->bufsize; } void freestdout(void) { INTOFF; if (output.buf) { ckfree(output.buf); output.buf = NULL; output.nleft = 0; } INTON; } int outiserror(struct output *file) { return (file->flags & OUTPUT_ERR); } void outclearerror(struct output *file) { file->flags &= ~OUTPUT_ERR; } void outfmt(struct output *file, const char *fmt, ...) { va_list ap; va_start(ap, fmt); doformat(file, fmt, ap); va_end(ap); } void out1fmt(const char *fmt, ...) { va_list ap; va_start(ap, fmt); doformat(out1, fmt, ap); va_end(ap); } void out2fmt_flush(const char *fmt, ...) { va_list ap; va_start(ap, fmt); doformat(out2, fmt, ap); va_end(ap); flushout(out2); } void fmtstr(char *outbuf, int length, const char *fmt, ...) { va_list ap; INTOFF; va_start(ap, fmt); vsnprintf(outbuf, length, fmt, ap); va_end(ap); INTON; } static int doformat_wr(void *cookie, const char *buf, int len) { struct output *o; o = (struct output *)cookie; outbin(buf, len, o); return (len); } void doformat(struct output *dest, const char *f, va_list ap) { FILE *fp; if ((fp = fwopen(dest, doformat_wr)) != NULL) { vfprintf(fp, f, ap); fclose(fp); } } /* * Version of write which resumes after a signal is caught. */ int xwrite(int fd, const char *buf, int nbytes) { int ntry; int i; int n; n = nbytes; ntry = 0; for (;;) { i = write(fd, buf, n); if (i > 0) { if ((n -= i) <= 0) return nbytes; buf += i; ntry = 0; } else if (i == 0) { if (++ntry > 10) return nbytes - n; } else if (errno != EINTR) { return -1; } } } Index: projects/sendfile/contrib/binutils/bfd/elf32-arm.c =================================================================== --- projects/sendfile/contrib/binutils/bfd/elf32-arm.c (revision 275355) +++ projects/sendfile/contrib/binutils/bfd/elf32-arm.c (revision 275356) @@ -1,10447 +1,10447 @@ /* 32-bit ELF support for ARM Copyright 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc. This file is part of BFD, the Binary File Descriptor library. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "sysdep.h" #include "bfd.h" #include "libiberty.h" #include "libbfd.h" #include "elf-bfd.h" #include "elf-vxworks.h" #include "elf/arm.h" #ifndef NUM_ELEM #define NUM_ELEM(a) (sizeof (a) / (sizeof (a)[0])) #endif /* Return the relocation section associated with NAME. HTAB is the bfd's elf32_arm_link_hash_entry. */ #define RELOC_SECTION(HTAB, NAME) \ ((HTAB)->use_rel ? ".rel" NAME : ".rela" NAME) /* Return size of a relocation entry. HTAB is the bfd's elf32_arm_link_hash_entry. */ #define RELOC_SIZE(HTAB) \ ((HTAB)->use_rel \ ? sizeof (Elf32_External_Rel) \ : sizeof (Elf32_External_Rela)) /* Return function to swap relocations in. HTAB is the bfd's elf32_arm_link_hash_entry. */ #define SWAP_RELOC_IN(HTAB) \ ((HTAB)->use_rel \ ? bfd_elf32_swap_reloc_in \ : bfd_elf32_swap_reloca_in) /* Return function to swap relocations out. HTAB is the bfd's elf32_arm_link_hash_entry. */ #define SWAP_RELOC_OUT(HTAB) \ ((HTAB)->use_rel \ ? bfd_elf32_swap_reloc_out \ : bfd_elf32_swap_reloca_out) #define elf_info_to_howto 0 #define elf_info_to_howto_rel elf32_arm_info_to_howto #define ARM_ELF_ABI_VERSION 0 #ifdef __FreeBSD__ #define ARM_ELF_OS_ABI_VERSION ELFOSABI_FREEBSD #else #define ARM_ELF_OS_ABI_VERSION ELFOSABI_ARM #endif static struct elf_backend_data elf32_arm_vxworks_bed; /* Note: code such as elf32_arm_reloc_type_lookup expect to use e.g. R_ARM_PC24 as an index into this, and find the R_ARM_PC24 HOWTO in that slot. */ static reloc_howto_type elf32_arm_howto_table_1[] = { /* No relocation */ HOWTO (R_ARM_NONE, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_NONE", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_PC24, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_PC24", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* 32 bit absolute */ HOWTO (R_ARM_ABS32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS32", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* standard 32bit pc-relative reloc */ HOWTO (R_ARM_REL32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_REL32", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* 8 bit absolute - R_ARM_LDR_PC_G0 in AAELF */ HOWTO (R_ARM_LDR_PC_G0, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_PC_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* 16 bit absolute */ HOWTO (R_ARM_ABS16, /* type */ 0, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS16", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* 12 bit absolute */ HOWTO (R_ARM_ABS12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_ABS5, /* type */ 6, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 5, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_ABS5", /* name */ FALSE, /* partial_inplace */ 0x000007e0, /* src_mask */ 0x000007e0, /* dst_mask */ FALSE), /* pcrel_offset */ /* 8 bit absolute */ HOWTO (R_ARM_ABS8, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS8", /* name */ FALSE, /* partial_inplace */ 0x000000ff, /* src_mask */ 0x000000ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_SBREL32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_SBREL32", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_CALL, /* type */ 1, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 25, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_CALL", /* name */ FALSE, /* partial_inplace */ 0x07ff07ff, /* src_mask */ 0x07ff07ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_PC8, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_PC8", /* name */ FALSE, /* partial_inplace */ 0x000000ff, /* src_mask */ 0x000000ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_BREL_ADJ, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_BREL_ADJ", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_SWI24, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_SWI24", /* name */ FALSE, /* partial_inplace */ 0x00000000, /* src_mask */ 0x00000000, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_SWI8, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_SWI8", /* name */ FALSE, /* partial_inplace */ 0x00000000, /* src_mask */ 0x00000000, /* dst_mask */ FALSE), /* pcrel_offset */ /* BLX instruction for the ARM. */ HOWTO (R_ARM_XPC25, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 25, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_XPC25", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* BLX instruction for the Thumb. */ HOWTO (R_ARM_THM_XPC22, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 22, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_XPC22", /* name */ FALSE, /* partial_inplace */ 0x07ff07ff, /* src_mask */ 0x07ff07ff, /* dst_mask */ TRUE), /* pcrel_offset */ /* Dynamic TLS relocations. */ HOWTO (R_ARM_TLS_DTPMOD32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_DTPMOD32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_DTPOFF32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_DTPOFF32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_TPOFF32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_TPOFF32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* Relocs used in ARM Linux */ HOWTO (R_ARM_COPY, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_COPY", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GLOB_DAT, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GLOB_DAT", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_JUMP_SLOT, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_JUMP_SLOT", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_RELATIVE, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RELATIVE", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOTOFF32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOTOFF32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOTPC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOTPC", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_GOT32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOT32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_PLT32, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_PLT32", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_CALL, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_CALL", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_JUMP24, /* type */ 2, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_JUMP24", /* name */ FALSE, /* partial_inplace */ 0x00ffffff, /* src_mask */ 0x00ffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP24, /* type */ 1, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 24, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP24", /* name */ FALSE, /* partial_inplace */ 0x07ff2fff, /* src_mask */ 0x07ff2fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_BASE_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_BASE_ABS", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PCREL7_0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PCREL_7_0", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PCREL15_8, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ TRUE, /* pc_relative */ 8, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PCREL_15_8",/* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PCREL23_15, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ TRUE, /* pc_relative */ 16, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PCREL_23_15",/* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_SBREL_11_0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_SBREL_11_0",/* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SBREL_19_12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 12, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SBREL_19_12",/* name */ FALSE, /* partial_inplace */ 0x000ff000, /* src_mask */ 0x000ff000, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SBREL_27_20, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ FALSE, /* pc_relative */ 20, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SBREL_27_20",/* name */ FALSE, /* partial_inplace */ 0x0ff00000, /* src_mask */ 0x0ff00000, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TARGET1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TARGET1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_ROSEGREL32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ROSEGREL32", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_V4BX, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_V4BX", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TARGET2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TARGET2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_PREL31, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 31, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_PREL31", /* name */ FALSE, /* partial_inplace */ 0x7fffffff, /* src_mask */ 0x7fffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_MOVW_ABS_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVW_ABS_NC", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_MOVT_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVT_ABS", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_MOVW_PREL_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVW_PREL_NC", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_MOVT_PREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVT_PREL", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVW_ABS_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVW_ABS_NC",/* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVT_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVT_ABS", /* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVW_PREL_NC,/* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVW_PREL_NC",/* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVT_PREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVT_PREL", /* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP19, /* type */ 1, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 19, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP19", /* name */ FALSE, /* partial_inplace */ 0x043f2fff, /* src_mask */ 0x043f2fff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP6, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 6, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_unsigned,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP6", /* name */ FALSE, /* partial_inplace */ 0x02f8, /* src_mask */ 0x02f8, /* dst_mask */ TRUE), /* pcrel_offset */ /* These are declared as 13-bit signed relocations because we can address -4095 .. 4095(base) by altering ADDW to SUBW or vice versa. */ HOWTO (R_ARM_THM_ALU_PREL_11_0,/* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 13, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_ALU_PREL_11_0",/* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_PC12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 13, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_PC12", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ABS32_NOI, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ABS32_NOI", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_REL32_NOI, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_REL32_NOI", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ /* Group relocations. */ HOWTO (R_ARM_ALU_PC_G0_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G0_NC", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PC_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PC_G1_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G1_NC", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PC_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_PC_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_PC_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_PC_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_PC_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_PC_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_PC_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_PC_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_PC_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_PC_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_PC_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_PC_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_PC_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_PC_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_PC_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_PC_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_PC_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_PC_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_PC_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G0_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G0_NC", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G1_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G1_NC", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_ALU_SB_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_ALU_SB_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_SB_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_SB_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_SB_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_SB_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDR_SB_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDR_SB_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_SB_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_SB_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_SB_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_SB_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDRS_SB_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDRS_SB_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_SB_G0, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_SB_G0", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_SB_G1, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_SB_G1", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_LDC_SB_G2, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_LDC_SB_G2", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ /* End of group relocations. */ HOWTO (R_ARM_MOVW_BREL_NC, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVW_BREL_NC", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_MOVT_BREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVT_BREL", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_MOVW_BREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_MOVW_BREL", /* name */ FALSE, /* partial_inplace */ 0x0000ffff, /* src_mask */ 0x0000ffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVW_BREL_NC,/* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVW_BREL_NC",/* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVT_BREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVT_BREL", /* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_MOVW_BREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 16, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_MOVW_BREL", /* name */ FALSE, /* partial_inplace */ 0x040f70ff, /* src_mask */ 0x040f70ff, /* dst_mask */ FALSE), /* pcrel_offset */ EMPTY_HOWTO (90), /* unallocated */ EMPTY_HOWTO (91), EMPTY_HOWTO (92), EMPTY_HOWTO (93), HOWTO (R_ARM_PLT32_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_PLT32_ABS", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOT_ABS, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOT_ABS", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOT_PREL, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOT_PREL", /* name */ FALSE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_GOT_BREL12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOT_BREL12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_GOTOFF12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_GOTOFF12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ EMPTY_HOWTO (R_ARM_GOTRELAX), /* reserved for future GOT-load optimizations */ /* GNU extension to record C++ vtable member usage */ HOWTO (R_ARM_GNU_VTENTRY, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ _bfd_elf_rel_vtable_reloc_fn, /* special_function */ "R_ARM_GNU_VTENTRY", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ /* GNU extension to record C++ vtable hierarchy */ HOWTO (R_ARM_GNU_VTINHERIT, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont, /* complain_on_overflow */ NULL, /* special_function */ "R_ARM_GNU_VTINHERIT", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP11, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 11, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP11", /* name */ FALSE, /* partial_inplace */ 0x000007ff, /* src_mask */ 0x000007ff, /* dst_mask */ TRUE), /* pcrel_offset */ HOWTO (R_ARM_THM_JUMP8, /* type */ 1, /* rightshift */ 1, /* size (0 = byte, 1 = short, 2 = long) */ 8, /* bitsize */ TRUE, /* pc_relative */ 0, /* bitpos */ complain_overflow_signed, /* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_THM_JUMP8", /* name */ FALSE, /* partial_inplace */ 0x000000ff, /* src_mask */ 0x000000ff, /* dst_mask */ TRUE), /* pcrel_offset */ /* TLS relocations */ HOWTO (R_ARM_TLS_GD32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ NULL, /* special_function */ "R_ARM_TLS_GD32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LDM32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LDM32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LDO32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LDO32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_IE32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ NULL, /* special_function */ "R_ARM_TLS_IE32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LE32, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 32, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LE32", /* name */ TRUE, /* partial_inplace */ 0xffffffff, /* src_mask */ 0xffffffff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LDO12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LDO12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_LE12, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_LE12", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_TLS_IE12GP, /* type */ 0, /* rightshift */ 2, /* size (0 = byte, 1 = short, 2 = long) */ 12, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_bitfield,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_TLS_IE12GP", /* name */ FALSE, /* partial_inplace */ 0x00000fff, /* src_mask */ 0x00000fff, /* dst_mask */ FALSE), /* pcrel_offset */ }; /* 112-127 private relocations 128 R_ARM_ME_TOO, obsolete 129-255 unallocated in AAELF. 249-255 extended, currently unused, relocations: */ static reloc_howto_type elf32_arm_howto_table_2[4] = { HOWTO (R_ARM_RREL32, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RREL32", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_RABS32, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RABS32", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_RPC24, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RPC24", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE), /* pcrel_offset */ HOWTO (R_ARM_RBASE, /* type */ 0, /* rightshift */ 0, /* size (0 = byte, 1 = short, 2 = long) */ 0, /* bitsize */ FALSE, /* pc_relative */ 0, /* bitpos */ complain_overflow_dont,/* complain_on_overflow */ bfd_elf_generic_reloc, /* special_function */ "R_ARM_RBASE", /* name */ FALSE, /* partial_inplace */ 0, /* src_mask */ 0, /* dst_mask */ FALSE) /* pcrel_offset */ }; static reloc_howto_type * elf32_arm_howto_from_type (unsigned int r_type) { if (r_type < NUM_ELEM (elf32_arm_howto_table_1)) return &elf32_arm_howto_table_1[r_type]; if (r_type >= R_ARM_RREL32 && r_type < R_ARM_RREL32 + NUM_ELEM (elf32_arm_howto_table_2)) return &elf32_arm_howto_table_2[r_type - R_ARM_RREL32]; return NULL; } static void elf32_arm_info_to_howto (bfd * abfd ATTRIBUTE_UNUSED, arelent * bfd_reloc, Elf_Internal_Rela * elf_reloc) { unsigned int r_type; r_type = ELF32_R_TYPE (elf_reloc->r_info); bfd_reloc->howto = elf32_arm_howto_from_type (r_type); } struct elf32_arm_reloc_map { bfd_reloc_code_real_type bfd_reloc_val; unsigned char elf_reloc_val; }; /* All entries in this list must also be present in elf32_arm_howto_table. */ static const struct elf32_arm_reloc_map elf32_arm_reloc_map[] = { {BFD_RELOC_NONE, R_ARM_NONE}, {BFD_RELOC_ARM_PCREL_BRANCH, R_ARM_PC24}, {BFD_RELOC_ARM_PCREL_CALL, R_ARM_CALL}, {BFD_RELOC_ARM_PCREL_JUMP, R_ARM_JUMP24}, {BFD_RELOC_ARM_PCREL_BLX, R_ARM_XPC25}, {BFD_RELOC_THUMB_PCREL_BLX, R_ARM_THM_XPC22}, {BFD_RELOC_32, R_ARM_ABS32}, {BFD_RELOC_32_PCREL, R_ARM_REL32}, {BFD_RELOC_8, R_ARM_ABS8}, {BFD_RELOC_16, R_ARM_ABS16}, {BFD_RELOC_ARM_OFFSET_IMM, R_ARM_ABS12}, {BFD_RELOC_ARM_THUMB_OFFSET, R_ARM_THM_ABS5}, {BFD_RELOC_THUMB_PCREL_BRANCH25, R_ARM_THM_JUMP24}, {BFD_RELOC_THUMB_PCREL_BRANCH23, R_ARM_THM_CALL}, {BFD_RELOC_THUMB_PCREL_BRANCH12, R_ARM_THM_JUMP11}, {BFD_RELOC_THUMB_PCREL_BRANCH20, R_ARM_THM_JUMP19}, {BFD_RELOC_THUMB_PCREL_BRANCH9, R_ARM_THM_JUMP8}, {BFD_RELOC_THUMB_PCREL_BRANCH7, R_ARM_THM_JUMP6}, {BFD_RELOC_ARM_GLOB_DAT, R_ARM_GLOB_DAT}, {BFD_RELOC_ARM_JUMP_SLOT, R_ARM_JUMP_SLOT}, {BFD_RELOC_ARM_RELATIVE, R_ARM_RELATIVE}, {BFD_RELOC_ARM_GOTOFF, R_ARM_GOTOFF32}, {BFD_RELOC_ARM_GOTPC, R_ARM_GOTPC}, {BFD_RELOC_ARM_GOT32, R_ARM_GOT32}, {BFD_RELOC_ARM_PLT32, R_ARM_PLT32}, {BFD_RELOC_ARM_TARGET1, R_ARM_TARGET1}, {BFD_RELOC_ARM_ROSEGREL32, R_ARM_ROSEGREL32}, {BFD_RELOC_ARM_SBREL32, R_ARM_SBREL32}, {BFD_RELOC_ARM_PREL31, R_ARM_PREL31}, {BFD_RELOC_ARM_TARGET2, R_ARM_TARGET2}, {BFD_RELOC_ARM_PLT32, R_ARM_PLT32}, {BFD_RELOC_ARM_TLS_GD32, R_ARM_TLS_GD32}, {BFD_RELOC_ARM_TLS_LDO32, R_ARM_TLS_LDO32}, {BFD_RELOC_ARM_TLS_LDM32, R_ARM_TLS_LDM32}, {BFD_RELOC_ARM_TLS_DTPMOD32, R_ARM_TLS_DTPMOD32}, {BFD_RELOC_ARM_TLS_DTPOFF32, R_ARM_TLS_DTPOFF32}, {BFD_RELOC_ARM_TLS_TPOFF32, R_ARM_TLS_TPOFF32}, {BFD_RELOC_ARM_TLS_IE32, R_ARM_TLS_IE32}, {BFD_RELOC_ARM_TLS_LE32, R_ARM_TLS_LE32}, {BFD_RELOC_VTABLE_INHERIT, R_ARM_GNU_VTINHERIT}, {BFD_RELOC_VTABLE_ENTRY, R_ARM_GNU_VTENTRY}, {BFD_RELOC_ARM_MOVW, R_ARM_MOVW_ABS_NC}, {BFD_RELOC_ARM_MOVT, R_ARM_MOVT_ABS}, {BFD_RELOC_ARM_MOVW_PCREL, R_ARM_MOVW_PREL_NC}, {BFD_RELOC_ARM_MOVT_PCREL, R_ARM_MOVT_PREL}, {BFD_RELOC_ARM_THUMB_MOVW, R_ARM_THM_MOVW_ABS_NC}, {BFD_RELOC_ARM_THUMB_MOVT, R_ARM_THM_MOVT_ABS}, {BFD_RELOC_ARM_THUMB_MOVW_PCREL, R_ARM_THM_MOVW_PREL_NC}, {BFD_RELOC_ARM_THUMB_MOVT_PCREL, R_ARM_THM_MOVT_PREL}, {BFD_RELOC_ARM_ALU_PC_G0_NC, R_ARM_ALU_PC_G0_NC}, {BFD_RELOC_ARM_ALU_PC_G0, R_ARM_ALU_PC_G0}, {BFD_RELOC_ARM_ALU_PC_G1_NC, R_ARM_ALU_PC_G1_NC}, {BFD_RELOC_ARM_ALU_PC_G1, R_ARM_ALU_PC_G1}, {BFD_RELOC_ARM_ALU_PC_G2, R_ARM_ALU_PC_G2}, {BFD_RELOC_ARM_LDR_PC_G0, R_ARM_LDR_PC_G0}, {BFD_RELOC_ARM_LDR_PC_G1, R_ARM_LDR_PC_G1}, {BFD_RELOC_ARM_LDR_PC_G2, R_ARM_LDR_PC_G2}, {BFD_RELOC_ARM_LDRS_PC_G0, R_ARM_LDRS_PC_G0}, {BFD_RELOC_ARM_LDRS_PC_G1, R_ARM_LDRS_PC_G1}, {BFD_RELOC_ARM_LDRS_PC_G2, R_ARM_LDRS_PC_G2}, {BFD_RELOC_ARM_LDC_PC_G0, R_ARM_LDC_PC_G0}, {BFD_RELOC_ARM_LDC_PC_G1, R_ARM_LDC_PC_G1}, {BFD_RELOC_ARM_LDC_PC_G2, R_ARM_LDC_PC_G2}, {BFD_RELOC_ARM_ALU_SB_G0_NC, R_ARM_ALU_SB_G0_NC}, {BFD_RELOC_ARM_ALU_SB_G0, R_ARM_ALU_SB_G0}, {BFD_RELOC_ARM_ALU_SB_G1_NC, R_ARM_ALU_SB_G1_NC}, {BFD_RELOC_ARM_ALU_SB_G1, R_ARM_ALU_SB_G1}, {BFD_RELOC_ARM_ALU_SB_G2, R_ARM_ALU_SB_G2}, {BFD_RELOC_ARM_LDR_SB_G0, R_ARM_LDR_SB_G0}, {BFD_RELOC_ARM_LDR_SB_G1, R_ARM_LDR_SB_G1}, {BFD_RELOC_ARM_LDR_SB_G2, R_ARM_LDR_SB_G2}, {BFD_RELOC_ARM_LDRS_SB_G0, R_ARM_LDRS_SB_G0}, {BFD_RELOC_ARM_LDRS_SB_G1, R_ARM_LDRS_SB_G1}, {BFD_RELOC_ARM_LDRS_SB_G2, R_ARM_LDRS_SB_G2}, {BFD_RELOC_ARM_LDC_SB_G0, R_ARM_LDC_SB_G0}, {BFD_RELOC_ARM_LDC_SB_G1, R_ARM_LDC_SB_G1}, {BFD_RELOC_ARM_LDC_SB_G2, R_ARM_LDC_SB_G2} }; static reloc_howto_type * elf32_arm_reloc_type_lookup (bfd *abfd ATTRIBUTE_UNUSED, bfd_reloc_code_real_type code) { unsigned int i; for (i = 0; i < NUM_ELEM (elf32_arm_reloc_map); i ++) if (elf32_arm_reloc_map[i].bfd_reloc_val == code) return elf32_arm_howto_from_type (elf32_arm_reloc_map[i].elf_reloc_val); return NULL; } static reloc_howto_type * elf32_arm_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED, const char *r_name) { unsigned int i; for (i = 0; i < (sizeof (elf32_arm_howto_table_1) / sizeof (elf32_arm_howto_table_1[0])); i++) if (elf32_arm_howto_table_1[i].name != NULL && strcasecmp (elf32_arm_howto_table_1[i].name, r_name) == 0) return &elf32_arm_howto_table_1[i]; for (i = 0; i < (sizeof (elf32_arm_howto_table_2) / sizeof (elf32_arm_howto_table_2[0])); i++) if (elf32_arm_howto_table_2[i].name != NULL && strcasecmp (elf32_arm_howto_table_2[i].name, r_name) == 0) return &elf32_arm_howto_table_2[i]; return NULL; } /* Support for core dump NOTE sections */ static bfd_boolean elf32_arm_nabi_grok_prstatus (bfd *abfd, Elf_Internal_Note *note) { int offset; size_t size; switch (note->descsz) { default: return FALSE; case 148: /* Linux/ARM 32-bit*/ /* pr_cursig */ elf_tdata (abfd)->core_signal = bfd_get_16 (abfd, note->descdata + 12); /* pr_pid */ elf_tdata (abfd)->core_pid = bfd_get_32 (abfd, note->descdata + 24); /* pr_reg */ offset = 72; size = 72; break; case 96: /* FreeBSD/ARM */ /* pr_cursig */ if (elf_tdata(abfd)->core_signal == 0) elf_tdata (abfd)->core_signal = ((int *)(note->descdata))[5]; /* pr_pid */ elf_tdata (abfd)->core_pid = ((int *)(note->descdata))[6]; /* pr_reg */ offset = 28; size = 68; break; } /* Make a ".reg/999" section. */ return _bfd_elfcore_make_pseudosection (abfd, ".reg", size, note->descpos + offset); } static bfd_boolean elf32_arm_nabi_grok_psinfo (bfd *abfd, Elf_Internal_Note *note) { switch (note->descsz) { default: return FALSE; case 124: /* Linux/ARM elf_prpsinfo */ elf_tdata (abfd)->core_program = _bfd_elfcore_strndup (abfd, note->descdata + 28, 16); elf_tdata (abfd)->core_command = _bfd_elfcore_strndup (abfd, note->descdata + 44, 80); } /* Note that for some reason, a spurious space is tacked onto the end of the args in some (at least one anyway) implementations, so strip it off if it exists. */ { char *command = elf_tdata (abfd)->core_command; int n = strlen (command); if (0 < n && command[n - 1] == ' ') command[n - 1] = '\0'; } return TRUE; } #define TARGET_LITTLE_SYM bfd_elf32_littlearm_vec #define TARGET_LITTLE_NAME "elf32-littlearm" #define TARGET_BIG_SYM bfd_elf32_bigarm_vec #define TARGET_BIG_NAME "elf32-bigarm" #define elf_backend_grok_prstatus elf32_arm_nabi_grok_prstatus #define elf_backend_grok_psinfo elf32_arm_nabi_grok_psinfo typedef unsigned long int insn32; typedef unsigned short int insn16; /* In lieu of proper flags, assume all EABIv4 or later objects are interworkable. */ #define INTERWORK_FLAG(abfd) \ (EF_ARM_EABI_VERSION (elf_elfheader (abfd)->e_flags) >= EF_ARM_EABI_VER4 \ || (elf_elfheader (abfd)->e_flags & EF_ARM_INTERWORK)) /* The linker script knows the section names for placement. The entry_names are used to do simple name mangling on the stubs. Given a function name, and its type, the stub can be found. The name can be changed. The only requirement is the %s be present. */ #define THUMB2ARM_GLUE_SECTION_NAME ".glue_7t" #define THUMB2ARM_GLUE_ENTRY_NAME "__%s_from_thumb" #define ARM2THUMB_GLUE_SECTION_NAME ".glue_7" #define ARM2THUMB_GLUE_ENTRY_NAME "__%s_from_arm" #define VFP11_ERRATUM_VENEER_SECTION_NAME ".vfp11_veneer" #define VFP11_ERRATUM_VENEER_ENTRY_NAME "__vfp11_veneer_%x" /* The name of the dynamic interpreter. This is put in the .interp section. */ #define ELF_DYNAMIC_INTERPRETER "/usr/lib/ld.so.1" #ifdef FOUR_WORD_PLT /* The first entry in a procedure linkage table looks like this. It is set up so that any shared library function that is called before the relocation has been set up calls the dynamic linker first. */ static const bfd_vma elf32_arm_plt0_entry [] = { 0xe52de004, /* str lr, [sp, #-4]! */ 0xe59fe010, /* ldr lr, [pc, #16] */ 0xe08fe00e, /* add lr, pc, lr */ 0xe5bef008, /* ldr pc, [lr, #8]! */ }; /* Subsequent entries in a procedure linkage table look like this. */ static const bfd_vma elf32_arm_plt_entry [] = { 0xe28fc600, /* add ip, pc, #NN */ 0xe28cca00, /* add ip, ip, #NN */ 0xe5bcf000, /* ldr pc, [ip, #NN]! */ 0x00000000, /* unused */ }; #else /* The first entry in a procedure linkage table looks like this. It is set up so that any shared library function that is called before the relocation has been set up calls the dynamic linker first. */ static const bfd_vma elf32_arm_plt0_entry [] = { 0xe52de004, /* str lr, [sp, #-4]! */ 0xe59fe004, /* ldr lr, [pc, #4] */ 0xe08fe00e, /* add lr, pc, lr */ 0xe5bef008, /* ldr pc, [lr, #8]! */ 0x00000000, /* &GOT[0] - . */ }; /* Subsequent entries in a procedure linkage table look like this. */ static const bfd_vma elf32_arm_plt_entry [] = { 0xe28fc600, /* add ip, pc, #0xNN00000 */ 0xe28cca00, /* add ip, ip, #0xNN000 */ 0xe5bcf000, /* ldr pc, [ip, #0xNNN]! */ }; #endif /* The format of the first entry in the procedure linkage table for a VxWorks executable. */ static const bfd_vma elf32_arm_vxworks_exec_plt0_entry[] = { 0xe52dc008, /* str ip,[sp,#-8]! */ 0xe59fc000, /* ldr ip,[pc] */ 0xe59cf008, /* ldr pc,[ip,#8] */ 0x00000000, /* .long _GLOBAL_OFFSET_TABLE_ */ }; /* The format of subsequent entries in a VxWorks executable. */ static const bfd_vma elf32_arm_vxworks_exec_plt_entry[] = { 0xe59fc000, /* ldr ip,[pc] */ 0xe59cf000, /* ldr pc,[ip] */ 0x00000000, /* .long @got */ 0xe59fc000, /* ldr ip,[pc] */ 0xea000000, /* b _PLT */ 0x00000000, /* .long @pltindex*sizeof(Elf32_Rela) */ }; /* The format of entries in a VxWorks shared library. */ static const bfd_vma elf32_arm_vxworks_shared_plt_entry[] = { 0xe59fc000, /* ldr ip,[pc] */ 0xe79cf009, /* ldr pc,[ip,r9] */ 0x00000000, /* .long @got */ 0xe59fc000, /* ldr ip,[pc] */ 0xe599f008, /* ldr pc,[r9,#8] */ 0x00000000, /* .long @pltindex*sizeof(Elf32_Rela) */ }; /* An initial stub used if the PLT entry is referenced from Thumb code. */ #define PLT_THUMB_STUB_SIZE 4 static const bfd_vma elf32_arm_plt_thumb_stub [] = { 0x4778, /* bx pc */ 0x46c0 /* nop */ }; /* The entries in a PLT when using a DLL-based target with multiple address spaces. */ static const bfd_vma elf32_arm_symbian_plt_entry [] = { 0xe51ff004, /* ldr pc, [pc, #-4] */ 0x00000000, /* dcd R_ARM_GLOB_DAT(X) */ }; /* Used to build a map of a section. This is required for mixed-endian code/data. */ typedef struct elf32_elf_section_map { bfd_vma vma; char type; } elf32_arm_section_map; /* Information about a VFP11 erratum veneer, or a branch to such a veneer. */ typedef enum { VFP11_ERRATUM_BRANCH_TO_ARM_VENEER, VFP11_ERRATUM_BRANCH_TO_THUMB_VENEER, VFP11_ERRATUM_ARM_VENEER, VFP11_ERRATUM_THUMB_VENEER } elf32_vfp11_erratum_type; typedef struct elf32_vfp11_erratum_list { struct elf32_vfp11_erratum_list *next; bfd_vma vma; union { struct { struct elf32_vfp11_erratum_list *veneer; unsigned int vfp_insn; } b; struct { struct elf32_vfp11_erratum_list *branch; unsigned int id; } v; } u; elf32_vfp11_erratum_type type; } elf32_vfp11_erratum_list; typedef struct _arm_elf_section_data { struct bfd_elf_section_data elf; unsigned int mapcount; unsigned int mapsize; elf32_arm_section_map *map; unsigned int erratumcount; elf32_vfp11_erratum_list *erratumlist; } _arm_elf_section_data; #define elf32_arm_section_data(sec) \ ((_arm_elf_section_data *) elf_section_data (sec)) /* The size of the thread control block. */ #define TCB_SIZE 8 struct elf32_arm_obj_tdata { struct elf_obj_tdata root; /* tls_type for each local got entry. */ char *local_got_tls_type; /* Zero to warn when linking objects with incompatible enum sizes. */ int no_enum_size_warning; }; #define elf32_arm_tdata(abfd) \ ((struct elf32_arm_obj_tdata *) (abfd)->tdata.any) #define elf32_arm_local_got_tls_type(abfd) \ (elf32_arm_tdata (abfd)->local_got_tls_type) static bfd_boolean elf32_arm_mkobject (bfd *abfd) { if (abfd->tdata.any == NULL) { bfd_size_type amt = sizeof (struct elf32_arm_obj_tdata); abfd->tdata.any = bfd_zalloc (abfd, amt); if (abfd->tdata.any == NULL) return FALSE; } return bfd_elf_mkobject (abfd); } /* The ARM linker needs to keep track of the number of relocs that it decides to copy in check_relocs for each symbol. This is so that it can discard PC relative relocs if it doesn't need them when linking with -Bsymbolic. We store the information in a field extending the regular ELF linker hash table. */ /* This structure keeps track of the number of relocs we have copied for a given symbol. */ struct elf32_arm_relocs_copied { /* Next section. */ struct elf32_arm_relocs_copied * next; /* A section in dynobj. */ asection * section; /* Number of relocs copied in this section. */ bfd_size_type count; /* Number of PC-relative relocs copied in this section. */ bfd_size_type pc_count; }; #define elf32_arm_hash_entry(ent) ((struct elf32_arm_link_hash_entry *)(ent)) /* Arm ELF linker hash entry. */ struct elf32_arm_link_hash_entry { struct elf_link_hash_entry root; /* Number of PC relative relocs copied for this symbol. */ struct elf32_arm_relocs_copied * relocs_copied; /* We reference count Thumb references to a PLT entry separately, so that we can emit the Thumb trampoline only if needed. */ bfd_signed_vma plt_thumb_refcount; /* Since PLT entries have variable size if the Thumb prologue is used, we need to record the index into .got.plt instead of recomputing it from the PLT offset. */ bfd_signed_vma plt_got_offset; #define GOT_UNKNOWN 0 #define GOT_NORMAL 1 #define GOT_TLS_GD 2 #define GOT_TLS_IE 4 unsigned char tls_type; /* The symbol marking the real symbol location for exported thumb symbols with Arm stubs. */ struct elf_link_hash_entry *export_glue; }; /* Traverse an arm ELF linker hash table. */ #define elf32_arm_link_hash_traverse(table, func, info) \ (elf_link_hash_traverse \ (&(table)->root, \ (bfd_boolean (*) (struct elf_link_hash_entry *, void *)) (func), \ (info))) /* Get the ARM elf linker hash table from a link_info structure. */ #define elf32_arm_hash_table(info) \ ((struct elf32_arm_link_hash_table *) ((info)->hash)) /* ARM ELF linker hash table. */ struct elf32_arm_link_hash_table { /* The main hash table. */ struct elf_link_hash_table root; /* The size in bytes of the section containing the Thumb-to-ARM glue. */ bfd_size_type thumb_glue_size; /* The size in bytes of the section containing the ARM-to-Thumb glue. */ bfd_size_type arm_glue_size; /* The size in bytes of the section containing glue for VFP11 erratum veneers. */ bfd_size_type vfp11_erratum_glue_size; /* An arbitrary input BFD chosen to hold the glue sections. */ bfd * bfd_of_glue_owner; /* Nonzero to output a BE8 image. */ int byteswap_code; /* Zero if R_ARM_TARGET1 means R_ARM_ABS32. Nonzero if R_ARM_TARGET1 means R_ARM_REL32. */ int target1_is_rel; /* The relocation to use for R_ARM_TARGET2 relocations. */ int target2_reloc; /* Nonzero to fix BX instructions for ARMv4 targets. */ int fix_v4bx; /* Nonzero if the ARM/Thumb BLX instructions are available for use. */ int use_blx; /* What sort of code sequences we should look for which may trigger the VFP11 denorm erratum. */ bfd_arm_vfp11_fix vfp11_fix; /* Global counter for the number of fixes we have emitted. */ int num_vfp11_fixes; /* Nonzero to force PIC branch veneers. */ int pic_veneer; /* The number of bytes in the initial entry in the PLT. */ bfd_size_type plt_header_size; /* The number of bytes in the subsequent PLT etries. */ bfd_size_type plt_entry_size; /* True if the target system is VxWorks. */ int vxworks_p; /* True if the target system is Symbian OS. */ int symbian_p; /* True if the target uses REL relocations. */ int use_rel; /* Short-cuts to get to dynamic linker sections. */ asection *sgot; asection *sgotplt; asection *srelgot; asection *splt; asection *srelplt; asection *sdynbss; asection *srelbss; /* The (unloaded but important) VxWorks .rela.plt.unloaded section. */ asection *srelplt2; /* Data for R_ARM_TLS_LDM32 relocations. */ union { bfd_signed_vma refcount; bfd_vma offset; } tls_ldm_got; /* Small local sym to section mapping cache. */ struct sym_sec_cache sym_sec; /* For convenience in allocate_dynrelocs. */ bfd * obfd; }; /* Create an entry in an ARM ELF linker hash table. */ static struct bfd_hash_entry * elf32_arm_link_hash_newfunc (struct bfd_hash_entry * entry, struct bfd_hash_table * table, const char * string) { struct elf32_arm_link_hash_entry * ret = (struct elf32_arm_link_hash_entry *) entry; /* Allocate the structure if it has not already been allocated by a subclass. */ if (ret == (struct elf32_arm_link_hash_entry *) NULL) ret = bfd_hash_allocate (table, sizeof (struct elf32_arm_link_hash_entry)); if (ret == NULL) return (struct bfd_hash_entry *) ret; /* Call the allocation method of the superclass. */ ret = ((struct elf32_arm_link_hash_entry *) _bfd_elf_link_hash_newfunc ((struct bfd_hash_entry *) ret, table, string)); if (ret != NULL) { ret->relocs_copied = NULL; ret->tls_type = GOT_UNKNOWN; ret->plt_thumb_refcount = 0; ret->plt_got_offset = -1; ret->export_glue = NULL; } return (struct bfd_hash_entry *) ret; } /* Return true if NAME is the name of the relocation section associated with S. */ static bfd_boolean reloc_section_p (struct elf32_arm_link_hash_table *htab, const char *name, asection *s) { if (htab->use_rel) return CONST_STRNEQ (name, ".rel") && strcmp (s->name, name + 4) == 0; else return CONST_STRNEQ (name, ".rela") && strcmp (s->name, name + 5) == 0; } /* Create .got, .gotplt, and .rel(a).got sections in DYNOBJ, and set up shortcuts to them in our hash table. */ static bfd_boolean create_got_section (bfd *dynobj, struct bfd_link_info *info) { struct elf32_arm_link_hash_table *htab; htab = elf32_arm_hash_table (info); /* BPABI objects never have a GOT, or associated sections. */ if (htab->symbian_p) return TRUE; if (! _bfd_elf_create_got_section (dynobj, info)) return FALSE; htab->sgot = bfd_get_section_by_name (dynobj, ".got"); htab->sgotplt = bfd_get_section_by_name (dynobj, ".got.plt"); if (!htab->sgot || !htab->sgotplt) abort (); htab->srelgot = bfd_make_section_with_flags (dynobj, RELOC_SECTION (htab, ".got"), (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED | SEC_READONLY)); if (htab->srelgot == NULL || ! bfd_set_section_alignment (dynobj, htab->srelgot, 2)) return FALSE; return TRUE; } /* Create .plt, .rel(a).plt, .got, .got.plt, .rel(a).got, .dynbss, and .rel(a).bss sections in DYNOBJ, and set up shortcuts to them in our hash table. */ static bfd_boolean elf32_arm_create_dynamic_sections (bfd *dynobj, struct bfd_link_info *info) { struct elf32_arm_link_hash_table *htab; htab = elf32_arm_hash_table (info); if (!htab->sgot && !create_got_section (dynobj, info)) return FALSE; if (!_bfd_elf_create_dynamic_sections (dynobj, info)) return FALSE; htab->splt = bfd_get_section_by_name (dynobj, ".plt"); htab->srelplt = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".plt")); htab->sdynbss = bfd_get_section_by_name (dynobj, ".dynbss"); if (!info->shared) htab->srelbss = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".bss")); if (htab->vxworks_p) { if (!elf_vxworks_create_dynamic_sections (dynobj, info, &htab->srelplt2)) return FALSE; if (info->shared) { htab->plt_header_size = 0; htab->plt_entry_size = 4 * ARRAY_SIZE (elf32_arm_vxworks_shared_plt_entry); } else { htab->plt_header_size = 4 * ARRAY_SIZE (elf32_arm_vxworks_exec_plt0_entry); htab->plt_entry_size = 4 * ARRAY_SIZE (elf32_arm_vxworks_exec_plt_entry); } } if (!htab->splt || !htab->srelplt || !htab->sdynbss || (!info->shared && !htab->srelbss)) abort (); return TRUE; } /* Copy the extra info we tack onto an elf_link_hash_entry. */ static void elf32_arm_copy_indirect_symbol (struct bfd_link_info *info, struct elf_link_hash_entry *dir, struct elf_link_hash_entry *ind) { struct elf32_arm_link_hash_entry *edir, *eind; edir = (struct elf32_arm_link_hash_entry *) dir; eind = (struct elf32_arm_link_hash_entry *) ind; if (eind->relocs_copied != NULL) { if (edir->relocs_copied != NULL) { struct elf32_arm_relocs_copied **pp; struct elf32_arm_relocs_copied *p; /* Add reloc counts against the indirect sym to the direct sym list. Merge any entries against the same section. */ for (pp = &eind->relocs_copied; (p = *pp) != NULL; ) { struct elf32_arm_relocs_copied *q; for (q = edir->relocs_copied; q != NULL; q = q->next) if (q->section == p->section) { q->pc_count += p->pc_count; q->count += p->count; *pp = p->next; break; } if (q == NULL) pp = &p->next; } *pp = edir->relocs_copied; } edir->relocs_copied = eind->relocs_copied; eind->relocs_copied = NULL; } if (ind->root.type == bfd_link_hash_indirect) { /* Copy over PLT info. */ edir->plt_thumb_refcount += eind->plt_thumb_refcount; eind->plt_thumb_refcount = 0; if (dir->got.refcount <= 0) { edir->tls_type = eind->tls_type; eind->tls_type = GOT_UNKNOWN; } } _bfd_elf_link_hash_copy_indirect (info, dir, ind); } /* Create an ARM elf linker hash table. */ static struct bfd_link_hash_table * elf32_arm_link_hash_table_create (bfd *abfd) { struct elf32_arm_link_hash_table *ret; bfd_size_type amt = sizeof (struct elf32_arm_link_hash_table); ret = bfd_malloc (amt); if (ret == NULL) return NULL; if (!_bfd_elf_link_hash_table_init (& ret->root, abfd, elf32_arm_link_hash_newfunc, sizeof (struct elf32_arm_link_hash_entry))) { free (ret); return NULL; } ret->sgot = NULL; ret->sgotplt = NULL; ret->srelgot = NULL; ret->splt = NULL; ret->srelplt = NULL; ret->sdynbss = NULL; ret->srelbss = NULL; ret->srelplt2 = NULL; ret->thumb_glue_size = 0; ret->arm_glue_size = 0; ret->vfp11_fix = BFD_ARM_VFP11_FIX_NONE; ret->vfp11_erratum_glue_size = 0; ret->num_vfp11_fixes = 0; ret->bfd_of_glue_owner = NULL; ret->byteswap_code = 0; ret->target1_is_rel = 0; ret->target2_reloc = R_ARM_NONE; #ifdef FOUR_WORD_PLT ret->plt_header_size = 16; ret->plt_entry_size = 16; #else ret->plt_header_size = 20; ret->plt_entry_size = 12; #endif ret->fix_v4bx = 0; ret->use_blx = 0; ret->vxworks_p = 0; ret->symbian_p = 0; ret->use_rel = 1; ret->sym_sec.abfd = NULL; ret->obfd = abfd; ret->tls_ldm_got.refcount = 0; return &ret->root.root; } /* Locate the Thumb encoded calling stub for NAME. */ static struct elf_link_hash_entry * find_thumb_glue (struct bfd_link_info *link_info, const char *name, char **error_message) { char *tmp_name; struct elf_link_hash_entry *hash; struct elf32_arm_link_hash_table *hash_table; /* We need a pointer to the armelf specific hash table. */ hash_table = elf32_arm_hash_table (link_info); tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name); hash = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, TRUE); if (hash == NULL) asprintf (error_message, _("unable to find THUMB glue '%s' for '%s'"), tmp_name, name); free (tmp_name); return hash; } /* Locate the ARM encoded calling stub for NAME. */ static struct elf_link_hash_entry * find_arm_glue (struct bfd_link_info *link_info, const char *name, char **error_message) { char *tmp_name; struct elf_link_hash_entry *myh; struct elf32_arm_link_hash_table *hash_table; /* We need a pointer to the elfarm specific hash table. */ hash_table = elf32_arm_hash_table (link_info); tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, ARM2THUMB_GLUE_ENTRY_NAME, name); myh = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, TRUE); if (myh == NULL) asprintf (error_message, _("unable to find ARM glue '%s' for '%s'"), tmp_name, name); free (tmp_name); return myh; } /* ARM->Thumb glue (static images): .arm __func_from_arm: ldr r12, __func_addr bx r12 __func_addr: .word func @ behave as if you saw a ARM_32 reloc. (v5t static images) .arm __func_from_arm: ldr pc, __func_addr __func_addr: .word func @ behave as if you saw a ARM_32 reloc. (relocatable images) .arm __func_from_arm: ldr r12, __func_offset add r12, r12, pc bx r12 __func_offset: .word func - . */ #define ARM2THUMB_STATIC_GLUE_SIZE 12 static const insn32 a2t1_ldr_insn = 0xe59fc000; static const insn32 a2t2_bx_r12_insn = 0xe12fff1c; static const insn32 a2t3_func_addr_insn = 0x00000001; #define ARM2THUMB_V5_STATIC_GLUE_SIZE 8 static const insn32 a2t1v5_ldr_insn = 0xe51ff004; static const insn32 a2t2v5_func_addr_insn = 0x00000001; #define ARM2THUMB_PIC_GLUE_SIZE 16 static const insn32 a2t1p_ldr_insn = 0xe59fc004; static const insn32 a2t2p_add_pc_insn = 0xe08cc00f; static const insn32 a2t3p_bx_r12_insn = 0xe12fff1c; /* Thumb->ARM: Thumb->(non-interworking aware) ARM .thumb .thumb .align 2 .align 2 __func_from_thumb: __func_from_thumb: bx pc push {r6, lr} nop ldr r6, __func_addr .arm mov lr, pc __func_change_to_arm: bx r6 b func .arm __func_back_to_thumb: ldmia r13! {r6, lr} bx lr __func_addr: .word func */ #define THUMB2ARM_GLUE_SIZE 8 static const insn16 t2a1_bx_pc_insn = 0x4778; static const insn16 t2a2_noop_insn = 0x46c0; static const insn32 t2a3_b_insn = 0xea000000; #define VFP11_ERRATUM_VENEER_SIZE 8 #ifndef ELFARM_NABI_C_INCLUDED bfd_boolean bfd_elf32_arm_allocate_interworking_sections (struct bfd_link_info * info) { asection * s; bfd_byte * foo; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); if (globals->arm_glue_size != 0) { BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); foo = bfd_alloc (globals->bfd_of_glue_owner, globals->arm_glue_size); BFD_ASSERT (s->size == globals->arm_glue_size); s->contents = foo; } if (globals->thumb_glue_size != 0) { BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); foo = bfd_alloc (globals->bfd_of_glue_owner, globals->thumb_glue_size); BFD_ASSERT (s->size == globals->thumb_glue_size); s->contents = foo; } if (globals->vfp11_erratum_glue_size != 0) { BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME); BFD_ASSERT (s != NULL); foo = bfd_alloc (globals->bfd_of_glue_owner, globals->vfp11_erratum_glue_size); BFD_ASSERT (s->size == globals->vfp11_erratum_glue_size); s->contents = foo; } return TRUE; } /* Allocate space and symbols for calling a Thumb function from Arm mode. returns the symbol identifying teh stub. */ static struct elf_link_hash_entry * record_arm_to_thumb_glue (struct bfd_link_info * link_info, struct elf_link_hash_entry * h) { const char * name = h->root.root.string; asection * s; char * tmp_name; struct elf_link_hash_entry * myh; struct bfd_link_hash_entry * bh; struct elf32_arm_link_hash_table * globals; bfd_vma val; bfd_size_type size; globals = elf32_arm_hash_table (link_info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (ARM2THUMB_GLUE_ENTRY_NAME) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, ARM2THUMB_GLUE_ENTRY_NAME, name); myh = elf_link_hash_lookup (&(globals)->root, tmp_name, FALSE, FALSE, TRUE); if (myh != NULL) { /* We've already seen this guy. */ free (tmp_name); return myh; } /* The only trick here is using hash_table->arm_glue_size as the value. Even though the section isn't allocated yet, this is where we will be putting it. */ bh = NULL; val = globals->arm_glue_size + 1; _bfd_generic_link_add_one_symbol (link_info, globals->bfd_of_glue_owner, tmp_name, BSF_GLOBAL, s, val, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC); myh->forced_local = 1; free (tmp_name); if (link_info->shared || globals->root.is_relocatable_executable || globals->pic_veneer) size = ARM2THUMB_PIC_GLUE_SIZE; else if (globals->use_blx) size = ARM2THUMB_V5_STATIC_GLUE_SIZE; else size = ARM2THUMB_STATIC_GLUE_SIZE; s->size += size; globals->arm_glue_size += size; return myh; } static void record_thumb_to_arm_glue (struct bfd_link_info *link_info, struct elf_link_hash_entry *h) { const char *name = h->root.root.string; asection *s; char *tmp_name; struct elf_link_hash_entry *myh; struct bfd_link_hash_entry *bh; struct elf32_arm_link_hash_table *hash_table; bfd_vma val; hash_table = elf32_arm_hash_table (link_info); BFD_ASSERT (hash_table != NULL); BFD_ASSERT (hash_table->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (hash_table->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (THUMB2ARM_GLUE_ENTRY_NAME) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, THUMB2ARM_GLUE_ENTRY_NAME, name); myh = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, TRUE); if (myh != NULL) { /* We've already seen this guy. */ free (tmp_name); return; } bh = NULL; val = hash_table->thumb_glue_size + 1; _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner, tmp_name, BSF_GLOBAL, s, val, NULL, TRUE, FALSE, &bh); /* If we mark it 'Thumb', the disassembler will do a better job. */ myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_ARM_TFUNC); myh->forced_local = 1; free (tmp_name); #define CHANGE_TO_ARM "__%s_change_to_arm" #define BACK_FROM_ARM "__%s_back_from_arm" /* Allocate another symbol to mark where we switch to Arm mode. */ tmp_name = bfd_malloc ((bfd_size_type) strlen (name) + strlen (CHANGE_TO_ARM) + 1); BFD_ASSERT (tmp_name); sprintf (tmp_name, CHANGE_TO_ARM, name); bh = NULL; val = hash_table->thumb_glue_size + 4, _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner, tmp_name, BSF_LOCAL, s, val, NULL, TRUE, FALSE, &bh); free (tmp_name); s->size += THUMB2ARM_GLUE_SIZE; hash_table->thumb_glue_size += THUMB2ARM_GLUE_SIZE; return; } /* Add an entry to the code/data map for section SEC. */ static void elf32_arm_section_map_add (asection *sec, char type, bfd_vma vma) { struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec); unsigned int newidx; if (sec_data->map == NULL) { sec_data->map = bfd_malloc (sizeof (elf32_arm_section_map)); sec_data->mapcount = 0; sec_data->mapsize = 1; } newidx = sec_data->mapcount++; if (sec_data->mapcount > sec_data->mapsize) { sec_data->mapsize *= 2; sec_data->map = bfd_realloc (sec_data->map, sec_data->mapsize * sizeof (elf32_arm_section_map)); } sec_data->map[newidx].vma = vma; sec_data->map[newidx].type = type; } /* Record information about a VFP11 denorm-erratum veneer. Only ARM-mode veneers are handled for now. */ static bfd_vma record_vfp11_erratum_veneer (struct bfd_link_info *link_info, elf32_vfp11_erratum_list *branch, bfd *branch_bfd, asection *branch_sec, unsigned int offset) { asection *s; struct elf32_arm_link_hash_table *hash_table; char *tmp_name; struct elf_link_hash_entry *myh; struct bfd_link_hash_entry *bh; bfd_vma val; struct _arm_elf_section_data *sec_data; int errcount; elf32_vfp11_erratum_list *newerr; hash_table = elf32_arm_hash_table (link_info); BFD_ASSERT (hash_table != NULL); BFD_ASSERT (hash_table->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (hash_table->bfd_of_glue_owner, VFP11_ERRATUM_VENEER_SECTION_NAME); sec_data = elf32_arm_section_data (s); BFD_ASSERT (s != NULL); tmp_name = bfd_malloc ((bfd_size_type) strlen (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10); BFD_ASSERT (tmp_name); sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME, hash_table->num_vfp11_fixes); myh = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE); BFD_ASSERT (myh == NULL); bh = NULL; val = hash_table->vfp11_erratum_glue_size; _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner, tmp_name, BSF_FUNCTION | BSF_LOCAL, s, val, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC); myh->forced_local = 1; /* Link veneer back to calling location. */ errcount = ++(sec_data->erratumcount); newerr = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list)); newerr->type = VFP11_ERRATUM_ARM_VENEER; newerr->vma = -1; newerr->u.v.branch = branch; newerr->u.v.id = hash_table->num_vfp11_fixes; branch->u.b.veneer = newerr; newerr->next = sec_data->erratumlist; sec_data->erratumlist = newerr; /* A symbol for the return from the veneer. */ sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME "_r", hash_table->num_vfp11_fixes); myh = elf_link_hash_lookup (&(hash_table)->root, tmp_name, FALSE, FALSE, FALSE); if (myh != NULL) abort (); bh = NULL; val = offset + 4; _bfd_generic_link_add_one_symbol (link_info, branch_bfd, tmp_name, BSF_LOCAL, branch_sec, val, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_FUNC); myh->forced_local = 1; free (tmp_name); /* Generate a mapping symbol for the veneer section, and explicitly add an entry for that symbol to the code/data map for the section. */ if (hash_table->vfp11_erratum_glue_size == 0) { bh = NULL; /* FIXME: Creates an ARM symbol. Thumb mode will need attention if it ever requires this erratum fix. */ _bfd_generic_link_add_one_symbol (link_info, hash_table->bfd_of_glue_owner, "$a", BSF_LOCAL, s, 0, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE); myh->forced_local = 1; /* The elf32_arm_init_maps function only cares about symbols from input BFDs. We must make a note of this generated mapping symbol ourselves so that code byteswapping works properly in elf32_arm_write_section. */ elf32_arm_section_map_add (s, 'a', 0); } s->size += VFP11_ERRATUM_VENEER_SIZE; hash_table->vfp11_erratum_glue_size += VFP11_ERRATUM_VENEER_SIZE; hash_table->num_vfp11_fixes++; /* The offset of the veneer. */ return val; } /* Add the glue sections to ABFD. This function is called from the linker scripts in ld/emultempl/{armelf}.em. */ bfd_boolean bfd_elf32_arm_add_glue_sections_to_bfd (bfd *abfd, struct bfd_link_info *info) { flagword flags; asection *sec; /* If we are only performing a partial link do not bother adding the glue. */ if (info->relocatable) return TRUE; sec = bfd_get_section_by_name (abfd, ARM2THUMB_GLUE_SECTION_NAME); if (sec == NULL) { /* Note: we do not include the flag SEC_LINKER_CREATED, as this will prevent elf_link_input_bfd() from processing the contents of this section. */ flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY); sec = bfd_make_section_with_flags (abfd, ARM2THUMB_GLUE_SECTION_NAME, flags); if (sec == NULL || !bfd_set_section_alignment (abfd, sec, 2)) return FALSE; /* Set the gc mark to prevent the section from being removed by garbage collection, despite the fact that no relocs refer to this section. */ sec->gc_mark = 1; } sec = bfd_get_section_by_name (abfd, THUMB2ARM_GLUE_SECTION_NAME); if (sec == NULL) { flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY); sec = bfd_make_section_with_flags (abfd, THUMB2ARM_GLUE_SECTION_NAME, flags); if (sec == NULL || !bfd_set_section_alignment (abfd, sec, 2)) return FALSE; sec->gc_mark = 1; } sec = bfd_get_section_by_name (abfd, VFP11_ERRATUM_VENEER_SECTION_NAME); if (sec == NULL) { flags = (SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_CODE | SEC_READONLY); sec = bfd_make_section_with_flags (abfd, VFP11_ERRATUM_VENEER_SECTION_NAME, flags); if (sec == NULL || !bfd_set_section_alignment (abfd, sec, 2)) return FALSE; sec->gc_mark = 1; } return TRUE; } /* Select a BFD to be used to hold the sections used by the glue code. This function is called from the linker scripts in ld/emultempl/ {armelf/pe}.em */ bfd_boolean bfd_elf32_arm_get_bfd_for_interworking (bfd *abfd, struct bfd_link_info *info) { struct elf32_arm_link_hash_table *globals; /* If we are only performing a partial link do not bother getting a bfd to hold the glue. */ if (info->relocatable) return TRUE; /* Make sure we don't attach the glue sections to a dynamic object. */ BFD_ASSERT (!(abfd->flags & DYNAMIC)); globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); if (globals->bfd_of_glue_owner != NULL) return TRUE; /* Save the bfd for later use. */ globals->bfd_of_glue_owner = abfd; return TRUE; } static void check_use_blx(struct elf32_arm_link_hash_table *globals) { if (bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC, Tag_CPU_arch) > 2) globals->use_blx = 1; } bfd_boolean bfd_elf32_arm_process_before_allocation (bfd *abfd, struct bfd_link_info *link_info) { Elf_Internal_Shdr *symtab_hdr; Elf_Internal_Rela *internal_relocs = NULL; Elf_Internal_Rela *irel, *irelend; bfd_byte *contents = NULL; asection *sec; struct elf32_arm_link_hash_table *globals; /* If we are only performing a partial link do not bother to construct any glue. */ if (link_info->relocatable) return TRUE; /* Here we have a bfd that is to be included on the link. We have a hook to do reloc rummaging, before section sizes are nailed down. */ globals = elf32_arm_hash_table (link_info); check_use_blx (globals); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); if (globals->byteswap_code && !bfd_big_endian (abfd)) { _bfd_error_handler (_("%B: BE8 images only valid in big-endian mode."), abfd); return FALSE; } /* Rummage around all the relocs and map the glue vectors. */ sec = abfd->sections; if (sec == NULL) return TRUE; for (; sec != NULL; sec = sec->next) { if (sec->reloc_count == 0) continue; if ((sec->flags & SEC_EXCLUDE) != 0) continue; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; /* Load the relocs. */ internal_relocs = _bfd_elf_link_read_relocs (abfd, sec, (void *) NULL, (Elf_Internal_Rela *) NULL, FALSE); if (internal_relocs == NULL) goto error_return; irelend = internal_relocs + sec->reloc_count; for (irel = internal_relocs; irel < irelend; irel++) { long r_type; unsigned long r_index; struct elf_link_hash_entry *h; r_type = ELF32_R_TYPE (irel->r_info); r_index = ELF32_R_SYM (irel->r_info); /* These are the only relocation types we care about. */ if ( r_type != R_ARM_PC24 && r_type != R_ARM_PLT32 && r_type != R_ARM_CALL && r_type != R_ARM_JUMP24 && r_type != R_ARM_THM_CALL) continue; /* Get the section contents if we haven't done so already. */ if (contents == NULL) { /* Get cached copy if it exists. */ if (elf_section_data (sec)->this_hdr.contents != NULL) contents = elf_section_data (sec)->this_hdr.contents; else { /* Go get them off disk. */ if (! bfd_malloc_and_get_section (abfd, sec, &contents)) goto error_return; } } /* If the relocation is not against a symbol it cannot concern us. */ h = NULL; /* We don't care about local symbols. */ if (r_index < symtab_hdr->sh_info) continue; /* This is an external symbol. */ r_index -= symtab_hdr->sh_info; h = (struct elf_link_hash_entry *) elf_sym_hashes (abfd)[r_index]; /* If the relocation is against a static symbol it must be within the current section and so cannot be a cross ARM/Thumb relocation. */ if (h == NULL) continue; /* If the call will go through a PLT entry then we do not need glue. */ if (globals->splt != NULL && h->plt.offset != (bfd_vma) -1) continue; switch (r_type) { case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_CALL: case R_ARM_JUMP24: /* This one is a call from arm code. We need to look up the target of the call. If it is a thumb target, we insert glue. */ if (ELF_ST_TYPE(h->type) == STT_ARM_TFUNC && !(r_type == R_ARM_CALL && globals->use_blx)) record_arm_to_thumb_glue (link_info, h); break; case R_ARM_THM_CALL: /* This one is a call from thumb code. We look up the target of the call. If it is not a thumb target, we insert glue. */ if (ELF_ST_TYPE (h->type) != STT_ARM_TFUNC && !globals->use_blx && h->root.type != bfd_link_hash_undefweak) record_thumb_to_arm_glue (link_info, h); break; default: abort (); } } if (contents != NULL && elf_section_data (sec)->this_hdr.contents != contents) free (contents); contents = NULL; if (internal_relocs != NULL && elf_section_data (sec)->relocs != internal_relocs) free (internal_relocs); internal_relocs = NULL; } return TRUE; error_return: if (contents != NULL && elf_section_data (sec)->this_hdr.contents != contents) free (contents); if (internal_relocs != NULL && elf_section_data (sec)->relocs != internal_relocs) free (internal_relocs); return FALSE; } #endif /* Initialise maps of ARM/Thumb/data for input BFDs. */ void bfd_elf32_arm_init_maps (bfd *abfd) { Elf_Internal_Sym *isymbuf; Elf_Internal_Shdr *hdr; unsigned int i, localsyms; if (bfd_get_flavour (abfd) != bfd_target_elf_flavour || elf_tdata (abfd) == NULL) return; if ((abfd->flags & DYNAMIC) != 0) return; hdr = &elf_tdata (abfd)->symtab_hdr; localsyms = hdr->sh_info; /* Obtain a buffer full of symbols for this BFD. The hdr->sh_info field should contain the number of local symbols, which should come before any global symbols. Mapping symbols are always local. */ isymbuf = bfd_elf_get_elf_syms (abfd, hdr, localsyms, 0, NULL, NULL, NULL); /* No internal symbols read? Skip this BFD. */ if (isymbuf == NULL) return; for (i = 0; i < localsyms; i++) { Elf_Internal_Sym *isym = &isymbuf[i]; asection *sec = bfd_section_from_elf_index (abfd, isym->st_shndx); const char *name; if (sec != NULL && ELF_ST_BIND (isym->st_info) == STB_LOCAL) { name = bfd_elf_string_from_elf_section (abfd, hdr->sh_link, isym->st_name); if (bfd_is_arm_special_symbol_name (name, BFD_ARM_SPECIAL_SYM_TYPE_MAP)) elf32_arm_section_map_add (sec, name[1], isym->st_value); } } } void bfd_elf32_arm_set_vfp11_fix (bfd *obfd, struct bfd_link_info *link_info) { struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info); obj_attribute *out_attr = elf_known_obj_attributes_proc (obfd); /* We assume that ARMv7+ does not need the VFP11 denorm erratum fix. */ if (out_attr[Tag_CPU_arch].i >= TAG_CPU_ARCH_V7) { switch (globals->vfp11_fix) { case BFD_ARM_VFP11_FIX_DEFAULT: case BFD_ARM_VFP11_FIX_NONE: globals->vfp11_fix = BFD_ARM_VFP11_FIX_NONE; break; default: /* Give a warning, but do as the user requests anyway. */ (*_bfd_error_handler) (_("%B: warning: selected VFP11 erratum " "workaround is not necessary for target architecture"), obfd); } } else if (globals->vfp11_fix == BFD_ARM_VFP11_FIX_DEFAULT) /* For earlier architectures, we might need the workaround, but do not enable it by default. If users is running with broken hardware, they must enable the erratum fix explicitly. */ globals->vfp11_fix = BFD_ARM_VFP11_FIX_NONE; } enum bfd_arm_vfp11_pipe { VFP11_FMAC, VFP11_LS, VFP11_DS, VFP11_BAD }; /* Return a VFP register number. This is encoded as RX:X for single-precision registers, or X:RX for double-precision registers, where RX is the group of four bits in the instruction encoding and X is the single extension bit. RX and X fields are specified using their lowest (starting) bit. The return value is: 0...31: single-precision registers s0...s31 32...63: double-precision registers d0...d31. Although X should be zero for VFP11 (encoding d0...d15 only), we might encounter VFP3 instructions, so we allow the full range for DP registers. */ static unsigned int bfd_arm_vfp11_regno (unsigned int insn, bfd_boolean is_double, unsigned int rx, unsigned int x) { if (is_double) return (((insn >> rx) & 0xf) | (((insn >> x) & 1) << 4)) + 32; else return (((insn >> rx) & 0xf) << 1) | ((insn >> x) & 1); } /* Set bits in *WMASK according to a register number REG as encoded by bfd_arm_vfp11_regno(). Ignore d16-d31. */ static void bfd_arm_vfp11_write_mask (unsigned int *wmask, unsigned int reg) { if (reg < 32) *wmask |= 1 << reg; else if (reg < 48) *wmask |= 3 << ((reg - 32) * 2); } /* Return TRUE if WMASK overwrites anything in REGS. */ static bfd_boolean bfd_arm_vfp11_antidependency (unsigned int wmask, int *regs, int numregs) { int i; for (i = 0; i < numregs; i++) { unsigned int reg = regs[i]; if (reg < 32 && (wmask & (1 << reg)) != 0) return TRUE; reg -= 32; if (reg >= 16) continue; if ((wmask & (3 << (reg * 2))) != 0) return TRUE; } return FALSE; } /* In this function, we're interested in two things: finding input registers for VFP data-processing instructions, and finding the set of registers which arbitrary VFP instructions may write to. We use a 32-bit unsigned int to hold the written set, so FLDM etc. are easy to deal with (we're only interested in 32 SP registers or 16 dp registers, due to the VFP version implemented by the chip in question). DP registers are marked by setting both SP registers in the write mask). */ static enum bfd_arm_vfp11_pipe bfd_arm_vfp11_insn_decode (unsigned int insn, unsigned int *destmask, int *regs, int *numregs) { enum bfd_arm_vfp11_pipe pipe = VFP11_BAD; bfd_boolean is_double = ((insn & 0xf00) == 0xb00) ? 1 : 0; if ((insn & 0x0f000e10) == 0x0e000a00) /* A data-processing insn. */ { unsigned int pqrs; unsigned int fd = bfd_arm_vfp11_regno (insn, is_double, 12, 22); unsigned int fm = bfd_arm_vfp11_regno (insn, is_double, 0, 5); pqrs = ((insn & 0x00800000) >> 20) | ((insn & 0x00300000) >> 19) | ((insn & 0x00000040) >> 6); switch (pqrs) { case 0: /* fmac[sd]. */ case 1: /* fnmac[sd]. */ case 2: /* fmsc[sd]. */ case 3: /* fnmsc[sd]. */ pipe = VFP11_FMAC; bfd_arm_vfp11_write_mask (destmask, fd); regs[0] = fd; regs[1] = bfd_arm_vfp11_regno (insn, is_double, 16, 7); /* Fn. */ regs[2] = fm; *numregs = 3; break; case 4: /* fmul[sd]. */ case 5: /* fnmul[sd]. */ case 6: /* fadd[sd]. */ case 7: /* fsub[sd]. */ pipe = VFP11_FMAC; goto vfp_binop; case 8: /* fdiv[sd]. */ pipe = VFP11_DS; vfp_binop: bfd_arm_vfp11_write_mask (destmask, fd); regs[0] = bfd_arm_vfp11_regno (insn, is_double, 16, 7); /* Fn. */ regs[1] = fm; *numregs = 2; break; case 15: /* extended opcode. */ { unsigned int extn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1); switch (extn) { case 0: /* fcpy[sd]. */ case 1: /* fabs[sd]. */ case 2: /* fneg[sd]. */ case 8: /* fcmp[sd]. */ case 9: /* fcmpe[sd]. */ case 10: /* fcmpz[sd]. */ case 11: /* fcmpez[sd]. */ case 16: /* fuito[sd]. */ case 17: /* fsito[sd]. */ case 24: /* ftoui[sd]. */ case 25: /* ftouiz[sd]. */ case 26: /* ftosi[sd]. */ case 27: /* ftosiz[sd]. */ /* These instructions will not bounce due to underflow. */ *numregs = 0; pipe = VFP11_FMAC; break; case 3: /* fsqrt[sd]. */ /* fsqrt cannot underflow, but it can (perhaps) overwrite registers to cause the erratum in previous instructions. */ bfd_arm_vfp11_write_mask (destmask, fd); pipe = VFP11_DS; break; case 15: /* fcvt{ds,sd}. */ { int rnum = 0; bfd_arm_vfp11_write_mask (destmask, fd); /* Only FCVTSD can underflow. */ if ((insn & 0x100) != 0) regs[rnum++] = fm; *numregs = rnum; pipe = VFP11_FMAC; } break; default: return VFP11_BAD; } } break; default: return VFP11_BAD; } } /* Two-register transfer. */ else if ((insn & 0x0fe00ed0) == 0x0c400a10) { unsigned int fm = bfd_arm_vfp11_regno (insn, is_double, 0, 5); if ((insn & 0x100000) == 0) { if (is_double) bfd_arm_vfp11_write_mask (destmask, fm); else { bfd_arm_vfp11_write_mask (destmask, fm); bfd_arm_vfp11_write_mask (destmask, fm + 1); } } pipe = VFP11_LS; } else if ((insn & 0x0e100e00) == 0x0c100a00) /* A load insn. */ { int fd = bfd_arm_vfp11_regno (insn, is_double, 12, 22); unsigned int puw = ((insn >> 21) & 0x1) | (((insn >> 23) & 3) << 1); switch (puw) { case 0: /* Two-reg transfer. We should catch these above. */ abort (); case 2: /* fldm[sdx]. */ case 3: case 5: { unsigned int i, offset = insn & 0xff; if (is_double) offset >>= 1; for (i = fd; i < fd + offset; i++) bfd_arm_vfp11_write_mask (destmask, i); } break; case 4: /* fld[sd]. */ case 6: bfd_arm_vfp11_write_mask (destmask, fd); break; default: return VFP11_BAD; } pipe = VFP11_LS; } /* Single-register transfer. Note L==0. */ else if ((insn & 0x0f100e10) == 0x0e000a10) { unsigned int opcode = (insn >> 21) & 7; unsigned int fn = bfd_arm_vfp11_regno (insn, is_double, 16, 7); switch (opcode) { case 0: /* fmsr/fmdlr. */ case 1: /* fmdhr. */ /* Mark fmdhr and fmdlr as writing to the whole of the DP destination register. I don't know if this is exactly right, but it is the conservative choice. */ bfd_arm_vfp11_write_mask (destmask, fn); break; case 7: /* fmxr. */ break; } pipe = VFP11_LS; } return pipe; } static int elf32_arm_compare_mapping (const void * a, const void * b); /* Look for potentially-troublesome code sequences which might trigger the VFP11 denormal/antidependency erratum. See, e.g., the ARM1136 errata sheet (available from ARM) for details of the erratum. A short version is described in ld.texinfo. */ bfd_boolean bfd_elf32_arm_vfp11_erratum_scan (bfd *abfd, struct bfd_link_info *link_info) { asection *sec; bfd_byte *contents = NULL; int state = 0; int regs[3], numregs = 0; struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info); int use_vector = (globals->vfp11_fix == BFD_ARM_VFP11_FIX_VECTOR); /* We use a simple FSM to match troublesome VFP11 instruction sequences. The states transition as follows: 0 -> 1 (vector) or 0 -> 2 (scalar) A VFP FMAC-pipeline instruction has been seen. Fill regs[0]..regs[numregs-1] with its input operands. Remember this instruction in 'first_fmac'. 1 -> 2 Any instruction, except for a VFP instruction which overwrites regs[*]. 1 -> 3 [ -> 0 ] or 2 -> 3 [ -> 0 ] A VFP instruction has been seen which overwrites any of regs[*]. We must make a veneer! Reset state to 0 before examining next instruction. 2 -> 0 If we fail to match anything in state 2, reset to state 0 and reset the instruction pointer to the instruction after 'first_fmac'. If the VFP11 vector mode is in use, there must be at least two unrelated instructions between anti-dependent VFP11 instructions to properly avoid triggering the erratum, hence the use of the extra state 1. */ /* If we are only performing a partial link do not bother to construct any glue. */ if (link_info->relocatable) return TRUE; /* We should have chosen a fix type by the time we get here. */ BFD_ASSERT (globals->vfp11_fix != BFD_ARM_VFP11_FIX_DEFAULT); if (globals->vfp11_fix == BFD_ARM_VFP11_FIX_NONE) return TRUE; /* Skip if this bfd does not correspond to an ELF image. */ if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) return TRUE; for (sec = abfd->sections; sec != NULL; sec = sec->next) { unsigned int i, span, first_fmac = 0, veneer_of_insn = 0; struct _arm_elf_section_data *sec_data; /* If we don't have executable progbits, we're not interested in this section. Also skip if section is to be excluded. */ if (elf_section_type (sec) != SHT_PROGBITS || (elf_section_flags (sec) & SHF_EXECINSTR) == 0 || (sec->flags & SEC_EXCLUDE) != 0 || strcmp (sec->name, VFP11_ERRATUM_VENEER_SECTION_NAME) == 0) continue; sec_data = elf32_arm_section_data (sec); if (sec_data->mapcount == 0) continue; if (elf_section_data (sec)->this_hdr.contents != NULL) contents = elf_section_data (sec)->this_hdr.contents; else if (! bfd_malloc_and_get_section (abfd, sec, &contents)) goto error_return; qsort (sec_data->map, sec_data->mapcount, sizeof (elf32_arm_section_map), elf32_arm_compare_mapping); for (span = 0; span < sec_data->mapcount; span++) { unsigned int span_start = sec_data->map[span].vma; unsigned int span_end = (span == sec_data->mapcount - 1) ? sec->size : sec_data->map[span + 1].vma; char span_type = sec_data->map[span].type; /* FIXME: Only ARM mode is supported at present. We may need to support Thumb-2 mode also at some point. */ if (span_type != 'a') continue; for (i = span_start; i < span_end;) { unsigned int next_i = i + 4; unsigned int insn = bfd_big_endian (abfd) ? (contents[i] << 24) | (contents[i + 1] << 16) | (contents[i + 2] << 8) | contents[i + 3] : (contents[i + 3] << 24) | (contents[i + 2] << 16) | (contents[i + 1] << 8) | contents[i]; unsigned int writemask = 0; enum bfd_arm_vfp11_pipe pipe; switch (state) { case 0: pipe = bfd_arm_vfp11_insn_decode (insn, &writemask, regs, &numregs); /* I'm assuming the VFP11 erratum can trigger with denorm operands on either the FMAC or the DS pipeline. This might lead to slightly overenthusiastic veneer insertion. */ if (pipe == VFP11_FMAC || pipe == VFP11_DS) { state = use_vector ? 1 : 2; first_fmac = i; veneer_of_insn = insn; } break; case 1: { int other_regs[3], other_numregs; pipe = bfd_arm_vfp11_insn_decode (insn, &writemask, other_regs, &other_numregs); if (pipe != VFP11_BAD && bfd_arm_vfp11_antidependency (writemask, regs, numregs)) state = 3; else state = 2; } break; case 2: { int other_regs[3], other_numregs; pipe = bfd_arm_vfp11_insn_decode (insn, &writemask, other_regs, &other_numregs); if (pipe != VFP11_BAD && bfd_arm_vfp11_antidependency (writemask, regs, numregs)) state = 3; else { state = 0; next_i = first_fmac + 4; } } break; case 3: abort (); /* Should be unreachable. */ } if (state == 3) { elf32_vfp11_erratum_list *newerr = bfd_zmalloc (sizeof (elf32_vfp11_erratum_list)); int errcount; errcount = ++(elf32_arm_section_data (sec)->erratumcount); newerr->u.b.vfp_insn = veneer_of_insn; switch (span_type) { case 'a': newerr->type = VFP11_ERRATUM_BRANCH_TO_ARM_VENEER; break; default: abort (); } record_vfp11_erratum_veneer (link_info, newerr, abfd, sec, first_fmac); newerr->vma = -1; newerr->next = sec_data->erratumlist; sec_data->erratumlist = newerr; state = 0; } i = next_i; } } if (contents != NULL && elf_section_data (sec)->this_hdr.contents != contents) free (contents); contents = NULL; } return TRUE; error_return: if (contents != NULL && elf_section_data (sec)->this_hdr.contents != contents) free (contents); return FALSE; } /* Find virtual-memory addresses for VFP11 erratum veneers and return locations after sections have been laid out, using specially-named symbols. */ void bfd_elf32_arm_vfp11_fix_veneer_locations (bfd *abfd, struct bfd_link_info *link_info) { asection *sec; struct elf32_arm_link_hash_table *globals; char *tmp_name; if (link_info->relocatable) return; /* Skip if this bfd does not correspond to an ELF image. */ if (bfd_get_flavour (abfd) != bfd_target_elf_flavour) return; globals = elf32_arm_hash_table (link_info); tmp_name = bfd_malloc ((bfd_size_type) strlen (VFP11_ERRATUM_VENEER_ENTRY_NAME) + 10); for (sec = abfd->sections; sec != NULL; sec = sec->next) { struct _arm_elf_section_data *sec_data = elf32_arm_section_data (sec); elf32_vfp11_erratum_list *errnode = sec_data->erratumlist; for (; errnode != NULL; errnode = errnode->next) { struct elf_link_hash_entry *myh; bfd_vma vma; switch (errnode->type) { case VFP11_ERRATUM_BRANCH_TO_ARM_VENEER: case VFP11_ERRATUM_BRANCH_TO_THUMB_VENEER: /* Find veneer symbol. */ sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME, errnode->u.b.veneer->u.v.id); myh = elf_link_hash_lookup (&(globals)->root, tmp_name, FALSE, FALSE, TRUE); if (myh == NULL) (*_bfd_error_handler) (_("%B: unable to find VFP11 veneer " "`%s'"), abfd, tmp_name); vma = myh->root.u.def.section->output_section->vma + myh->root.u.def.section->output_offset + myh->root.u.def.value; errnode->u.b.veneer->vma = vma; break; case VFP11_ERRATUM_ARM_VENEER: case VFP11_ERRATUM_THUMB_VENEER: /* Find return location. */ sprintf (tmp_name, VFP11_ERRATUM_VENEER_ENTRY_NAME "_r", errnode->u.v.id); myh = elf_link_hash_lookup (&(globals)->root, tmp_name, FALSE, FALSE, TRUE); if (myh == NULL) (*_bfd_error_handler) (_("%B: unable to find VFP11 veneer " "`%s'"), abfd, tmp_name); vma = myh->root.u.def.section->output_section->vma + myh->root.u.def.section->output_offset + myh->root.u.def.value; errnode->u.v.branch->vma = vma; break; default: abort (); } } } free (tmp_name); } /* Set target relocation values needed during linking. */ void bfd_elf32_arm_set_target_relocs (struct bfd *output_bfd, struct bfd_link_info *link_info, int target1_is_rel, char * target2_type, int fix_v4bx, int use_blx, bfd_arm_vfp11_fix vfp11_fix, int no_enum_warn, int pic_veneer) { struct elf32_arm_link_hash_table *globals; globals = elf32_arm_hash_table (link_info); globals->target1_is_rel = target1_is_rel; if (strcmp (target2_type, "rel") == 0) globals->target2_reloc = R_ARM_REL32; else if (strcmp (target2_type, "abs") == 0) globals->target2_reloc = R_ARM_ABS32; else if (strcmp (target2_type, "got-rel") == 0) globals->target2_reloc = R_ARM_GOT_PREL; else { _bfd_error_handler (_("Invalid TARGET2 relocation type '%s'."), target2_type); } globals->fix_v4bx = fix_v4bx; globals->use_blx |= use_blx; globals->vfp11_fix = vfp11_fix; globals->pic_veneer = pic_veneer; elf32_arm_tdata (output_bfd)->no_enum_size_warning = no_enum_warn; } /* The thumb form of a long branch is a bit finicky, because the offset encoding is split over two fields, each in it's own instruction. They can occur in any order. So given a thumb form of long branch, and an offset, insert the offset into the thumb branch and return finished instruction. It takes two thumb instructions to encode the target address. Each has 11 bits to invest. The upper 11 bits are stored in one (identified by H-0.. see below), the lower 11 bits are stored in the other (identified by H-1). Combine together and shifted left by 1 (it's a half word address) and there you have it. Op: 1111 = F, H-0, upper address-0 = 000 Op: 1111 = F, H-1, lower address-0 = 800 They can be ordered either way, but the arm tools I've seen always put the lower one first. It probably doesn't matter. krk@cygnus.com XXX: Actually the order does matter. The second instruction (H-1) moves the computed address into the PC, so it must be the second one in the sequence. The problem, however is that whilst little endian code stores the instructions in HI then LOW order, big endian code does the reverse. nickc@cygnus.com. */ #define LOW_HI_ORDER 0xF800F000 #define HI_LOW_ORDER 0xF000F800 static insn32 insert_thumb_branch (insn32 br_insn, int rel_off) { unsigned int low_bits; unsigned int high_bits; BFD_ASSERT ((rel_off & 1) != 1); rel_off >>= 1; /* Half word aligned address. */ low_bits = rel_off & 0x000007FF; /* The bottom 11 bits. */ high_bits = (rel_off >> 11) & 0x000007FF; /* The top 11 bits. */ if ((br_insn & LOW_HI_ORDER) == LOW_HI_ORDER) br_insn = LOW_HI_ORDER | (low_bits << 16) | high_bits; else if ((br_insn & HI_LOW_ORDER) == HI_LOW_ORDER) br_insn = HI_LOW_ORDER | (high_bits << 16) | low_bits; else /* FIXME: abort is probably not the right call. krk@cygnus.com */ abort (); /* Error - not a valid branch instruction form. */ return br_insn; } /* Store an Arm insn into an output section not processed by elf32_arm_write_section. */ static void put_arm_insn (struct elf32_arm_link_hash_table *htab, bfd * output_bfd, bfd_vma val, void * ptr) { if (htab->byteswap_code != bfd_little_endian (output_bfd)) bfd_putl32 (val, ptr); else bfd_putb32 (val, ptr); } /* Store a 16-bit Thumb insn into an output section not processed by elf32_arm_write_section. */ static void put_thumb_insn (struct elf32_arm_link_hash_table *htab, bfd * output_bfd, bfd_vma val, void * ptr) { if (htab->byteswap_code != bfd_little_endian (output_bfd)) bfd_putl16 (val, ptr); else bfd_putb16 (val, ptr); } /* Thumb code calling an ARM function. */ static int elf32_thumb_to_arm_stub (struct bfd_link_info * info, const char * name, bfd * input_bfd, bfd * output_bfd, asection * input_section, bfd_byte * hit_data, asection * sym_sec, bfd_vma offset, bfd_signed_vma addend, bfd_vma val, char **error_message) { asection * s = 0; bfd_vma my_offset; unsigned long int tmp; long int ret_offset; struct elf_link_hash_entry * myh; struct elf32_arm_link_hash_table * globals; myh = find_thumb_glue (info, name, error_message); if (myh == NULL) return FALSE; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); my_offset = myh->root.u.def.value; s = bfd_get_section_by_name (globals->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); BFD_ASSERT (s->contents != NULL); BFD_ASSERT (s->output_section != NULL); if ((my_offset & 0x01) == 0x01) { if (sym_sec != NULL && sym_sec->owner != NULL && !INTERWORK_FLAG (sym_sec->owner)) { (*_bfd_error_handler) (_("%B(%s): warning: interworking not enabled.\n" " first occurrence: %B: thumb call to arm"), sym_sec->owner, input_bfd, name); return FALSE; } --my_offset; myh->root.u.def.value = my_offset; put_thumb_insn (globals, output_bfd, (bfd_vma) t2a1_bx_pc_insn, s->contents + my_offset); put_thumb_insn (globals, output_bfd, (bfd_vma) t2a2_noop_insn, s->contents + my_offset + 2); ret_offset = /* Address of destination of the stub. */ ((bfd_signed_vma) val) - ((bfd_signed_vma) /* Offset from the start of the current section to the start of the stubs. */ (s->output_offset /* Offset of the start of this stub from the start of the stubs. */ + my_offset /* Address of the start of the current section. */ + s->output_section->vma) /* The branch instruction is 4 bytes into the stub. */ + 4 /* ARM branches work from the pc of the instruction + 8. */ + 8); put_arm_insn (globals, output_bfd, (bfd_vma) t2a3_b_insn | ((ret_offset >> 2) & 0x00FFFFFF), s->contents + my_offset + 4); } BFD_ASSERT (my_offset <= globals->thumb_glue_size); /* Now go back and fix up the original BL insn to point to here. */ ret_offset = /* Address of where the stub is located. */ (s->output_section->vma + s->output_offset + my_offset) /* Address of where the BL is located. */ - (input_section->output_section->vma + input_section->output_offset + offset) /* Addend in the relocation. */ - addend /* Biassing for PC-relative addressing. */ - 8; tmp = bfd_get_32 (input_bfd, hit_data - input_section->vma); bfd_put_32 (output_bfd, (bfd_vma) insert_thumb_branch (tmp, ret_offset), hit_data - input_section->vma); return TRUE; } /* Populate an Arm to Thumb stub. Returns the stub symbol. */ static struct elf_link_hash_entry * elf32_arm_create_thumb_stub (struct bfd_link_info * info, const char * name, bfd * input_bfd, bfd * output_bfd, asection * sym_sec, bfd_vma val, asection *s, char **error_message) { bfd_vma my_offset; long int ret_offset; struct elf_link_hash_entry * myh; struct elf32_arm_link_hash_table * globals; myh = find_arm_glue (info, name, error_message); if (myh == NULL) return NULL; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); my_offset = myh->root.u.def.value; if ((my_offset & 0x01) == 0x01) { if (sym_sec != NULL && sym_sec->owner != NULL && !INTERWORK_FLAG (sym_sec->owner)) { (*_bfd_error_handler) (_("%B(%s): warning: interworking not enabled.\n" " first occurrence: %B: arm call to thumb"), sym_sec->owner, input_bfd, name); } --my_offset; myh->root.u.def.value = my_offset; if (info->shared || globals->root.is_relocatable_executable || globals->pic_veneer) { /* For relocatable objects we can't use absolute addresses, so construct the address from a relative offset. */ /* TODO: If the offset is small it's probably worth constructing the address with adds. */ put_arm_insn (globals, output_bfd, (bfd_vma) a2t1p_ldr_insn, s->contents + my_offset); put_arm_insn (globals, output_bfd, (bfd_vma) a2t2p_add_pc_insn, s->contents + my_offset + 4); put_arm_insn (globals, output_bfd, (bfd_vma) a2t3p_bx_r12_insn, s->contents + my_offset + 8); /* Adjust the offset by 4 for the position of the add, and 8 for the pipeline offset. */ ret_offset = (val - (s->output_offset + s->output_section->vma + my_offset + 12)) | 1; bfd_put_32 (output_bfd, ret_offset, s->contents + my_offset + 12); } else if (globals->use_blx) { put_arm_insn (globals, output_bfd, (bfd_vma) a2t1v5_ldr_insn, s->contents + my_offset); /* It's a thumb address. Add the low order bit. */ bfd_put_32 (output_bfd, val | a2t2v5_func_addr_insn, s->contents + my_offset + 4); } else { put_arm_insn (globals, output_bfd, (bfd_vma) a2t1_ldr_insn, s->contents + my_offset); put_arm_insn (globals, output_bfd, (bfd_vma) a2t2_bx_r12_insn, s->contents + my_offset + 4); /* It's a thumb address. Add the low order bit. */ bfd_put_32 (output_bfd, val | a2t3_func_addr_insn, s->contents + my_offset + 8); } } BFD_ASSERT (my_offset <= globals->arm_glue_size); return myh; } /* Arm code calling a Thumb function. */ static int elf32_arm_to_thumb_stub (struct bfd_link_info * info, const char * name, bfd * input_bfd, bfd * output_bfd, asection * input_section, bfd_byte * hit_data, asection * sym_sec, bfd_vma offset, bfd_signed_vma addend, bfd_vma val, char **error_message) { unsigned long int tmp; bfd_vma my_offset; asection * s; long int ret_offset; struct elf_link_hash_entry * myh; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); BFD_ASSERT (s->contents != NULL); BFD_ASSERT (s->output_section != NULL); myh = elf32_arm_create_thumb_stub (info, name, input_bfd, output_bfd, sym_sec, val, s, error_message); if (!myh) return FALSE; my_offset = myh->root.u.def.value; tmp = bfd_get_32 (input_bfd, hit_data); tmp = tmp & 0xFF000000; /* Somehow these are both 4 too far, so subtract 8. */ ret_offset = (s->output_offset + my_offset + s->output_section->vma - (input_section->output_offset + input_section->output_section->vma + offset + addend) - 8); tmp = tmp | ((ret_offset >> 2) & 0x00FFFFFF); bfd_put_32 (output_bfd, (bfd_vma) tmp, hit_data - input_section->vma); return TRUE; } /* Populate Arm stub for an exported Thumb function. */ static bfd_boolean elf32_arm_to_thumb_export_stub (struct elf_link_hash_entry *h, void * inf) { struct bfd_link_info * info = (struct bfd_link_info *) inf; asection * s; struct elf_link_hash_entry * myh; struct elf32_arm_link_hash_entry *eh; struct elf32_arm_link_hash_table * globals; asection *sec; bfd_vma val; char *error_message; eh = elf32_arm_hash_entry(h); /* Allocate stubs for exported Thumb functions on v4t. */ if (eh->export_glue == NULL) return TRUE; globals = elf32_arm_hash_table (info); BFD_ASSERT (globals != NULL); BFD_ASSERT (globals->bfd_of_glue_owner != NULL); s = bfd_get_section_by_name (globals->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); BFD_ASSERT (s != NULL); BFD_ASSERT (s->contents != NULL); BFD_ASSERT (s->output_section != NULL); sec = eh->export_glue->root.u.def.section; BFD_ASSERT (sec->output_section != NULL); val = eh->export_glue->root.u.def.value + sec->output_offset + sec->output_section->vma; myh = elf32_arm_create_thumb_stub (info, h->root.root.string, h->root.u.def.section->owner, globals->obfd, sec, val, s, &error_message); BFD_ASSERT (myh); return TRUE; } /* Generate Arm stubs for exported Thumb symbols. */ static void elf32_arm_begin_write_processing (bfd *abfd ATTRIBUTE_UNUSED, struct bfd_link_info *link_info) { struct elf32_arm_link_hash_table * globals; if (!link_info) return; globals = elf32_arm_hash_table (link_info); /* If blx is available then exported Thumb symbols are OK and there is nothing to do. */ if (globals->use_blx) return; elf_link_hash_traverse (&globals->root, elf32_arm_to_thumb_export_stub, link_info); } /* Some relocations map to different relocations depending on the target. Return the real relocation. */ static int arm_real_reloc_type (struct elf32_arm_link_hash_table * globals, int r_type) { switch (r_type) { case R_ARM_TARGET1: if (globals->target1_is_rel) return R_ARM_REL32; else return R_ARM_ABS32; case R_ARM_TARGET2: return globals->target2_reloc; default: return r_type; } } /* Return the base VMA address which should be subtracted from real addresses when resolving @dtpoff relocation. This is PT_TLS segment p_vaddr. */ static bfd_vma dtpoff_base (struct bfd_link_info *info) { /* If tls_sec is NULL, we should have signalled an error already. */ if (elf_hash_table (info)->tls_sec == NULL) return 0; return elf_hash_table (info)->tls_sec->vma; } /* Return the relocation value for @tpoff relocation if STT_TLS virtual address is ADDRESS. */ static bfd_vma tpoff (struct bfd_link_info *info, bfd_vma address) { struct elf_link_hash_table *htab = elf_hash_table (info); bfd_vma base; /* If tls_sec is NULL, we should have signalled an error already. */ if (htab->tls_sec == NULL) return 0; base = align_power ((bfd_vma) TCB_SIZE, htab->tls_sec->alignment_power); return address - htab->tls_sec->vma + base; } /* Perform an R_ARM_ABS12 relocation on the field pointed to by DATA. VALUE is the relocation value. */ static bfd_reloc_status_type elf32_arm_abs12_reloc (bfd *abfd, void *data, bfd_vma value) { if (value > 0xfff) return bfd_reloc_overflow; value |= bfd_get_32 (abfd, data) & 0xfffff000; bfd_put_32 (abfd, value, data); return bfd_reloc_ok; } /* For a given value of n, calculate the value of G_n as required to deal with group relocations. We return it in the form of an encoded constant-and-rotation, together with the final residual. If n is specified as less than zero, then final_residual is filled with the input value and no further action is performed. */ static bfd_vma calculate_group_reloc_mask (bfd_vma value, int n, bfd_vma *final_residual) { int current_n; bfd_vma g_n; bfd_vma encoded_g_n = 0; bfd_vma residual = value; /* Also known as Y_n. */ for (current_n = 0; current_n <= n; current_n++) { int shift; /* Calculate which part of the value to mask. */ if (residual == 0) shift = 0; else { int msb; /* Determine the most significant bit in the residual and align the resulting value to a 2-bit boundary. */ for (msb = 30; msb >= 0; msb -= 2) if (residual & (3 << msb)) break; /* The desired shift is now (msb - 6), or zero, whichever is the greater. */ shift = msb - 6; if (shift < 0) shift = 0; } /* Calculate g_n in 32-bit as well as encoded constant+rotation form. */ g_n = residual & (0xff << shift); encoded_g_n = (g_n >> shift) | ((g_n <= 0xff ? 0 : (32 - shift) / 2) << 8); /* Calculate the residual for the next time around. */ residual &= ~g_n; } *final_residual = residual; return encoded_g_n; } /* Given an ARM instruction, determine whether it is an ADD or a SUB. Returns 1 if it is an ADD, -1 if it is a SUB, and 0 otherwise. */ static int identify_add_or_sub(bfd_vma insn) { int opcode = insn & 0x1e00000; if (opcode == 1 << 23) /* ADD */ return 1; if (opcode == 1 << 22) /* SUB */ return -1; return 0; } /* Determine if we're dealing with a Thumb-2 object. */ static int using_thumb2 (struct elf32_arm_link_hash_table *globals) { int arch = bfd_elf_get_obj_attr_int (globals->obfd, OBJ_ATTR_PROC, Tag_CPU_arch); return arch == TAG_CPU_ARCH_V6T2 || arch >= TAG_CPU_ARCH_V7; } /* Perform a relocation as part of a final link. */ static bfd_reloc_status_type elf32_arm_final_link_relocate (reloc_howto_type * howto, bfd * input_bfd, bfd * output_bfd, asection * input_section, bfd_byte * contents, Elf_Internal_Rela * rel, bfd_vma value, struct bfd_link_info * info, asection * sym_sec, const char * sym_name, int sym_flags, struct elf_link_hash_entry * h, bfd_boolean * unresolved_reloc_p, char **error_message) { unsigned long r_type = howto->type; unsigned long r_symndx; bfd_byte * hit_data = contents + rel->r_offset; bfd * dynobj = NULL; Elf_Internal_Shdr * symtab_hdr; struct elf_link_hash_entry ** sym_hashes; bfd_vma * local_got_offsets; asection * sgot = NULL; asection * splt = NULL; asection * sreloc = NULL; bfd_vma addend; bfd_signed_vma signed_addend; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); /* Some relocation type map to different relocations depending on the target. We pick the right one here. */ r_type = arm_real_reloc_type (globals, r_type); if (r_type != howto->type) howto = elf32_arm_howto_from_type (r_type); /* If the start address has been set, then set the EF_ARM_HASENTRY flag. Setting this more than once is redundant, but the cost is not too high, and it keeps the code simple. The test is done here, rather than somewhere else, because the start address is only set just before the final link commences. Note - if the user deliberately sets a start address of 0, the flag will not be set. */ if (bfd_get_start_address (output_bfd) != 0) elf_elfheader (output_bfd)->e_flags |= EF_ARM_HASENTRY; dynobj = elf_hash_table (info)->dynobj; if (dynobj) { sgot = bfd_get_section_by_name (dynobj, ".got"); splt = bfd_get_section_by_name (dynobj, ".plt"); } symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; sym_hashes = elf_sym_hashes (input_bfd); local_got_offsets = elf_local_got_offsets (input_bfd); r_symndx = ELF32_R_SYM (rel->r_info); if (globals->use_rel) { addend = bfd_get_32 (input_bfd, hit_data) & howto->src_mask; if (addend & ((howto->src_mask + 1) >> 1)) { signed_addend = -1; signed_addend &= ~ howto->src_mask; signed_addend |= addend; } else signed_addend = addend; } else addend = signed_addend = rel->r_addend; switch (r_type) { case R_ARM_NONE: /* We don't need to find a value for this symbol. It's just a marker. */ *unresolved_reloc_p = FALSE; return bfd_reloc_ok; case R_ARM_ABS12: if (!globals->vxworks_p) return elf32_arm_abs12_reloc (input_bfd, hit_data, value + addend); case R_ARM_PC24: case R_ARM_ABS32: case R_ARM_ABS32_NOI: case R_ARM_REL32: case R_ARM_REL32_NOI: case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_XPC25: case R_ARM_PREL31: case R_ARM_PLT32: /* Handle relocations which should use the PLT entry. ABS32/REL32 will use the symbol's value, which may point to a PLT entry, but we don't need to handle that here. If we created a PLT entry, all branches in this object should go to it. */ if ((r_type != R_ARM_ABS32 && r_type != R_ARM_REL32 && r_type != R_ARM_ABS32_NOI && r_type != R_ARM_REL32_NOI) && h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1) { /* If we've created a .plt section, and assigned a PLT entry to this function, it should not be known to bind locally. If it were, we would have cleared the PLT entry. */ BFD_ASSERT (!SYMBOL_CALLS_LOCAL (info, h)); value = (splt->output_section->vma + splt->output_offset + h->plt.offset); *unresolved_reloc_p = FALSE; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); } /* When generating a shared object or relocatable executable, these relocations are copied into the output file to be resolved at run time. */ if ((info->shared || globals->root.is_relocatable_executable) && (input_section->flags & SEC_ALLOC) && ((r_type != R_ARM_REL32 && r_type != R_ARM_REL32_NOI) || !SYMBOL_CALLS_LOCAL (info, h)) && (h == NULL || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak) && r_type != R_ARM_PC24 && r_type != R_ARM_CALL && r_type != R_ARM_JUMP24 && r_type != R_ARM_PREL31 && r_type != R_ARM_PLT32) { Elf_Internal_Rela outrel; bfd_byte *loc; bfd_boolean skip, relocate; *unresolved_reloc_p = FALSE; if (sreloc == NULL) { const char * name; name = (bfd_elf_string_from_elf_section (input_bfd, elf_elfheader (input_bfd)->e_shstrndx, elf_section_data (input_section)->rel_hdr.sh_name)); if (name == NULL) return bfd_reloc_notsupported; BFD_ASSERT (reloc_section_p (globals, name, input_section)); sreloc = bfd_get_section_by_name (dynobj, name); BFD_ASSERT (sreloc != NULL); } skip = FALSE; relocate = FALSE; outrel.r_addend = addend; outrel.r_offset = _bfd_elf_section_offset (output_bfd, info, input_section, rel->r_offset); if (outrel.r_offset == (bfd_vma) -1) skip = TRUE; else if (outrel.r_offset == (bfd_vma) -2) skip = TRUE, relocate = TRUE; outrel.r_offset += (input_section->output_section->vma + input_section->output_offset); if (skip) memset (&outrel, 0, sizeof outrel); else if (h != NULL && h->dynindx != -1 && (!info->shared || !info->symbolic || !h->def_regular)) outrel.r_info = ELF32_R_INFO (h->dynindx, r_type); else { int symbol; /* This symbol is local, or marked to become local. */ if (sym_flags == STT_ARM_TFUNC) value |= 1; if (globals->symbian_p) { asection *osec; /* On Symbian OS, the data segment and text segement can be relocated independently. Therefore, we must indicate the segment to which this relocation is relative. The BPABI allows us to use any symbol in the right segment; we just use the section symbol as it is convenient. (We cannot use the symbol given by "h" directly as it will not appear in the dynamic symbol table.) Note that the dynamic linker ignores the section symbol value, so we don't subtract osec->vma from the emitted reloc addend. */ if (sym_sec) osec = sym_sec->output_section; else osec = input_section->output_section; symbol = elf_section_data (osec)->dynindx; if (symbol == 0) { struct elf_link_hash_table *htab = elf_hash_table (info); if ((osec->flags & SEC_READONLY) == 0 && htab->data_index_section != NULL) osec = htab->data_index_section; else osec = htab->text_index_section; symbol = elf_section_data (osec)->dynindx; } BFD_ASSERT (symbol != 0); } else /* On SVR4-ish systems, the dynamic loader cannot relocate the text and data segments independently, so the symbol does not matter. */ symbol = 0; outrel.r_info = ELF32_R_INFO (symbol, R_ARM_RELATIVE); if (globals->use_rel) relocate = TRUE; else outrel.r_addend += value; } loc = sreloc->contents; loc += sreloc->reloc_count++ * RELOC_SIZE (globals); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); /* If this reloc is against an external symbol, we do not want to fiddle with the addend. Otherwise, we need to include the symbol value so that it becomes an addend for the dynamic reloc. */ if (! relocate) return bfd_reloc_ok; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, (bfd_vma) 0); } else switch (r_type) { case R_ARM_ABS12: return elf32_arm_abs12_reloc (input_bfd, hit_data, value + addend); case R_ARM_XPC25: /* Arm BLX instruction. */ case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PC24: /* Arm B/BL instruction */ case R_ARM_PLT32: if (r_type == R_ARM_XPC25) { /* Check for Arm calling Arm function. */ /* FIXME: Should we translate the instruction into a BL instruction instead ? */ if (sym_flags != STT_ARM_TFUNC) (*_bfd_error_handler) (_("\%B: Warning: Arm BLX instruction targets Arm function '%s'."), input_bfd, h ? h->root.root.string : "(local)"); } else if (r_type != R_ARM_CALL || !globals->use_blx) { /* Check for Arm calling Thumb function. */ if (sym_flags == STT_ARM_TFUNC) { if (elf32_arm_to_thumb_stub (info, sym_name, input_bfd, output_bfd, input_section, hit_data, sym_sec, rel->r_offset, signed_addend, value, error_message)) return bfd_reloc_ok; else return bfd_reloc_dangerous; } } /* The ARM ELF ABI says that this reloc is computed as: S - P + A where: S is the address of the symbol in the relocation. P is address of the instruction being relocated. A is the addend (extracted from the instruction) in bytes. S is held in 'value'. P is the base address of the section containing the instruction plus the offset of the reloc into that section, ie: (input_section->output_section->vma + input_section->output_offset + rel->r_offset). A is the addend, converted into bytes, ie: (signed_addend * 4) Note: None of these operations have knowledge of the pipeline size of the processor, thus it is up to the assembler to encode this information into the addend. */ value -= (input_section->output_section->vma + input_section->output_offset); value -= rel->r_offset; if (globals->use_rel) value += (signed_addend << howto->size); else /* RELA addends do not have to be adjusted by howto->size. */ value += signed_addend; signed_addend = value; signed_addend >>= howto->rightshift; /* A branch to an undefined weak symbol is turned into a jump to the next instruction. */ if (h && h->root.type == bfd_link_hash_undefweak) { value = (bfd_get_32 (input_bfd, hit_data) & 0xf0000000) | 0x0affffff; } else { /* Perform a signed range check. */ if ( signed_addend > ((bfd_signed_vma) (howto->dst_mask >> 1)) || signed_addend < - ((bfd_signed_vma) ((howto->dst_mask + 1) >> 1))) return bfd_reloc_overflow; addend = (value & 2); value = (signed_addend & howto->dst_mask) | (bfd_get_32 (input_bfd, hit_data) & (~ howto->dst_mask)); /* Set the H bit in the BLX instruction. */ if (sym_flags == STT_ARM_TFUNC) { if (addend) value |= (1 << 24); else value &= ~(bfd_vma)(1 << 24); } if (r_type == R_ARM_CALL) { /* Select the correct instruction (BL or BLX). */ if (sym_flags == STT_ARM_TFUNC) value |= (1 << 28); else { value &= ~(bfd_vma)(1 << 28); value |= (1 << 24); } } } break; case R_ARM_ABS32: value += addend; if (sym_flags == STT_ARM_TFUNC) value |= 1; break; case R_ARM_ABS32_NOI: value += addend; break; case R_ARM_REL32: value += addend; if (sym_flags == STT_ARM_TFUNC) value |= 1; value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); break; case R_ARM_REL32_NOI: value += addend; value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); break; case R_ARM_PREL31: value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); value += signed_addend; if (! h || h->root.type != bfd_link_hash_undefweak) { /* Check for overflow */ if ((value ^ (value >> 1)) & (1 << 30)) return bfd_reloc_overflow; } value &= 0x7fffffff; value |= (bfd_get_32 (input_bfd, hit_data) & 0x80000000); if (sym_flags == STT_ARM_TFUNC) value |= 1; break; } bfd_put_32 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_ARM_ABS8: value += addend; if ((long) value > 0x7f || (long) value < -0x80) return bfd_reloc_overflow; bfd_put_8 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_ARM_ABS16: value += addend; if ((long) value > 0x7fff || (long) value < -0x8000) return bfd_reloc_overflow; bfd_put_16 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_ARM_THM_ABS5: /* Support ldr and str instructions for the thumb. */ if (globals->use_rel) { /* Need to refetch addend. */ addend = bfd_get_16 (input_bfd, hit_data) & howto->src_mask; /* ??? Need to determine shift amount from operand size. */ addend >>= howto->rightshift; } value += addend; /* ??? Isn't value unsigned? */ if ((long) value > 0x1f || (long) value < -0x10) return bfd_reloc_overflow; /* ??? Value needs to be properly shifted into place first. */ value |= bfd_get_16 (input_bfd, hit_data) & 0xf83f; bfd_put_16 (input_bfd, value, hit_data); return bfd_reloc_ok; case R_ARM_THM_ALU_PREL_11_0: /* Corresponds to: addw.w reg, pc, #offset (and similarly for subw). */ { bfd_vma insn; bfd_signed_vma relocation; insn = (bfd_get_16 (input_bfd, hit_data) << 16) | bfd_get_16 (input_bfd, hit_data + 2); if (globals->use_rel) { signed_addend = (insn & 0xff) | ((insn & 0x7000) >> 4) | ((insn & (1 << 26)) >> 15); if (insn & 0xf00000) signed_addend = -signed_addend; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); - value = abs (relocation); + value = llabs (relocation); if (value >= 0x1000) return bfd_reloc_overflow; insn = (insn & 0xfb0f8f00) | (value & 0xff) | ((value & 0x700) << 4) | ((value & 0x800) << 15); if (relocation < 0) insn |= 0xa00000; bfd_put_16 (input_bfd, insn >> 16, hit_data); bfd_put_16 (input_bfd, insn & 0xffff, hit_data + 2); return bfd_reloc_ok; } case R_ARM_THM_PC12: /* Corresponds to: ldr.w reg, [pc, #offset]. */ { bfd_vma insn; bfd_signed_vma relocation; insn = (bfd_get_16 (input_bfd, hit_data) << 16) | bfd_get_16 (input_bfd, hit_data + 2); if (globals->use_rel) { signed_addend = insn & 0xfff; if (!(insn & (1 << 23))) signed_addend = -signed_addend; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); - value = abs (relocation); + value = llabs (relocation); if (value >= 0x1000) return bfd_reloc_overflow; insn = (insn & 0xff7ff000) | value; if (relocation >= 0) insn |= (1 << 23); bfd_put_16 (input_bfd, insn >> 16, hit_data); bfd_put_16 (input_bfd, insn & 0xffff, hit_data + 2); return bfd_reloc_ok; } case R_ARM_THM_XPC22: case R_ARM_THM_CALL: /* Thumb BL (branch long instruction). */ { bfd_vma relocation; bfd_vma reloc_sign; bfd_boolean overflow = FALSE; bfd_vma upper_insn = bfd_get_16 (input_bfd, hit_data); bfd_vma lower_insn = bfd_get_16 (input_bfd, hit_data + 2); bfd_signed_vma reloc_signed_max; bfd_signed_vma reloc_signed_min; bfd_vma check; bfd_signed_vma signed_check; int bitsize; int thumb2 = using_thumb2 (globals); /* A branch to an undefined weak symbol is turned into a jump to the next instruction. */ if (h && h->root.type == bfd_link_hash_undefweak) { bfd_put_16 (input_bfd, 0xe000, hit_data); bfd_put_16 (input_bfd, 0xbf00, hit_data + 2); return bfd_reloc_ok; } /* Fetch the addend. We use the Thumb-2 encoding (backwards compatible with Thumb-1) involving the J1 and J2 bits. */ if (globals->use_rel) { bfd_vma s = (upper_insn & (1 << 10)) >> 10; bfd_vma upper = upper_insn & 0x3ff; bfd_vma lower = lower_insn & 0x7ff; bfd_vma j1 = (lower_insn & (1 << 13)) >> 13; bfd_vma j2 = (lower_insn & (1 << 11)) >> 11; bfd_vma i1 = j1 ^ s ? 0 : 1; bfd_vma i2 = j2 ^ s ? 0 : 1; addend = (i1 << 23) | (i2 << 22) | (upper << 12) | (lower << 1); /* Sign extend. */ addend = (addend | ((s ? 0 : 1) << 24)) - (1 << 24); signed_addend = addend; } if (r_type == R_ARM_THM_XPC22) { /* Check for Thumb to Thumb call. */ /* FIXME: Should we translate the instruction into a BL instruction instead ? */ if (sym_flags == STT_ARM_TFUNC) (*_bfd_error_handler) (_("%B: Warning: Thumb BLX instruction targets thumb function '%s'."), input_bfd, h ? h->root.root.string : "(local)"); } else { /* If it is not a call to Thumb, assume call to Arm. If it is a call relative to a section name, then it is not a function call at all, but rather a long jump. Calls through the PLT do not require stubs. */ if (sym_flags != STT_ARM_TFUNC && sym_flags != STT_SECTION && (h == NULL || splt == NULL || h->plt.offset == (bfd_vma) -1)) { if (globals->use_blx) { /* Convert BL to BLX. */ lower_insn = (lower_insn & ~0x1000) | 0x0800; } else if (elf32_thumb_to_arm_stub (info, sym_name, input_bfd, output_bfd, input_section, hit_data, sym_sec, rel->r_offset, signed_addend, value, error_message)) return bfd_reloc_ok; else return bfd_reloc_dangerous; } else if (sym_flags == STT_ARM_TFUNC && globals->use_blx) { /* Make sure this is a BL. */ lower_insn |= 0x1800; } } /* Handle calls via the PLT. */ if (h != NULL && splt != NULL && h->plt.offset != (bfd_vma) -1) { value = (splt->output_section->vma + splt->output_offset + h->plt.offset); if (globals->use_blx) { /* If the Thumb BLX instruction is available, convert the BL to a BLX instruction to call the ARM-mode PLT entry. */ lower_insn = (lower_insn & ~0x1000) | 0x0800; } else /* Target the Thumb stub before the ARM PLT entry. */ value -= PLT_THUMB_STUB_SIZE; *unresolved_reloc_p = FALSE; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); check = relocation >> howto->rightshift; /* If this is a signed value, the rightshift just dropped leading 1 bits (assuming twos complement). */ if ((bfd_signed_vma) relocation >= 0) signed_check = check; else signed_check = check | ~((bfd_vma) -1 >> howto->rightshift); /* Calculate the permissable maximum and minimum values for this relocation according to whether we're relocating for Thumb-2 or not. */ bitsize = howto->bitsize; if (!thumb2) bitsize -= 2; reloc_signed_max = ((1 << (bitsize - 1)) - 1) >> howto->rightshift; reloc_signed_min = ~reloc_signed_max; /* Assumes two's complement. */ if (signed_check > reloc_signed_max || signed_check < reloc_signed_min) overflow = TRUE; if ((lower_insn & 0x1800) == 0x0800) /* For a BLX instruction, make sure that the relocation is rounded up to a word boundary. This follows the semantics of the instruction which specifies that bit 1 of the target address will come from bit 1 of the base address. */ relocation = (relocation + 2) & ~ 3; /* Put RELOCATION back into the insn. Assumes two's complement. We use the Thumb-2 encoding, which is safe even if dealing with a Thumb-1 instruction by virtue of our overflow check above. */ reloc_sign = (signed_check < 0) ? 1 : 0; upper_insn = (upper_insn & ~(bfd_vma) 0x7ff) | ((relocation >> 12) & 0x3ff) | (reloc_sign << 10); lower_insn = (lower_insn & ~(bfd_vma) 0x2fff) | (((!((relocation >> 23) & 1)) ^ reloc_sign) << 13) | (((!((relocation >> 22) & 1)) ^ reloc_sign) << 11) | ((relocation >> 1) & 0x7ff); /* Put the relocated value back in the object file: */ bfd_put_16 (input_bfd, upper_insn, hit_data); bfd_put_16 (input_bfd, lower_insn, hit_data + 2); return (overflow ? bfd_reloc_overflow : bfd_reloc_ok); } break; case R_ARM_THM_JUMP24: /* Thumb32 unconditional branch instruction. */ { bfd_vma relocation; bfd_boolean overflow = FALSE; bfd_vma upper_insn = bfd_get_16 (input_bfd, hit_data); bfd_vma lower_insn = bfd_get_16 (input_bfd, hit_data + 2); bfd_signed_vma reloc_signed_max = ((1 << (howto->bitsize - 1)) - 1) >> howto->rightshift; bfd_signed_vma reloc_signed_min = ~ reloc_signed_max; bfd_vma check; bfd_signed_vma signed_check; /* Need to refetch the addend, reconstruct the top three bits, and glue the two pieces together. */ if (globals->use_rel) { bfd_vma S = (upper_insn & 0x0400) >> 10; bfd_vma hi = (upper_insn & 0x03ff); bfd_vma I1 = (lower_insn & 0x2000) >> 13; bfd_vma I2 = (lower_insn & 0x0800) >> 11; bfd_vma lo = (lower_insn & 0x07ff); I1 = !(I1 ^ S); I2 = !(I2 ^ S); S = !S; signed_addend = (S << 24) | (I1 << 23) | (I2 << 22) | (hi << 12) | (lo << 1); signed_addend -= (1 << 24); /* Sign extend. */ } /* ??? Should handle interworking? GCC might someday try to use this for tail calls. */ relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); check = relocation >> howto->rightshift; /* If this is a signed value, the rightshift just dropped leading 1 bits (assuming twos complement). */ if ((bfd_signed_vma) relocation >= 0) signed_check = check; else signed_check = check | ~((bfd_vma) -1 >> howto->rightshift); /* Assumes two's complement. */ if (signed_check > reloc_signed_max || signed_check < reloc_signed_min) overflow = TRUE; /* Put RELOCATION back into the insn. */ { bfd_vma S = (relocation & 0x01000000) >> 24; bfd_vma I1 = (relocation & 0x00800000) >> 23; bfd_vma I2 = (relocation & 0x00400000) >> 22; bfd_vma hi = (relocation & 0x003ff000) >> 12; bfd_vma lo = (relocation & 0x00000ffe) >> 1; I1 = !(I1 ^ S); I2 = !(I2 ^ S); upper_insn = (upper_insn & (bfd_vma) 0xf800) | (S << 10) | hi; lower_insn = (lower_insn & (bfd_vma) 0xd000) | (I1 << 13) | (I2 << 11) | lo; } /* Put the relocated value back in the object file: */ bfd_put_16 (input_bfd, upper_insn, hit_data); bfd_put_16 (input_bfd, lower_insn, hit_data + 2); return (overflow ? bfd_reloc_overflow : bfd_reloc_ok); } case R_ARM_THM_JUMP19: /* Thumb32 conditional branch instruction. */ { bfd_vma relocation; bfd_boolean overflow = FALSE; bfd_vma upper_insn = bfd_get_16 (input_bfd, hit_data); bfd_vma lower_insn = bfd_get_16 (input_bfd, hit_data + 2); bfd_signed_vma reloc_signed_max = 0xffffe; bfd_signed_vma reloc_signed_min = -0x100000; bfd_signed_vma signed_check; /* Need to refetch the addend, reconstruct the top three bits, and squish the two 11 bit pieces together. */ if (globals->use_rel) { bfd_vma S = (upper_insn & 0x0400) >> 10; bfd_vma upper = (upper_insn & 0x003f); bfd_vma J1 = (lower_insn & 0x2000) >> 13; bfd_vma J2 = (lower_insn & 0x0800) >> 11; bfd_vma lower = (lower_insn & 0x07ff); upper |= J1 << 6; upper |= J2 << 7; upper |= (!S) << 8; upper -= 0x0100; /* Sign extend. */ addend = (upper << 12) | (lower << 1); signed_addend = addend; } /* ??? Should handle interworking? GCC might someday try to use this for tail calls. */ relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); signed_check = (bfd_signed_vma) relocation; if (signed_check > reloc_signed_max || signed_check < reloc_signed_min) overflow = TRUE; /* Put RELOCATION back into the insn. */ { bfd_vma S = (relocation & 0x00100000) >> 20; bfd_vma J2 = (relocation & 0x00080000) >> 19; bfd_vma J1 = (relocation & 0x00040000) >> 18; bfd_vma hi = (relocation & 0x0003f000) >> 12; bfd_vma lo = (relocation & 0x00000ffe) >> 1; upper_insn = (upper_insn & 0xfbc0) | (S << 10) | hi; lower_insn = (lower_insn & 0xd000) | (J1 << 13) | (J2 << 11) | lo; } /* Put the relocated value back in the object file: */ bfd_put_16 (input_bfd, upper_insn, hit_data); bfd_put_16 (input_bfd, lower_insn, hit_data + 2); return (overflow ? bfd_reloc_overflow : bfd_reloc_ok); } case R_ARM_THM_JUMP11: case R_ARM_THM_JUMP8: case R_ARM_THM_JUMP6: /* Thumb B (branch) instruction). */ { bfd_signed_vma relocation; bfd_signed_vma reloc_signed_max = (1 << (howto->bitsize - 1)) - 1; bfd_signed_vma reloc_signed_min = ~ reloc_signed_max; bfd_signed_vma signed_check; /* CZB cannot jump backward. */ if (r_type == R_ARM_THM_JUMP6) reloc_signed_min = 0; if (globals->use_rel) { /* Need to refetch addend. */ addend = bfd_get_16 (input_bfd, hit_data) & howto->src_mask; if (addend & ((howto->src_mask + 1) >> 1)) { signed_addend = -1; signed_addend &= ~ howto->src_mask; signed_addend |= addend; } else signed_addend = addend; /* The value in the insn has been right shifted. We need to undo this, so that we can perform the address calculation in terms of bytes. */ signed_addend <<= howto->rightshift; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); relocation >>= howto->rightshift; signed_check = relocation; if (r_type == R_ARM_THM_JUMP6) relocation = ((relocation & 0x0020) << 4) | ((relocation & 0x001f) << 3); else relocation &= howto->dst_mask; relocation |= (bfd_get_16 (input_bfd, hit_data) & (~ howto->dst_mask)); bfd_put_16 (input_bfd, relocation, hit_data); /* Assumes two's complement. */ if (signed_check > reloc_signed_max || signed_check < reloc_signed_min) return bfd_reloc_overflow; return bfd_reloc_ok; } case R_ARM_ALU_PCREL7_0: case R_ARM_ALU_PCREL15_8: case R_ARM_ALU_PCREL23_15: { bfd_vma insn; bfd_vma relocation; insn = bfd_get_32 (input_bfd, hit_data); if (globals->use_rel) { /* Extract the addend. */ addend = (insn & 0xff) << ((insn & 0xf00) >> 7); signed_addend = addend; } relocation = value + signed_addend; relocation -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); insn = (insn & ~0xfff) | ((howto->bitpos << 7) & 0xf00) | ((relocation >> howto->bitpos) & 0xff); bfd_put_32 (input_bfd, value, hit_data); } return bfd_reloc_ok; case R_ARM_GNU_VTINHERIT: case R_ARM_GNU_VTENTRY: return bfd_reloc_ok; case R_ARM_GOTOFF32: /* Relocation is relative to the start of the global offset table. */ BFD_ASSERT (sgot != NULL); if (sgot == NULL) return bfd_reloc_notsupported; /* If we are addressing a Thumb function, we need to adjust the address by one, so that attempts to call the function pointer will correctly interpret it as Thumb code. */ if (sym_flags == STT_ARM_TFUNC) value += 1; /* Note that sgot->output_offset is not involved in this calculation. We always want the start of .got. If we define _GLOBAL_OFFSET_TABLE in a different way, as is permitted by the ABI, we might have to change this calculation. */ value -= sgot->output_section->vma; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_GOTPC: /* Use global offset table as symbol value. */ BFD_ASSERT (sgot != NULL); if (sgot == NULL) return bfd_reloc_notsupported; *unresolved_reloc_p = FALSE; value = sgot->output_section->vma; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_GOT32: case R_ARM_GOT_PREL: /* Relocation is to the entry for this symbol in the global offset table. */ if (sgot == NULL) return bfd_reloc_notsupported; if (h != NULL) { bfd_vma off; bfd_boolean dyn; off = h->got.offset; BFD_ASSERT (off != (bfd_vma) -1); dyn = globals->root.dynamic_sections_created; if (! WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) || (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) || (ELF_ST_VISIBILITY (h->other) && h->root.type == bfd_link_hash_undefweak)) { /* This is actually a static link, or it is a -Bsymbolic link and the symbol is defined locally. We must initialize this entry in the global offset table. Since the offset must always be a multiple of 4, we use the least significant bit to record whether we have initialized it already. When doing a dynamic link, we create a .rel(a).got relocation entry to initialize the value. This is done in the finish_dynamic_symbol routine. */ if ((off & 1) != 0) off &= ~1; else { /* If we are addressing a Thumb function, we need to adjust the address by one, so that attempts to call the function pointer will correctly interpret it as Thumb code. */ if (sym_flags == STT_ARM_TFUNC) value |= 1; bfd_put_32 (output_bfd, value, sgot->contents + off); h->got.offset |= 1; } } else *unresolved_reloc_p = FALSE; value = sgot->output_offset + off; } else { bfd_vma off; BFD_ASSERT (local_got_offsets != NULL && local_got_offsets[r_symndx] != (bfd_vma) -1); off = local_got_offsets[r_symndx]; /* The offset must always be a multiple of 4. We use the least significant bit to record whether we have already generated the necessary reloc. */ if ((off & 1) != 0) off &= ~1; else { /* If we are addressing a Thumb function, we need to adjust the address by one, so that attempts to call the function pointer will correctly interpret it as Thumb code. */ if (sym_flags == STT_ARM_TFUNC) value |= 1; if (globals->use_rel) bfd_put_32 (output_bfd, value, sgot->contents + off); if (info->shared) { asection * srelgot; Elf_Internal_Rela outrel; bfd_byte *loc; srelgot = (bfd_get_section_by_name (dynobj, RELOC_SECTION (globals, ".got"))); BFD_ASSERT (srelgot != NULL); outrel.r_addend = addend + value; outrel.r_offset = (sgot->output_section->vma + sgot->output_offset + off); outrel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE); loc = srelgot->contents; loc += srelgot->reloc_count++ * RELOC_SIZE (globals); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); } local_got_offsets[r_symndx] |= 1; } value = sgot->output_offset + off; } if (r_type != R_ARM_GOT32) value += sgot->output_section->vma; return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_TLS_LDO32: value = value - dtpoff_base (info); return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_TLS_LDM32: { bfd_vma off; if (globals->sgot == NULL) abort (); off = globals->tls_ldm_got.offset; if ((off & 1) != 0) off &= ~1; else { /* If we don't know the module number, create a relocation for it. */ if (info->shared) { Elf_Internal_Rela outrel; bfd_byte *loc; if (globals->srelgot == NULL) abort (); outrel.r_addend = 0; outrel.r_offset = (globals->sgot->output_section->vma + globals->sgot->output_offset + off); outrel.r_info = ELF32_R_INFO (0, R_ARM_TLS_DTPMOD32); if (globals->use_rel) bfd_put_32 (output_bfd, outrel.r_addend, globals->sgot->contents + off); loc = globals->srelgot->contents; loc += globals->srelgot->reloc_count++ * RELOC_SIZE (globals); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); } else bfd_put_32 (output_bfd, 1, globals->sgot->contents + off); globals->tls_ldm_got.offset |= 1; } value = globals->sgot->output_section->vma + globals->sgot->output_offset + off - (input_section->output_section->vma + input_section->output_offset + rel->r_offset); return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); } case R_ARM_TLS_GD32: case R_ARM_TLS_IE32: { bfd_vma off; int indx; char tls_type; if (globals->sgot == NULL) abort (); indx = 0; if (h != NULL) { bfd_boolean dyn; dyn = globals->root.dynamic_sections_created; if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, h))) { *unresolved_reloc_p = FALSE; indx = h->dynindx; } off = h->got.offset; tls_type = ((struct elf32_arm_link_hash_entry *) h)->tls_type; } else { if (local_got_offsets == NULL) abort (); off = local_got_offsets[r_symndx]; tls_type = elf32_arm_local_got_tls_type (input_bfd)[r_symndx]; } if (tls_type == GOT_UNKNOWN) abort (); if ((off & 1) != 0) off &= ~1; else { bfd_boolean need_relocs = FALSE; Elf_Internal_Rela outrel; bfd_byte *loc = NULL; int cur_off = off; /* The GOT entries have not been initialized yet. Do it now, and emit any relocations. If both an IE GOT and a GD GOT are necessary, we emit the GD first. */ if ((info->shared || indx != 0) && (h == NULL || ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak)) { need_relocs = TRUE; if (globals->srelgot == NULL) abort (); loc = globals->srelgot->contents; loc += globals->srelgot->reloc_count * RELOC_SIZE (globals); } if (tls_type & GOT_TLS_GD) { if (need_relocs) { outrel.r_addend = 0; outrel.r_offset = (globals->sgot->output_section->vma + globals->sgot->output_offset + cur_off); outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DTPMOD32); if (globals->use_rel) bfd_put_32 (output_bfd, outrel.r_addend, globals->sgot->contents + cur_off); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); globals->srelgot->reloc_count++; loc += RELOC_SIZE (globals); if (indx == 0) bfd_put_32 (output_bfd, value - dtpoff_base (info), globals->sgot->contents + cur_off + 4); else { outrel.r_addend = 0; outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_DTPOFF32); outrel.r_offset += 4; if (globals->use_rel) bfd_put_32 (output_bfd, outrel.r_addend, globals->sgot->contents + cur_off + 4); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); globals->srelgot->reloc_count++; loc += RELOC_SIZE (globals); } } else { /* If we are not emitting relocations for a general dynamic reference, then we must be in a static link or an executable link with the symbol binding locally. Mark it as belonging to module 1, the executable. */ bfd_put_32 (output_bfd, 1, globals->sgot->contents + cur_off); bfd_put_32 (output_bfd, value - dtpoff_base (info), globals->sgot->contents + cur_off + 4); } cur_off += 8; } if (tls_type & GOT_TLS_IE) { if (need_relocs) { if (indx == 0) outrel.r_addend = value - dtpoff_base (info); else outrel.r_addend = 0; outrel.r_offset = (globals->sgot->output_section->vma + globals->sgot->output_offset + cur_off); outrel.r_info = ELF32_R_INFO (indx, R_ARM_TLS_TPOFF32); if (globals->use_rel) bfd_put_32 (output_bfd, outrel.r_addend, globals->sgot->contents + cur_off); SWAP_RELOC_OUT (globals) (output_bfd, &outrel, loc); globals->srelgot->reloc_count++; loc += RELOC_SIZE (globals); } else bfd_put_32 (output_bfd, tpoff (info, value), globals->sgot->contents + cur_off); cur_off += 4; } if (h != NULL) h->got.offset |= 1; else local_got_offsets[r_symndx] |= 1; } if ((tls_type & GOT_TLS_GD) && r_type != R_ARM_TLS_GD32) off += 8; value = globals->sgot->output_section->vma + globals->sgot->output_offset + off - (input_section->output_section->vma + input_section->output_offset + rel->r_offset); return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); } case R_ARM_TLS_LE32: if (info->shared) { (*_bfd_error_handler) (_("%B(%A+0x%lx): R_ARM_TLS_LE32 relocation not permitted in shared object"), input_bfd, input_section, (long) rel->r_offset, howto->name); return FALSE; } else value = tpoff (info, value); return _bfd_final_link_relocate (howto, input_bfd, input_section, contents, rel->r_offset, value, rel->r_addend); case R_ARM_V4BX: if (globals->fix_v4bx) { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); /* Ensure that we have a BX instruction. */ BFD_ASSERT ((insn & 0x0ffffff0) == 0x012fff10); /* Preserve Rm (lowest four bits) and the condition code (highest four bits). Other bits encode MOV PC,Rm. */ insn = (insn & 0xf000000f) | 0x01a0f000; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: /* Until we properly support segment-base-relative addressing then we assume the segment base to be zero, as for the group relocations. Thus R_ARM_MOVW_BREL_NC has the same semantics as R_ARM_MOVW_ABS_NC and R_ARM_MOVT_BREL has the same semantics as R_ARM_MOVT_ABS. */ case R_ARM_MOVW_BREL_NC: case R_ARM_MOVW_BREL: case R_ARM_MOVT_BREL: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); if (globals->use_rel) { addend = ((insn >> 4) & 0xf000) | (insn & 0xfff); signed_addend = (addend ^ 0x10000) - 0x10000; } value += signed_addend; if (r_type == R_ARM_MOVW_PREL_NC || r_type == R_ARM_MOVT_PREL) value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); if (r_type == R_ARM_MOVW_BREL && value >= 0x10000) return bfd_reloc_overflow; if (sym_flags == STT_ARM_TFUNC) value |= 1; if (r_type == R_ARM_MOVT_ABS || r_type == R_ARM_MOVT_PREL || r_type == R_ARM_MOVT_BREL) value >>= 16; insn &= 0xfff0f000; insn |= value & 0xfff; insn |= (value & 0xf000) << 4; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: /* Until we properly support segment-base-relative addressing then we assume the segment base to be zero, as for the above relocations. Thus R_ARM_THM_MOVW_BREL_NC has the same semantics as R_ARM_THM_MOVW_ABS_NC and R_ARM_THM_MOVT_BREL has the same semantics as R_ARM_THM_MOVT_ABS. */ case R_ARM_THM_MOVW_BREL_NC: case R_ARM_THM_MOVW_BREL: case R_ARM_THM_MOVT_BREL: { bfd_vma insn; insn = bfd_get_16 (input_bfd, hit_data) << 16; insn |= bfd_get_16 (input_bfd, hit_data + 2); if (globals->use_rel) { addend = ((insn >> 4) & 0xf000) | ((insn >> 15) & 0x0800) | ((insn >> 4) & 0x0700) | (insn & 0x00ff); signed_addend = (addend ^ 0x10000) - 0x10000; } value += signed_addend; if (r_type == R_ARM_THM_MOVW_PREL_NC || r_type == R_ARM_THM_MOVT_PREL) value -= (input_section->output_section->vma + input_section->output_offset + rel->r_offset); if (r_type == R_ARM_THM_MOVW_BREL && value >= 0x10000) return bfd_reloc_overflow; if (sym_flags == STT_ARM_TFUNC) value |= 1; if (r_type == R_ARM_THM_MOVT_ABS || r_type == R_ARM_THM_MOVT_PREL || r_type == R_ARM_THM_MOVT_BREL) value >>= 16; insn &= 0xfbf08f00; insn |= (value & 0xf000) << 4; insn |= (value & 0x0800) << 15; insn |= (value & 0x0700) << 4; insn |= (value & 0x00ff); bfd_put_16 (input_bfd, insn >> 16, hit_data); bfd_put_16 (input_bfd, insn & 0xffff, hit_data + 2); } return bfd_reloc_ok; case R_ARM_ALU_PC_G0_NC: case R_ARM_ALU_PC_G1_NC: case R_ARM_ALU_PC_G0: case R_ARM_ALU_PC_G1: case R_ARM_ALU_PC_G2: case R_ARM_ALU_SB_G0_NC: case R_ARM_ALU_SB_G1_NC: case R_ARM_ALU_SB_G0: case R_ARM_ALU_SB_G1: case R_ARM_ALU_SB_G2: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); bfd_vma pc = input_section->output_section->vma + input_section->output_offset + rel->r_offset; /* sb should be the origin of the *segment* containing the symbol. It is not clear how to obtain this OS-dependent value, so we make an arbitrary choice of zero. */ bfd_vma sb = 0; bfd_vma residual; bfd_vma g_n; bfd_signed_vma signed_value; int group = 0; /* Determine which group of bits to select. */ switch (r_type) { case R_ARM_ALU_PC_G0_NC: case R_ARM_ALU_PC_G0: case R_ARM_ALU_SB_G0_NC: case R_ARM_ALU_SB_G0: group = 0; break; case R_ARM_ALU_PC_G1_NC: case R_ARM_ALU_PC_G1: case R_ARM_ALU_SB_G1_NC: case R_ARM_ALU_SB_G1: group = 1; break; case R_ARM_ALU_PC_G2: case R_ARM_ALU_SB_G2: group = 2; break; default: abort(); } /* If REL, extract the addend from the insn. If RELA, it will have already been fetched for us. */ if (globals->use_rel) { int negative; bfd_vma constant = insn & 0xff; bfd_vma rotation = (insn & 0xf00) >> 8; if (rotation == 0) signed_addend = constant; else { /* Compensate for the fact that in the instruction, the rotation is stored in multiples of 2 bits. */ rotation *= 2; /* Rotate "constant" right by "rotation" bits. */ signed_addend = (constant >> rotation) | (constant << (8 * sizeof (bfd_vma) - rotation)); } /* Determine if the instruction is an ADD or a SUB. (For REL, this determines the sign of the addend.) */ negative = identify_add_or_sub (insn); if (negative == 0) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Only ADD or SUB instructions are allowed for ALU group relocations"), input_bfd, input_section, (long) rel->r_offset, howto->name); return bfd_reloc_overflow; } signed_addend *= negative; } /* Compute the value (X) to go in the place. */ if (r_type == R_ARM_ALU_PC_G0_NC || r_type == R_ARM_ALU_PC_G1_NC || r_type == R_ARM_ALU_PC_G0 || r_type == R_ARM_ALU_PC_G1 || r_type == R_ARM_ALU_PC_G2) /* PC relative. */ signed_value = value - pc + signed_addend; else /* Section base relative. */ signed_value = value - sb + signed_addend; /* If the target symbol is a Thumb function, then set the Thumb bit in the address. */ if (sym_flags == STT_ARM_TFUNC) signed_value |= 1; /* Calculate the value of the relevant G_n, in encoded constant-with-rotation format. */ - g_n = calculate_group_reloc_mask (abs (signed_value), group, + g_n = calculate_group_reloc_mask (llabs (signed_value), group, &residual); /* Check for overflow if required. */ if ((r_type == R_ARM_ALU_PC_G0 || r_type == R_ARM_ALU_PC_G1 || r_type == R_ARM_ALU_PC_G2 || r_type == R_ARM_ALU_SB_G0 || r_type == R_ARM_ALU_SB_G1 || r_type == R_ARM_ALU_SB_G2) && residual != 0) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"), input_bfd, input_section, - (long) rel->r_offset, abs (signed_value), howto->name); + (long) rel->r_offset, llabs (signed_value), howto->name); return bfd_reloc_overflow; } /* Mask out the value and the ADD/SUB part of the opcode; take care not to destroy the S bit. */ insn &= 0xff1ff000; /* Set the opcode according to whether the value to go in the place is negative. */ if (signed_value < 0) insn |= 1 << 22; else insn |= 1 << 23; /* Encode the offset. */ insn |= g_n; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_LDR_PC_G0: case R_ARM_LDR_PC_G1: case R_ARM_LDR_PC_G2: case R_ARM_LDR_SB_G0: case R_ARM_LDR_SB_G1: case R_ARM_LDR_SB_G2: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); bfd_vma pc = input_section->output_section->vma + input_section->output_offset + rel->r_offset; bfd_vma sb = 0; /* See note above. */ bfd_vma residual; bfd_signed_vma signed_value; int group = 0; /* Determine which groups of bits to calculate. */ switch (r_type) { case R_ARM_LDR_PC_G0: case R_ARM_LDR_SB_G0: group = 0; break; case R_ARM_LDR_PC_G1: case R_ARM_LDR_SB_G1: group = 1; break; case R_ARM_LDR_PC_G2: case R_ARM_LDR_SB_G2: group = 2; break; default: abort(); } /* If REL, extract the addend from the insn. If RELA, it will have already been fetched for us. */ if (globals->use_rel) { int negative = (insn & (1 << 23)) ? 1 : -1; signed_addend = negative * (insn & 0xfff); } /* Compute the value (X) to go in the place. */ if (r_type == R_ARM_LDR_PC_G0 || r_type == R_ARM_LDR_PC_G1 || r_type == R_ARM_LDR_PC_G2) /* PC relative. */ signed_value = value - pc + signed_addend; else /* Section base relative. */ signed_value = value - sb + signed_addend; /* Calculate the value of the relevant G_{n-1} to obtain the residual at that stage. */ - calculate_group_reloc_mask (abs (signed_value), group - 1, &residual); + calculate_group_reloc_mask (llabs (signed_value), group - 1, &residual); /* Check for overflow. */ if (residual >= 0x1000) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"), input_bfd, input_section, - (long) rel->r_offset, abs (signed_value), howto->name); + (long) rel->r_offset, llabs (signed_value), howto->name); return bfd_reloc_overflow; } /* Mask out the value and U bit. */ insn &= 0xff7ff000; /* Set the U bit if the value to go in the place is non-negative. */ if (signed_value >= 0) insn |= 1 << 23; /* Encode the offset. */ insn |= residual; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_LDRS_PC_G0: case R_ARM_LDRS_PC_G1: case R_ARM_LDRS_PC_G2: case R_ARM_LDRS_SB_G0: case R_ARM_LDRS_SB_G1: case R_ARM_LDRS_SB_G2: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); bfd_vma pc = input_section->output_section->vma + input_section->output_offset + rel->r_offset; bfd_vma sb = 0; /* See note above. */ bfd_vma residual; bfd_signed_vma signed_value; int group = 0; /* Determine which groups of bits to calculate. */ switch (r_type) { case R_ARM_LDRS_PC_G0: case R_ARM_LDRS_SB_G0: group = 0; break; case R_ARM_LDRS_PC_G1: case R_ARM_LDRS_SB_G1: group = 1; break; case R_ARM_LDRS_PC_G2: case R_ARM_LDRS_SB_G2: group = 2; break; default: abort(); } /* If REL, extract the addend from the insn. If RELA, it will have already been fetched for us. */ if (globals->use_rel) { int negative = (insn & (1 << 23)) ? 1 : -1; signed_addend = negative * (((insn & 0xf00) >> 4) + (insn & 0xf)); } /* Compute the value (X) to go in the place. */ if (r_type == R_ARM_LDRS_PC_G0 || r_type == R_ARM_LDRS_PC_G1 || r_type == R_ARM_LDRS_PC_G2) /* PC relative. */ signed_value = value - pc + signed_addend; else /* Section base relative. */ signed_value = value - sb + signed_addend; /* Calculate the value of the relevant G_{n-1} to obtain the residual at that stage. */ - calculate_group_reloc_mask (abs (signed_value), group - 1, &residual); + calculate_group_reloc_mask (llabs (signed_value), group - 1, &residual); /* Check for overflow. */ if (residual >= 0x100) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"), input_bfd, input_section, - (long) rel->r_offset, abs (signed_value), howto->name); + (long) rel->r_offset, llabs (signed_value), howto->name); return bfd_reloc_overflow; } /* Mask out the value and U bit. */ insn &= 0xff7ff0f0; /* Set the U bit if the value to go in the place is non-negative. */ if (signed_value >= 0) insn |= 1 << 23; /* Encode the offset. */ insn |= ((residual & 0xf0) << 4) | (residual & 0xf); bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; case R_ARM_LDC_PC_G0: case R_ARM_LDC_PC_G1: case R_ARM_LDC_PC_G2: case R_ARM_LDC_SB_G0: case R_ARM_LDC_SB_G1: case R_ARM_LDC_SB_G2: { bfd_vma insn = bfd_get_32 (input_bfd, hit_data); bfd_vma pc = input_section->output_section->vma + input_section->output_offset + rel->r_offset; bfd_vma sb = 0; /* See note above. */ bfd_vma residual; bfd_signed_vma signed_value; int group = 0; /* Determine which groups of bits to calculate. */ switch (r_type) { case R_ARM_LDC_PC_G0: case R_ARM_LDC_SB_G0: group = 0; break; case R_ARM_LDC_PC_G1: case R_ARM_LDC_SB_G1: group = 1; break; case R_ARM_LDC_PC_G2: case R_ARM_LDC_SB_G2: group = 2; break; default: abort(); } /* If REL, extract the addend from the insn. If RELA, it will have already been fetched for us. */ if (globals->use_rel) { int negative = (insn & (1 << 23)) ? 1 : -1; signed_addend = negative * ((insn & 0xff) << 2); } /* Compute the value (X) to go in the place. */ if (r_type == R_ARM_LDC_PC_G0 || r_type == R_ARM_LDC_PC_G1 || r_type == R_ARM_LDC_PC_G2) /* PC relative. */ signed_value = value - pc + signed_addend; else /* Section base relative. */ signed_value = value - sb + signed_addend; /* Calculate the value of the relevant G_{n-1} to obtain the residual at that stage. */ - calculate_group_reloc_mask (abs (signed_value), group - 1, &residual); + calculate_group_reloc_mask (llabs (signed_value), group - 1, &residual); /* Check for overflow. (The absolute value to go in the place must be divisible by four and, after having been divided by four, must fit in eight bits.) */ if ((residual & 0x3) != 0 || residual >= 0x400) { (*_bfd_error_handler) (_("%B(%A+0x%lx): Overflow whilst splitting 0x%lx for group relocation %s"), input_bfd, input_section, - (long) rel->r_offset, abs (signed_value), howto->name); + (long) rel->r_offset, llabs (signed_value), howto->name); return bfd_reloc_overflow; } /* Mask out the value and U bit. */ insn &= 0xff7fff00; /* Set the U bit if the value to go in the place is non-negative. */ if (signed_value >= 0) insn |= 1 << 23; /* Encode the offset. */ insn |= residual >> 2; bfd_put_32 (input_bfd, insn, hit_data); } return bfd_reloc_ok; default: return bfd_reloc_notsupported; } } /* Add INCREMENT to the reloc (of type HOWTO) at ADDRESS. */ static void arm_add_to_rel (bfd * abfd, bfd_byte * address, reloc_howto_type * howto, bfd_signed_vma increment) { bfd_signed_vma addend; if (howto->type == R_ARM_THM_CALL) { int upper_insn, lower_insn; int upper, lower; upper_insn = bfd_get_16 (abfd, address); lower_insn = bfd_get_16 (abfd, address + 2); upper = upper_insn & 0x7ff; lower = lower_insn & 0x7ff; addend = (upper << 12) | (lower << 1); addend += increment; addend >>= 1; upper_insn = (upper_insn & 0xf800) | ((addend >> 11) & 0x7ff); lower_insn = (lower_insn & 0xf800) | (addend & 0x7ff); bfd_put_16 (abfd, (bfd_vma) upper_insn, address); bfd_put_16 (abfd, (bfd_vma) lower_insn, address + 2); } else { bfd_vma contents; contents = bfd_get_32 (abfd, address); /* Get the (signed) value from the instruction. */ addend = contents & howto->src_mask; if (addend & ((howto->src_mask + 1) >> 1)) { bfd_signed_vma mask; mask = -1; mask &= ~ howto->src_mask; addend |= mask; } /* Add in the increment, (which is a byte value). */ switch (howto->type) { default: addend += increment; break; case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_CALL: case R_ARM_JUMP24: addend <<= howto->size; addend += increment; /* Should we check for overflow here ? */ /* Drop any undesired bits. */ addend >>= howto->rightshift; break; } contents = (contents & ~ howto->dst_mask) | (addend & howto->dst_mask); bfd_put_32 (abfd, contents, address); } } #define IS_ARM_TLS_RELOC(R_TYPE) \ ((R_TYPE) == R_ARM_TLS_GD32 \ || (R_TYPE) == R_ARM_TLS_LDO32 \ || (R_TYPE) == R_ARM_TLS_LDM32 \ || (R_TYPE) == R_ARM_TLS_DTPOFF32 \ || (R_TYPE) == R_ARM_TLS_DTPMOD32 \ || (R_TYPE) == R_ARM_TLS_TPOFF32 \ || (R_TYPE) == R_ARM_TLS_LE32 \ || (R_TYPE) == R_ARM_TLS_IE32) /* Relocate an ARM ELF section. */ static bfd_boolean elf32_arm_relocate_section (bfd * output_bfd, struct bfd_link_info * info, bfd * input_bfd, asection * input_section, bfd_byte * contents, Elf_Internal_Rela * relocs, Elf_Internal_Sym * local_syms, asection ** local_sections) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; Elf_Internal_Rela *rel; Elf_Internal_Rela *relend; const char *name; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); symtab_hdr = & elf_tdata (input_bfd)->symtab_hdr; sym_hashes = elf_sym_hashes (input_bfd); rel = relocs; relend = relocs + input_section->reloc_count; for (; rel < relend; rel++) { int r_type; reloc_howto_type * howto; unsigned long r_symndx; Elf_Internal_Sym * sym; asection * sec; struct elf_link_hash_entry * h; bfd_vma relocation; bfd_reloc_status_type r; arelent bfd_reloc; char sym_type; bfd_boolean unresolved_reloc = FALSE; char *error_message = NULL; r_symndx = ELF32_R_SYM (rel->r_info); r_type = ELF32_R_TYPE (rel->r_info); r_type = arm_real_reloc_type (globals, r_type); if ( r_type == R_ARM_GNU_VTENTRY || r_type == R_ARM_GNU_VTINHERIT) continue; bfd_reloc.howto = elf32_arm_howto_from_type (r_type); howto = bfd_reloc.howto; h = NULL; sym = NULL; sec = NULL; if (r_symndx < symtab_hdr->sh_info) { sym = local_syms + r_symndx; sym_type = ELF32_ST_TYPE (sym->st_info); sec = local_sections[r_symndx]; if (globals->use_rel) { relocation = (sec->output_section->vma + sec->output_offset + sym->st_value); if (!info->relocatable && (sec->flags & SEC_MERGE) && ELF_ST_TYPE (sym->st_info) == STT_SECTION) { asection *msec; bfd_vma addend, value; if (howto->rightshift) { (*_bfd_error_handler) (_("%B(%A+0x%lx): %s relocation against SEC_MERGE section"), input_bfd, input_section, (long) rel->r_offset, howto->name); return FALSE; } value = bfd_get_32 (input_bfd, contents + rel->r_offset); /* Get the (signed) value from the instruction. */ addend = value & howto->src_mask; if (addend & ((howto->src_mask + 1) >> 1)) { bfd_signed_vma mask; mask = -1; mask &= ~ howto->src_mask; addend |= mask; } msec = sec; addend = _bfd_elf_rel_local_sym (output_bfd, sym, &msec, addend) - relocation; addend += msec->output_section->vma + msec->output_offset; value = (value & ~ howto->dst_mask) | (addend & howto->dst_mask); bfd_put_32 (input_bfd, value, contents + rel->r_offset); } } else relocation = _bfd_elf_rela_local_sym (output_bfd, sym, &sec, rel); } else { bfd_boolean warned; RELOC_FOR_GLOBAL_SYMBOL (info, input_bfd, input_section, rel, r_symndx, symtab_hdr, sym_hashes, h, sec, relocation, unresolved_reloc, warned); sym_type = h->type; } if (sec != NULL && elf_discarded_section (sec)) { /* For relocs against symbols from removed linkonce sections, or sections discarded by a linker script, we just want the section contents zeroed. Avoid any special processing. */ _bfd_clear_contents (howto, input_bfd, contents + rel->r_offset); rel->r_info = 0; rel->r_addend = 0; continue; } if (info->relocatable) { /* This is a relocatable link. We don't have to change anything, unless the reloc is against a section symbol, in which case we have to adjust according to where the section symbol winds up in the output section. */ if (sym != NULL && ELF_ST_TYPE (sym->st_info) == STT_SECTION) { if (globals->use_rel) arm_add_to_rel (input_bfd, contents + rel->r_offset, howto, (bfd_signed_vma) sec->output_offset); else rel->r_addend += sec->output_offset; } continue; } if (h != NULL) name = h->root.root.string; else { name = (bfd_elf_string_from_elf_section (input_bfd, symtab_hdr->sh_link, sym->st_name)); if (name == NULL || *name == '\0') name = bfd_section_name (input_bfd, sec); } if (r_symndx != 0 && r_type != R_ARM_NONE && (h == NULL || h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak) && IS_ARM_TLS_RELOC (r_type) != (sym_type == STT_TLS)) { (*_bfd_error_handler) ((sym_type == STT_TLS ? _("%B(%A+0x%lx): %s used with TLS symbol %s") : _("%B(%A+0x%lx): %s used with non-TLS symbol %s")), input_bfd, input_section, (long) rel->r_offset, howto->name, name); } r = elf32_arm_final_link_relocate (howto, input_bfd, output_bfd, input_section, contents, rel, relocation, info, sec, name, (h ? ELF_ST_TYPE (h->type) : ELF_ST_TYPE (sym->st_info)), h, &unresolved_reloc, &error_message); /* Dynamic relocs are not propagated for SEC_DEBUGGING sections because such sections are not SEC_ALLOC and thus ld.so will not process them. */ if (unresolved_reloc && !((input_section->flags & SEC_DEBUGGING) != 0 && h->def_dynamic)) { (*_bfd_error_handler) (_("%B(%A+0x%lx): unresolvable %s relocation against symbol `%s'"), input_bfd, input_section, (long) rel->r_offset, howto->name, h->root.root.string); return FALSE; } if (r != bfd_reloc_ok) { switch (r) { case bfd_reloc_overflow: /* If the overflowing reloc was to an undefined symbol, we have already printed one error message and there is no point complaining again. */ if ((! h || h->root.type != bfd_link_hash_undefined) && (!((*info->callbacks->reloc_overflow) (info, (h ? &h->root : NULL), name, howto->name, (bfd_vma) 0, input_bfd, input_section, rel->r_offset)))) return FALSE; break; case bfd_reloc_undefined: if (!((*info->callbacks->undefined_symbol) (info, name, input_bfd, input_section, rel->r_offset, TRUE))) return FALSE; break; case bfd_reloc_outofrange: error_message = _("out of range"); goto common_error; case bfd_reloc_notsupported: error_message = _("unsupported relocation"); goto common_error; case bfd_reloc_dangerous: /* error_message should already be set. */ goto common_error; default: error_message = _("unknown error"); /* fall through */ common_error: BFD_ASSERT (error_message != NULL); if (!((*info->callbacks->reloc_dangerous) (info, error_message, input_bfd, input_section, rel->r_offset))) return FALSE; break; } } } return TRUE; } /* Set the right machine number. */ static bfd_boolean elf32_arm_object_p (bfd *abfd) { unsigned int mach; mach = bfd_arm_get_mach_from_notes (abfd, ARM_NOTE_SECTION); if (mach != bfd_mach_arm_unknown) bfd_default_set_arch_mach (abfd, bfd_arch_arm, mach); else if (elf_elfheader (abfd)->e_flags & EF_ARM_MAVERICK_FLOAT) bfd_default_set_arch_mach (abfd, bfd_arch_arm, bfd_mach_arm_ep9312); else bfd_default_set_arch_mach (abfd, bfd_arch_arm, mach); return TRUE; } /* Function to keep ARM specific flags in the ELF header. */ static bfd_boolean elf32_arm_set_private_flags (bfd *abfd, flagword flags) { if (elf_flags_init (abfd) && elf_elfheader (abfd)->e_flags != flags) { if (EF_ARM_EABI_VERSION (flags) == EF_ARM_EABI_UNKNOWN) { if (flags & EF_ARM_INTERWORK) (*_bfd_error_handler) (_("Warning: Not setting interworking flag of %B since it has already been specified as non-interworking"), abfd); else _bfd_error_handler (_("Warning: Clearing the interworking flag of %B due to outside request"), abfd); } } else { elf_elfheader (abfd)->e_flags = flags; elf_flags_init (abfd) = TRUE; } return TRUE; } /* Copy backend specific data from one object module to another. */ static bfd_boolean elf32_arm_copy_private_bfd_data (bfd *ibfd, bfd *obfd) { flagword in_flags; flagword out_flags; if ( bfd_get_flavour (ibfd) != bfd_target_elf_flavour || bfd_get_flavour (obfd) != bfd_target_elf_flavour) return TRUE; in_flags = elf_elfheader (ibfd)->e_flags; out_flags = elf_elfheader (obfd)->e_flags; if (elf_flags_init (obfd) && EF_ARM_EABI_VERSION (out_flags) == EF_ARM_EABI_UNKNOWN && in_flags != out_flags) { /* Cannot mix APCS26 and APCS32 code. */ if ((in_flags & EF_ARM_APCS_26) != (out_flags & EF_ARM_APCS_26)) return FALSE; /* Cannot mix float APCS and non-float APCS code. */ if ((in_flags & EF_ARM_APCS_FLOAT) != (out_flags & EF_ARM_APCS_FLOAT)) return FALSE; /* If the src and dest have different interworking flags then turn off the interworking bit. */ if ((in_flags & EF_ARM_INTERWORK) != (out_flags & EF_ARM_INTERWORK)) { if (out_flags & EF_ARM_INTERWORK) _bfd_error_handler (_("Warning: Clearing the interworking flag of %B because non-interworking code in %B has been linked with it"), obfd, ibfd); in_flags &= ~EF_ARM_INTERWORK; } /* Likewise for PIC, though don't warn for this case. */ if ((in_flags & EF_ARM_PIC) != (out_flags & EF_ARM_PIC)) in_flags &= ~EF_ARM_PIC; } elf_elfheader (obfd)->e_flags = in_flags; elf_flags_init (obfd) = TRUE; /* Also copy the EI_OSABI field. */ elf_elfheader (obfd)->e_ident[EI_OSABI] = elf_elfheader (ibfd)->e_ident[EI_OSABI]; /* Copy object attributes. */ _bfd_elf_copy_obj_attributes (ibfd, obfd); return TRUE; } /* Values for Tag_ABI_PCS_R9_use. */ enum { AEABI_R9_V6, AEABI_R9_SB, AEABI_R9_TLS, AEABI_R9_unused }; /* Values for Tag_ABI_PCS_RW_data. */ enum { AEABI_PCS_RW_data_absolute, AEABI_PCS_RW_data_PCrel, AEABI_PCS_RW_data_SBrel, AEABI_PCS_RW_data_unused }; /* Values for Tag_ABI_enum_size. */ enum { AEABI_enum_unused, AEABI_enum_short, AEABI_enum_wide, AEABI_enum_forced_wide }; /* Determine whether an object attribute tag takes an integer, a string or both. */ static int elf32_arm_obj_attrs_arg_type (int tag) { if (tag == Tag_compatibility) return 3; else if (tag == 4 || tag == 5) return 2; else if (tag < 32) return 1; else return (tag & 1) != 0 ? 2 : 1; } /* Merge EABI object attributes from IBFD into OBFD. Raise an error if there are conflicting attributes. */ static bfd_boolean elf32_arm_merge_eabi_attributes (bfd *ibfd, bfd *obfd) { obj_attribute *in_attr; obj_attribute *out_attr; obj_attribute_list *in_list; /* Some tags have 0 = don't care, 1 = strong requirement, 2 = weak requirement. */ static const int order_312[3] = {3, 1, 2}; int i; if (!elf_known_obj_attributes_proc (obfd)[0].i) { /* This is the first object. Copy the attributes. */ _bfd_elf_copy_obj_attributes (ibfd, obfd); /* Use the Tag_null value to indicate the attributes have been initialized. */ elf_known_obj_attributes_proc (obfd)[0].i = 1; return TRUE; } in_attr = elf_known_obj_attributes_proc (ibfd); out_attr = elf_known_obj_attributes_proc (obfd); /* This needs to happen before Tag_ABI_FP_number_model is merged. */ if (in_attr[Tag_ABI_VFP_args].i != out_attr[Tag_ABI_VFP_args].i) { /* Ignore mismatches if teh object doesn't use floating point. */ if (out_attr[Tag_ABI_FP_number_model].i == 0) out_attr[Tag_ABI_VFP_args].i = in_attr[Tag_ABI_VFP_args].i; else if (in_attr[Tag_ABI_FP_number_model].i != 0) { _bfd_error_handler (_("ERROR: %B uses VFP register arguments, %B does not"), ibfd, obfd); return FALSE; } } for (i = 4; i < NUM_KNOWN_OBJ_ATTRIBUTES; i++) { /* Merge this attribute with existing attributes. */ switch (i) { case Tag_CPU_raw_name: case Tag_CPU_name: /* Use whichever has the greatest architecture requirements. We won't necessarily have both the above tags, so make sure input name is non-NULL. */ if (in_attr[Tag_CPU_arch].i > out_attr[Tag_CPU_arch].i && in_attr[i].s) out_attr[i].s = _bfd_elf_attr_strdup (obfd, in_attr[i].s); break; case Tag_ABI_optimization_goals: case Tag_ABI_FP_optimization_goals: /* Use the first value seen. */ break; case Tag_CPU_arch: case Tag_ARM_ISA_use: case Tag_THUMB_ISA_use: case Tag_VFP_arch: case Tag_WMMX_arch: case Tag_NEON_arch: /* ??? Do NEON and WMMX conflict? */ case Tag_ABI_FP_rounding: case Tag_ABI_FP_denormal: case Tag_ABI_FP_exceptions: case Tag_ABI_FP_user_exceptions: case Tag_ABI_FP_number_model: case Tag_ABI_align8_preserved: case Tag_ABI_HardFP_use: /* Use the largest value specified. */ if (in_attr[i].i > out_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_CPU_arch_profile: /* Warn if conflicting architecture profiles used. */ if (out_attr[i].i && in_attr[i].i && in_attr[i].i != out_attr[i].i) { _bfd_error_handler (_("ERROR: %B: Conflicting architecture profiles %c/%c"), ibfd, in_attr[i].i, out_attr[i].i); return FALSE; } if (in_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_PCS_config: if (out_attr[i].i == 0) out_attr[i].i = in_attr[i].i; else if (in_attr[i].i != 0 && out_attr[i].i != 0) { /* It's sometimes ok to mix different configs, so this is only a warning. */ _bfd_error_handler (_("Warning: %B: Conflicting platform configuration"), ibfd); } break; case Tag_ABI_PCS_R9_use: if (in_attr[i].i != out_attr[i].i && out_attr[i].i != AEABI_R9_unused && in_attr[i].i != AEABI_R9_unused) { _bfd_error_handler (_("ERROR: %B: Conflicting use of R9"), ibfd); return FALSE; } if (out_attr[i].i == AEABI_R9_unused) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_PCS_RW_data: if (in_attr[i].i == AEABI_PCS_RW_data_SBrel && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_SB && out_attr[Tag_ABI_PCS_R9_use].i != AEABI_R9_unused) { _bfd_error_handler (_("ERROR: %B: SB relative addressing conflicts with use of R9"), ibfd); return FALSE; } /* Use the smallest value specified. */ if (in_attr[i].i < out_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_PCS_RO_data: /* Use the smallest value specified. */ if (in_attr[i].i < out_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_PCS_GOT_use: if (in_attr[i].i > 2 || out_attr[i].i > 2 || order_312[in_attr[i].i] < order_312[out_attr[i].i]) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_PCS_wchar_t: if (out_attr[i].i && in_attr[i].i && out_attr[i].i != in_attr[i].i) { _bfd_error_handler (_("ERROR: %B: Conflicting definitions of wchar_t"), ibfd); return FALSE; } if (in_attr[i].i) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_align8_needed: /* ??? Check against Tag_ABI_align8_preserved. */ if (in_attr[i].i > 2 || out_attr[i].i > 2 || order_312[in_attr[i].i] < order_312[out_attr[i].i]) out_attr[i].i = in_attr[i].i; break; case Tag_ABI_enum_size: if (in_attr[i].i != AEABI_enum_unused) { if (out_attr[i].i == AEABI_enum_unused || out_attr[i].i == AEABI_enum_forced_wide) { /* The existing object is compatible with anything. Use whatever requirements the new object has. */ out_attr[i].i = in_attr[i].i; } else if (in_attr[i].i != AEABI_enum_forced_wide && out_attr[i].i != in_attr[i].i && !elf32_arm_tdata (obfd)->no_enum_size_warning) { const char *aeabi_enum_names[] = { "", "variable-size", "32-bit", "" }; _bfd_error_handler (_("warning: %B uses %s enums yet the output is to use %s enums; use of enum values across objects may fail"), ibfd, aeabi_enum_names[in_attr[i].i], aeabi_enum_names[out_attr[i].i]); } } break; case Tag_ABI_VFP_args: /* Aready done. */ break; case Tag_ABI_WMMX_args: if (in_attr[i].i != out_attr[i].i) { _bfd_error_handler (_("ERROR: %B uses iWMMXt register arguments, %B does not"), ibfd, obfd); return FALSE; } break; default: /* All known attributes should be explicitly covered. */ abort (); } } /* Merge Tag_compatibility attributes and any common GNU ones. */ _bfd_elf_merge_object_attributes (ibfd, obfd); /* Check for any attributes not known on ARM. */ in_list = elf_other_obj_attributes_proc (ibfd); while (in_list && in_list->tag == Tag_compatibility) in_list = in_list->next; for (; in_list; in_list = in_list->next) { if ((in_list->tag & 128) < 64 && in_list->tag != Tag_Virtualization_use) { _bfd_error_handler (_("Warning: %B: Unknown EABI object attribute %d"), ibfd, in_list->tag); break; } } return TRUE; } /* Return TRUE if the two EABI versions are incompatible. */ static bfd_boolean elf32_arm_versions_compatible (unsigned iver, unsigned over) { /* v4 and v5 are the same spec before and after it was released, so allow mixing them. */ if ((iver == EF_ARM_EABI_VER4 && over == EF_ARM_EABI_VER5) || (iver == EF_ARM_EABI_VER5 && over == EF_ARM_EABI_VER4)) return TRUE; return (iver == over); } /* Merge backend specific data from an object file to the output object file when linking. */ static bfd_boolean elf32_arm_merge_private_bfd_data (bfd * ibfd, bfd * obfd) { flagword out_flags; flagword in_flags; bfd_boolean flags_compatible = TRUE; asection *sec; /* Check if we have the same endianess. */ if (! _bfd_generic_verify_endian_match (ibfd, obfd)) return FALSE; if ( bfd_get_flavour (ibfd) != bfd_target_elf_flavour || bfd_get_flavour (obfd) != bfd_target_elf_flavour) return TRUE; if (!elf32_arm_merge_eabi_attributes (ibfd, obfd)) return FALSE; /* The input BFD must have had its flags initialised. */ /* The following seems bogus to me -- The flags are initialized in the assembler but I don't think an elf_flags_init field is written into the object. */ /* BFD_ASSERT (elf_flags_init (ibfd)); */ in_flags = elf_elfheader (ibfd)->e_flags; out_flags = elf_elfheader (obfd)->e_flags; if (!elf_flags_init (obfd)) { /* If the input is the default architecture and had the default flags then do not bother setting the flags for the output architecture, instead allow future merges to do this. If no future merges ever set these flags then they will retain their uninitialised values, which surprise surprise, correspond to the default values. */ if (bfd_get_arch_info (ibfd)->the_default && elf_elfheader (ibfd)->e_flags == 0) return TRUE; elf_flags_init (obfd) = TRUE; elf_elfheader (obfd)->e_flags = in_flags; if (bfd_get_arch (obfd) == bfd_get_arch (ibfd) && bfd_get_arch_info (obfd)->the_default) return bfd_set_arch_mach (obfd, bfd_get_arch (ibfd), bfd_get_mach (ibfd)); return TRUE; } /* Determine what should happen if the input ARM architecture does not match the output ARM architecture. */ if (! bfd_arm_merge_machines (ibfd, obfd)) return FALSE; /* Identical flags must be compatible. */ if (in_flags == out_flags) return TRUE; /* Check to see if the input BFD actually contains any sections. If not, its flags may not have been initialised either, but it cannot actually cause any incompatiblity. Do not short-circuit dynamic objects; their section list may be emptied by elf_link_add_object_symbols. Also check to see if there are no code sections in the input. In this case there is no need to check for code specific flags. XXX - do we need to worry about floating-point format compatability in data sections ? */ if (!(ibfd->flags & DYNAMIC)) { bfd_boolean null_input_bfd = TRUE; bfd_boolean only_data_sections = TRUE; for (sec = ibfd->sections; sec != NULL; sec = sec->next) { /* Ignore synthetic glue sections. */ if (strcmp (sec->name, ".glue_7") && strcmp (sec->name, ".glue_7t")) { if ((bfd_get_section_flags (ibfd, sec) & (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) == (SEC_LOAD | SEC_CODE | SEC_HAS_CONTENTS)) only_data_sections = FALSE; null_input_bfd = FALSE; break; } } if (null_input_bfd || only_data_sections) return TRUE; } /* Complain about various flag mismatches. */ if (!elf32_arm_versions_compatible (EF_ARM_EABI_VERSION (in_flags), EF_ARM_EABI_VERSION (out_flags))) { _bfd_error_handler (_("ERROR: Source object %B has EABI version %d, but target %B has EABI version %d"), ibfd, obfd, (in_flags & EF_ARM_EABIMASK) >> 24, (out_flags & EF_ARM_EABIMASK) >> 24); return FALSE; } /* Not sure what needs to be checked for EABI versions >= 1. */ /* VxWorks libraries do not use these flags. */ if (get_elf_backend_data (obfd) != &elf32_arm_vxworks_bed && get_elf_backend_data (ibfd) != &elf32_arm_vxworks_bed && EF_ARM_EABI_VERSION (in_flags) == EF_ARM_EABI_UNKNOWN) { if ((in_flags & EF_ARM_APCS_26) != (out_flags & EF_ARM_APCS_26)) { _bfd_error_handler (_("ERROR: %B is compiled for APCS-%d, whereas target %B uses APCS-%d"), ibfd, obfd, in_flags & EF_ARM_APCS_26 ? 26 : 32, out_flags & EF_ARM_APCS_26 ? 26 : 32); flags_compatible = FALSE; } if ((in_flags & EF_ARM_APCS_FLOAT) != (out_flags & EF_ARM_APCS_FLOAT)) { if (in_flags & EF_ARM_APCS_FLOAT) _bfd_error_handler (_("ERROR: %B passes floats in float registers, whereas %B passes them in integer registers"), ibfd, obfd); else _bfd_error_handler (_("ERROR: %B passes floats in integer registers, whereas %B passes them in float registers"), ibfd, obfd); flags_compatible = FALSE; } if ((in_flags & EF_ARM_VFP_FLOAT) != (out_flags & EF_ARM_VFP_FLOAT)) { if (in_flags & EF_ARM_VFP_FLOAT) _bfd_error_handler (_("ERROR: %B uses VFP instructions, whereas %B does not"), ibfd, obfd); else _bfd_error_handler (_("ERROR: %B uses FPA instructions, whereas %B does not"), ibfd, obfd); flags_compatible = FALSE; } if ((in_flags & EF_ARM_MAVERICK_FLOAT) != (out_flags & EF_ARM_MAVERICK_FLOAT)) { if (in_flags & EF_ARM_MAVERICK_FLOAT) _bfd_error_handler (_("ERROR: %B uses Maverick instructions, whereas %B does not"), ibfd, obfd); else _bfd_error_handler (_("ERROR: %B does not use Maverick instructions, whereas %B does"), ibfd, obfd); flags_compatible = FALSE; } #ifdef EF_ARM_SOFT_FLOAT if ((in_flags & EF_ARM_SOFT_FLOAT) != (out_flags & EF_ARM_SOFT_FLOAT)) { /* We can allow interworking between code that is VFP format layout, and uses either soft float or integer regs for passing floating point arguments and results. We already know that the APCS_FLOAT flags match; similarly for VFP flags. */ if ((in_flags & EF_ARM_APCS_FLOAT) != 0 || (in_flags & EF_ARM_VFP_FLOAT) == 0) { if (in_flags & EF_ARM_SOFT_FLOAT) _bfd_error_handler (_("ERROR: %B uses software FP, whereas %B uses hardware FP"), ibfd, obfd); else _bfd_error_handler (_("ERROR: %B uses hardware FP, whereas %B uses software FP"), ibfd, obfd); flags_compatible = FALSE; } } #endif /* Interworking mismatch is only a warning. */ if ((in_flags & EF_ARM_INTERWORK) != (out_flags & EF_ARM_INTERWORK)) { if (in_flags & EF_ARM_INTERWORK) { _bfd_error_handler (_("Warning: %B supports interworking, whereas %B does not"), ibfd, obfd); } else { _bfd_error_handler (_("Warning: %B does not support interworking, whereas %B does"), ibfd, obfd); } } } return flags_compatible; } /* Display the flags field. */ static bfd_boolean elf32_arm_print_private_bfd_data (bfd *abfd, void * ptr) { FILE * file = (FILE *) ptr; unsigned long flags; BFD_ASSERT (abfd != NULL && ptr != NULL); /* Print normal ELF private data. */ _bfd_elf_print_private_bfd_data (abfd, ptr); flags = elf_elfheader (abfd)->e_flags; /* Ignore init flag - it may not be set, despite the flags field containing valid data. */ /* xgettext:c-format */ fprintf (file, _("private flags = %lx:"), elf_elfheader (abfd)->e_flags); switch (EF_ARM_EABI_VERSION (flags)) { case EF_ARM_EABI_UNKNOWN: /* The following flag bits are GNU extensions and not part of the official ARM ELF extended ABI. Hence they are only decoded if the EABI version is not set. */ if (flags & EF_ARM_INTERWORK) fprintf (file, _(" [interworking enabled]")); if (flags & EF_ARM_APCS_26) fprintf (file, " [APCS-26]"); else fprintf (file, " [APCS-32]"); if (flags & EF_ARM_VFP_FLOAT) fprintf (file, _(" [VFP float format]")); else if (flags & EF_ARM_MAVERICK_FLOAT) fprintf (file, _(" [Maverick float format]")); else fprintf (file, _(" [FPA float format]")); if (flags & EF_ARM_APCS_FLOAT) fprintf (file, _(" [floats passed in float registers]")); if (flags & EF_ARM_PIC) fprintf (file, _(" [position independent]")); if (flags & EF_ARM_NEW_ABI) fprintf (file, _(" [new ABI]")); if (flags & EF_ARM_OLD_ABI) fprintf (file, _(" [old ABI]")); if (flags & EF_ARM_SOFT_FLOAT) fprintf (file, _(" [software FP]")); flags &= ~(EF_ARM_INTERWORK | EF_ARM_APCS_26 | EF_ARM_APCS_FLOAT | EF_ARM_PIC | EF_ARM_NEW_ABI | EF_ARM_OLD_ABI | EF_ARM_SOFT_FLOAT | EF_ARM_VFP_FLOAT | EF_ARM_MAVERICK_FLOAT); break; case EF_ARM_EABI_VER1: fprintf (file, _(" [Version1 EABI]")); if (flags & EF_ARM_SYMSARESORTED) fprintf (file, _(" [sorted symbol table]")); else fprintf (file, _(" [unsorted symbol table]")); flags &= ~ EF_ARM_SYMSARESORTED; break; case EF_ARM_EABI_VER2: fprintf (file, _(" [Version2 EABI]")); if (flags & EF_ARM_SYMSARESORTED) fprintf (file, _(" [sorted symbol table]")); else fprintf (file, _(" [unsorted symbol table]")); if (flags & EF_ARM_DYNSYMSUSESEGIDX) fprintf (file, _(" [dynamic symbols use segment index]")); if (flags & EF_ARM_MAPSYMSFIRST) fprintf (file, _(" [mapping symbols precede others]")); flags &= ~(EF_ARM_SYMSARESORTED | EF_ARM_DYNSYMSUSESEGIDX | EF_ARM_MAPSYMSFIRST); break; case EF_ARM_EABI_VER3: fprintf (file, _(" [Version3 EABI]")); break; case EF_ARM_EABI_VER4: fprintf (file, _(" [Version4 EABI]")); goto eabi; case EF_ARM_EABI_VER5: fprintf (file, _(" [Version5 EABI]")); eabi: if (flags & EF_ARM_BE8) fprintf (file, _(" [BE8]")); if (flags & EF_ARM_LE8) fprintf (file, _(" [LE8]")); flags &= ~(EF_ARM_LE8 | EF_ARM_BE8); break; default: fprintf (file, _(" ")); break; } flags &= ~ EF_ARM_EABIMASK; if (flags & EF_ARM_RELEXEC) fprintf (file, _(" [relocatable executable]")); if (flags & EF_ARM_HASENTRY) fprintf (file, _(" [has entry point]")); flags &= ~ (EF_ARM_RELEXEC | EF_ARM_HASENTRY); if (flags) fprintf (file, _("")); fputc ('\n', file); return TRUE; } static int elf32_arm_get_symbol_type (Elf_Internal_Sym * elf_sym, int type) { switch (ELF_ST_TYPE (elf_sym->st_info)) { case STT_ARM_TFUNC: return ELF_ST_TYPE (elf_sym->st_info); case STT_ARM_16BIT: /* If the symbol is not an object, return the STT_ARM_16BIT flag. This allows us to distinguish between data used by Thumb instructions and non-data (which is probably code) inside Thumb regions of an executable. */ if (type != STT_OBJECT && type != STT_TLS) return ELF_ST_TYPE (elf_sym->st_info); break; default: break; } return type; } static asection * elf32_arm_gc_mark_hook (asection *sec, struct bfd_link_info *info, Elf_Internal_Rela *rel, struct elf_link_hash_entry *h, Elf_Internal_Sym *sym) { if (h != NULL) switch (ELF32_R_TYPE (rel->r_info)) { case R_ARM_GNU_VTINHERIT: case R_ARM_GNU_VTENTRY: return NULL; } return _bfd_elf_gc_mark_hook (sec, info, rel, h, sym); } /* Update the got entry reference counts for the section being removed. */ static bfd_boolean elf32_arm_gc_sweep_hook (bfd * abfd, struct bfd_link_info * info, asection * sec, const Elf_Internal_Rela * relocs) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; bfd_signed_vma *local_got_refcounts; const Elf_Internal_Rela *rel, *relend; struct elf32_arm_link_hash_table * globals; globals = elf32_arm_hash_table (info); elf_section_data (sec)->local_dynrel = NULL; symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); local_got_refcounts = elf_local_got_refcounts (abfd); relend = relocs + sec->reloc_count; for (rel = relocs; rel < relend; rel++) { unsigned long r_symndx; struct elf_link_hash_entry *h = NULL; int r_type; r_symndx = ELF32_R_SYM (rel->r_info); if (r_symndx >= symtab_hdr->sh_info) { h = sym_hashes[r_symndx - symtab_hdr->sh_info]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; } r_type = ELF32_R_TYPE (rel->r_info); r_type = arm_real_reloc_type (globals, r_type); switch (r_type) { case R_ARM_GOT32: case R_ARM_GOT_PREL: case R_ARM_TLS_GD32: case R_ARM_TLS_IE32: if (h != NULL) { if (h->got.refcount > 0) h->got.refcount -= 1; } else if (local_got_refcounts != NULL) { if (local_got_refcounts[r_symndx] > 0) local_got_refcounts[r_symndx] -= 1; } break; case R_ARM_TLS_LDM32: elf32_arm_hash_table (info)->tls_ldm_got.refcount -= 1; break; case R_ARM_ABS32: case R_ARM_ABS32_NOI: case R_ARM_REL32: case R_ARM_REL32_NOI: case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PREL31: case R_ARM_THM_CALL: case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: /* Should the interworking branches be here also? */ if (h != NULL) { struct elf32_arm_link_hash_entry *eh; struct elf32_arm_relocs_copied **pp; struct elf32_arm_relocs_copied *p; eh = (struct elf32_arm_link_hash_entry *) h; if (h->plt.refcount > 0) { h->plt.refcount -= 1; if (ELF32_R_TYPE (rel->r_info) == R_ARM_THM_CALL) eh->plt_thumb_refcount--; } if (r_type == R_ARM_ABS32 || r_type == R_ARM_REL32 || r_type == R_ARM_ABS32_NOI || r_type == R_ARM_REL32_NOI) { for (pp = &eh->relocs_copied; (p = *pp) != NULL; pp = &p->next) if (p->section == sec) { p->count -= 1; if (ELF32_R_TYPE (rel->r_info) == R_ARM_REL32 || ELF32_R_TYPE (rel->r_info) == R_ARM_REL32_NOI) p->pc_count -= 1; if (p->count == 0) *pp = p->next; break; } } } break; default: break; } } return TRUE; } /* Look through the relocs for a section during the first phase. */ static bfd_boolean elf32_arm_check_relocs (bfd *abfd, struct bfd_link_info *info, asection *sec, const Elf_Internal_Rela *relocs) { Elf_Internal_Shdr *symtab_hdr; struct elf_link_hash_entry **sym_hashes; struct elf_link_hash_entry **sym_hashes_end; const Elf_Internal_Rela *rel; const Elf_Internal_Rela *rel_end; bfd *dynobj; asection *sreloc; bfd_vma *local_got_offsets; struct elf32_arm_link_hash_table *htab; if (info->relocatable) return TRUE; htab = elf32_arm_hash_table (info); sreloc = NULL; /* Create dynamic sections for relocatable executables so that we can copy relocations. */ if (htab->root.is_relocatable_executable && ! htab->root.dynamic_sections_created) { if (! _bfd_elf_link_create_dynamic_sections (abfd, info)) return FALSE; } dynobj = elf_hash_table (info)->dynobj; local_got_offsets = elf_local_got_offsets (abfd); symtab_hdr = &elf_tdata (abfd)->symtab_hdr; sym_hashes = elf_sym_hashes (abfd); sym_hashes_end = sym_hashes + symtab_hdr->sh_size / sizeof (Elf32_External_Sym); if (!elf_bad_symtab (abfd)) sym_hashes_end -= symtab_hdr->sh_info; rel_end = relocs + sec->reloc_count; for (rel = relocs; rel < rel_end; rel++) { struct elf_link_hash_entry *h; struct elf32_arm_link_hash_entry *eh; unsigned long r_symndx; int r_type; r_symndx = ELF32_R_SYM (rel->r_info); r_type = ELF32_R_TYPE (rel->r_info); r_type = arm_real_reloc_type (htab, r_type); if (r_symndx >= NUM_SHDR_ENTRIES (symtab_hdr)) { (*_bfd_error_handler) (_("%B: bad symbol index: %d"), abfd, r_symndx); return FALSE; } if (r_symndx < symtab_hdr->sh_info) h = NULL; else { h = sym_hashes[r_symndx - symtab_hdr->sh_info]; while (h->root.type == bfd_link_hash_indirect || h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; } eh = (struct elf32_arm_link_hash_entry *) h; switch (r_type) { case R_ARM_GOT32: case R_ARM_GOT_PREL: case R_ARM_TLS_GD32: case R_ARM_TLS_IE32: /* This symbol requires a global offset table entry. */ { int tls_type, old_tls_type; switch (r_type) { case R_ARM_TLS_GD32: tls_type = GOT_TLS_GD; break; case R_ARM_TLS_IE32: tls_type = GOT_TLS_IE; break; default: tls_type = GOT_NORMAL; break; } if (h != NULL) { h->got.refcount++; old_tls_type = elf32_arm_hash_entry (h)->tls_type; } else { bfd_signed_vma *local_got_refcounts; /* This is a global offset table entry for a local symbol. */ local_got_refcounts = elf_local_got_refcounts (abfd); if (local_got_refcounts == NULL) { bfd_size_type size; size = symtab_hdr->sh_info; size *= (sizeof (bfd_signed_vma) + sizeof(char)); local_got_refcounts = bfd_zalloc (abfd, size); if (local_got_refcounts == NULL) return FALSE; elf_local_got_refcounts (abfd) = local_got_refcounts; elf32_arm_local_got_tls_type (abfd) = (char *) (local_got_refcounts + symtab_hdr->sh_info); } local_got_refcounts[r_symndx] += 1; old_tls_type = elf32_arm_local_got_tls_type (abfd) [r_symndx]; } /* We will already have issued an error message if there is a TLS / non-TLS mismatch, based on the symbol type. We don't support any linker relaxations. So just combine any TLS types needed. */ if (old_tls_type != GOT_UNKNOWN && old_tls_type != GOT_NORMAL && tls_type != GOT_NORMAL) tls_type |= old_tls_type; if (old_tls_type != tls_type) { if (h != NULL) elf32_arm_hash_entry (h)->tls_type = tls_type; else elf32_arm_local_got_tls_type (abfd) [r_symndx] = tls_type; } } /* Fall through */ case R_ARM_TLS_LDM32: if (r_type == R_ARM_TLS_LDM32) htab->tls_ldm_got.refcount++; /* Fall through */ case R_ARM_GOTOFF32: case R_ARM_GOTPC: if (htab->sgot == NULL) { if (htab->root.dynobj == NULL) htab->root.dynobj = abfd; if (!create_got_section (htab->root.dynobj, info)) return FALSE; } break; case R_ARM_ABS12: /* VxWorks uses dynamic R_ARM_ABS12 relocations for ldr __GOTT_INDEX__ offsets. */ if (!htab->vxworks_p) break; /* Fall through */ case R_ARM_ABS32: case R_ARM_ABS32_NOI: case R_ARM_REL32: case R_ARM_REL32_NOI: case R_ARM_PC24: case R_ARM_PLT32: case R_ARM_CALL: case R_ARM_JUMP24: case R_ARM_PREL31: case R_ARM_THM_CALL: case R_ARM_MOVW_ABS_NC: case R_ARM_MOVT_ABS: case R_ARM_MOVW_PREL_NC: case R_ARM_MOVT_PREL: case R_ARM_THM_MOVW_ABS_NC: case R_ARM_THM_MOVT_ABS: case R_ARM_THM_MOVW_PREL_NC: case R_ARM_THM_MOVT_PREL: /* Should the interworking branches be listed here? */ if (h != NULL) { /* If this reloc is in a read-only section, we might need a copy reloc. We can't check reliably at this stage whether the section is read-only, as input sections have not yet been mapped to output sections. Tentatively set the flag for now, and correct in adjust_dynamic_symbol. */ if (!info->shared) h->non_got_ref = 1; /* We may need a .plt entry if the function this reloc refers to is in a different object. We can't tell for sure yet, because something later might force the symbol local. */ if (r_type != R_ARM_ABS32 && r_type != R_ARM_REL32 && r_type != R_ARM_ABS32_NOI && r_type != R_ARM_REL32_NOI && r_type != R_ARM_ABS12) h->needs_plt = 1; /* If we create a PLT entry, this relocation will reference it, even if it's an ABS32 relocation. */ h->plt.refcount += 1; if (r_type == R_ARM_THM_CALL) eh->plt_thumb_refcount += 1; } /* If we are creating a shared library or relocatable executable, and this is a reloc against a global symbol, or a non PC relative reloc against a local symbol, then we need to copy the reloc into the shared library. However, if we are linking with -Bsymbolic, we do not need to copy a reloc against a global symbol which is defined in an object we are including in the link (i.e., DEF_REGULAR is set). At this point we have not seen all the input files, so it is possible that DEF_REGULAR is not set now but will be set later (it is never cleared). We account for that possibility below by storing information in the relocs_copied field of the hash table entry. */ if ((info->shared || htab->root.is_relocatable_executable) && (sec->flags & SEC_ALLOC) != 0 && ((r_type == R_ARM_ABS32 || r_type == R_ARM_ABS32_NOI) || (h != NULL && ! h->needs_plt && (! info->symbolic || ! h->def_regular)))) { struct elf32_arm_relocs_copied *p, **head; /* When creating a shared object, we must copy these reloc types into the output file. We create a reloc section in dynobj and make room for this reloc. */ if (sreloc == NULL) { const char * name; name = (bfd_elf_string_from_elf_section (abfd, elf_elfheader (abfd)->e_shstrndx, elf_section_data (sec)->rel_hdr.sh_name)); if (name == NULL) return FALSE; BFD_ASSERT (reloc_section_p (htab, name, sec)); sreloc = bfd_get_section_by_name (dynobj, name); if (sreloc == NULL) { flagword flags; flags = (SEC_HAS_CONTENTS | SEC_READONLY | SEC_IN_MEMORY | SEC_LINKER_CREATED); if ((sec->flags & SEC_ALLOC) != 0 /* BPABI objects never have dynamic relocations mapped. */ && !htab->symbian_p) flags |= SEC_ALLOC | SEC_LOAD; sreloc = bfd_make_section_with_flags (dynobj, name, flags); if (sreloc == NULL || ! bfd_set_section_alignment (dynobj, sreloc, 2)) return FALSE; } elf_section_data (sec)->sreloc = sreloc; } /* If this is a global symbol, we count the number of relocations we need for this symbol. */ if (h != NULL) { head = &((struct elf32_arm_link_hash_entry *) h)->relocs_copied; } else { /* Track dynamic relocs needed for local syms too. We really need local syms available to do this easily. Oh well. */ asection *s; void *vpp; s = bfd_section_from_r_symndx (abfd, &htab->sym_sec, sec, r_symndx); if (s == NULL) return FALSE; vpp = &elf_section_data (s)->local_dynrel; head = (struct elf32_arm_relocs_copied **) vpp; } p = *head; if (p == NULL || p->section != sec) { bfd_size_type amt = sizeof *p; p = bfd_alloc (htab->root.dynobj, amt); if (p == NULL) return FALSE; p->next = *head; *head = p; p->section = sec; p->count = 0; p->pc_count = 0; } if (r_type == R_ARM_REL32 || r_type == R_ARM_REL32_NOI) p->pc_count += 1; p->count += 1; } break; /* This relocation describes the C++ object vtable hierarchy. Reconstruct it for later use during GC. */ case R_ARM_GNU_VTINHERIT: if (!bfd_elf_gc_record_vtinherit (abfd, sec, h, rel->r_offset)) return FALSE; break; /* This relocation describes which C++ vtable entries are actually used. Record for later use during GC. */ case R_ARM_GNU_VTENTRY: if (!bfd_elf_gc_record_vtentry (abfd, sec, h, rel->r_offset)) return FALSE; break; } } return TRUE; } /* Unwinding tables are not referenced directly. This pass marks them as required if the corresponding code section is marked. */ static bfd_boolean elf32_arm_gc_mark_extra_sections(struct bfd_link_info *info, elf_gc_mark_hook_fn gc_mark_hook) { bfd *sub; Elf_Internal_Shdr **elf_shdrp; bfd_boolean again; /* Marking EH data may cause additional code sections to be marked, requiring multiple passes. */ again = TRUE; while (again) { again = FALSE; for (sub = info->input_bfds; sub != NULL; sub = sub->link_next) { asection *o; if (bfd_get_flavour (sub) != bfd_target_elf_flavour) continue; elf_shdrp = elf_elfsections (sub); for (o = sub->sections; o != NULL; o = o->next) { Elf_Internal_Shdr *hdr; hdr = &elf_section_data (o)->this_hdr; if (hdr->sh_type == SHT_ARM_EXIDX && hdr->sh_link && !o->gc_mark && elf_shdrp[hdr->sh_link]->bfd_section->gc_mark) { again = TRUE; if (!_bfd_elf_gc_mark (info, o, gc_mark_hook)) return FALSE; } } } } return TRUE; } /* Treat mapping symbols as special target symbols. */ static bfd_boolean elf32_arm_is_target_special_symbol (bfd * abfd ATTRIBUTE_UNUSED, asymbol * sym) { return bfd_is_arm_special_symbol_name (sym->name, BFD_ARM_SPECIAL_SYM_TYPE_ANY); } /* This is a copy of elf_find_function() from elf.c except that ARM mapping symbols are ignored when looking for function names and STT_ARM_TFUNC is considered to a function type. */ static bfd_boolean arm_elf_find_function (bfd * abfd ATTRIBUTE_UNUSED, asection * section, asymbol ** symbols, bfd_vma offset, const char ** filename_ptr, const char ** functionname_ptr) { const char * filename = NULL; asymbol * func = NULL; bfd_vma low_func = 0; asymbol ** p; for (p = symbols; *p != NULL; p++) { elf_symbol_type *q; q = (elf_symbol_type *) *p; switch (ELF_ST_TYPE (q->internal_elf_sym.st_info)) { default: break; case STT_FILE: filename = bfd_asymbol_name (&q->symbol); break; case STT_FUNC: case STT_ARM_TFUNC: case STT_NOTYPE: /* Skip mapping symbols. */ if ((q->symbol.flags & BSF_LOCAL) && bfd_is_arm_special_symbol_name (q->symbol.name, BFD_ARM_SPECIAL_SYM_TYPE_ANY)) continue; /* Fall through. */ if (bfd_get_section (&q->symbol) == section && q->symbol.value >= low_func && q->symbol.value <= offset) { func = (asymbol *) q; low_func = q->symbol.value; } break; } } if (func == NULL) return FALSE; if (filename_ptr) *filename_ptr = filename; if (functionname_ptr) *functionname_ptr = bfd_asymbol_name (func); return TRUE; } /* Find the nearest line to a particular section and offset, for error reporting. This code is a duplicate of the code in elf.c, except that it uses arm_elf_find_function. */ static bfd_boolean elf32_arm_find_nearest_line (bfd * abfd, asection * section, asymbol ** symbols, bfd_vma offset, const char ** filename_ptr, const char ** functionname_ptr, unsigned int * line_ptr) { bfd_boolean found = FALSE; /* We skip _bfd_dwarf1_find_nearest_line since no known ARM toolchain uses it. */ if (_bfd_dwarf2_find_nearest_line (abfd, section, symbols, offset, filename_ptr, functionname_ptr, line_ptr, 0, & elf_tdata (abfd)->dwarf2_find_line_info)) { if (!*functionname_ptr) arm_elf_find_function (abfd, section, symbols, offset, *filename_ptr ? NULL : filename_ptr, functionname_ptr); return TRUE; } if (! _bfd_stab_section_find_nearest_line (abfd, symbols, section, offset, & found, filename_ptr, functionname_ptr, line_ptr, & elf_tdata (abfd)->line_info)) return FALSE; if (found && (*functionname_ptr || *line_ptr)) return TRUE; if (symbols == NULL) return FALSE; if (! arm_elf_find_function (abfd, section, symbols, offset, filename_ptr, functionname_ptr)) return FALSE; *line_ptr = 0; return TRUE; } static bfd_boolean elf32_arm_find_inliner_info (bfd * abfd, const char ** filename_ptr, const char ** functionname_ptr, unsigned int * line_ptr) { bfd_boolean found; found = _bfd_dwarf2_find_inliner_info (abfd, filename_ptr, functionname_ptr, line_ptr, & elf_tdata (abfd)->dwarf2_find_line_info); return found; } /* Adjust a symbol defined by a dynamic object and referenced by a regular object. The current definition is in some section of the dynamic object, but we're not including those sections. We have to change the definition to something the rest of the link can understand. */ static bfd_boolean elf32_arm_adjust_dynamic_symbol (struct bfd_link_info * info, struct elf_link_hash_entry * h) { bfd * dynobj; asection * s; struct elf32_arm_link_hash_entry * eh; struct elf32_arm_link_hash_table *globals; globals = elf32_arm_hash_table (info); dynobj = elf_hash_table (info)->dynobj; /* Make sure we know what is going on here. */ BFD_ASSERT (dynobj != NULL && (h->needs_plt || h->u.weakdef != NULL || (h->def_dynamic && h->ref_regular && !h->def_regular))); eh = (struct elf32_arm_link_hash_entry *) h; /* If this is a function, put it in the procedure linkage table. We will fill in the contents of the procedure linkage table later, when we know the address of the .got section. */ if (h->type == STT_FUNC || h->type == STT_ARM_TFUNC || h->needs_plt) { if (h->plt.refcount <= 0 || SYMBOL_CALLS_LOCAL (info, h) || (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT && h->root.type == bfd_link_hash_undefweak)) { /* This case can occur if we saw a PLT32 reloc in an input file, but the symbol was never referred to by a dynamic object, or if all references were garbage collected. In such a case, we don't actually need to build a procedure linkage table, and we can just do a PC24 reloc instead. */ h->plt.offset = (bfd_vma) -1; eh->plt_thumb_refcount = 0; h->needs_plt = 0; } return TRUE; } else { /* It's possible that we incorrectly decided a .plt reloc was needed for an R_ARM_PC24 or similar reloc to a non-function sym in check_relocs. We can't decide accurately between function and non-function syms in check-relocs; Objects loaded later in the link may change h->type. So fix it now. */ h->plt.offset = (bfd_vma) -1; eh->plt_thumb_refcount = 0; } /* If this is a weak symbol, and there is a real definition, the processor independent code will have arranged for us to see the real definition first, and we can just use the same value. */ if (h->u.weakdef != NULL) { BFD_ASSERT (h->u.weakdef->root.type == bfd_link_hash_defined || h->u.weakdef->root.type == bfd_link_hash_defweak); h->root.u.def.section = h->u.weakdef->root.u.def.section; h->root.u.def.value = h->u.weakdef->root.u.def.value; return TRUE; } /* If there are no non-GOT references, we do not need a copy relocation. */ if (!h->non_got_ref) return TRUE; /* This is a reference to a symbol defined by a dynamic object which is not a function. */ /* If we are creating a shared library, we must presume that the only references to the symbol are via the global offset table. For such cases we need not do anything here; the relocations will be handled correctly by relocate_section. Relocatable executables can reference data in shared objects directly, so we don't need to do anything here. */ if (info->shared || globals->root.is_relocatable_executable) return TRUE; if (h->size == 0) { (*_bfd_error_handler) (_("dynamic variable `%s' is zero size"), h->root.root.string); return TRUE; } /* We must allocate the symbol in our .dynbss section, which will become part of the .bss section of the executable. There will be an entry for this symbol in the .dynsym section. The dynamic object will contain position independent code, so all references from the dynamic object to this symbol will go through the global offset table. The dynamic linker will use the .dynsym entry to determine the address it must put in the global offset table, so both the dynamic object and the regular object will refer to the same memory location for the variable. */ s = bfd_get_section_by_name (dynobj, ".dynbss"); BFD_ASSERT (s != NULL); /* We must generate a R_ARM_COPY reloc to tell the dynamic linker to copy the initial value out of the dynamic object and into the runtime process image. We need to remember the offset into the .rel(a).bss section we are going to use. */ if ((h->root.u.def.section->flags & SEC_ALLOC) != 0) { asection *srel; srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (globals, ".bss")); BFD_ASSERT (srel != NULL); srel->size += RELOC_SIZE (globals); h->needs_copy = 1; } return _bfd_elf_adjust_dynamic_copy (h, s); } /* Allocate space in .plt, .got and associated reloc sections for dynamic relocs. */ static bfd_boolean allocate_dynrelocs (struct elf_link_hash_entry *h, void * inf) { struct bfd_link_info *info; struct elf32_arm_link_hash_table *htab; struct elf32_arm_link_hash_entry *eh; struct elf32_arm_relocs_copied *p; eh = (struct elf32_arm_link_hash_entry *) h; if (h->root.type == bfd_link_hash_indirect) return TRUE; if (h->root.type == bfd_link_hash_warning) /* When warning symbols are created, they **replace** the "real" entry in the hash table, thus we never get to see the real symbol in a hash traversal. So look at it now. */ h = (struct elf_link_hash_entry *) h->root.u.i.link; info = (struct bfd_link_info *) inf; htab = elf32_arm_hash_table (info); if (htab->root.dynamic_sections_created && h->plt.refcount > 0) { /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ if (h->dynindx == -1 && !h->forced_local) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } if (info->shared || WILL_CALL_FINISH_DYNAMIC_SYMBOL (1, 0, h)) { asection *s = htab->splt; /* If this is the first .plt entry, make room for the special first entry. */ if (s->size == 0) s->size += htab->plt_header_size; h->plt.offset = s->size; /* If we will insert a Thumb trampoline before this PLT, leave room for it. */ if (!htab->use_blx && eh->plt_thumb_refcount > 0) { h->plt.offset += PLT_THUMB_STUB_SIZE; s->size += PLT_THUMB_STUB_SIZE; } /* If this symbol is not defined in a regular file, and we are not generating a shared library, then set the symbol to this location in the .plt. This is required to make function pointers compare as equal between the normal executable and the shared library. */ if (! info->shared && !h->def_regular) { h->root.u.def.section = s; h->root.u.def.value = h->plt.offset; /* Make sure the function is not marked as Thumb, in case it is the target of an ABS32 relocation, which will point to the PLT entry. */ if (ELF_ST_TYPE (h->type) == STT_ARM_TFUNC) h->type = ELF_ST_INFO (ELF_ST_BIND (h->type), STT_FUNC); } /* Make room for this entry. */ s->size += htab->plt_entry_size; if (!htab->symbian_p) { /* We also need to make an entry in the .got.plt section, which will be placed in the .got section by the linker script. */ eh->plt_got_offset = htab->sgotplt->size; htab->sgotplt->size += 4; } /* We also need to make an entry in the .rel(a).plt section. */ htab->srelplt->size += RELOC_SIZE (htab); /* VxWorks executables have a second set of relocations for each PLT entry. They go in a separate relocation section, which is processed by the kernel loader. */ if (htab->vxworks_p && !info->shared) { /* There is a relocation for the initial PLT entry: an R_ARM_32 relocation for _GLOBAL_OFFSET_TABLE_. */ if (h->plt.offset == htab->plt_header_size) htab->srelplt2->size += RELOC_SIZE (htab); /* There are two extra relocations for each subsequent PLT entry: an R_ARM_32 relocation for the GOT entry, and an R_ARM_32 relocation for the PLT entry. */ htab->srelplt2->size += RELOC_SIZE (htab) * 2; } } else { h->plt.offset = (bfd_vma) -1; h->needs_plt = 0; } } else { h->plt.offset = (bfd_vma) -1; h->needs_plt = 0; } if (h->got.refcount > 0) { asection *s; bfd_boolean dyn; int tls_type = elf32_arm_hash_entry (h)->tls_type; int indx; /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ if (h->dynindx == -1 && !h->forced_local) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } if (!htab->symbian_p) { s = htab->sgot; h->got.offset = s->size; if (tls_type == GOT_UNKNOWN) abort (); if (tls_type == GOT_NORMAL) /* Non-TLS symbols need one GOT slot. */ s->size += 4; else { if (tls_type & GOT_TLS_GD) /* R_ARM_TLS_GD32 needs 2 consecutive GOT slots. */ s->size += 8; if (tls_type & GOT_TLS_IE) /* R_ARM_TLS_IE32 needs one GOT slot. */ s->size += 4; } dyn = htab->root.dynamic_sections_created; indx = 0; if (WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, info->shared, h) && (!info->shared || !SYMBOL_REFERENCES_LOCAL (info, h))) indx = h->dynindx; if (tls_type != GOT_NORMAL && (info->shared || indx != 0) && (ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak)) { if (tls_type & GOT_TLS_IE) htab->srelgot->size += RELOC_SIZE (htab); if (tls_type & GOT_TLS_GD) htab->srelgot->size += RELOC_SIZE (htab); if ((tls_type & GOT_TLS_GD) && indx != 0) htab->srelgot->size += RELOC_SIZE (htab); } else if ((ELF_ST_VISIBILITY (h->other) == STV_DEFAULT || h->root.type != bfd_link_hash_undefweak) && (info->shared || WILL_CALL_FINISH_DYNAMIC_SYMBOL (dyn, 0, h))) htab->srelgot->size += RELOC_SIZE (htab); } } else h->got.offset = (bfd_vma) -1; /* Allocate stubs for exported Thumb functions on v4t. */ if (!htab->use_blx && h->dynindx != -1 && h->def_regular && ELF_ST_TYPE (h->type) == STT_ARM_TFUNC && ELF_ST_VISIBILITY (h->other) == STV_DEFAULT) { struct elf_link_hash_entry * th; struct bfd_link_hash_entry * bh; struct elf_link_hash_entry * myh; char name[1024]; asection *s; bh = NULL; /* Create a new symbol to regist the real location of the function. */ s = h->root.u.def.section; sprintf(name, "__real_%s", h->root.root.string); _bfd_generic_link_add_one_symbol (info, s->owner, name, BSF_GLOBAL, s, h->root.u.def.value, NULL, TRUE, FALSE, &bh); myh = (struct elf_link_hash_entry *) bh; myh->type = ELF_ST_INFO (STB_LOCAL, STT_ARM_TFUNC); myh->forced_local = 1; eh->export_glue = myh; th = record_arm_to_thumb_glue (info, h); /* Point the symbol at the stub. */ h->type = ELF_ST_INFO (ELF_ST_BIND (h->type), STT_FUNC); h->root.u.def.section = th->root.u.def.section; h->root.u.def.value = th->root.u.def.value & ~1; } if (eh->relocs_copied == NULL) return TRUE; /* In the shared -Bsymbolic case, discard space allocated for dynamic pc-relative relocs against symbols which turn out to be defined in regular objects. For the normal shared case, discard space for pc-relative relocs that have become local due to symbol visibility changes. */ if (info->shared || htab->root.is_relocatable_executable) { /* The only relocs that use pc_count are R_ARM_REL32 and R_ARM_REL32_NOI, which will appear on something like ".long foo - .". We want calls to protected symbols to resolve directly to the function rather than going via the plt. If people want function pointer comparisons to work as expected then they should avoid writing assembly like ".long foo - .". */ if (SYMBOL_CALLS_LOCAL (info, h)) { struct elf32_arm_relocs_copied **pp; for (pp = &eh->relocs_copied; (p = *pp) != NULL; ) { p->count -= p->pc_count; p->pc_count = 0; if (p->count == 0) *pp = p->next; else pp = &p->next; } } /* Also discard relocs on undefined weak syms with non-default visibility. */ if (eh->relocs_copied != NULL && h->root.type == bfd_link_hash_undefweak) { if (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT) eh->relocs_copied = NULL; /* Make sure undefined weak symbols are output as a dynamic symbol in PIEs. */ else if (h->dynindx == -1 && !h->forced_local) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } } else if (htab->root.is_relocatable_executable && h->dynindx == -1 && h->root.type == bfd_link_hash_new) { /* Output absolute symbols so that we can create relocations against them. For normal symbols we output a relocation against the section that contains them. */ if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } } else { /* For the non-shared case, discard space for relocs against symbols which turn out to need copy relocs or are not dynamic. */ if (!h->non_got_ref && ((h->def_dynamic && !h->def_regular) || (htab->root.dynamic_sections_created && (h->root.type == bfd_link_hash_undefweak || h->root.type == bfd_link_hash_undefined)))) { /* Make sure this symbol is output as a dynamic symbol. Undefined weak syms won't yet be marked as dynamic. */ if (h->dynindx == -1 && !h->forced_local) { if (! bfd_elf_link_record_dynamic_symbol (info, h)) return FALSE; } /* If that succeeded, we know we'll be keeping all the relocs. */ if (h->dynindx != -1) goto keep; } eh->relocs_copied = NULL; keep: ; } /* Finally, allocate space. */ for (p = eh->relocs_copied; p != NULL; p = p->next) { asection *sreloc = elf_section_data (p->section)->sreloc; sreloc->size += p->count * RELOC_SIZE (htab); } return TRUE; } /* Find any dynamic relocs that apply to read-only sections. */ static bfd_boolean elf32_arm_readonly_dynrelocs (struct elf_link_hash_entry *h, PTR inf) { struct elf32_arm_link_hash_entry *eh; struct elf32_arm_relocs_copied *p; if (h->root.type == bfd_link_hash_warning) h = (struct elf_link_hash_entry *) h->root.u.i.link; eh = (struct elf32_arm_link_hash_entry *) h; for (p = eh->relocs_copied; p != NULL; p = p->next) { asection *s = p->section; if (s != NULL && (s->flags & SEC_READONLY) != 0) { struct bfd_link_info *info = (struct bfd_link_info *) inf; info->flags |= DF_TEXTREL; /* Not an error, just cut short the traversal. */ return FALSE; } } return TRUE; } void bfd_elf32_arm_set_byteswap_code (struct bfd_link_info *info, int byteswap_code) { struct elf32_arm_link_hash_table *globals; globals = elf32_arm_hash_table (info); globals->byteswap_code = byteswap_code; } /* Set the sizes of the dynamic sections. */ static bfd_boolean elf32_arm_size_dynamic_sections (bfd * output_bfd ATTRIBUTE_UNUSED, struct bfd_link_info * info) { bfd * dynobj; asection * s; bfd_boolean plt; bfd_boolean relocs; bfd *ibfd; struct elf32_arm_link_hash_table *htab; htab = elf32_arm_hash_table (info); dynobj = elf_hash_table (info)->dynobj; BFD_ASSERT (dynobj != NULL); check_use_blx (htab); if (elf_hash_table (info)->dynamic_sections_created) { /* Set the contents of the .interp section to the interpreter. */ if (info->executable) { s = bfd_get_section_by_name (dynobj, ".interp"); BFD_ASSERT (s != NULL); s->size = sizeof ELF_DYNAMIC_INTERPRETER; s->contents = (unsigned char *) ELF_DYNAMIC_INTERPRETER; } } /* Set up .got offsets for local syms, and space for local dynamic relocs. */ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) { bfd_signed_vma *local_got; bfd_signed_vma *end_local_got; char *local_tls_type; bfd_size_type locsymcount; Elf_Internal_Shdr *symtab_hdr; asection *srel; if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour) continue; for (s = ibfd->sections; s != NULL; s = s->next) { struct elf32_arm_relocs_copied *p; for (p = elf_section_data (s)->local_dynrel; p != NULL; p = p->next) { if (!bfd_is_abs_section (p->section) && bfd_is_abs_section (p->section->output_section)) { /* Input section has been discarded, either because it is a copy of a linkonce section or due to linker script /DISCARD/, so we'll be discarding the relocs too. */ } else if (p->count != 0) { srel = elf_section_data (p->section)->sreloc; srel->size += p->count * RELOC_SIZE (htab); if ((p->section->output_section->flags & SEC_READONLY) != 0) info->flags |= DF_TEXTREL; } } } local_got = elf_local_got_refcounts (ibfd); if (!local_got) continue; symtab_hdr = &elf_tdata (ibfd)->symtab_hdr; locsymcount = symtab_hdr->sh_info; end_local_got = local_got + locsymcount; local_tls_type = elf32_arm_local_got_tls_type (ibfd); s = htab->sgot; srel = htab->srelgot; for (; local_got < end_local_got; ++local_got, ++local_tls_type) { if (*local_got > 0) { *local_got = s->size; if (*local_tls_type & GOT_TLS_GD) /* TLS_GD relocs need an 8-byte structure in the GOT. */ s->size += 8; if (*local_tls_type & GOT_TLS_IE) s->size += 4; if (*local_tls_type == GOT_NORMAL) s->size += 4; if (info->shared || *local_tls_type == GOT_TLS_GD) srel->size += RELOC_SIZE (htab); } else *local_got = (bfd_vma) -1; } } if (htab->tls_ldm_got.refcount > 0) { /* Allocate two GOT entries and one dynamic relocation (if necessary) for R_ARM_TLS_LDM32 relocations. */ htab->tls_ldm_got.offset = htab->sgot->size; htab->sgot->size += 8; if (info->shared) htab->srelgot->size += RELOC_SIZE (htab); } else htab->tls_ldm_got.offset = -1; /* Allocate global sym .plt and .got entries, and space for global sym dynamic relocs. */ elf_link_hash_traverse (& htab->root, allocate_dynrelocs, info); /* Here we rummage through the found bfds to collect glue information. */ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next) { /* Initialise mapping tables for code/data. */ bfd_elf32_arm_init_maps (ibfd); if (!bfd_elf32_arm_process_before_allocation (ibfd, info) || !bfd_elf32_arm_vfp11_erratum_scan (ibfd, info)) /* xgettext:c-format */ _bfd_error_handler (_("Errors encountered processing file %s"), ibfd->filename); } /* The check_relocs and adjust_dynamic_symbol entry points have determined the sizes of the various dynamic sections. Allocate memory for them. */ plt = FALSE; relocs = FALSE; for (s = dynobj->sections; s != NULL; s = s->next) { const char * name; if ((s->flags & SEC_LINKER_CREATED) == 0) continue; /* It's OK to base decisions on the section name, because none of the dynobj section names depend upon the input files. */ name = bfd_get_section_name (dynobj, s); if (strcmp (name, ".plt") == 0) { /* Remember whether there is a PLT. */ plt = s->size != 0; } else if (CONST_STRNEQ (name, ".rel")) { if (s->size != 0) { /* Remember whether there are any reloc sections other than .rel(a).plt and .rela.plt.unloaded. */ if (s != htab->srelplt && s != htab->srelplt2) relocs = TRUE; /* We use the reloc_count field as a counter if we need to copy relocs into the output file. */ s->reloc_count = 0; } } else if (! CONST_STRNEQ (name, ".got") && strcmp (name, ".dynbss") != 0) { /* It's not one of our sections, so don't allocate space. */ continue; } if (s->size == 0) { /* If we don't need this section, strip it from the output file. This is mostly to handle .rel(a).bss and .rel(a).plt. We must create both sections in create_dynamic_sections, because they must be created before the linker maps input sections to output sections. The linker does that before adjust_dynamic_symbol is called, and it is that function which decides whether anything needs to go into these sections. */ s->flags |= SEC_EXCLUDE; continue; } if ((s->flags & SEC_HAS_CONTENTS) == 0) continue; /* Allocate memory for the section contents. */ s->contents = (bfd_byte *) bfd_zalloc (dynobj, s->size); if (s->contents == NULL) return FALSE; } if (elf_hash_table (info)->dynamic_sections_created) { /* Add some entries to the .dynamic section. We fill in the values later, in elf32_arm_finish_dynamic_sections, but we must add the entries now so that we get the correct size for the .dynamic section. The DT_DEBUG entry is filled in by the dynamic linker and used by the debugger. */ #define add_dynamic_entry(TAG, VAL) \ _bfd_elf_add_dynamic_entry (info, TAG, VAL) if (info->executable) { if (!add_dynamic_entry (DT_DEBUG, 0)) return FALSE; } if (plt) { if ( !add_dynamic_entry (DT_PLTGOT, 0) || !add_dynamic_entry (DT_PLTRELSZ, 0) || !add_dynamic_entry (DT_PLTREL, htab->use_rel ? DT_REL : DT_RELA) || !add_dynamic_entry (DT_JMPREL, 0)) return FALSE; } if (relocs) { if (htab->use_rel) { if (!add_dynamic_entry (DT_REL, 0) || !add_dynamic_entry (DT_RELSZ, 0) || !add_dynamic_entry (DT_RELENT, RELOC_SIZE (htab))) return FALSE; } else { if (!add_dynamic_entry (DT_RELA, 0) || !add_dynamic_entry (DT_RELASZ, 0) || !add_dynamic_entry (DT_RELAENT, RELOC_SIZE (htab))) return FALSE; } } /* If any dynamic relocs apply to a read-only section, then we need a DT_TEXTREL entry. */ if ((info->flags & DF_TEXTREL) == 0) elf_link_hash_traverse (&htab->root, elf32_arm_readonly_dynrelocs, (PTR) info); if ((info->flags & DF_TEXTREL) != 0) { if (!add_dynamic_entry (DT_TEXTREL, 0)) return FALSE; } } #undef add_dynamic_entry return TRUE; } /* Finish up dynamic symbol handling. We set the contents of various dynamic sections here. */ static bfd_boolean elf32_arm_finish_dynamic_symbol (bfd * output_bfd, struct bfd_link_info * info, struct elf_link_hash_entry * h, Elf_Internal_Sym * sym) { bfd * dynobj; struct elf32_arm_link_hash_table *htab; struct elf32_arm_link_hash_entry *eh; dynobj = elf_hash_table (info)->dynobj; htab = elf32_arm_hash_table (info); eh = (struct elf32_arm_link_hash_entry *) h; if (h->plt.offset != (bfd_vma) -1) { asection * splt; asection * srel; bfd_byte *loc; bfd_vma plt_index; Elf_Internal_Rela rel; /* This symbol has an entry in the procedure linkage table. Set it up. */ BFD_ASSERT (h->dynindx != -1); splt = bfd_get_section_by_name (dynobj, ".plt"); srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".plt")); BFD_ASSERT (splt != NULL && srel != NULL); /* Fill in the entry in the procedure linkage table. */ if (htab->symbian_p) { put_arm_insn (htab, output_bfd, elf32_arm_symbian_plt_entry[0], splt->contents + h->plt.offset); bfd_put_32 (output_bfd, elf32_arm_symbian_plt_entry[1], splt->contents + h->plt.offset + 4); /* Fill in the entry in the .rel.plt section. */ rel.r_offset = (splt->output_section->vma + splt->output_offset + h->plt.offset + 4); rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_GLOB_DAT); /* Get the index in the procedure linkage table which corresponds to this symbol. This is the index of this symbol in all the symbols for which we are making plt entries. The first entry in the procedure linkage table is reserved. */ plt_index = ((h->plt.offset - htab->plt_header_size) / htab->plt_entry_size); } else { bfd_vma got_offset, got_address, plt_address; bfd_vma got_displacement; asection * sgot; bfd_byte * ptr; sgot = bfd_get_section_by_name (dynobj, ".got.plt"); BFD_ASSERT (sgot != NULL); /* Get the offset into the .got.plt table of the entry that corresponds to this function. */ got_offset = eh->plt_got_offset; /* Get the index in the procedure linkage table which corresponds to this symbol. This is the index of this symbol in all the symbols for which we are making plt entries. The first three entries in .got.plt are reserved; after that symbols appear in the same order as in .plt. */ plt_index = (got_offset - 12) / 4; /* Calculate the address of the GOT entry. */ got_address = (sgot->output_section->vma + sgot->output_offset + got_offset); /* ...and the address of the PLT entry. */ plt_address = (splt->output_section->vma + splt->output_offset + h->plt.offset); ptr = htab->splt->contents + h->plt.offset; if (htab->vxworks_p && info->shared) { unsigned int i; bfd_vma val; for (i = 0; i != htab->plt_entry_size / 4; i++, ptr += 4) { val = elf32_arm_vxworks_shared_plt_entry[i]; if (i == 2) val |= got_address - sgot->output_section->vma; if (i == 5) val |= plt_index * RELOC_SIZE (htab); if (i == 2 || i == 5) bfd_put_32 (output_bfd, val, ptr); else put_arm_insn (htab, output_bfd, val, ptr); } } else if (htab->vxworks_p) { unsigned int i; bfd_vma val; for (i = 0; i != htab->plt_entry_size / 4; i++, ptr += 4) { val = elf32_arm_vxworks_exec_plt_entry[i]; if (i == 2) val |= got_address; if (i == 4) val |= 0xffffff & -((h->plt.offset + i * 4 + 8) >> 2); if (i == 5) val |= plt_index * RELOC_SIZE (htab); if (i == 2 || i == 5) bfd_put_32 (output_bfd, val, ptr); else put_arm_insn (htab, output_bfd, val, ptr); } loc = (htab->srelplt2->contents + (plt_index * 2 + 1) * RELOC_SIZE (htab)); /* Create the .rela.plt.unloaded R_ARM_ABS32 relocation referencing the GOT for this PLT entry. */ rel.r_offset = plt_address + 8; rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32); rel.r_addend = got_offset; SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); loc += RELOC_SIZE (htab); /* Create the R_ARM_ABS32 relocation referencing the beginning of the PLT for this GOT entry. */ rel.r_offset = got_address; rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_ARM_ABS32); rel.r_addend = 0; SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); } else { /* Calculate the displacement between the PLT slot and the entry in the GOT. The eight-byte offset accounts for the value produced by adding to pc in the first instruction of the PLT stub. */ got_displacement = got_address - (plt_address + 8); BFD_ASSERT ((got_displacement & 0xf0000000) == 0); if (!htab->use_blx && eh->plt_thumb_refcount > 0) { put_thumb_insn (htab, output_bfd, elf32_arm_plt_thumb_stub[0], ptr - 4); put_thumb_insn (htab, output_bfd, elf32_arm_plt_thumb_stub[1], ptr - 2); } put_arm_insn (htab, output_bfd, elf32_arm_plt_entry[0] | ((got_displacement & 0x0ff00000) >> 20), ptr + 0); put_arm_insn (htab, output_bfd, elf32_arm_plt_entry[1] | ((got_displacement & 0x000ff000) >> 12), ptr+ 4); put_arm_insn (htab, output_bfd, elf32_arm_plt_entry[2] | (got_displacement & 0x00000fff), ptr + 8); #ifdef FOUR_WORD_PLT bfd_put_32 (output_bfd, elf32_arm_plt_entry[3], ptr + 12); #endif } /* Fill in the entry in the global offset table. */ bfd_put_32 (output_bfd, (splt->output_section->vma + splt->output_offset), sgot->contents + got_offset); /* Fill in the entry in the .rel(a).plt section. */ rel.r_addend = 0; rel.r_offset = got_address; rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_JUMP_SLOT); } loc = srel->contents + plt_index * RELOC_SIZE (htab); SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); if (!h->def_regular) { /* Mark the symbol as undefined, rather than as defined in the .plt section. Leave the value alone. */ sym->st_shndx = SHN_UNDEF; /* If the symbol is weak, we do need to clear the value. Otherwise, the PLT entry would provide a definition for the symbol even if the symbol wasn't defined anywhere, and so the symbol would never be NULL. */ if (!h->ref_regular_nonweak) sym->st_value = 0; } } if (h->got.offset != (bfd_vma) -1 && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_GD) == 0 && (elf32_arm_hash_entry (h)->tls_type & GOT_TLS_IE) == 0) { asection * sgot; asection * srel; Elf_Internal_Rela rel; bfd_byte *loc; bfd_vma offset; /* This symbol has an entry in the global offset table. Set it up. */ sgot = bfd_get_section_by_name (dynobj, ".got"); srel = bfd_get_section_by_name (dynobj, RELOC_SECTION (htab, ".got")); BFD_ASSERT (sgot != NULL && srel != NULL); offset = (h->got.offset & ~(bfd_vma) 1); rel.r_addend = 0; rel.r_offset = (sgot->output_section->vma + sgot->output_offset + offset); /* If this is a static link, or it is a -Bsymbolic link and the symbol is defined locally or was forced to be local because of a version file, we just want to emit a RELATIVE reloc. The entry in the global offset table will already have been initialized in the relocate_section function. */ if (info->shared && SYMBOL_REFERENCES_LOCAL (info, h)) { BFD_ASSERT((h->got.offset & 1) != 0); rel.r_info = ELF32_R_INFO (0, R_ARM_RELATIVE); if (!htab->use_rel) { rel.r_addend = bfd_get_32 (output_bfd, sgot->contents + offset); bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset); } } else { BFD_ASSERT((h->got.offset & 1) == 0); bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + offset); rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_GLOB_DAT); } loc = srel->contents + srel->reloc_count++ * RELOC_SIZE (htab); SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); } if (h->needs_copy) { asection * s; Elf_Internal_Rela rel; bfd_byte *loc; /* This symbol needs a copy reloc. Set it up. */ BFD_ASSERT (h->dynindx != -1 && (h->root.type == bfd_link_hash_defined || h->root.type == bfd_link_hash_defweak)); s = bfd_get_section_by_name (h->root.u.def.section->owner, RELOC_SECTION (htab, ".bss")); BFD_ASSERT (s != NULL); rel.r_addend = 0; rel.r_offset = (h->root.u.def.value + h->root.u.def.section->output_section->vma + h->root.u.def.section->output_offset); rel.r_info = ELF32_R_INFO (h->dynindx, R_ARM_COPY); loc = s->contents + s->reloc_count++ * RELOC_SIZE (htab); SWAP_RELOC_OUT (htab) (output_bfd, &rel, loc); } /* Mark _DYNAMIC and _GLOBAL_OFFSET_TABLE_ as absolute. On VxWorks, the _GLOBAL_OFFSET_TABLE_ symbol is not absolute: it is relative to the ".got" section. */ if (strcmp (h->root.root.string, "_DYNAMIC") == 0 || (!htab->vxworks_p && h == htab->root.hgot)) sym->st_shndx = SHN_ABS; return TRUE; } /* Finish up the dynamic sections. */ static bfd_boolean elf32_arm_finish_dynamic_sections (bfd * output_bfd, struct bfd_link_info * info) { bfd * dynobj; asection * sgot; asection * sdyn; dynobj = elf_hash_table (info)->dynobj; sgot = bfd_get_section_by_name (dynobj, ".got.plt"); BFD_ASSERT (elf32_arm_hash_table (info)->symbian_p || sgot != NULL); sdyn = bfd_get_section_by_name (dynobj, ".dynamic"); if (elf_hash_table (info)->dynamic_sections_created) { asection *splt; Elf32_External_Dyn *dyncon, *dynconend; struct elf32_arm_link_hash_table *htab; htab = elf32_arm_hash_table (info); splt = bfd_get_section_by_name (dynobj, ".plt"); BFD_ASSERT (splt != NULL && sdyn != NULL); dyncon = (Elf32_External_Dyn *) sdyn->contents; dynconend = (Elf32_External_Dyn *) (sdyn->contents + sdyn->size); for (; dyncon < dynconend; dyncon++) { Elf_Internal_Dyn dyn; const char * name; asection * s; bfd_elf32_swap_dyn_in (dynobj, dyncon, &dyn); switch (dyn.d_tag) { unsigned int type; default: break; case DT_HASH: name = ".hash"; goto get_vma_if_bpabi; case DT_STRTAB: name = ".dynstr"; goto get_vma_if_bpabi; case DT_SYMTAB: name = ".dynsym"; goto get_vma_if_bpabi; case DT_VERSYM: name = ".gnu.version"; goto get_vma_if_bpabi; case DT_VERDEF: name = ".gnu.version_d"; goto get_vma_if_bpabi; case DT_VERNEED: name = ".gnu.version_r"; goto get_vma_if_bpabi; case DT_PLTGOT: name = ".got"; goto get_vma; case DT_JMPREL: name = RELOC_SECTION (htab, ".plt"); get_vma: s = bfd_get_section_by_name (output_bfd, name); BFD_ASSERT (s != NULL); if (!htab->symbian_p) dyn.d_un.d_ptr = s->vma; else /* In the BPABI, tags in the PT_DYNAMIC section point at the file offset, not the memory address, for the convenience of the post linker. */ dyn.d_un.d_ptr = s->filepos; bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); break; get_vma_if_bpabi: if (htab->symbian_p) goto get_vma; break; case DT_PLTRELSZ: s = bfd_get_section_by_name (output_bfd, RELOC_SECTION (htab, ".plt")); BFD_ASSERT (s != NULL); dyn.d_un.d_val = s->size; bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); break; case DT_RELSZ: case DT_RELASZ: if (!htab->symbian_p) { /* My reading of the SVR4 ABI indicates that the procedure linkage table relocs (DT_JMPREL) should be included in the overall relocs (DT_REL). This is what Solaris does. However, UnixWare can not handle that case. Therefore, we override the DT_RELSZ entry here to make it not include the JMPREL relocs. Since the linker script arranges for .rel(a).plt to follow all other relocation sections, we don't have to worry about changing the DT_REL entry. */ s = bfd_get_section_by_name (output_bfd, RELOC_SECTION (htab, ".plt")); if (s != NULL) dyn.d_un.d_val -= s->size; bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); break; } /* Fall through */ case DT_REL: case DT_RELA: /* In the BPABI, the DT_REL tag must point at the file offset, not the VMA, of the first relocation section. So, we use code similar to that in elflink.c, but do not check for SHF_ALLOC on the relcoation section, since relocations sections are never allocated under the BPABI. The comments above about Unixware notwithstanding, we include all of the relocations here. */ if (htab->symbian_p) { unsigned int i; type = ((dyn.d_tag == DT_REL || dyn.d_tag == DT_RELSZ) ? SHT_REL : SHT_RELA); dyn.d_un.d_val = 0; for (i = 1; i < elf_numsections (output_bfd); i++) { Elf_Internal_Shdr *hdr = elf_elfsections (output_bfd)[i]; if (hdr->sh_type == type) { if (dyn.d_tag == DT_RELSZ || dyn.d_tag == DT_RELASZ) dyn.d_un.d_val += hdr->sh_size; else if ((ufile_ptr) hdr->sh_offset <= dyn.d_un.d_val - 1) dyn.d_un.d_val = hdr->sh_offset; } } bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); } break; /* Set the bottom bit of DT_INIT/FINI if the corresponding function is Thumb. */ case DT_INIT: name = info->init_function; goto get_sym; case DT_FINI: name = info->fini_function; get_sym: /* If it wasn't set by elf_bfd_final_link then there is nothing to adjust. */ if (dyn.d_un.d_val != 0) { struct elf_link_hash_entry * eh; eh = elf_link_hash_lookup (elf_hash_table (info), name, FALSE, FALSE, TRUE); if (eh != (struct elf_link_hash_entry *) NULL && ELF_ST_TYPE (eh->type) == STT_ARM_TFUNC) { dyn.d_un.d_val |= 1; bfd_elf32_swap_dyn_out (output_bfd, &dyn, dyncon); } } break; } } /* Fill in the first entry in the procedure linkage table. */ if (splt->size > 0 && elf32_arm_hash_table (info)->plt_header_size) { const bfd_vma *plt0_entry; bfd_vma got_address, plt_address, got_displacement; /* Calculate the addresses of the GOT and PLT. */ got_address = sgot->output_section->vma + sgot->output_offset; plt_address = splt->output_section->vma + splt->output_offset; if (htab->vxworks_p) { /* The VxWorks GOT is relocated by the dynamic linker. Therefore, we must emit relocations rather than simply computing the values now. */ Elf_Internal_Rela rel; plt0_entry = elf32_arm_vxworks_exec_plt0_entry; put_arm_insn (htab, output_bfd, plt0_entry[0], splt->contents + 0); put_arm_insn (htab, output_bfd, plt0_entry[1], splt->contents + 4); put_arm_insn (htab, output_bfd, plt0_entry[2], splt->contents + 8); bfd_put_32 (output_bfd, got_address, splt->contents + 12); /* Generate a relocation for _GLOBAL_OFFSET_TABLE_. */ rel.r_offset = plt_address + 12; rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32); rel.r_addend = 0; SWAP_RELOC_OUT (htab) (output_bfd, &rel, htab->srelplt2->contents); } else { got_displacement = got_address - (plt_address + 16); plt0_entry = elf32_arm_plt0_entry; put_arm_insn (htab, output_bfd, plt0_entry[0], splt->contents + 0); put_arm_insn (htab, output_bfd, plt0_entry[1], splt->contents + 4); put_arm_insn (htab, output_bfd, plt0_entry[2], splt->contents + 8); put_arm_insn (htab, output_bfd, plt0_entry[3], splt->contents + 12); #ifdef FOUR_WORD_PLT /* The displacement value goes in the otherwise-unused last word of the second entry. */ bfd_put_32 (output_bfd, got_displacement, splt->contents + 28); #else bfd_put_32 (output_bfd, got_displacement, splt->contents + 16); #endif } } /* UnixWare sets the entsize of .plt to 4, although that doesn't really seem like the right value. */ if (splt->output_section->owner == output_bfd) elf_section_data (splt->output_section)->this_hdr.sh_entsize = 4; if (htab->vxworks_p && !info->shared && htab->splt->size > 0) { /* Correct the .rel(a).plt.unloaded relocations. They will have incorrect symbol indexes. */ int num_plts; unsigned char *p; num_plts = ((htab->splt->size - htab->plt_header_size) / htab->plt_entry_size); p = htab->srelplt2->contents + RELOC_SIZE (htab); for (; num_plts; num_plts--) { Elf_Internal_Rela rel; SWAP_RELOC_IN (htab) (output_bfd, p, &rel); rel.r_info = ELF32_R_INFO (htab->root.hgot->indx, R_ARM_ABS32); SWAP_RELOC_OUT (htab) (output_bfd, &rel, p); p += RELOC_SIZE (htab); SWAP_RELOC_IN (htab) (output_bfd, p, &rel); rel.r_info = ELF32_R_INFO (htab->root.hplt->indx, R_ARM_ABS32); SWAP_RELOC_OUT (htab) (output_bfd, &rel, p); p += RELOC_SIZE (htab); } } } /* Fill in the first three entries in the global offset table. */ if (sgot) { if (sgot->size > 0) { if (sdyn == NULL) bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents); else bfd_put_32 (output_bfd, sdyn->output_section->vma + sdyn->output_offset, sgot->contents); bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 4); bfd_put_32 (output_bfd, (bfd_vma) 0, sgot->contents + 8); } elf_section_data (sgot->output_section)->this_hdr.sh_entsize = 4; } return TRUE; } static void elf32_arm_post_process_headers (bfd * abfd, struct bfd_link_info * link_info ATTRIBUTE_UNUSED) { Elf_Internal_Ehdr * i_ehdrp; /* ELF file header, internal form. */ struct elf32_arm_link_hash_table *globals; i_ehdrp = elf_elfheader (abfd); if (EF_ARM_EABI_VERSION (i_ehdrp->e_flags) == EF_ARM_EABI_UNKNOWN) i_ehdrp->e_ident[EI_OSABI] = ARM_ELF_OS_ABI_VERSION; else i_ehdrp->e_ident[EI_OSABI] = 0; i_ehdrp->e_ident[EI_ABIVERSION] = ARM_ELF_ABI_VERSION; if (link_info) { globals = elf32_arm_hash_table (link_info); if (globals->byteswap_code) i_ehdrp->e_flags |= EF_ARM_BE8; } } static enum elf_reloc_type_class elf32_arm_reloc_type_class (const Elf_Internal_Rela *rela) { switch ((int) ELF32_R_TYPE (rela->r_info)) { case R_ARM_RELATIVE: return reloc_class_relative; case R_ARM_JUMP_SLOT: return reloc_class_plt; case R_ARM_COPY: return reloc_class_copy; default: return reloc_class_normal; } } /* Set the right machine number for an Arm ELF file. */ static bfd_boolean elf32_arm_section_flags (flagword *flags, const Elf_Internal_Shdr *hdr) { if (hdr->sh_type == SHT_NOTE) *flags |= SEC_LINK_ONCE | SEC_LINK_DUPLICATES_SAME_CONTENTS; return TRUE; } static void elf32_arm_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED) { bfd_arm_update_notes (abfd, ARM_NOTE_SECTION); } /* Return TRUE if this is an unwinding table entry. */ static bfd_boolean is_arm_elf_unwind_section_name (bfd * abfd ATTRIBUTE_UNUSED, const char * name) { return (CONST_STRNEQ (name, ELF_STRING_ARM_unwind) || CONST_STRNEQ (name, ELF_STRING_ARM_unwind_once)); } /* Set the type and flags for an ARM section. We do this by the section name, which is a hack, but ought to work. */ static bfd_boolean elf32_arm_fake_sections (bfd * abfd, Elf_Internal_Shdr * hdr, asection * sec) { const char * name; name = bfd_get_section_name (abfd, sec); if (is_arm_elf_unwind_section_name (abfd, name)) { hdr->sh_type = SHT_ARM_EXIDX; hdr->sh_flags |= SHF_LINK_ORDER; } return TRUE; } /* Handle an ARM specific section when reading an object file. This is called when bfd_section_from_shdr finds a section with an unknown type. */ static bfd_boolean elf32_arm_section_from_shdr (bfd *abfd, Elf_Internal_Shdr * hdr, const char *name, int shindex) { /* There ought to be a place to keep ELF backend specific flags, but at the moment there isn't one. We just keep track of the sections by their name, instead. Fortunately, the ABI gives names for all the ARM specific sections, so we will probably get away with this. */ switch (hdr->sh_type) { case SHT_ARM_EXIDX: case SHT_ARM_PREEMPTMAP: case SHT_ARM_ATTRIBUTES: break; default: return FALSE; } if (! _bfd_elf_make_section_from_shdr (abfd, hdr, name, shindex)) return FALSE; return TRUE; } /* A structure used to record a list of sections, independently of the next and prev fields in the asection structure. */ typedef struct section_list { asection * sec; struct section_list * next; struct section_list * prev; } section_list; /* Unfortunately we need to keep a list of sections for which an _arm_elf_section_data structure has been allocated. This is because it is possible for functions like elf32_arm_write_section to be called on a section which has had an elf_data_structure allocated for it (and so the used_by_bfd field is valid) but for which the ARM extended version of this structure - the _arm_elf_section_data structure - has not been allocated. */ static section_list * sections_with_arm_elf_section_data = NULL; static void record_section_with_arm_elf_section_data (asection * sec) { struct section_list * entry; entry = bfd_malloc (sizeof (* entry)); if (entry == NULL) return; entry->sec = sec; entry->next = sections_with_arm_elf_section_data; entry->prev = NULL; if (entry->next != NULL) entry->next->prev = entry; sections_with_arm_elf_section_data = entry; } static struct section_list * find_arm_elf_section_entry (asection * sec) { struct section_list * entry; static struct section_list * last_entry = NULL; /* This is a short cut for the typical case where the sections are added to the sections_with_arm_elf_section_data list in forward order and then looked up here in backwards order. This makes a real difference to the ld-srec/sec64k.exp linker test. */ entry = sections_with_arm_elf_section_data; if (last_entry != NULL) { if (last_entry->sec == sec) entry = last_entry; else if (last_entry->next != NULL && last_entry->next->sec == sec) entry = last_entry->next; } for (; entry; entry = entry->next) if (entry->sec == sec) break; if (entry) /* Record the entry prior to this one - it is the entry we are most likely to want to locate next time. Also this way if we have been called from unrecord_section_with_arm_elf_section_data() we will not be caching a pointer that is about to be freed. */ last_entry = entry->prev; return entry; } static _arm_elf_section_data * get_arm_elf_section_data (asection * sec) { struct section_list * entry; entry = find_arm_elf_section_entry (sec); if (entry) return elf32_arm_section_data (entry->sec); else return NULL; } static void unrecord_section_with_arm_elf_section_data (asection * sec) { struct section_list * entry; entry = find_arm_elf_section_entry (sec); if (entry) { if (entry->prev != NULL) entry->prev->next = entry->next; if (entry->next != NULL) entry->next->prev = entry->prev; if (entry == sections_with_arm_elf_section_data) sections_with_arm_elf_section_data = entry->next; free (entry); } } typedef struct { void *finfo; struct bfd_link_info *info; asection *sec; int sec_shndx; bfd_boolean (*func) (void *, const char *, Elf_Internal_Sym *, asection *, struct elf_link_hash_entry *); } output_arch_syminfo; enum map_symbol_type { ARM_MAP_ARM, ARM_MAP_THUMB, ARM_MAP_DATA }; /* Output a single PLT mapping symbol. */ static bfd_boolean elf32_arm_ouput_plt_map_sym (output_arch_syminfo *osi, enum map_symbol_type type, bfd_vma offset) { static const char *names[3] = {"$a", "$t", "$d"}; struct elf32_arm_link_hash_table *htab; Elf_Internal_Sym sym; htab = elf32_arm_hash_table (osi->info); sym.st_value = osi->sec->output_section->vma + osi->sec->output_offset + offset; sym.st_size = 0; sym.st_other = 0; sym.st_info = ELF_ST_INFO (STB_LOCAL, STT_NOTYPE); sym.st_shndx = osi->sec_shndx; if (!osi->func (osi->finfo, names[type], &sym, osi->sec, NULL)) return FALSE; return TRUE; } /* Output mapping symbols for PLT entries associated with H. */ static bfd_boolean elf32_arm_output_plt_map (struct elf_link_hash_entry *h, void *inf) { output_arch_syminfo *osi = (output_arch_syminfo *) inf; struct elf32_arm_link_hash_table *htab; struct elf32_arm_link_hash_entry *eh; bfd_vma addr; htab = elf32_arm_hash_table (osi->info); if (h->root.type == bfd_link_hash_indirect) return TRUE; if (h->root.type == bfd_link_hash_warning) /* When warning symbols are created, they **replace** the "real" entry in the hash table, thus we never get to see the real symbol in a hash traversal. So look at it now. */ h = (struct elf_link_hash_entry *) h->root.u.i.link; if (h->plt.offset == (bfd_vma) -1) return TRUE; eh = (struct elf32_arm_link_hash_entry *) h; addr = h->plt.offset; if (htab->symbian_p) { if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 4)) return FALSE; } else if (htab->vxworks_p) { if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 8)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr + 12)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 20)) return FALSE; } else { bfd_boolean thumb_stub; thumb_stub = eh->plt_thumb_refcount > 0 && !htab->use_blx; if (thumb_stub) { if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_THUMB, addr - 4)) return FALSE; } #ifdef FOUR_WORD_PLT if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_DATA, addr + 12)) return FALSE; #else /* A three-word PLT with no Thumb thunk contains only Arm code, so only need to output a mapping symbol for the first PLT entry and entries with thumb thunks. */ if (thumb_stub || addr == 20) { if (!elf32_arm_ouput_plt_map_sym (osi, ARM_MAP_ARM, addr)) return FALSE; } #endif } return TRUE; } /* Output mapping symbols for linker generated sections. */ static bfd_boolean elf32_arm_output_arch_local_syms (bfd *output_bfd, struct bfd_link_info *info, void *finfo, bfd_boolean (*func) (void *, const char *, Elf_Internal_Sym *, asection *, struct elf_link_hash_entry *)) { output_arch_syminfo osi; struct elf32_arm_link_hash_table *htab; bfd_vma offset; bfd_size_type size; htab = elf32_arm_hash_table (info); check_use_blx(htab); osi.finfo = finfo; osi.info = info; osi.func = func; /* ARM->Thumb glue. */ if (htab->arm_glue_size > 0) { osi.sec = bfd_get_section_by_name (htab->bfd_of_glue_owner, ARM2THUMB_GLUE_SECTION_NAME); osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd, osi.sec->output_section); if (info->shared || htab->root.is_relocatable_executable || htab->pic_veneer) size = ARM2THUMB_PIC_GLUE_SIZE; else if (htab->use_blx) size = ARM2THUMB_V5_STATIC_GLUE_SIZE; else size = ARM2THUMB_STATIC_GLUE_SIZE; for (offset = 0; offset < htab->arm_glue_size; offset += size) { elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset); elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, offset + size - 4); } } /* Thumb->ARM glue. */ if (htab->thumb_glue_size > 0) { osi.sec = bfd_get_section_by_name (htab->bfd_of_glue_owner, THUMB2ARM_GLUE_SECTION_NAME); osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd, osi.sec->output_section); size = THUMB2ARM_GLUE_SIZE; for (offset = 0; offset < htab->thumb_glue_size; offset += size) { elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_THUMB, offset); elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, offset + 4); } } /* Finally, output mapping symbols for the PLT. */ if (!htab->splt || htab->splt->size == 0) return TRUE; osi.sec_shndx = _bfd_elf_section_from_bfd_section (output_bfd, htab->splt->output_section); osi.sec = htab->splt; /* Output mapping symbols for the plt header. SymbianOS does not have a plt header. */ if (htab->vxworks_p) { /* VxWorks shared libraries have no PLT header. */ if (!info->shared) { if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0)) return FALSE; if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, 12)) return FALSE; } } else if (!htab->symbian_p) { if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_ARM, 0)) return FALSE; #ifndef FOUR_WORD_PLT if (!elf32_arm_ouput_plt_map_sym (&osi, ARM_MAP_DATA, 16)) return FALSE; #endif } elf_link_hash_traverse (&htab->root, elf32_arm_output_plt_map, (void *) &osi); return TRUE; } /* Allocate target specific section data. */ static bfd_boolean elf32_arm_new_section_hook (bfd *abfd, asection *sec) { if (!sec->used_by_bfd) { _arm_elf_section_data *sdata; bfd_size_type amt = sizeof (*sdata); sdata = bfd_zalloc (abfd, amt); if (sdata == NULL) return FALSE; sec->used_by_bfd = sdata; } record_section_with_arm_elf_section_data (sec); return _bfd_elf_new_section_hook (abfd, sec); } /* Used to order a list of mapping symbols by address. */ static int elf32_arm_compare_mapping (const void * a, const void * b) { return ((const elf32_arm_section_map *) a)->vma > ((const elf32_arm_section_map *) b)->vma; } /* Do code byteswapping. Return FALSE afterwards so that the section is written out as normal. */ static bfd_boolean elf32_arm_write_section (bfd *output_bfd, struct bfd_link_info *link_info, asection *sec, bfd_byte *contents) { int mapcount, errcount; _arm_elf_section_data *arm_data; struct elf32_arm_link_hash_table *globals = elf32_arm_hash_table (link_info); elf32_arm_section_map *map; elf32_vfp11_erratum_list *errnode; bfd_vma ptr; bfd_vma end; bfd_vma offset = sec->output_section->vma + sec->output_offset; bfd_byte tmp; int i; /* If this section has not been allocated an _arm_elf_section_data structure then we cannot record anything. */ arm_data = get_arm_elf_section_data (sec); if (arm_data == NULL) return FALSE; mapcount = arm_data->mapcount; map = arm_data->map; errcount = arm_data->erratumcount; if (errcount != 0) { unsigned int endianflip = bfd_big_endian (output_bfd) ? 3 : 0; for (errnode = arm_data->erratumlist; errnode != 0; errnode = errnode->next) { bfd_vma index = errnode->vma - offset; switch (errnode->type) { case VFP11_ERRATUM_BRANCH_TO_ARM_VENEER: { bfd_vma branch_to_veneer; /* Original condition code of instruction, plus bit mask for ARM B instruction. */ unsigned int insn = (errnode->u.b.vfp_insn & 0xf0000000) | 0x0a000000; /* The instruction is before the label. */ index -= 4; /* Above offset included in -4 below. */ branch_to_veneer = errnode->u.b.veneer->vma - errnode->vma - 4; if ((signed) branch_to_veneer < -(1 << 25) || (signed) branch_to_veneer >= (1 << 25)) (*_bfd_error_handler) (_("%B: error: VFP11 veneer out of " "range"), output_bfd); insn |= (branch_to_veneer >> 2) & 0xffffff; contents[endianflip ^ index] = insn & 0xff; contents[endianflip ^ (index + 1)] = (insn >> 8) & 0xff; contents[endianflip ^ (index + 2)] = (insn >> 16) & 0xff; contents[endianflip ^ (index + 3)] = (insn >> 24) & 0xff; } break; case VFP11_ERRATUM_ARM_VENEER: { bfd_vma branch_from_veneer; unsigned int insn; /* Take size of veneer into account. */ branch_from_veneer = errnode->u.v.branch->vma - errnode->vma - 12; if ((signed) branch_from_veneer < -(1 << 25) || (signed) branch_from_veneer >= (1 << 25)) (*_bfd_error_handler) (_("%B: error: VFP11 veneer out of " "range"), output_bfd); /* Original instruction. */ insn = errnode->u.v.branch->u.b.vfp_insn; contents[endianflip ^ index] = insn & 0xff; contents[endianflip ^ (index + 1)] = (insn >> 8) & 0xff; contents[endianflip ^ (index + 2)] = (insn >> 16) & 0xff; contents[endianflip ^ (index + 3)] = (insn >> 24) & 0xff; /* Branch back to insn after original insn. */ insn = 0xea000000 | ((branch_from_veneer >> 2) & 0xffffff); contents[endianflip ^ (index + 4)] = insn & 0xff; contents[endianflip ^ (index + 5)] = (insn >> 8) & 0xff; contents[endianflip ^ (index + 6)] = (insn >> 16) & 0xff; contents[endianflip ^ (index + 7)] = (insn >> 24) & 0xff; } break; default: abort (); } } } if (mapcount == 0) return FALSE; if (globals->byteswap_code) { qsort (map, mapcount, sizeof (* map), elf32_arm_compare_mapping); ptr = map[0].vma; for (i = 0; i < mapcount; i++) { if (i == mapcount - 1) end = sec->size; else end = map[i + 1].vma; switch (map[i].type) { case 'a': /* Byte swap code words. */ while (ptr + 3 < end) { tmp = contents[ptr]; contents[ptr] = contents[ptr + 3]; contents[ptr + 3] = tmp; tmp = contents[ptr + 1]; contents[ptr + 1] = contents[ptr + 2]; contents[ptr + 2] = tmp; ptr += 4; } break; case 't': /* Byte swap code halfwords. */ while (ptr + 1 < end) { tmp = contents[ptr]; contents[ptr] = contents[ptr + 1]; contents[ptr + 1] = tmp; ptr += 2; } break; case 'd': /* Leave data alone. */ break; } ptr = end; } } free (map); arm_data->mapcount = 0; arm_data->mapsize = 0; arm_data->map = NULL; unrecord_section_with_arm_elf_section_data (sec); return FALSE; } static void unrecord_section_via_map_over_sections (bfd * abfd ATTRIBUTE_UNUSED, asection * sec, void * ignore ATTRIBUTE_UNUSED) { unrecord_section_with_arm_elf_section_data (sec); } static bfd_boolean elf32_arm_close_and_cleanup (bfd * abfd) { if (abfd->sections) bfd_map_over_sections (abfd, unrecord_section_via_map_over_sections, NULL); return _bfd_elf_close_and_cleanup (abfd); } static bfd_boolean elf32_arm_bfd_free_cached_info (bfd * abfd) { if (abfd->sections) bfd_map_over_sections (abfd, unrecord_section_via_map_over_sections, NULL); return _bfd_free_cached_info (abfd); } /* Display STT_ARM_TFUNC symbols as functions. */ static void elf32_arm_symbol_processing (bfd *abfd ATTRIBUTE_UNUSED, asymbol *asym) { elf_symbol_type *elfsym = (elf_symbol_type *) asym; if (ELF_ST_TYPE (elfsym->internal_elf_sym.st_info) == STT_ARM_TFUNC) elfsym->symbol.flags |= BSF_FUNCTION; } /* Mangle thumb function symbols as we read them in. */ static bfd_boolean elf32_arm_swap_symbol_in (bfd * abfd, const void *psrc, const void *pshn, Elf_Internal_Sym *dst) { if (!bfd_elf32_swap_symbol_in (abfd, psrc, pshn, dst)) return FALSE; /* New EABI objects mark thumb function symbols by setting the low bit of the address. Turn these into STT_ARM_TFUNC. */ if (ELF_ST_TYPE (dst->st_info) == STT_FUNC && (dst->st_value & 1)) { dst->st_info = ELF_ST_INFO (ELF_ST_BIND (dst->st_info), STT_ARM_TFUNC); dst->st_value &= ~(bfd_vma) 1; } return TRUE; } /* Mangle thumb function symbols as we write them out. */ static void elf32_arm_swap_symbol_out (bfd *abfd, const Elf_Internal_Sym *src, void *cdst, void *shndx) { Elf_Internal_Sym newsym; /* We convert STT_ARM_TFUNC symbols into STT_FUNC with the low bit of the address set, as per the new EABI. We do this unconditionally because objcopy does not set the elf header flags until after it writes out the symbol table. */ if (ELF_ST_TYPE (src->st_info) == STT_ARM_TFUNC) { newsym = *src; newsym.st_info = ELF_ST_INFO (ELF_ST_BIND (src->st_info), STT_FUNC); if (newsym.st_shndx != SHN_UNDEF) { /* Do this only for defined symbols. At link type, the static linker will simulate the work of dynamic linker of resolving symbols and will carry over the thumbness of found symbols to the output symbol table. It's not clear how it happens, but the thumbness of undefined symbols can well be different at runtime, and writing '1' for them will be confusing for users and possibly for dynamic linker itself. */ newsym.st_value |= 1; } src = &newsym; } bfd_elf32_swap_symbol_out (abfd, src, cdst, shndx); } /* Add the PT_ARM_EXIDX program header. */ static bfd_boolean elf32_arm_modify_segment_map (bfd *abfd, struct bfd_link_info *info ATTRIBUTE_UNUSED) { struct elf_segment_map *m; asection *sec; sec = bfd_get_section_by_name (abfd, ".ARM.exidx"); if (sec != NULL && (sec->flags & SEC_LOAD) != 0) { /* If there is already a PT_ARM_EXIDX header, then we do not want to add another one. This situation arises when running "strip"; the input binary already has the header. */ m = elf_tdata (abfd)->segment_map; while (m && m->p_type != PT_ARM_EXIDX) m = m->next; if (!m) { m = bfd_zalloc (abfd, sizeof (struct elf_segment_map)); if (m == NULL) return FALSE; m->p_type = PT_ARM_EXIDX; m->count = 1; m->sections[0] = sec; m->next = elf_tdata (abfd)->segment_map; elf_tdata (abfd)->segment_map = m; } } return TRUE; } /* We may add a PT_ARM_EXIDX program header. */ static int elf32_arm_additional_program_headers (bfd *abfd, struct bfd_link_info *info ATTRIBUTE_UNUSED) { asection *sec; sec = bfd_get_section_by_name (abfd, ".ARM.exidx"); if (sec != NULL && (sec->flags & SEC_LOAD) != 0) return 1; else return 0; } /* We have two function types: STT_FUNC and STT_ARM_TFUNC. */ static bfd_boolean elf32_arm_is_function_type (unsigned int type) { return (type == STT_FUNC) || (type == STT_ARM_TFUNC); } /* We use this to override swap_symbol_in and swap_symbol_out. */ const struct elf_size_info elf32_arm_size_info = { sizeof (Elf32_External_Ehdr), sizeof (Elf32_External_Phdr), sizeof (Elf32_External_Shdr), sizeof (Elf32_External_Rel), sizeof (Elf32_External_Rela), sizeof (Elf32_External_Sym), sizeof (Elf32_External_Dyn), sizeof (Elf_External_Note), 4, 1, 32, 2, ELFCLASS32, EV_CURRENT, bfd_elf32_write_out_phdrs, bfd_elf32_write_shdrs_and_ehdr, bfd_elf32_write_relocs, elf32_arm_swap_symbol_in, elf32_arm_swap_symbol_out, bfd_elf32_slurp_reloc_table, bfd_elf32_slurp_symbol_table, bfd_elf32_swap_dyn_in, bfd_elf32_swap_dyn_out, bfd_elf32_swap_reloc_in, bfd_elf32_swap_reloc_out, bfd_elf32_swap_reloca_in, bfd_elf32_swap_reloca_out }; #define ELF_ARCH bfd_arch_arm #define ELF_MACHINE_CODE EM_ARM #ifdef __QNXTARGET__ #define ELF_MAXPAGESIZE 0x1000 #else #define ELF_MAXPAGESIZE 0x8000 #endif #define ELF_MINPAGESIZE 0x1000 #define ELF_COMMONPAGESIZE 0x1000 #define bfd_elf32_mkobject elf32_arm_mkobject #define bfd_elf32_bfd_copy_private_bfd_data elf32_arm_copy_private_bfd_data #define bfd_elf32_bfd_merge_private_bfd_data elf32_arm_merge_private_bfd_data #define bfd_elf32_bfd_set_private_flags elf32_arm_set_private_flags #define bfd_elf32_bfd_print_private_bfd_data elf32_arm_print_private_bfd_data #define bfd_elf32_bfd_link_hash_table_create elf32_arm_link_hash_table_create #define bfd_elf32_bfd_reloc_type_lookup elf32_arm_reloc_type_lookup #define bfd_elf32_bfd_reloc_name_lookup elf32_arm_reloc_name_lookup #define bfd_elf32_find_nearest_line elf32_arm_find_nearest_line #define bfd_elf32_find_inliner_info elf32_arm_find_inliner_info #define bfd_elf32_new_section_hook elf32_arm_new_section_hook #define bfd_elf32_bfd_is_target_special_symbol elf32_arm_is_target_special_symbol #define bfd_elf32_close_and_cleanup elf32_arm_close_and_cleanup #define bfd_elf32_bfd_free_cached_info elf32_arm_bfd_free_cached_info #define elf_backend_get_symbol_type elf32_arm_get_symbol_type #define elf_backend_gc_mark_hook elf32_arm_gc_mark_hook #define elf_backend_gc_mark_extra_sections elf32_arm_gc_mark_extra_sections #define elf_backend_gc_sweep_hook elf32_arm_gc_sweep_hook #define elf_backend_check_relocs elf32_arm_check_relocs #define elf_backend_relocate_section elf32_arm_relocate_section #define elf_backend_write_section elf32_arm_write_section #define elf_backend_adjust_dynamic_symbol elf32_arm_adjust_dynamic_symbol #define elf_backend_create_dynamic_sections elf32_arm_create_dynamic_sections #define elf_backend_finish_dynamic_symbol elf32_arm_finish_dynamic_symbol #define elf_backend_finish_dynamic_sections elf32_arm_finish_dynamic_sections #define elf_backend_size_dynamic_sections elf32_arm_size_dynamic_sections #define elf_backend_init_index_section _bfd_elf_init_2_index_sections #define elf_backend_post_process_headers elf32_arm_post_process_headers #define elf_backend_reloc_type_class elf32_arm_reloc_type_class #define elf_backend_object_p elf32_arm_object_p #define elf_backend_section_flags elf32_arm_section_flags #define elf_backend_fake_sections elf32_arm_fake_sections #define elf_backend_section_from_shdr elf32_arm_section_from_shdr #define elf_backend_final_write_processing elf32_arm_final_write_processing #define elf_backend_copy_indirect_symbol elf32_arm_copy_indirect_symbol #define elf_backend_symbol_processing elf32_arm_symbol_processing #define elf_backend_size_info elf32_arm_size_info #define elf_backend_modify_segment_map elf32_arm_modify_segment_map #define elf_backend_additional_program_headers \ elf32_arm_additional_program_headers #define elf_backend_output_arch_local_syms \ elf32_arm_output_arch_local_syms #define elf_backend_begin_write_processing \ elf32_arm_begin_write_processing #define elf_backend_is_function_type elf32_arm_is_function_type #define elf_backend_can_refcount 1 #define elf_backend_can_gc_sections 1 #define elf_backend_plt_readonly 1 #define elf_backend_want_got_plt 1 #define elf_backend_want_plt_sym 0 #define elf_backend_may_use_rel_p 1 #define elf_backend_may_use_rela_p 0 #define elf_backend_default_use_rela_p 0 #define elf_backend_got_header_size 12 #undef elf_backend_obj_attrs_vendor #define elf_backend_obj_attrs_vendor "aeabi" #undef elf_backend_obj_attrs_section #define elf_backend_obj_attrs_section ".ARM.attributes" #undef elf_backend_obj_attrs_arg_type #define elf_backend_obj_attrs_arg_type elf32_arm_obj_attrs_arg_type #undef elf_backend_obj_attrs_section_type #define elf_backend_obj_attrs_section_type SHT_ARM_ATTRIBUTES #include "elf32-target.h" /* VxWorks Targets */ #undef TARGET_LITTLE_SYM #define TARGET_LITTLE_SYM bfd_elf32_littlearm_vxworks_vec #undef TARGET_LITTLE_NAME #define TARGET_LITTLE_NAME "elf32-littlearm-vxworks" #undef TARGET_BIG_SYM #define TARGET_BIG_SYM bfd_elf32_bigarm_vxworks_vec #undef TARGET_BIG_NAME #define TARGET_BIG_NAME "elf32-bigarm-vxworks" /* Like elf32_arm_link_hash_table_create -- but overrides appropriately for VxWorks. */ static struct bfd_link_hash_table * elf32_arm_vxworks_link_hash_table_create (bfd *abfd) { struct bfd_link_hash_table *ret; ret = elf32_arm_link_hash_table_create (abfd); if (ret) { struct elf32_arm_link_hash_table *htab = (struct elf32_arm_link_hash_table *) ret; htab->use_rel = 0; htab->vxworks_p = 1; } return ret; } static void elf32_arm_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker) { elf32_arm_final_write_processing (abfd, linker); elf_vxworks_final_write_processing (abfd, linker); } #undef elf32_bed #define elf32_bed elf32_arm_vxworks_bed #undef bfd_elf32_bfd_link_hash_table_create #define bfd_elf32_bfd_link_hash_table_create \ elf32_arm_vxworks_link_hash_table_create #undef elf_backend_add_symbol_hook #define elf_backend_add_symbol_hook \ elf_vxworks_add_symbol_hook #undef elf_backend_final_write_processing #define elf_backend_final_write_processing \ elf32_arm_vxworks_final_write_processing #undef elf_backend_emit_relocs #define elf_backend_emit_relocs \ elf_vxworks_emit_relocs #undef elf_backend_may_use_rel_p #define elf_backend_may_use_rel_p 0 #undef elf_backend_may_use_rela_p #define elf_backend_may_use_rela_p 1 #undef elf_backend_default_use_rela_p #define elf_backend_default_use_rela_p 1 #undef elf_backend_want_plt_sym #define elf_backend_want_plt_sym 1 #undef ELF_MAXPAGESIZE #define ELF_MAXPAGESIZE 0x1000 #include "elf32-target.h" /* Symbian OS Targets */ #undef TARGET_LITTLE_SYM #define TARGET_LITTLE_SYM bfd_elf32_littlearm_symbian_vec #undef TARGET_LITTLE_NAME #define TARGET_LITTLE_NAME "elf32-littlearm-symbian" #undef TARGET_BIG_SYM #define TARGET_BIG_SYM bfd_elf32_bigarm_symbian_vec #undef TARGET_BIG_NAME #define TARGET_BIG_NAME "elf32-bigarm-symbian" /* Like elf32_arm_link_hash_table_create -- but overrides appropriately for Symbian OS. */ static struct bfd_link_hash_table * elf32_arm_symbian_link_hash_table_create (bfd *abfd) { struct bfd_link_hash_table *ret; ret = elf32_arm_link_hash_table_create (abfd); if (ret) { struct elf32_arm_link_hash_table *htab = (struct elf32_arm_link_hash_table *)ret; /* There is no PLT header for Symbian OS. */ htab->plt_header_size = 0; /* The PLT entries are each three instructions. */ htab->plt_entry_size = 4 * NUM_ELEM (elf32_arm_symbian_plt_entry); htab->symbian_p = 1; /* Symbian uses armv5t or above, so use_blx is always true. */ htab->use_blx = 1; htab->root.is_relocatable_executable = 1; } return ret; } static const struct bfd_elf_special_section elf32_arm_symbian_special_sections[] = { /* In a BPABI executable, the dynamic linking sections do not go in the loadable read-only segment. The post-linker may wish to refer to these sections, but they are not part of the final program image. */ { STRING_COMMA_LEN (".dynamic"), 0, SHT_DYNAMIC, 0 }, { STRING_COMMA_LEN (".dynstr"), 0, SHT_STRTAB, 0 }, { STRING_COMMA_LEN (".dynsym"), 0, SHT_DYNSYM, 0 }, { STRING_COMMA_LEN (".got"), 0, SHT_PROGBITS, 0 }, { STRING_COMMA_LEN (".hash"), 0, SHT_HASH, 0 }, /* These sections do not need to be writable as the SymbianOS postlinker will arrange things so that no dynamic relocation is required. */ { STRING_COMMA_LEN (".init_array"), 0, SHT_INIT_ARRAY, SHF_ALLOC }, { STRING_COMMA_LEN (".fini_array"), 0, SHT_FINI_ARRAY, SHF_ALLOC }, { STRING_COMMA_LEN (".preinit_array"), 0, SHT_PREINIT_ARRAY, SHF_ALLOC }, { NULL, 0, 0, 0, 0 } }; static void elf32_arm_symbian_begin_write_processing (bfd *abfd, struct bfd_link_info *link_info) { /* BPABI objects are never loaded directly by an OS kernel; they are processed by a postlinker first, into an OS-specific format. If the D_PAGED bit is set on the file, BFD will align segments on page boundaries, so that an OS can directly map the file. With BPABI objects, that just results in wasted space. In addition, because we clear the D_PAGED bit, map_sections_to_segments will recognize that the program headers should not be mapped into any loadable segment. */ abfd->flags &= ~D_PAGED; elf32_arm_begin_write_processing(abfd, link_info); } static bfd_boolean elf32_arm_symbian_modify_segment_map (bfd *abfd, struct bfd_link_info *info) { struct elf_segment_map *m; asection *dynsec; /* BPABI shared libraries and executables should have a PT_DYNAMIC segment. However, because the .dynamic section is not marked with SEC_LOAD, the generic ELF code will not create such a segment. */ dynsec = bfd_get_section_by_name (abfd, ".dynamic"); if (dynsec) { for (m = elf_tdata (abfd)->segment_map; m != NULL; m = m->next) if (m->p_type == PT_DYNAMIC) break; if (m == NULL) { m = _bfd_elf_make_dynamic_segment (abfd, dynsec); m->next = elf_tdata (abfd)->segment_map; elf_tdata (abfd)->segment_map = m; } } /* Also call the generic arm routine. */ return elf32_arm_modify_segment_map (abfd, info); } #undef elf32_bed #define elf32_bed elf32_arm_symbian_bed /* The dynamic sections are not allocated on SymbianOS; the postlinker will process them and then discard them. */ #undef ELF_DYNAMIC_SEC_FLAGS #define ELF_DYNAMIC_SEC_FLAGS \ (SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_LINKER_CREATED) #undef bfd_elf32_bfd_link_hash_table_create #define bfd_elf32_bfd_link_hash_table_create \ elf32_arm_symbian_link_hash_table_create #undef elf_backend_add_symbol_hook #undef elf_backend_special_sections #define elf_backend_special_sections elf32_arm_symbian_special_sections #undef elf_backend_begin_write_processing #define elf_backend_begin_write_processing \ elf32_arm_symbian_begin_write_processing #undef elf_backend_final_write_processing #define elf_backend_final_write_processing \ elf32_arm_final_write_processing #undef elf_backend_emit_relocs #undef elf_backend_modify_segment_map #define elf_backend_modify_segment_map elf32_arm_symbian_modify_segment_map /* There is no .got section for BPABI objects, and hence no header. */ #undef elf_backend_got_header_size #define elf_backend_got_header_size 0 /* Similarly, there is no .got.plt section. */ #undef elf_backend_want_got_plt #define elf_backend_want_got_plt 0 #undef elf_backend_may_use_rel_p #define elf_backend_may_use_rel_p 1 #undef elf_backend_may_use_rela_p #define elf_backend_may_use_rela_p 0 #undef elf_backend_default_use_rela_p #define elf_backend_default_use_rela_p 0 #undef elf_backend_want_plt_sym #define elf_backend_want_plt_sym 0 #undef ELF_MAXPAGESIZE #define ELF_MAXPAGESIZE 0x8000 #include "elf32-target.h" Index: projects/sendfile/contrib/binutils =================================================================== --- projects/sendfile/contrib/binutils (revision 275355) +++ projects/sendfile/contrib/binutils (revision 275356) Property changes on: projects/sendfile/contrib/binutils ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/binutils:r275249-275355 Index: projects/sendfile/contrib/gcc/config/arm/lib1funcs.asm =================================================================== --- projects/sendfile/contrib/gcc/config/arm/lib1funcs.asm (revision 275355) +++ projects/sendfile/contrib/gcc/config/arm/lib1funcs.asm (revision 275356) @@ -1,1309 +1,1307 @@ @ libgcc routines for ARM cpu. @ Division routines, written by Richard Earnshaw, (rearnsha@armltd.co.uk) /* Copyright 1995, 1996, 1998, 1999, 2000, 2003, 2004, 2005 Free Software Foundation, Inc. This file is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2, or (at your option) any later version. In addition to the permissions in the GNU General Public License, the Free Software Foundation gives you unlimited permission to link the compiled version of this file into combinations with other programs, and to distribute those combinations without any restriction coming from the use of this file. (The General Public License restrictions do apply in other respects; for example, they cover modification of the file, and distribution when not linked into a combine executable.) This file is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /* ------------------------------------------------------------------------ */ /* We need to know what prefix to add to function names. */ #ifndef __USER_LABEL_PREFIX__ #error __USER_LABEL_PREFIX__ not defined #endif /* ANSI concatenation macros. */ #define CONCAT1(a, b) CONCAT2(a, b) #define CONCAT2(a, b) a ## b /* Use the right prefix for global labels. */ #define SYM(x) CONCAT1 (__USER_LABEL_PREFIX__, x) #ifdef __ELF__ #ifdef __thumb__ #define __PLT__ /* Not supported in Thumb assembler (for now). */ #else #define __PLT__ (PLT) #endif #define TYPE(x) .type SYM(x),function #define SIZE(x) .size SYM(x), . - SYM(x) #define LSYM(x) .x #else #define __PLT__ #define TYPE(x) #define SIZE(x) #define LSYM(x) x #endif /* Function end macros. Variants for interworking. */ @ This selects the minimum architecture level required. #define __ARM_ARCH__ 3 #if defined(__ARM_ARCH_3M__) || defined(__ARM_ARCH_4__) \ || defined(__ARM_ARCH_4T__) /* We use __ARM_ARCH__ set to 4 here, but in reality it's any processor with long multiply instructions. That includes v3M. */ # undef __ARM_ARCH__ # define __ARM_ARCH__ 4 #endif #if defined(__ARM_ARCH_5__) || defined(__ARM_ARCH_5T__) \ || defined(__ARM_ARCH_5E__) || defined(__ARM_ARCH_5TE__) \ || defined(__ARM_ARCH_5TEJ__) # undef __ARM_ARCH__ # define __ARM_ARCH__ 5 #endif #if defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \ || defined(__ARM_ARCH_6ZK__) # undef __ARM_ARCH__ # define __ARM_ARCH__ 6 #endif #ifndef __ARM_ARCH__ #error Unable to determine architecture. #endif /* How to return from a function call depends on the architecture variant. */ #if (__ARM_ARCH__ > 4) || defined(__ARM_ARCH_4T__) # define RET bx lr # define RETc(x) bx##x lr /* Special precautions for interworking on armv4t. */ # if (__ARM_ARCH__ == 4) /* Always use bx, not ldr pc. */ # if (defined(__thumb__) || defined(__THUMB_INTERWORK__)) # define __INTERWORKING__ # endif /* __THUMB__ || __THUMB_INTERWORK__ */ /* Include thumb stub before arm mode code. */ # if defined(__thumb__) && !defined(__THUMB_INTERWORK__) # define __INTERWORKING_STUBS__ # endif /* __thumb__ && !__THUMB_INTERWORK__ */ #endif /* __ARM_ARCH == 4 */ #else # define RET mov pc, lr # define RETc(x) mov##x pc, lr #endif .macro cfi_pop advance, reg, cfa_offset #ifdef __ELF__ .pushsection .debug_frame .byte 0x4 /* DW_CFA_advance_loc4 */ .4byte \advance .byte (0xc0 | \reg) /* DW_CFA_restore */ .byte 0xe /* DW_CFA_def_cfa_offset */ .uleb128 \cfa_offset .popsection #endif .endm .macro cfi_push advance, reg, offset, cfa_offset #ifdef __ELF__ .pushsection .debug_frame .byte 0x4 /* DW_CFA_advance_loc4 */ .4byte \advance .byte (0x80 | \reg) /* DW_CFA_offset */ .uleb128 (\offset / -4) .byte 0xe /* DW_CFA_def_cfa_offset */ .uleb128 \cfa_offset .popsection #endif .endm .macro cfi_start start_label, end_label #ifdef __ELF__ .pushsection .debug_frame LSYM(Lstart_frame): .4byte LSYM(Lend_cie) - LSYM(Lstart_cie) @ Length of CIE LSYM(Lstart_cie): .4byte 0xffffffff @ CIE Identifier Tag .byte 0x1 @ CIE Version .ascii "\0" @ CIE Augmentation .uleb128 0x1 @ CIE Code Alignment Factor .sleb128 -4 @ CIE Data Alignment Factor .byte 0xe @ CIE RA Column .byte 0xc @ DW_CFA_def_cfa .uleb128 0xd .uleb128 0x0 .align 2 LSYM(Lend_cie): .4byte LSYM(Lend_fde)-LSYM(Lstart_fde) @ FDE Length LSYM(Lstart_fde): .4byte LSYM(Lstart_frame) @ FDE CIE offset .4byte \start_label @ FDE initial location .4byte \end_label-\start_label @ FDE address range .popsection #endif .endm .macro cfi_end end_label #ifdef __ELF__ .pushsection .debug_frame .align 2 LSYM(Lend_fde): .popsection \end_label: #endif .endm /* Don't pass dirn, it's there just to get token pasting right. */ .macro RETLDM regs=, cond=, unwind=, dirn=ia #if defined (__INTERWORKING__) .ifc "\regs","" ldr\cond lr, [sp], #8 .else ldm\cond\dirn sp!, {\regs, lr} .endif .ifnc "\unwind", "" /* Mark LR as restored. */ 97: cfi_pop 97b - \unwind, 0xe, 0x0 .endif bx\cond lr #else .ifc "\regs","" ldr\cond pc, [sp], #8 .else ldm\cond\dirn sp!, {\regs, pc} .endif #endif .endm .macro ARM_LDIV0 name str lr, [sp, #-8]! 98: cfi_push 98b - __\name, 0xe, -0x8, 0x8 bl SYM (__div0) __PLT__ mov r0, #0 @ About as wrong as it could be. RETLDM unwind=98b .endm .macro THUMB_LDIV0 name push { r1, lr } 98: cfi_push 98b - __\name, 0xe, -0x4, 0x8 bl SYM (__div0) mov r0, #0 @ About as wrong as it could be. #if defined (__INTERWORKING__) pop { r1, r2 } bx r2 #else pop { r1, pc } #endif .endm .macro FUNC_END name SIZE (__\name) .endm .macro DIV_FUNC_END name cfi_start __\name, LSYM(Lend_div0) LSYM(Ldiv0): #ifdef __thumb__ THUMB_LDIV0 \name #else ARM_LDIV0 \name #endif cfi_end LSYM(Lend_div0) FUNC_END \name .endm .macro THUMB_FUNC_START name .globl SYM (\name) TYPE (\name) .thumb_func SYM (\name): .endm /* Function start macros. Variants for ARM and Thumb. */ #ifdef __thumb__ #define THUMB_FUNC .thumb_func #define THUMB_CODE .force_thumb #else #define THUMB_FUNC #define THUMB_CODE #endif .macro FUNC_START name .text .globl SYM (__\name) TYPE (__\name) .align 0 THUMB_CODE THUMB_FUNC SYM (__\name): .endm /* Special function that will always be coded in ARM assembly, even if in Thumb-only compilation. */ #if defined(__INTERWORKING_STUBS__) .macro ARM_FUNC_START name FUNC_START \name bx pc nop .arm /* A hook to tell gdb that we've switched to ARM mode. Also used to call directly from other local arm routines. */ _L__\name: .endm #define EQUIV .thumb_set /* Branch directly to a function declared with ARM_FUNC_START. Must be called in arm mode. */ .macro ARM_CALL name bl _L__\name .endm #else .macro ARM_FUNC_START name .text .globl SYM (__\name) TYPE (__\name) .align 0 .arm SYM (__\name): .endm #define EQUIV .set .macro ARM_CALL name bl __\name .endm #endif .macro FUNC_ALIAS new old .globl SYM (__\new) #if defined (__thumb__) .thumb_set SYM (__\new), SYM (__\old) #else .set SYM (__\new), SYM (__\old) #endif .endm .macro ARM_FUNC_ALIAS new old .globl SYM (__\new) EQUIV SYM (__\new), SYM (__\old) #if defined(__INTERWORKING_STUBS__) .set SYM (_L__\new), SYM (_L__\old) #endif .endm #ifdef __thumb__ /* Register aliases. */ work .req r4 @ XXXX is this safe ? dividend .req r0 divisor .req r1 overdone .req r2 result .req r2 curbit .req r3 #endif #if 0 ip .req r12 sp .req r13 lr .req r14 pc .req r15 #endif /* ------------------------------------------------------------------------ */ /* Bodies of the division and modulo routines. */ /* ------------------------------------------------------------------------ */ .macro ARM_DIV_BODY dividend, divisor, result, curbit #if __ARM_ARCH__ >= 5 && ! defined (__OPTIMIZE_SIZE__) clz \curbit, \dividend clz \result, \divisor sub \curbit, \result, \curbit rsbs \curbit, \curbit, #31 addne \curbit, \curbit, \curbit, lsl #1 mov \result, #0 addne pc, pc, \curbit, lsl #2 nop .set shift, 32 .rept 32 .set shift, shift - 1 cmp \dividend, \divisor, lsl #shift adc \result, \result, \result subcs \dividend, \dividend, \divisor, lsl #shift .endr #else /* __ARM_ARCH__ < 5 || defined (__OPTIMIZE_SIZE__) */ #if __ARM_ARCH__ >= 5 clz \curbit, \divisor clz \result, \dividend sub \result, \curbit, \result mov \curbit, #1 mov \divisor, \divisor, lsl \result mov \curbit, \curbit, lsl \result mov \result, #0 #else /* __ARM_ARCH__ < 5 */ @ Initially shift the divisor left 3 bits if possible, @ set curbit accordingly. This allows for curbit to be located @ at the left end of each 4 bit nibbles in the division loop @ to save one loop in most cases. tst \divisor, #0xe0000000 moveq \divisor, \divisor, lsl #3 moveq \curbit, #8 movne \curbit, #1 @ Unless the divisor is very big, shift it up in multiples of @ four bits, since this is the amount of unwinding in the main @ division loop. Continue shifting until the divisor is @ larger than the dividend. 1: cmp \divisor, #0x10000000 cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #4 movlo \curbit, \curbit, lsl #4 blo 1b @ For very big divisors, we must shift it a bit at a time, or @ we will be in danger of overflowing. 1: cmp \divisor, #0x80000000 cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #1 movlo \curbit, \curbit, lsl #1 blo 1b mov \result, #0 #endif /* __ARM_ARCH__ < 5 */ @ Division loop 1: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor orrhs \result, \result, \curbit cmp \dividend, \divisor, lsr #1 subhs \dividend, \dividend, \divisor, lsr #1 orrhs \result, \result, \curbit, lsr #1 cmp \dividend, \divisor, lsr #2 subhs \dividend, \dividend, \divisor, lsr #2 orrhs \result, \result, \curbit, lsr #2 cmp \dividend, \divisor, lsr #3 subhs \dividend, \dividend, \divisor, lsr #3 orrhs \result, \result, \curbit, lsr #3 cmp \dividend, #0 @ Early termination? movnes \curbit, \curbit, lsr #4 @ No, any more bits to do? movne \divisor, \divisor, lsr #4 bne 1b #endif /* __ARM_ARCH__ < 5 || defined (__OPTIMIZE_SIZE__) */ .endm /* ------------------------------------------------------------------------ */ .macro ARM_DIV2_ORDER divisor, order #if __ARM_ARCH__ >= 5 clz \order, \divisor rsb \order, \order, #31 #else cmp \divisor, #(1 << 16) movhs \divisor, \divisor, lsr #16 movhs \order, #16 movlo \order, #0 cmp \divisor, #(1 << 8) movhs \divisor, \divisor, lsr #8 addhs \order, \order, #8 cmp \divisor, #(1 << 4) movhs \divisor, \divisor, lsr #4 addhs \order, \order, #4 cmp \divisor, #(1 << 2) addhi \order, \order, #3 addls \order, \order, \divisor, lsr #1 #endif .endm /* ------------------------------------------------------------------------ */ .macro ARM_MOD_BODY dividend, divisor, order, spare #if __ARM_ARCH__ >= 5 && ! defined (__OPTIMIZE_SIZE__) clz \order, \divisor clz \spare, \dividend sub \order, \order, \spare rsbs \order, \order, #31 addne pc, pc, \order, lsl #3 nop .set shift, 32 .rept 32 .set shift, shift - 1 cmp \dividend, \divisor, lsl #shift subcs \dividend, \dividend, \divisor, lsl #shift .endr #else /* __ARM_ARCH__ < 5 || defined (__OPTIMIZE_SIZE__) */ #if __ARM_ARCH__ >= 5 clz \order, \divisor clz \spare, \dividend sub \order, \order, \spare mov \divisor, \divisor, lsl \order #else /* __ARM_ARCH__ < 5 */ mov \order, #0 @ Unless the divisor is very big, shift it up in multiples of @ four bits, since this is the amount of unwinding in the main @ division loop. Continue shifting until the divisor is @ larger than the dividend. 1: cmp \divisor, #0x10000000 cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #4 addlo \order, \order, #4 blo 1b @ For very big divisors, we must shift it a bit at a time, or @ we will be in danger of overflowing. 1: cmp \divisor, #0x80000000 cmplo \divisor, \dividend movlo \divisor, \divisor, lsl #1 addlo \order, \order, #1 blo 1b #endif /* __ARM_ARCH__ < 5 */ @ Perform all needed substractions to keep only the reminder. @ Do comparisons in batch of 4 first. subs \order, \order, #3 @ yes, 3 is intended here blt 2f 1: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor cmp \dividend, \divisor, lsr #1 subhs \dividend, \dividend, \divisor, lsr #1 cmp \dividend, \divisor, lsr #2 subhs \dividend, \dividend, \divisor, lsr #2 cmp \dividend, \divisor, lsr #3 subhs \dividend, \dividend, \divisor, lsr #3 cmp \dividend, #1 mov \divisor, \divisor, lsr #4 subges \order, \order, #4 bge 1b tst \order, #3 teqne \dividend, #0 beq 5f @ Either 1, 2 or 3 comparison/substractions are left. 2: cmn \order, #2 blt 4f beq 3f cmp \dividend, \divisor subhs \dividend, \dividend, \divisor mov \divisor, \divisor, lsr #1 3: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor mov \divisor, \divisor, lsr #1 4: cmp \dividend, \divisor subhs \dividend, \dividend, \divisor 5: #endif /* __ARM_ARCH__ < 5 || defined (__OPTIMIZE_SIZE__) */ .endm /* ------------------------------------------------------------------------ */ .macro THUMB_DIV_MOD_BODY modulo @ Load the constant 0x10000000 into our work register. mov work, #1 lsl work, #28 LSYM(Loop1): @ Unless the divisor is very big, shift it up in multiples of @ four bits, since this is the amount of unwinding in the main @ division loop. Continue shifting until the divisor is @ larger than the dividend. cmp divisor, work bhs LSYM(Lbignum) cmp divisor, dividend bhs LSYM(Lbignum) lsl divisor, #4 lsl curbit, #4 b LSYM(Loop1) LSYM(Lbignum): @ Set work to 0x80000000 lsl work, #3 LSYM(Loop2): @ For very big divisors, we must shift it a bit at a time, or @ we will be in danger of overflowing. cmp divisor, work bhs LSYM(Loop3) cmp divisor, dividend bhs LSYM(Loop3) lsl divisor, #1 lsl curbit, #1 b LSYM(Loop2) LSYM(Loop3): @ Test for possible subtractions ... .if \modulo @ ... On the final pass, this may subtract too much from the dividend, @ so keep track of which subtractions are done, we can fix them up @ afterwards. mov overdone, #0 cmp dividend, divisor blo LSYM(Lover1) sub dividend, dividend, divisor LSYM(Lover1): lsr work, divisor, #1 cmp dividend, work blo LSYM(Lover2) sub dividend, dividend, work mov ip, curbit mov work, #1 ror curbit, work orr overdone, curbit mov curbit, ip LSYM(Lover2): lsr work, divisor, #2 cmp dividend, work blo LSYM(Lover3) sub dividend, dividend, work mov ip, curbit mov work, #2 ror curbit, work orr overdone, curbit mov curbit, ip LSYM(Lover3): lsr work, divisor, #3 cmp dividend, work blo LSYM(Lover4) sub dividend, dividend, work mov ip, curbit mov work, #3 ror curbit, work orr overdone, curbit mov curbit, ip LSYM(Lover4): mov ip, curbit .else @ ... and note which bits are done in the result. On the final pass, @ this may subtract too much from the dividend, but the result will be ok, @ since the "bit" will have been shifted out at the bottom. cmp dividend, divisor blo LSYM(Lover1) sub dividend, dividend, divisor orr result, result, curbit LSYM(Lover1): lsr work, divisor, #1 cmp dividend, work blo LSYM(Lover2) sub dividend, dividend, work lsr work, curbit, #1 orr result, work LSYM(Lover2): lsr work, divisor, #2 cmp dividend, work blo LSYM(Lover3) sub dividend, dividend, work lsr work, curbit, #2 orr result, work LSYM(Lover3): lsr work, divisor, #3 cmp dividend, work blo LSYM(Lover4) sub dividend, dividend, work lsr work, curbit, #3 orr result, work LSYM(Lover4): .endif cmp dividend, #0 @ Early termination? beq LSYM(Lover5) lsr curbit, #4 @ No, any more bits to do? beq LSYM(Lover5) lsr divisor, #4 b LSYM(Loop3) LSYM(Lover5): .if \modulo @ Any subtractions that we should not have done will be recorded in @ the top three bits of "overdone". Exactly which were not needed @ are governed by the position of the bit, stored in ip. mov work, #0xe lsl work, #28 and overdone, work beq LSYM(Lgot_result) @ If we terminated early, because dividend became zero, then the @ bit in ip will not be in the bottom nibble, and we should not @ perform the additions below. We must test for this though @ (rather relying upon the TSTs to prevent the additions) since @ the bit in ip could be in the top two bits which might then match @ with one of the smaller RORs. mov curbit, ip mov work, #0x7 tst curbit, work beq LSYM(Lgot_result) mov curbit, ip mov work, #3 ror curbit, work tst overdone, curbit beq LSYM(Lover6) lsr work, divisor, #3 add dividend, work LSYM(Lover6): mov curbit, ip mov work, #2 ror curbit, work tst overdone, curbit beq LSYM(Lover7) lsr work, divisor, #2 add dividend, work LSYM(Lover7): mov curbit, ip mov work, #1 ror curbit, work tst overdone, curbit beq LSYM(Lgot_result) lsr work, divisor, #1 add dividend, work .endif LSYM(Lgot_result): .endm /* ------------------------------------------------------------------------ */ /* Start of the Real Functions */ /* ------------------------------------------------------------------------ */ #ifdef L_udivsi3 FUNC_START udivsi3 FUNC_ALIAS aeabi_uidiv udivsi3 #ifdef __thumb__ cmp divisor, #0 beq LSYM(Ldiv0) mov curbit, #1 mov result, #0 push { work } cmp dividend, divisor blo LSYM(Lgot_result) THUMB_DIV_MOD_BODY 0 mov r0, result pop { work } RET #else /* ARM version. */ subs r2, r1, #1 RETc(eq) bcc LSYM(Ldiv0) cmp r0, r1 bls 11f tst r1, r2 beq 12f ARM_DIV_BODY r0, r1, r2, r3 mov r0, r2 RET 11: moveq r0, #1 movne r0, #0 RET 12: ARM_DIV2_ORDER r1, r2 mov r0, r0, lsr r2 RET #endif /* ARM version */ DIV_FUNC_END udivsi3 FUNC_START aeabi_uidivmod #ifdef __thumb__ push {r0, r1, lr} bl SYM(__udivsi3) POP {r1, r2, r3} mul r2, r0 sub r1, r1, r2 bx r3 #else stmfd sp!, { r0, r1, lr } bl SYM(__udivsi3) ldmfd sp!, { r1, r2, lr } mul r3, r2, r0 sub r1, r1, r3 RET #endif FUNC_END aeabi_uidivmod #endif /* L_udivsi3 */ /* ------------------------------------------------------------------------ */ #ifdef L_umodsi3 FUNC_START umodsi3 #ifdef __thumb__ cmp divisor, #0 beq LSYM(Ldiv0) mov curbit, #1 cmp dividend, divisor bhs LSYM(Lover10) RET LSYM(Lover10): push { work } THUMB_DIV_MOD_BODY 1 pop { work } RET #else /* ARM version. */ subs r2, r1, #1 @ compare divisor with 1 bcc LSYM(Ldiv0) cmpne r0, r1 @ compare dividend with divisor moveq r0, #0 tsthi r1, r2 @ see if divisor is power of 2 andeq r0, r0, r2 RETc(ls) ARM_MOD_BODY r0, r1, r2, r3 RET #endif /* ARM version. */ DIV_FUNC_END umodsi3 #endif /* L_umodsi3 */ /* ------------------------------------------------------------------------ */ #ifdef L_divsi3 FUNC_START divsi3 FUNC_ALIAS aeabi_idiv divsi3 #ifdef __thumb__ cmp divisor, #0 beq LSYM(Ldiv0) push { work } mov work, dividend eor work, divisor @ Save the sign of the result. mov ip, work mov curbit, #1 mov result, #0 cmp divisor, #0 bpl LSYM(Lover10) neg divisor, divisor @ Loops below use unsigned. LSYM(Lover10): cmp dividend, #0 bpl LSYM(Lover11) neg dividend, dividend LSYM(Lover11): cmp dividend, divisor blo LSYM(Lgot_result) THUMB_DIV_MOD_BODY 0 mov r0, result mov work, ip cmp work, #0 bpl LSYM(Lover12) neg r0, r0 LSYM(Lover12): pop { work } RET #else /* ARM version. */ cmp r1, #0 eor ip, r0, r1 @ save the sign of the result. beq LSYM(Ldiv0) rsbmi r1, r1, #0 @ loops below use unsigned. subs r2, r1, #1 @ division by 1 or -1 ? beq 10f movs r3, r0 rsbmi r3, r0, #0 @ positive dividend value cmp r3, r1 bls 11f tst r1, r2 @ divisor is power of 2 ? beq 12f ARM_DIV_BODY r3, r1, r0, r2 cmp ip, #0 rsbmi r0, r0, #0 RET 10: teq ip, r0 @ same sign ? rsbmi r0, r0, #0 RET 11: movlo r0, #0 moveq r0, ip, asr #31 orreq r0, r0, #1 RET 12: ARM_DIV2_ORDER r1, r2 cmp ip, #0 mov r0, r3, lsr r2 rsbmi r0, r0, #0 RET #endif /* ARM version */ DIV_FUNC_END divsi3 FUNC_START aeabi_idivmod #ifdef __thumb__ push {r0, r1, lr} bl SYM(__divsi3) POP {r1, r2, r3} mul r2, r0 sub r1, r1, r2 bx r3 #else stmfd sp!, { r0, r1, lr } bl SYM(__divsi3) ldmfd sp!, { r1, r2, lr } mul r3, r2, r0 sub r1, r1, r3 RET #endif FUNC_END aeabi_idivmod #endif /* L_divsi3 */ /* ------------------------------------------------------------------------ */ #ifdef L_modsi3 FUNC_START modsi3 #ifdef __thumb__ mov curbit, #1 cmp divisor, #0 beq LSYM(Ldiv0) bpl LSYM(Lover10) neg divisor, divisor @ Loops below use unsigned. LSYM(Lover10): push { work } @ Need to save the sign of the dividend, unfortunately, we need @ work later on. Must do this after saving the original value of @ the work register, because we will pop this value off first. push { dividend } cmp dividend, #0 bpl LSYM(Lover11) neg dividend, dividend LSYM(Lover11): cmp dividend, divisor blo LSYM(Lgot_result) THUMB_DIV_MOD_BODY 1 pop { work } cmp work, #0 bpl LSYM(Lover12) neg dividend, dividend LSYM(Lover12): pop { work } RET #else /* ARM version. */ cmp r1, #0 beq LSYM(Ldiv0) rsbmi r1, r1, #0 @ loops below use unsigned. movs ip, r0 @ preserve sign of dividend rsbmi r0, r0, #0 @ if negative make positive subs r2, r1, #1 @ compare divisor with 1 cmpne r0, r1 @ compare dividend with divisor moveq r0, #0 tsthi r1, r2 @ see if divisor is power of 2 andeq r0, r0, r2 bls 10f ARM_MOD_BODY r0, r1, r2, r3 10: cmp ip, #0 rsbmi r0, r0, #0 RET #endif /* ARM version */ DIV_FUNC_END modsi3 #endif /* L_modsi3 */ /* ------------------------------------------------------------------------ */ #ifdef L_dvmd_tls FUNC_START div0 FUNC_ALIAS aeabi_idiv0 div0 FUNC_ALIAS aeabi_ldiv0 div0 RET - FUNC_END aeabi_ldiv0 - FUNC_END aeabi_idiv0 FUNC_END div0 #endif /* L_divmodsi_tools */ /* ------------------------------------------------------------------------ */ #ifdef L_dvmd_lnx @ GNU/Linux division-by zero handler. Used in place of L_dvmd_tls /* Constant taken from . */ #define SIGFPE 8 .code 32 FUNC_START div0 stmfd sp!, {r1, lr} mov r0, #SIGFPE bl SYM(raise) __PLT__ RETLDM r1 FUNC_END div0 #endif /* L_dvmd_lnx */ /* ------------------------------------------------------------------------ */ /* Dword shift operations. */ /* All the following Dword shift variants rely on the fact that shft xxx, Reg is in fact done as shft xxx, (Reg & 255) so for Reg value in (32...63) and (-1...-31) we will get zero (in the case of logical shifts) or the sign (for asr). */ #ifdef __ARMEB__ #define al r1 #define ah r0 #else #define al r0 #define ah r1 #endif /* Prevent __aeabi double-word shifts from being produced on SymbianOS. */ #ifndef __symbian__ #ifdef L_lshrdi3 FUNC_START lshrdi3 FUNC_ALIAS aeabi_llsr lshrdi3 #ifdef __thumb__ lsr al, r2 mov r3, ah lsr ah, r2 mov ip, r3 sub r2, #32 lsr r3, r2 orr al, r3 neg r2, r2 mov r3, ip lsl r3, r2 orr al, r3 RET #else subs r3, r2, #32 rsb ip, r2, #32 movmi al, al, lsr r2 movpl al, ah, lsr r3 orrmi al, al, ah, lsl ip mov ah, ah, lsr r2 RET #endif FUNC_END aeabi_llsr FUNC_END lshrdi3 #endif #ifdef L_ashrdi3 FUNC_START ashrdi3 FUNC_ALIAS aeabi_lasr ashrdi3 #ifdef __thumb__ lsr al, r2 mov r3, ah asr ah, r2 sub r2, #32 @ If r2 is negative at this point the following step would OR @ the sign bit into all of AL. That's not what we want... bmi 1f mov ip, r3 asr r3, r2 orr al, r3 mov r3, ip 1: neg r2, r2 lsl r3, r2 orr al, r3 RET #else subs r3, r2, #32 rsb ip, r2, #32 movmi al, al, lsr r2 movpl al, ah, asr r3 orrmi al, al, ah, lsl ip mov ah, ah, asr r2 RET #endif FUNC_END aeabi_lasr FUNC_END ashrdi3 #endif #ifdef L_ashldi3 FUNC_START ashldi3 FUNC_ALIAS aeabi_llsl ashldi3 #ifdef __thumb__ lsl ah, r2 mov r3, al lsl al, r2 mov ip, r3 sub r2, #32 lsl r3, r2 orr ah, r3 neg r2, r2 mov r3, ip lsr r3, r2 orr ah, r3 RET #else subs r3, r2, #32 rsb ip, r2, #32 movmi ah, ah, lsl r2 movpl ah, al, lsl r3 orrmi ah, ah, al, lsr ip mov al, al, lsl r2 RET #endif FUNC_END aeabi_llsl FUNC_END ashldi3 #endif #endif /* __symbian__ */ /* ------------------------------------------------------------------------ */ /* These next two sections are here despite the fact that they contain Thumb assembler because their presence allows interworked code to be linked even when the GCC library is this one. */ /* Do not build the interworking functions when the target architecture does not support Thumb instructions. (This can be a multilib option). */ #if defined __ARM_ARCH_4T__ || defined __ARM_ARCH_5T__\ || defined __ARM_ARCH_5TE__ || defined __ARM_ARCH_5TEJ__ \ || __ARM_ARCH__ >= 6 #if defined L_call_via_rX /* These labels & instructions are used by the Arm/Thumb interworking code. The address of function to be called is loaded into a register and then one of these labels is called via a BL instruction. This puts the return address into the link register with the bottom bit set, and the code here switches to the correct mode before executing the function. */ .text .align 0 .force_thumb .macro call_via register THUMB_FUNC_START _call_via_\register bx \register nop SIZE (_call_via_\register) .endm call_via r0 call_via r1 call_via r2 call_via r3 call_via r4 call_via r5 call_via r6 call_via r7 call_via r8 call_via r9 call_via sl call_via fp call_via ip call_via sp call_via lr #endif /* L_call_via_rX */ #if defined L_interwork_call_via_rX /* These labels & instructions are used by the Arm/Thumb interworking code, when the target address is in an unknown instruction set. The address of function to be called is loaded into a register and then one of these labels is called via a BL instruction. This puts the return address into the link register with the bottom bit set, and the code here switches to the correct mode before executing the function. Unfortunately the target code cannot be relied upon to return via a BX instruction, so instead we have to store the resturn address on the stack and allow the called function to return here instead. Upon return we recover the real return address and use a BX to get back to Thumb mode. There are three variations of this code. The first, _interwork_call_via_rN(), will push the return address onto the stack and pop it in _arm_return(). It should only be used if all arguments are passed in registers. The second, _interwork_r7_call_via_rN(), instead stores the return address at [r7, #-4]. It is the caller's responsibility to ensure that this address is valid and contains no useful data. The third, _interwork_r11_call_via_rN(), works in the same way but uses r11 instead of r7. It is useful if the caller does not really need a frame pointer. */ .text .align 0 .code 32 .globl _arm_return LSYM(Lstart_arm_return): cfi_start LSYM(Lstart_arm_return) LSYM(Lend_arm_return) cfi_push 0, 0xe, -0x8, 0x8 nop @ This nop is for the benefit of debuggers, so that @ backtraces will use the correct unwind information. _arm_return: RETLDM unwind=LSYM(Lstart_arm_return) cfi_end LSYM(Lend_arm_return) .globl _arm_return_r7 _arm_return_r7: ldr lr, [r7, #-4] bx lr .globl _arm_return_r11 _arm_return_r11: ldr lr, [r11, #-4] bx lr .macro interwork_with_frame frame, register, name, return .code 16 THUMB_FUNC_START \name bx pc nop .code 32 tst \register, #1 streq lr, [\frame, #-4] adreq lr, _arm_return_\frame bx \register SIZE (\name) .endm .macro interwork register .code 16 THUMB_FUNC_START _interwork_call_via_\register bx pc nop .code 32 .globl LSYM(Lchange_\register) LSYM(Lchange_\register): tst \register, #1 streq lr, [sp, #-8]! adreq lr, _arm_return bx \register SIZE (_interwork_call_via_\register) interwork_with_frame r7,\register,_interwork_r7_call_via_\register interwork_with_frame r11,\register,_interwork_r11_call_via_\register .endm interwork r0 interwork r1 interwork r2 interwork r3 interwork r4 interwork r5 interwork r6 interwork r7 interwork r8 interwork r9 interwork sl interwork fp interwork ip interwork sp /* The LR case has to be handled a little differently... */ .code 16 THUMB_FUNC_START _interwork_call_via_lr bx pc nop .code 32 .globl .Lchange_lr .Lchange_lr: tst lr, #1 stmeqdb r13!, {lr, pc} mov ip, lr adreq lr, _arm_return bx ip SIZE (_interwork_call_via_lr) #endif /* L_interwork_call_via_rX */ #endif /* Arch supports thumb. */ #ifndef __symbian__ #include "ieee754-df.S" #include "ieee754-sf.S" #include "bpabi.S" #endif /* __symbian__ */ Index: projects/sendfile/contrib/gcc =================================================================== --- projects/sendfile/contrib/gcc (revision 275355) +++ projects/sendfile/contrib/gcc (revision 275356) Property changes on: projects/sendfile/contrib/gcc ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/contrib/gcc:r270880-275355 Index: projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibnetdiscover.c =================================================================== --- projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibnetdiscover.c (revision 275355) +++ projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibnetdiscover.c (revision 275356) @@ -1,1051 +1,1051 @@ /* * Copyright (c) 2004-2008 Voltaire Inc. All rights reserved. * Copyright (c) 2007 Xsigo Systems Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #if HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "ibnetdiscover.h" #include "grouping.h" #include "ibdiag_common.h" static char *node_type_str[] = { "???", "ca", "switch", "router", "iwarp rnic" }; static char *linkwidth_str[] = { "??", "1x", "4x", "??", "8x", "??", "??", "??", "12x" }; static char *linkspeed_str[] = { "???", "SDR", "DDR", "???", "QDR" }; static int timeout = 2000; /* ms */ static int dumplevel = 0; static int verbose; static FILE *f; char *argv0 = "ibnetdiscover"; static char *node_name_map_file = NULL; static nn_map_t *node_name_map = NULL; Node *nodesdist[MAXHOPS+1]; /* last is Ca list */ Node *mynode; int maxhops_discovered = 0; struct ChassisList *chassis = NULL; static char * get_linkwidth_str(int linkwidth) { if (linkwidth > 8) return linkwidth_str[0]; else return linkwidth_str[linkwidth]; } static char * get_linkspeed_str(int linkspeed) { if (linkspeed > 4) return linkspeed_str[0]; else return linkspeed_str[linkspeed]; } static inline const char* node_type_str2(Node *node) { switch(node->type) { case SWITCH_NODE: return "SW"; case CA_NODE: return "CA"; case ROUTER_NODE: return "RT"; } return "??"; } void decode_port_info(void *pi, Port *port) { mad_decode_field(pi, IB_PORT_LID_F, &port->lid); mad_decode_field(pi, IB_PORT_LMC_F, &port->lmc); mad_decode_field(pi, IB_PORT_STATE_F, &port->state); mad_decode_field(pi, IB_PORT_PHYS_STATE_F, &port->physstate); mad_decode_field(pi, IB_PORT_LINK_WIDTH_ACTIVE_F, &port->linkwidth); mad_decode_field(pi, IB_PORT_LINK_SPEED_ACTIVE_F, &port->linkspeed); } int get_port(Port *port, int portnum, ib_portid_t *portid) { char portinfo[64]; void *pi = portinfo; port->portnum = portnum; if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, portnum, timeout)) return -1; decode_port_info(pi, port); DEBUG("portid %s portnum %d: lid %d state %d physstate %d %s %s", portid2str(portid), portnum, port->lid, port->state, port->physstate, get_linkwidth_str(port->linkwidth), get_linkspeed_str(port->linkspeed)); return 1; } /* * Returns 0 if non switch node is found, 1 if switch is found, -1 if error. */ int get_node(Node *node, Port *port, ib_portid_t *portid) { char portinfo[64]; char switchinfo[64]; void *pi = portinfo, *ni = node->nodeinfo, *nd = node->nodedesc; void *si = switchinfo; if (!smp_query(ni, portid, IB_ATTR_NODE_INFO, 0, timeout)) return -1; mad_decode_field(ni, IB_NODE_GUID_F, &node->nodeguid); mad_decode_field(ni, IB_NODE_TYPE_F, &node->type); mad_decode_field(ni, IB_NODE_NPORTS_F, &node->numports); mad_decode_field(ni, IB_NODE_DEVID_F, &node->devid); mad_decode_field(ni, IB_NODE_VENDORID_F, &node->vendid); mad_decode_field(ni, IB_NODE_SYSTEM_GUID_F, &node->sysimgguid); mad_decode_field(ni, IB_NODE_PORT_GUID_F, &node->portguid); mad_decode_field(ni, IB_NODE_LOCAL_PORT_F, &node->localport); port->portnum = node->localport; port->portguid = node->portguid; if (!smp_query(nd, portid, IB_ATTR_NODE_DESC, 0, timeout)) return -1; if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, 0, timeout)) return -1; decode_port_info(pi, port); if (node->type != SWITCH_NODE) return 0; node->smalid = port->lid; node->smalmc = port->lmc; /* after we have the sma information find out the real PortInfo for this port */ if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, node->localport, timeout)) return -1; decode_port_info(pi, port); if (!smp_query(si, portid, IB_ATTR_SWITCH_INFO, 0, timeout)) node->smaenhsp0 = 0; /* assume base SP0 */ else mad_decode_field(si, IB_SW_ENHANCED_PORT0_F, &node->smaenhsp0); DEBUG("portid %s: got switch node %" PRIx64 " '%s'", portid2str(portid), node->nodeguid, node->nodedesc); return 1; } static int extend_dpath(ib_dr_path_t *path, int nextport) { if (path->cnt+2 >= sizeof(path->p)) return -1; ++path->cnt; if (path->cnt > maxhops_discovered) maxhops_discovered = path->cnt; path->p[path->cnt] = nextport; return path->cnt; } static void dump_endnode(ib_portid_t *path, char *prompt, Node *node, Port *port) { if (!dumplevel) return; fprintf(f, "%s -> %s %s {%016" PRIx64 "} portnum %d lid %d-%d\"%s\"\n", portid2str(path), prompt, (node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"), node->nodeguid, node->type == SWITCH_NODE ? 0 : port->portnum, port->lid, port->lid + (1 << port->lmc) - 1, clean_nodedesc(node->nodedesc)); } #define HASHGUID(guid) ((uint32_t)(((uint32_t)(guid) * 101) ^ ((uint32_t)((guid) >> 32) * 103))) #define HTSZ 137 static Node *nodestbl[HTSZ]; static Node * find_node(Node *new) { int hash = HASHGUID(new->nodeguid) % HTSZ; Node *node; for (node = nodestbl[hash]; node; node = node->htnext) if (node->nodeguid == new->nodeguid) return node; return NULL; } static Node * create_node(Node *temp, ib_portid_t *path, int dist) { Node *node; int hash = HASHGUID(temp->nodeguid) % HTSZ; node = malloc(sizeof(*node)); if (!node) return NULL; memcpy(node, temp, sizeof(*node)); node->dist = dist; node->path = *path; node->htnext = nodestbl[hash]; nodestbl[hash] = node; if (node->type != SWITCH_NODE) dist = MAXHOPS; /* special Ca list */ node->dnext = nodesdist[dist]; nodesdist[dist] = node; return node; } static Port * find_port(Node *node, Port *port) { Port *old; for (old = node->ports; old; old = old->next) if (old->portnum == port->portnum) return old; return NULL; } static Port * create_port(Node *node, Port *temp) { Port *port; port = malloc(sizeof(*port)); if (!port) return NULL; memcpy(port, temp, sizeof(*port)); port->node = node; port->next = node->ports; node->ports = port; return port; } static void link_ports(Node *node, Port *port, Node *remotenode, Port *remoteport) { DEBUG("linking: 0x%" PRIx64 " %p->%p:%u and 0x%" PRIx64 " %p->%p:%u", node->nodeguid, node, port, port->portnum, remotenode->nodeguid, remotenode, remoteport, remoteport->portnum); if (port->remoteport) port->remoteport->remoteport = NULL; if (remoteport->remoteport) remoteport->remoteport->remoteport = NULL; port->remoteport = remoteport; remoteport->remoteport = port; } static int handle_port(Node *node, Port *port, ib_portid_t *path, int portnum, int dist) { Node node_buf; Port port_buf; Node *remotenode, *oldnode; Port *remoteport, *oldport; memset(&node_buf, 0, sizeof(node_buf)); memset(&port_buf, 0, sizeof(port_buf)); DEBUG("handle node %p port %p:%d dist %d", node, port, portnum, dist); if (port->physstate != 5) /* LinkUp */ return -1; if (extend_dpath(&path->drpath, portnum) < 0) return -1; if (get_node(&node_buf, &port_buf, path) < 0) { IBWARN("NodeInfo on %s failed, skipping port", portid2str(path)); path->drpath.cnt--; /* restore path */ return -1; } oldnode = find_node(&node_buf); if (oldnode) remotenode = oldnode; else if (!(remotenode = create_node(&node_buf, path, dist + 1))) IBERROR("no memory"); oldport = find_port(remotenode, &port_buf); if (oldport) { remoteport = oldport; if (node != remotenode || port != remoteport) IBWARN("port moving..."); } else if (!(remoteport = create_port(remotenode, &port_buf))) IBERROR("no memory"); dump_endnode(path, oldnode ? "known remote" : "new remote", remotenode, remoteport); link_ports(node, port, remotenode, remoteport); path->drpath.cnt--; /* restore path */ return 0; } /* * Return 1 if found, 0 if not, -1 on errors. */ static int discover(ib_portid_t *from) { Node node_buf; Port port_buf; Node *node; Port *port; int i; int dist = 0; ib_portid_t *path; DEBUG("from %s", portid2str(from)); memset(&node_buf, 0, sizeof(node_buf)); memset(&port_buf, 0, sizeof(port_buf)); if (get_node(&node_buf, &port_buf, from) < 0) { IBWARN("can't reach node %s", portid2str(from)); return -1; } node = create_node(&node_buf, from, 0); if (!node) IBERROR("out of memory"); mynode = node; port = create_port(node, &port_buf); if (!port) IBERROR("out of memory"); if (node->type != SWITCH_NODE && handle_port(node, port, from, node->localport, 0) < 0) return 0; for (dist = 0; dist < MAXHOPS; dist++) { for (node = nodesdist[dist]; node; node = node->dnext) { path = &node->path; DEBUG("dist %d node %p", dist, node); dump_endnode(path, "processing", node, port); for (i = 1; i <= node->numports; i++) { if (i == node->localport) continue; if (get_port(&port_buf, i, path) < 0) { IBWARN("can't reach node %s port %d", portid2str(path), i); continue; } port = find_port(node, &port_buf); if (port) continue; port = create_port(node, &port_buf); if (!port) IBERROR("out of memory"); /* If switch, set port GUID to node GUID */ if (node->type == SWITCH_NODE) port->portguid = node->portguid; handle_port(node, port, path, i, dist); } } } return 0; } char * node_name(Node *node) { static char buf[256]; switch(node->type) { case SWITCH_NODE: sprintf(buf, "\"%s", "S"); break; case CA_NODE: sprintf(buf, "\"%s", "H"); break; case ROUTER_NODE: sprintf(buf, "\"%s", "R"); break; default: sprintf(buf, "\"%s", "?"); break; } sprintf(buf+2, "-%016" PRIx64 "\"", node->nodeguid); return buf; } void list_node(Node *node) { char *node_type; char *nodename = remap_node_name(node_name_map, node->nodeguid, node->nodedesc); switch(node->type) { case SWITCH_NODE: node_type = "Switch"; break; case CA_NODE: node_type = "Ca"; break; case ROUTER_NODE: node_type = "Router"; break; default: node_type = "???"; break; } fprintf(f, "%s\t : 0x%016" PRIx64 " ports %d devid 0x%x vendid 0x%x \"%s\"\n", node_type, node->nodeguid, node->numports, node->devid, node->vendid, nodename); free(nodename); } void out_ids(Node *node, int group, char *chname) { fprintf(f, "\nvendid=0x%x\ndevid=0x%x\n", node->vendid, node->devid); if (node->sysimgguid) fprintf(f, "sysimgguid=0x%" PRIx64, node->sysimgguid); if (group && node->chrecord && node->chrecord->chassisnum) { fprintf(f, "\t\t# Chassis %d", node->chrecord->chassisnum); if (chname) fprintf(f, " (%s)", chname); if (is_xsigo_tca(node->nodeguid) && node->ports->remoteport) fprintf(f, " slot %d", node->ports->remoteport->portnum); } fprintf(f, "\n"); } uint64_t out_chassis(int chassisnum) { uint64_t guid; fprintf(f, "\nChassis %d", chassisnum); guid = get_chassis_guid(chassisnum); if (guid) fprintf(f, " (guid 0x%" PRIx64 ")", guid); fprintf(f, "\n"); return guid; } void out_switch(Node *node, int group, char *chname) { char *str; char *nodename = NULL; out_ids(node, group, chname); fprintf(f, "switchguid=0x%" PRIx64, node->nodeguid); fprintf(f, "(%" PRIx64 ")", node->portguid); /* Currently, only if Voltaire chassis */ if (group && node->chrecord && node->chrecord->chassisnum && node->vendid == VTR_VENDOR_ID) { str = get_chassis_type(node->chrecord->chassistype); if (str) fprintf(f, "%s ", str); str = get_chassis_slot(node->chrecord->chassisslot); if (str) fprintf(f, "%s ", str); fprintf(f, "%d Chip %d", node->chrecord->slotnum, node->chrecord->anafanum); } nodename = remap_node_name(node_name_map, node->nodeguid, node->nodedesc); fprintf(f, "\nSwitch\t%d %s\t\t# \"%s\" %s port 0 lid %d lmc %d\n", node->numports, node_name(node), nodename, node->smaenhsp0 ? "enhanced" : "base", node->smalid, node->smalmc); free(nodename); } void out_ca(Node *node, int group, char *chname) { char *node_type; char *node_type2; char *nodename = remap_node_name(node_name_map, node->nodeguid, node->nodedesc); out_ids(node, group, chname); switch(node->type) { case CA_NODE: node_type = "ca"; node_type2 = "Ca"; break; case ROUTER_NODE: node_type = "rt"; node_type2 = "Rt"; break; default: node_type = "???"; node_type2 = "???"; break; } fprintf(f, "%sguid=0x%" PRIx64 "\n", node_type, node->nodeguid); fprintf(f, "%s\t%d %s\t\t# \"%s\"", node_type2, node->numports, node_name(node), nodename); if (group && is_xsigo_hca(node->nodeguid)) fprintf(f, " (scp)"); fprintf(f, "\n"); free(nodename); } static char * out_ext_port(Port *port, int group) { char *str = NULL; /* Currently, only if Voltaire chassis */ if (group && port->node->chrecord && port->node->vendid == VTR_VENDOR_ID) str = portmapstring(port); return (str); } void out_switch_port(Port *port, int group) { char *ext_port_str = NULL; char *rem_nodename = NULL; DEBUG("port %p:%d remoteport %p", port, port->portnum, port->remoteport); fprintf(f, "[%d]", port->portnum); ext_port_str = out_ext_port(port, group); if (ext_port_str) fprintf(f, "%s", ext_port_str); rem_nodename = remap_node_name(node_name_map, port->remoteport->node->nodeguid, port->remoteport->node->nodedesc); ext_port_str = out_ext_port(port->remoteport, group); fprintf(f, "\t%s[%d]%s", node_name(port->remoteport->node), port->remoteport->portnum, ext_port_str ? ext_port_str : ""); if (port->remoteport->node->type != SWITCH_NODE) fprintf(f, "(%" PRIx64 ") ", port->remoteport->portguid); fprintf(f, "\t\t# \"%s\" lid %d %s%s", rem_nodename, port->remoteport->node->type == SWITCH_NODE ? port->remoteport->node->smalid : port->remoteport->lid, get_linkwidth_str(port->linkwidth), get_linkspeed_str(port->linkspeed)); if (is_xsigo_tca(port->remoteport->portguid)) fprintf(f, " slot %d", port->portnum); else if (is_xsigo_hca(port->remoteport->portguid)) fprintf(f, " (scp)"); fprintf(f, "\n"); free(rem_nodename); } void out_ca_port(Port *port, int group) { char *str = NULL; char *rem_nodename = NULL; fprintf(f, "[%d]", port->portnum); if (port->node->type != SWITCH_NODE) fprintf(f, "(%" PRIx64 ") ", port->portguid); fprintf(f, "\t%s[%d]", node_name(port->remoteport->node), port->remoteport->portnum); str = out_ext_port(port->remoteport, group); if (str) fprintf(f, "%s", str); if (port->remoteport->node->type != SWITCH_NODE) fprintf(f, " (%" PRIx64 ") ", port->remoteport->portguid); rem_nodename = remap_node_name(node_name_map, port->remoteport->node->nodeguid, port->remoteport->node->nodedesc); fprintf(f, "\t\t# lid %d lmc %d \"%s\" lid %d %s%s\n", port->lid, port->lmc, rem_nodename, port->remoteport->node->type == SWITCH_NODE ? port->remoteport->node->smalid : port->remoteport->lid, get_linkwidth_str(port->linkwidth), get_linkspeed_str(port->linkspeed)); free(rem_nodename); } int dump_topology(int listtype, int group) { Node *node; Port *port; int i = 0, dist = 0; time_t t = time(0); uint64_t chguid; char *chname = NULL; if (!listtype) { fprintf(f, "#\n# Topology file: generated on %s#\n", ctime(&t)); fprintf(f, "# Max of %d hops discovered\n", maxhops_discovered); fprintf(f, "# Initiated from node %016" PRIx64 " port %016" PRIx64 "\n", mynode->nodeguid, mynode->portguid); } /* Make pass on switches */ if (group && !listtype) { ChassisList *ch = NULL; /* Chassis based switches first */ for (ch = chassis; ch; ch = ch->next) { int n = 0; if (!ch->chassisnum) continue; chguid = out_chassis(ch->chassisnum); if (chname) free(chname); chname = NULL; if (is_xsigo_guid(chguid)) { for (node = nodesdist[MAXHOPS]; node; node = node->dnext) { if (!node->chrecord || !node->chrecord->chassisnum) continue; if (node->chrecord->chassisnum != ch->chassisnum) continue; if (is_xsigo_hca(node->nodeguid)) { chname = remap_node_name(node_name_map, node->nodeguid, node->nodedesc); fprintf(f, "Hostname: %s\n", chname); } } } fprintf(f, "\n# Spine Nodes"); for (n = 1; n <= (SPINES_MAX_NUM+1); n++) { if (ch->spinenode[n]) { out_switch(ch->spinenode[n], group, chname); for (port = ch->spinenode[n]->ports; port; port = port->next, i++) if (port->remoteport) out_switch_port(port, group); } } fprintf(f, "\n# Line Nodes"); for (n = 1; n <= (LINES_MAX_NUM+1); n++) { if (ch->linenode[n]) { out_switch(ch->linenode[n], group, chname); for (port = ch->linenode[n]->ports; port; port = port->next, i++) if (port->remoteport) out_switch_port(port, group); } } fprintf(f, "\n# Chassis Switches"); for (dist = 0; dist <= maxhops_discovered; dist++) { for (node = nodesdist[dist]; node; node = node->dnext) { /* Non Voltaire chassis */ if (node->vendid == VTR_VENDOR_ID) continue; if (!node->chrecord || !node->chrecord->chassisnum) continue; if (node->chrecord->chassisnum != ch->chassisnum) continue; out_switch(node, group, chname); for (port = node->ports; port; port = port->next, i++) if (port->remoteport) out_switch_port(port, group); } } fprintf(f, "\n# Chassis CAs"); for (node = nodesdist[MAXHOPS]; node; node = node->dnext) { if (!node->chrecord || !node->chrecord->chassisnum) continue; if (node->chrecord->chassisnum != ch->chassisnum) continue; out_ca(node, group, chname); for (port = node->ports; port; port = port->next, i++) if (port->remoteport) out_ca_port(port, group); } } } else { for (dist = 0; dist <= maxhops_discovered; dist++) { for (node = nodesdist[dist]; node; node = node->dnext) { DEBUG("SWITCH: dist %d node %p", dist, node); if (!listtype) out_switch(node, group, chname); else { if (listtype & LIST_SWITCH_NODE) list_node(node); continue; } for (port = node->ports; port; port = port->next, i++) if (port->remoteport) out_switch_port(port, group); } } } if (chname) free(chname); chname = NULL; if (group && !listtype) { fprintf(f, "\nNon-Chassis Nodes\n"); for (dist = 0; dist <= maxhops_discovered; dist++) { for (node = nodesdist[dist]; node; node = node->dnext) { DEBUG("SWITCH: dist %d node %p", dist, node); /* Now, skip chassis based switches */ if (node->chrecord && node->chrecord->chassisnum) continue; out_switch(node, group, chname); for (port = node->ports; port; port = port->next, i++) if (port->remoteport) out_switch_port(port, group); } } } /* Make pass on CAs */ for (node = nodesdist[MAXHOPS]; node; node = node->dnext) { DEBUG("CA: dist %d node %p", dist, node); if (!listtype) { /* Now, skip chassis based CAs */ if (group && node->chrecord && node->chrecord->chassisnum) continue; out_ca(node, group, chname); } else { if (((listtype & LIST_CA_NODE) && (node->type == CA_NODE)) || ((listtype & LIST_ROUTER_NODE) && (node->type == ROUTER_NODE))) list_node(node); continue; } for (port = node->ports; port; port = port->next, i++) if (port->remoteport) out_ca_port(port, group); } if (chname) free(chname); return i; } void dump_ports_report () { int b, n = 0, p; Node *node; Port *port; // If switch and LID == 0, search of other switch ports with // valid LID and assign it to all ports of that switch for (b = 0; b <= MAXHOPS; b++) for (node = nodesdist[b]; node; node = node->dnext) if (node->type == SWITCH_NODE) { int swlid = 0; for (p = 0, port = node->ports; p < node->numports && port && !swlid; port = port->next) if (port->lid != 0) swlid = port->lid; for (p = 0, port = node->ports; p < node->numports && port; port = port->next) port->lid = swlid; } for (b = 0; b <= MAXHOPS; b++) for (node = nodesdist[b]; node; node = node->dnext) { for (p = 0, port = node->ports; p < node->numports && port; p++, port = port->next) { fprintf(stdout, "%2s %5d %2d 0x%016" PRIx64 " %s %s", node_type_str2(port->node), port->lid, port->portnum, port->portguid, get_linkwidth_str(port->linkwidth), get_linkspeed_str(port->linkspeed)); if (port->remoteport) fprintf(stdout, " - %2s %5d %2d 0x%016" PRIx64 " ( '%s' - '%s' )\n", node_type_str2(port->remoteport->node), port->remoteport->lid, port->remoteport->portnum, port->remoteport->portguid, port->node->nodedesc, port->remoteport->node->nodedesc); else fprintf(stdout, "%36s'%s'\n", "", port->node->nodedesc); } n++; } } void usage(void) { fprintf(stderr, "Usage: %s [-d(ebug)] -e(rr_show) -v(erbose) -s(how) -l(ist) -g(rouping) -H(ca_list) -S(witch_list) -R(outer_list) -V(ersion) -C ca_name -P ca_port " "-t(imeout) timeout_ms --node-name-map node-name-map] -p(orts) []\n", argv0); fprintf(stderr, " --node-name-map specify a node name map file\n"); exit(-1); } int main(int argc, char **argv) { int mgmt_classes[2] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS}; ib_portid_t my_portid = {0}; int udebug = 0, list = 0; char *ca = 0; int ca_port = 0; int group = 0; int ports_report = 0; static char const str_opts[] = "C:P:t:devslgHSRpVhu"; static const struct option long_opts[] = { { "C", 1, 0, 'C'}, { "P", 1, 0, 'P'}, { "debug", 0, 0, 'd'}, { "err_show", 0, 0, 'e'}, { "verbose", 0, 0, 'v'}, { "show", 0, 0, 's'}, { "list", 0, 0, 'l'}, { "grouping", 0, 0, 'g'}, { "Hca_list", 0, 0, 'H'}, { "Switch_list", 0, 0, 'S'}, { "Router_list", 0, 0, 'R'}, { "timeout", 1, 0, 't'}, { "node-name-map", 1, 0, 1}, { "ports", 0, 0, 'p'}, { "Version", 0, 0, 'V'}, { "help", 0, 0, 'h'}, { "usage", 0, 0, 'u'}, { } }; f = stdout; argv0 = argv[0]; while (1) { int ch = getopt_long(argc, argv, str_opts, long_opts, NULL); if ( ch == -1 ) break; switch(ch) { case 1: node_name_map_file = strdup(optarg); break; case 'C': ca = optarg; break; case 'P': ca_port = strtoul(optarg, 0, 0); break; case 'd': ibdebug++; madrpc_show_errors(1); umad_debug(udebug); udebug++; break; case 't': timeout = strtoul(optarg, 0, 0); break; case 'v': verbose++; dumplevel++; break; case 's': dumplevel = 1; break; case 'e': madrpc_show_errors(1); break; case 'l': list = LIST_CA_NODE | LIST_SWITCH_NODE | LIST_ROUTER_NODE; break; case 'g': group = 1; break; case 'S': list = LIST_SWITCH_NODE; break; case 'H': list = LIST_CA_NODE; break; case 'R': list = LIST_ROUTER_NODE; break; case 'V': fprintf(stderr, "%s %s\n", argv0, get_build_version() ); exit(-1); case 'p': ports_report = 1; break; default: usage(); break; } } argc -= optind; argv += optind; if (argc && !(f = fopen(argv[0], "w"))) IBERROR("can't open file %s for writing", argv[0]); madrpc_init(ca, ca_port, mgmt_classes, 2); node_name_map = open_node_name_map(node_name_map_file); if (discover(&my_portid) < 0) IBERROR("discover"); if (group) chassis = group_nodes(); if (ports_report) dump_ports_report(); else dump_topology(list, group); close_node_name_map(node_name_map); exit(0); } Index: projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibroute.c =================================================================== --- projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibroute.c (revision 275355) +++ projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibroute.c (revision 275356) @@ -1,495 +1,495 @@ /* * Copyright (c) 2004-2008 Voltaire Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #if HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "ibdiag_common.h" static int dest_type = IB_DEST_LID; static int brief; static int verbose; static int dump_all; char *argv0 = "ibroute"; /*******************************************/ char * check_switch(ib_portid_t *portid, int *nports, uint64_t *guid, uint8_t *sw, char *nd) { uint8_t ni[IB_SMP_DATA_SIZE] = {0}; int type; DEBUG("checking node type"); if (!smp_query(ni, portid, IB_ATTR_NODE_INFO, 0, 0)) { xdump(stderr, "nodeinfo\n", ni, sizeof ni); return "node info failed: valid addr?"; } if (!smp_query(nd, portid, IB_ATTR_NODE_DESC, 0, 0)) return "node desc failed"; mad_decode_field(ni, IB_NODE_TYPE_F, &type); if (type != IB_NODE_SWITCH) return "not a switch"; DEBUG("Gathering information about switch"); mad_decode_field(ni, IB_NODE_NPORTS_F, nports); mad_decode_field(ni, IB_NODE_GUID_F, guid); if (!smp_query(sw, portid, IB_ATTR_SWITCH_INFO, 0, 0)) return "switch info failed: is a switch node?"; return 0; } #define IB_MLIDS_IN_BLOCK (IB_SMP_DATA_SIZE/2) int dump_mlid(char *str, int strlen, int mlid, int nports, uint16_t mft[16][IB_MLIDS_IN_BLOCK]) { uint16_t mask; int i, chunk, bit; int nonzero = 0; if (brief) { int n = 0, chunks = ALIGN(nports + 1, 16) / 16; for (i = 0; i < chunks; i++) { mask = ntohs(mft[i][mlid%IB_MLIDS_IN_BLOCK]); if (mask) nonzero++; n += snprintf(str + n, strlen - n, "%04hx", mask); if (n >= strlen) { n = strlen; break; } } if (!nonzero && !dump_all) { str[0] = 0; return 0; } return n; } for (i = 0; i <= nports; i++) { chunk = i / 16; bit = i % 16; mask = ntohs(mft[chunk][mlid%IB_MLIDS_IN_BLOCK]); if (mask) nonzero++; str[i*2] = (mask & (1 << bit)) ? 'x' : ' '; str[i*2+1] = ' '; } if (!nonzero && !dump_all) { str[0] = 0; return 0; } str[i*2] = 0; return i * 2; } uint16_t mft[16][IB_MLIDS_IN_BLOCK]; char * dump_multicast_tables(ib_portid_t *portid, int startlid, int endlid) { char nd[IB_SMP_DATA_SIZE] = {0}; uint8_t sw[IB_SMP_DATA_SIZE] = {0}; char str[512]; char *s; uint64_t nodeguid; uint32_t mod; int block, i, j, e, nports, cap, chunks; int n = 0, startblock, lastblock; if ((s = check_switch(portid, &nports, &nodeguid, sw, nd))) return s; mad_decode_field(sw, IB_SW_MCAST_FDB_CAP_F, &cap); if (!endlid || endlid > IB_MIN_MCAST_LID + cap - 1) endlid = IB_MIN_MCAST_LID + cap - 1; if (!startlid) startlid = IB_MIN_MCAST_LID; if (startlid < IB_MIN_MCAST_LID) { IBWARN("illegal start mlid %x, set to %x", startlid, IB_MIN_MCAST_LID); startlid = IB_MIN_MCAST_LID; } if (endlid > IB_MAX_MCAST_LID) { IBWARN("illegal end mlid %x, truncate to %x", endlid, IB_MAX_MCAST_LID); endlid = IB_MAX_MCAST_LID; } printf("Multicast mlids [0x%x-0x%x] of switch %s guid 0x%016" PRIx64 " (%s):\n", startlid, endlid, portid2str(portid), nodeguid, clean_nodedesc(nd)); if (brief) printf(" MLid Port Mask\n"); else { if (nports > 9) { for (i = 0, s = str; i <= nports; i++) { *s++ = (i%10) ? ' ' : '0' + i/10; *s++ = ' '; } *s = 0; printf(" %s\n", str); } for (i = 0, s = str; i <= nports; i++) s += sprintf(s, "%d ", i%10); printf(" Ports: %s\n", str); printf(" MLid\n"); } if (verbose) printf("Switch muticast mlids capability is 0x%d\n", cap); chunks = ALIGN(nports + 1, 16) / 16; startblock = startlid / IB_MLIDS_IN_BLOCK; lastblock = endlid / IB_MLIDS_IN_BLOCK; for (block = startblock; block <= lastblock; block++) { for (j = 0; j < chunks; j++) { mod = (block - IB_MIN_MCAST_LID/IB_MLIDS_IN_BLOCK) | (j << 28); DEBUG("reading block %x chunk %d mod %x", block, j, mod); if (!smp_query(mft + j, portid, IB_ATTR_MULTICASTFORWTBL, mod, 0)) return "multicast forwarding table get failed"; } i = block * IB_MLIDS_IN_BLOCK; e = i + IB_MLIDS_IN_BLOCK; if (i < startlid) i = startlid; if (e > endlid + 1) e = endlid + 1; for (; i < e; i++) { if (dump_mlid(str, sizeof str, i, nports, mft) == 0) continue; printf("0x%04x %s\n", i, str); n++; } } printf("%d %smlids dumped \n", n, dump_all ? "" : "valid "); return 0; } int dump_lid(char *str, int strlen, int lid, int valid) { char nd[IB_SMP_DATA_SIZE] = {0}; uint8_t ni[IB_SMP_DATA_SIZE] = {0}; uint8_t pi[IB_SMP_DATA_SIZE] = {0}; ib_portid_t lidport = {0}; static int last_port_lid, base_port_lid; char ntype[50], sguid[30], desc[64]; static uint64_t portguid; int baselid, lmc, type; if (brief) { str[0] = 0; return 0; } if (lid <= last_port_lid) { if (!valid) return snprintf(str, strlen, ": (path #%d - illegal port)", lid - base_port_lid); else if (!portguid) return snprintf(str, strlen, ": (path #%d out of %d)", lid - base_port_lid + 1, last_port_lid - base_port_lid + 1); else { return snprintf(str, strlen, ": (path #%d out of %d: portguid %s)", lid - base_port_lid + 1, last_port_lid - base_port_lid + 1, mad_dump_val(IB_NODE_PORT_GUID_F, sguid, sizeof sguid, &portguid)); } } if (!valid) return snprintf(str, strlen, ": (illegal port)"); portguid = 0; lidport.lid = lid; if (!smp_query(nd, &lidport, IB_ATTR_NODE_DESC, 0, 100) || !smp_query(pi, &lidport, IB_ATTR_PORT_INFO, 0, 100) || !smp_query(ni, &lidport, IB_ATTR_NODE_INFO, 0, 100)) return snprintf(str, strlen, ": (unknown node and type)"); mad_decode_field(ni, IB_NODE_PORT_GUID_F, &portguid); mad_decode_field(ni, IB_NODE_TYPE_F, &type); mad_decode_field(pi, IB_PORT_LID_F, &baselid); mad_decode_field(pi, IB_PORT_LMC_F, &lmc); if (lmc > 0) { base_port_lid = baselid; last_port_lid = baselid + (1 << lmc) - 1; } return snprintf(str, strlen, ": (%s portguid %s: %s)", mad_dump_val(IB_NODE_TYPE_F, ntype, sizeof ntype, &type), mad_dump_val(IB_NODE_PORT_GUID_F, sguid, sizeof sguid, &portguid), mad_dump_val(IB_NODE_DESC_F, desc, sizeof desc, clean_nodedesc(nd))); } char * dump_unicast_tables(ib_portid_t *portid, int startlid, int endlid) { char lft[IB_SMP_DATA_SIZE]; char nd[IB_SMP_DATA_SIZE]; uint8_t sw[IB_SMP_DATA_SIZE]; char str[200], *s; uint64_t nodeguid; int block, i, e, nports, top; int n = 0, startblock, endblock; if ((s = check_switch(portid, &nports, &nodeguid, sw, nd))) return s; mad_decode_field(sw, IB_SW_LINEAR_FDB_TOP_F, &top); if (!endlid || endlid > top) endlid = top; if (endlid > IB_MAX_UCAST_LID) { IBWARN("ilegal lft top %d, truncate to %d", endlid, IB_MAX_UCAST_LID); endlid = IB_MAX_UCAST_LID; } printf("Unicast lids [0x%x-0x%x] of switch %s guid 0x%016" PRIx64 " (%s):\n", startlid, endlid, portid2str(portid), nodeguid, clean_nodedesc(nd)); DEBUG("Switch top is 0x%x\n", top); printf(" Lid Out Destination\n"); printf(" Port Info \n"); startblock = startlid / IB_SMP_DATA_SIZE; endblock = ALIGN(endlid, IB_SMP_DATA_SIZE) / IB_SMP_DATA_SIZE; for (block = startblock; block <= endblock; block++) { DEBUG("reading block %d", block); if (!smp_query(lft, portid, IB_ATTR_LINEARFORWTBL, block, 0)) return "linear forwarding table get failed"; i = block * IB_SMP_DATA_SIZE; e = i + IB_SMP_DATA_SIZE; if (i < startlid) i = startlid; if (e > endlid + 1) e = endlid + 1; for (;i < e; i++) { unsigned outport = lft[i % IB_SMP_DATA_SIZE]; unsigned valid = (outport <= nports); if (!valid && !dump_all) continue; dump_lid(str, sizeof str, i, valid); printf("0x%04x %03u %s\n", i, outport & 0xff, str); n++; } } printf("%d %slids dumped \n", n, dump_all ? "" : "valid "); return 0; } void usage(void) { char *basename; if (!(basename = strrchr(argv0, '/'))) basename = argv0; else basename++; fprintf(stderr, "Usage: %s [-d(ebug)] -a(ll) -n(o_dests) -v(erbose) -D(irect) -G(uid) -M(ulticast) -s smlid -V(ersion) -C ca_name -P ca_port " "-t(imeout) timeout_ms] [ [ []]]\n", basename); fprintf(stderr, "\n\tUnicast examples:\n"); fprintf(stderr, "\t\t%s 4\t# dump all lids with valid out ports of switch with lid 4\n", basename); fprintf(stderr, "\t\t%s -a 4\t# same, but dump all lids, even with invalid out ports\n", basename); fprintf(stderr, "\t\t%s -n 4\t# simple dump format - no destination resolving\n", basename); fprintf(stderr, "\t\t%s 4 10\t# dump lids starting from 10\n", basename); fprintf(stderr, "\t\t%s 4 0x10 0x20\t# dump lid range\n", basename); fprintf(stderr, "\t\t%s -G 0x08f1040023\t# resolve switch by GUID\n", basename); fprintf(stderr, "\t\t%s -D 0,1\t# resolve switch by direct path\n", basename); fprintf(stderr, "\n\tMulticast examples:\n"); fprintf(stderr, "\t\t%s -M 4\t# dump all non empty mlids of switch with lid 4\n", basename); fprintf(stderr, "\t\t%s -M 4 0xc010 0xc020\t# same, but with range\n", basename); fprintf(stderr, "\t\t%s -M -n 4\t# simple dump format\n", basename); exit(-1); } int main(int argc, char **argv) { int mgmt_classes[3] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS}; ib_portid_t portid = {0}; ib_portid_t *sm_id = 0, sm_portid = {0}; int timeout; int multicast = 0, startlid = 0, endlid = 0; char *err; char *ca = 0; int ca_port = 0; static char const str_opts[] = "C:P:t:s:danvDGMVhu"; static const struct option long_opts[] = { { "C", 1, 0, 'C'}, { "P", 1, 0, 'P'}, { "debug", 0, 0, 'd'}, { "all", 0, 0, 'a'}, { "no_dests", 0, 0, 'n'}, { "verbose", 0, 0, 'v'}, { "Direct", 0, 0, 'D'}, { "Guid", 0, 0, 'G'}, { "Multicast", 0, 0, 'M'}, { "timeout", 1, 0, 't'}, { "s", 1, 0, 's'}, { "Version", 0, 0, 'V'}, { "help", 0, 0, 'h'}, { "usage", 0, 0, 'u'}, { } }; argv0 = argv[0]; while (1) { int ch = getopt_long(argc, argv, str_opts, long_opts, NULL); if ( ch == -1 ) break; switch(ch) { case 'C': ca = optarg; break; case 'P': ca_port = strtoul(optarg, 0, 0); break; case 'a': dump_all++; break; case 'd': ibdebug++; break; case 'D': dest_type = IB_DEST_DRPATH; break; case 'G': dest_type = IB_DEST_GUID; break; case 'M': multicast++; break; case 'n': brief++; break; case 's': if (ib_resolve_portid_str(&sm_portid, optarg, IB_DEST_LID, 0) < 0) IBERROR("can't resolve SM destination port %s", optarg); sm_id = &sm_portid; break; case 't': timeout = strtoul(optarg, 0, 0); madrpc_set_timeout(timeout); break; case 'v': madrpc_show_errors(1); verbose++; break; case 'V': fprintf(stderr, "%s %s\n", argv0, get_build_version() ); exit(-1); default: usage(); break; } } argc -= optind; argv += optind; if (!argc) usage(); if (argc > 1) startlid = strtoul(argv[1], 0, 0); if (argc > 2) endlid = strtoul(argv[2], 0, 0); madrpc_init(ca, ca_port, mgmt_classes, 3); if (!argc) { if (ib_resolve_self(&portid, 0, 0) < 0) IBERROR("can't resolve self addr"); } else { if (ib_resolve_portid_str(&portid, argv[0], dest_type, sm_id) < 0) IBERROR("can't resolve destination port %s", argv[1]); } if (multicast) err = dump_multicast_tables(&portid, startlid, endlid); else err = dump_unicast_tables(&portid, startlid, endlid); if (err) IBERROR("dump tables: %s", err); exit(0); } Index: projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibsendtrap.c =================================================================== --- projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibsendtrap.c (revision 275355) +++ projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibsendtrap.c (revision 275356) @@ -1,176 +1,176 @@ /* * Copyright (c) 2008 Lawrence Livermore National Security * * Produced at Lawrence Livermore National Laboratory. * Written by Ira Weiny . * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include #include #include #include #define _GNU_SOURCE #include #include -#include +#include #include "ibdiag_common.h" char *argv0 = ""; static int send_144_node_desc_update(void) { ib_portid_t sm_port; ib_portid_t selfportid; int selfport; ib_rpc_t trap_rpc; ib_mad_notice_attr_t notice; if (ib_resolve_self(&selfportid, &selfport, NULL)) IBERROR("can't resolve self"); if (ib_resolve_smlid(&sm_port, 0)) IBERROR("can't resolve SM destination port"); memset(&trap_rpc, 0, sizeof(trap_rpc)); trap_rpc.mgtclass = IB_SMI_CLASS; trap_rpc.method = IB_MAD_METHOD_TRAP; trap_rpc.trid = mad_trid(); trap_rpc.attr.id = NOTICE; trap_rpc.datasz = IB_SMP_DATA_SIZE; trap_rpc.dataoffs = IB_SMP_DATA_OFFS; memset(¬ice, 0, sizeof(notice)); notice.generic_type = 0x80 | IB_NOTICE_TYPE_INFO; notice.g_or_v.generic.prod_type_lsb = cl_hton16(IB_NODE_TYPE_CA); notice.g_or_v.generic.trap_num = cl_hton16(144); notice.issuer_lid = cl_hton16(selfportid.lid); notice.data_details.ntc_144.lid = cl_hton16(selfportid.lid); notice.data_details.ntc_144.local_changes = TRAP_144_MASK_OTHER_LOCAL_CHANGES; notice.data_details.ntc_144.change_flgs = TRAP_144_MASK_NODE_DESCRIPTION_CHANGE; return (mad_send(&trap_rpc, &sm_port, NULL, ¬ice)); } typedef struct _trap_def { char *trap_name; int (*send_func) (void); } trap_def_t; trap_def_t traps[2] = { {"node_desc_change", send_144_node_desc_update}, {NULL, NULL} }; static void usage(void) { int i; fprintf(stderr, "Usage: %s [-hV]" " [-C ] [-P ] []\n", argv0); fprintf(stderr, " -V print version\n"); fprintf(stderr, " can be one of the following\n"); for (i = 0; traps[i].trap_name; i++) { fprintf(stderr, " %s\n", traps[i].trap_name); } fprintf(stderr, " default behavior is to send \"%s\"\n", traps[0].trap_name); exit(-1); } int send_trap(char *trap_name) { int i; for (i = 0; traps[i].trap_name; i++) { if (strcmp(traps[i].trap_name, trap_name) == 0) { return (traps[i].send_func()); } } usage(); exit(1); } int main(int argc, char **argv) { int mgmt_classes[2] = { IB_SMI_CLASS, IB_SMI_DIRECT_CLASS }; int ch = 0; char *trap_name = NULL; char *ca = NULL; int ca_port = 0; static char const str_opts[] = "hVP:C:"; static const struct option long_opts[] = { {"Version", 0, 0, 'V'}, {"P", 1, 0, 'P'}, {"C", 1, 0, 'C'}, {"help", 0, 0, 'h'}, {} }; argv0 = argv[0]; while ((ch = getopt_long(argc, argv, str_opts, long_opts, NULL)) != -1) { switch (ch) { case 'V': fprintf(stderr, "%s %s\n", argv0, get_build_version()); exit(-1); case 'C': ca = optarg; break; case 'P': ca_port = strtoul(optarg, NULL, 0); break; case 'h': default: usage(); } } argc -= optind; argv += optind; if (!argv[0]) { trap_name = traps[0].trap_name; } else { trap_name = argv[0]; } madrpc_show_errors(1); madrpc_init(ca, ca_port, mgmt_classes, 2); return (send_trap(trap_name)); } Index: projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibtracert.c =================================================================== --- projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibtracert.c (revision 275355) +++ projects/sendfile/contrib/ofed/management/infiniband-diags/src/ibtracert.c (revision 275356) @@ -1,865 +1,865 @@ /* * Copyright (c) 2004-2008 Voltaire Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #if HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include #include "ibdiag_common.h" #define MAXHOPS 63 static char *node_type_str[] = { "???", "ca", "switch", "router", "iwarp rnic" }; static int timeout = 0; /* ms */ static int verbose; static int force; static FILE *f; char *argv0 = "ibtracert"; static char *node_name_map_file = NULL; static nn_map_t *node_name_map = NULL; typedef struct Port Port; typedef struct Switch Switch; typedef struct Node Node; struct Port { Port *next; Port *remoteport; uint64_t portguid; int portnum; int lid; int lmc; int state; int physstate; char portinfo[64]; }; struct Switch { int linearcap; int mccap; int linearFDBtop; int fdb_base; int8_t fdb[64]; char switchinfo[64]; }; struct Node { Node *htnext; Node *dnext; Port *ports; ib_portid_t path; int type; int dist; int numports; int upport; Node *upnode; uint64_t nodeguid; /* also portguid */ char nodedesc[64]; char nodeinfo[64]; }; Node *nodesdist[MAXHOPS]; uint64_t target_portguid; static int get_node(Node *node, Port *port, ib_portid_t *portid) { void *pi = port->portinfo, *ni = node->nodeinfo, *nd = node->nodedesc; char *s, *e; if (!smp_query(ni, portid, IB_ATTR_NODE_INFO, 0, timeout)) return -1; if (!smp_query(nd, portid, IB_ATTR_NODE_DESC, 0, timeout)) return -1; for (s = nd, e = s + 64; s < e; s++) { if (!*s) break; if (!isprint(*s)) *s = ' '; } if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, 0, timeout)) return -1; mad_decode_field(ni, IB_NODE_GUID_F, &node->nodeguid); mad_decode_field(ni, IB_NODE_TYPE_F, &node->type); mad_decode_field(ni, IB_NODE_NPORTS_F, &node->numports); mad_decode_field(ni, IB_NODE_PORT_GUID_F, &port->portguid); mad_decode_field(ni, IB_NODE_LOCAL_PORT_F, &port->portnum); mad_decode_field(pi, IB_PORT_LID_F, &port->lid); mad_decode_field(pi, IB_PORT_LMC_F, &port->lmc); mad_decode_field(pi, IB_PORT_STATE_F, &port->state); DEBUG("portid %s: got node %" PRIx64 " '%s'", portid2str(portid), node->nodeguid, node->nodedesc); return 0; } static int switch_lookup(Switch *sw, ib_portid_t *portid, int lid) { void *si = sw->switchinfo, *fdb = sw->fdb; if (!smp_query(si, portid, IB_ATTR_SWITCH_INFO, 0, timeout)) return -1; mad_decode_field(si, IB_SW_LINEAR_FDB_CAP_F, &sw->linearcap); mad_decode_field(si, IB_SW_LINEAR_FDB_TOP_F, &sw->linearFDBtop); if (lid > sw->linearcap && lid > sw->linearFDBtop) return -1; if (!smp_query(fdb, portid, IB_ATTR_LINEARFORWTBL, lid / 64, timeout)) return -1; DEBUG("portid %s: forward lid %d to port %d", portid2str(portid), lid, sw->fdb[lid % 64]); return sw->fdb[lid % 64]; } static int sameport(Port *a, Port *b) { return a->portguid == b->portguid || (force && a->lid == b->lid); } static int extend_dpath(ib_dr_path_t *path, int nextport) { if (path->cnt+2 >= sizeof(path->p)) return -1; ++path->cnt; path->p[path->cnt] = nextport; return path->cnt; } static void dump_endnode(int dump, char *prompt, Node *node, Port *port) { char *nodename = NULL; if (!dump) return; if (dump == 1) { fprintf(f, "%s {0x%016" PRIx64 "}[%d]\n", prompt, node->nodeguid, node->type == IB_NODE_SWITCH ? 0 : port->portnum); return; } nodename = remap_node_name(node_name_map, node->nodeguid, node->nodedesc); fprintf(f, "%s %s {0x%016" PRIx64 "} portnum %d lid %u-%u \"%s\"\n", prompt, (node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"), node->nodeguid, node->type == IB_NODE_SWITCH ? 0 : port->portnum, port->lid, port->lid + (1 << port->lmc) - 1, nodename); free(nodename); } static void dump_route(int dump, Node *node, int outport, Port *port) { char *nodename = NULL; if (!dump && !verbose) return; nodename = remap_node_name(node_name_map, node->nodeguid, node->nodedesc); if (dump == 1) fprintf(f, "[%d] -> {0x%016" PRIx64 "}[%d]\n", outport, port->portguid, port->portnum); else fprintf(f, "[%d] -> %s port {0x%016" PRIx64 "}[%d] lid %u-%u \"%s\"\n", outport, (node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"), port->portguid, port->portnum, port->lid, port->lid + (1 << port->lmc) - 1, nodename); free(nodename); } static int find_route(ib_portid_t *from, ib_portid_t *to, int dump) { Node *node, fromnode, tonode, nextnode; Port *port, fromport, toport, nextport; Switch sw; int maxhops = MAXHOPS; int portnum, outport; DEBUG("from %s", portid2str(from)); if (get_node(&fromnode, &fromport, from) < 0 || get_node(&tonode, &toport, to) < 0) { IBWARN("can't reach to/from ports"); if (!force) return -1; if (to->lid > 0) toport.lid = to->lid; IBWARN("Force: look for lid %d", to->lid); } node = &fromnode; port = &fromport; portnum = port->portnum; dump_endnode(dump, "From", node, port); while (maxhops--) { if (port->state != 4) goto badport; if (sameport(port, &toport)) break; /* found */ outport = portnum; if (node->type == IB_NODE_SWITCH) { DEBUG("switch node"); if ((outport = switch_lookup(&sw, from, to->lid)) < 0 || outport > node->numports) goto badtbl; if (extend_dpath(&from->drpath, outport) < 0) goto badpath; if (get_node(&nextnode, &nextport, from) < 0) { IBWARN("can't reach port at %s", portid2str(from)); return -1; } if (outport == 0) { if (!sameport(&nextport, &toport)) goto badtbl; else break; /* found SMA port */ } } else if ((node->type == IB_NODE_CA) || (node->type == IB_NODE_ROUTER)) { int ca_src = 0; DEBUG("ca or router node"); if (!sameport(port, &fromport)) { IBWARN("can't continue: reached CA or router port %" PRIx64 ", lid %d", port->portguid, port->lid); return -1; } /* we are at CA or router "from" - go one hop back to (hopefully) a switch */ if (from->drpath.cnt > 0) { DEBUG("ca or router node - return back 1 hop"); from->drpath.cnt--; } else { ca_src = 1; if (portnum && extend_dpath(&from->drpath, portnum) < 0) goto badpath; } if (get_node(&nextnode, &nextport, from) < 0) { IBWARN("can't reach port at %s", portid2str(from)); return -1; } /* fix port num to be seen from the CA or router side */ if (!ca_src) nextport.portnum = from->drpath.p[from->drpath.cnt+1]; } port = &nextport; if (port->state != 4) goto badoutport; node = &nextnode; portnum = port->portnum; dump_route(dump, node, outport, port); } if (maxhops <= 0) { IBWARN("no route found after %d hops", MAXHOPS); return -1; } dump_endnode(dump, "To", node, port); return 0; badport: IBWARN("Bad port state found: node \"%s\" port %d state %d", clean_nodedesc(node->nodedesc), portnum, port->state); return -1; badoutport: IBWARN("Bad out port state found: node \"%s\" outport %d state %d", clean_nodedesc(node->nodedesc), outport, port->state); return -1; badtbl: IBWARN("Bad forwarding table entry found at: node \"%s\" lid entry %d is %d (top %d)", clean_nodedesc(node->nodedesc), to->lid, outport, sw.linearFDBtop); return -1; badpath: IBWARN("Direct path too long!"); return -1; } /************************** * MC span part */ #define HASHGUID(guid) ((uint32_t)(((uint32_t)(guid) * 101) ^ ((uint32_t)((guid) >> 32) * 103))) #define HTSZ 137 static int insert_node(Node *new) { static Node *nodestbl[HTSZ]; int hash = HASHGUID(new->nodeguid) % HTSZ; Node *node ; for (node = nodestbl[hash]; node; node = node->htnext) if (node->nodeguid == new->nodeguid) { DEBUG("node %" PRIx64 " already exists", new->nodeguid); return -1; } new->htnext = nodestbl[hash]; nodestbl[hash] = new; return 0; } static int get_port(Port *port, int portnum, ib_portid_t *portid) { char portinfo[64]; void *pi = portinfo; port->portnum = portnum; if (!smp_query(pi, portid, IB_ATTR_PORT_INFO, portnum, timeout)) return -1; mad_decode_field(pi, IB_PORT_LID_F, &port->lid); mad_decode_field(pi, IB_PORT_LMC_F, &port->lmc); mad_decode_field(pi, IB_PORT_STATE_F, &port->state); mad_decode_field(pi, IB_PORT_PHYS_STATE_F, &port->physstate); VERBOSE("portid %s portnum %d: lid %d state %d physstate %d", portid2str(portid), portnum, port->lid, port->state, port->physstate); return 1; } static void link_port(Port *port, Node *node) { port->next = node->ports; node->ports = port; } static int new_node(Node *node, Port *port, ib_portid_t *path, int dist) { if (port->portguid == target_portguid) { node->dist = -1; /* tag as target */ link_port(port, node); dump_endnode(verbose, "found target", node, port); return 1; /* found; */ } /* BFS search start with my self */ if (insert_node(node) < 0) return -1; /* known switch */ VERBOSE("insert dist %d node %p port %d lid %d", dist, node, port->portnum, port->lid); link_port(port, node); node->dist = dist; node->path = *path; node->dnext = nodesdist[dist]; nodesdist[dist] = node; return 0; } static int switch_mclookup(Node *node, ib_portid_t *portid, int mlid, char *map) { Switch sw; char mdb[64]; void *si = sw.switchinfo; uint16_t *msets = (uint16_t *)mdb; int maxsets, block, i, set; memset(map, 0, 256); if (!smp_query(si, portid, IB_ATTR_SWITCH_INFO, 0, timeout)) return -1; mlid -= 0xc000; mad_decode_field(si, IB_SW_MCAST_FDB_CAP_F, &sw.mccap); if (mlid > sw.mccap) return -1; block = mlid / 32; maxsets = (node->numports + 15) / 16; /* round up */ for (set = 0; set < maxsets; set++) { if (!smp_query(mdb, portid, IB_ATTR_MULTICASTFORWTBL, block | (set << 28), timeout)) return -1; for (i = 0; i < 16; i++, map++) { uint16_t mask = ntohs(msets[mlid % 32]); if (mask & (1 << i)) *map = 1; else continue; VERBOSE("Switch guid 0x%" PRIx64 ": mlid 0x%x is forwarded to port %d", node->nodeguid, mlid + 0xc000, i + set * 16); } } return 0; } /* * Return 1 if found, 0 if not, -1 on errors. */ static Node * find_mcpath(ib_portid_t *from, int mlid) { Node *node, *remotenode; Port *port, *remoteport; char map[256]; int r, i; int dist = 0, leafport = 0; ib_portid_t *path; DEBUG("from %s", portid2str(from)); if (!(node = calloc(1, sizeof(Node)))) IBERROR("out of memory"); if (!(port = calloc(1, sizeof(Port)))) IBERROR("out of memory"); if (get_node(node, port, from) < 0) { IBWARN("can't reach node %s", portid2str(from)); return 0; } node->upnode = 0; /* root */ if ((r = new_node(node, port, from, 0)) > 0) { if (node->type != IB_NODE_SWITCH) { IBWARN("ibtracert from CA to CA is unsupported"); return 0; /* ibtracert from host to itself is unsupported */ } if (switch_mclookup(node, from, mlid, map) < 0 || !map[0]) return 0; return node; } for (dist = 0; dist < MAXHOPS; dist++) { for (node = nodesdist[dist]; node; node = node->dnext) { path = &node->path; VERBOSE("dist %d node %p", dist, node); dump_endnode(verbose, "processing", node, node->ports); memset(map, 0, sizeof(map)); if (node->type != IB_NODE_SWITCH) { if (dist) continue; leafport = path->drpath.p[path->drpath.cnt]; map[port->portnum] = 1; node->upport = 0; /* starting here */ DEBUG("Starting from CA 0x%" PRIx64 " lid %d port %d (leafport %d)", node->nodeguid, port->lid, port->portnum, leafport); } else { /* switch */ /* if starting from a leaf port fix up port (up port) */ if (dist == 1 && leafport) node->upport = leafport; if (switch_mclookup(node, path, mlid, map) < 0) { IBWARN("skipping bad Switch 0x%" PRIx64 "", node->nodeguid); continue; } } for (i = 1; i <= node->numports; i++) { if (!map[i] || i == node->upport) continue; if (dist == 0 && leafport) { if (from->drpath.cnt > 0) path->drpath.cnt--; } else { if (!(port = calloc(1, sizeof(Port)))) IBERROR("out of memory"); if (get_port(port, i, path) < 0) { IBWARN("can't reach node %s port %d", portid2str(path), i); return 0; } if (port->physstate != 5) { /* LinkUP */ free(port); continue; } #if 0 link_port(port, node); #endif if (extend_dpath(&path->drpath, i) < 0) return 0; } if (!(remotenode = calloc(1, sizeof(Node)))) IBERROR("out of memory"); if (!(remoteport = calloc(1, sizeof(Port)))) IBERROR("out of memory"); if (get_node(remotenode, remoteport, path) < 0) { IBWARN("NodeInfo on %s port %d failed, skipping port", portid2str(path), i); path->drpath.cnt--; /* restore path */ free(remotenode); free(remoteport); continue; } remotenode->upnode = node; remotenode->upport = remoteport->portnum; remoteport->remoteport = port; if ((r = new_node(remotenode, remoteport, path, dist+1)) > 0) return remotenode; if (r == 0) dump_endnode(verbose, "new remote", remotenode, remoteport); else if (remotenode->type == IB_NODE_SWITCH) dump_endnode(2, "ERR: circle discovered at", remotenode, remoteport); path->drpath.cnt--; /* restore path */ } } } return 0; /* not found */ } static uint64_t find_target_portguid(ib_portid_t *to) { Node tonode; Port toport; if (get_node(&tonode, &toport, to) < 0) { IBWARN("can't find to port\n"); return -1; } return toport.portguid; } static void dump_mcpath(Node *node, int dumplevel) { char *nodename = NULL; if (node->upnode) dump_mcpath(node->upnode, dumplevel); nodename = remap_node_name(node_name_map, node->nodeguid, node->nodedesc); if (!node->dist) { printf("From %s 0x%" PRIx64 " port %d lid %u-%u \"%s\"\n", (node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"), node->nodeguid, node->ports->portnum, node->ports->lid, node->ports->lid + (1 << node->ports->lmc) - 1, nodename); goto free_name; } if (node->dist) { if (dumplevel == 1) printf("[%d] -> %s {0x%016" PRIx64 "}[%d]\n", node->ports->remoteport->portnum, (node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"), node->nodeguid, node->upport); else printf("[%d] -> %s 0x%" PRIx64 "[%d] lid %u \"%s\"\n", node->ports->remoteport->portnum, (node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"), node->nodeguid, node->upport, node->ports->lid, nodename); } if (node->dist < 0) /* target node */ printf("To %s 0x%" PRIx64 " port %d lid %u-%u \"%s\"\n", (node->type <= IB_NODE_MAX ? node_type_str[node->type] : "???"), node->nodeguid, node->ports->portnum, node->ports->lid, node->ports->lid + (1 << node->ports->lmc) - 1, nodename); free_name: free(nodename); } static int resolve_lid(ib_portid_t *portid, const void *srcport) { uint8_t portinfo[64]; uint16_t lid; if (!smp_query_via(portinfo, portid, IB_ATTR_PORT_INFO, 0, 0, srcport)) return -1; mad_decode_field(portinfo, IB_PORT_LID_F, &lid); ib_portid_set(portid, lid, 0, 0); return 0; } static void usage(void) { char *basename; if (!(basename = strrchr(argv0, '/'))) basename = argv0; else basename++; fprintf(stderr, "Usage: %s [-d(ebug) -v(erbose) -D(irect) -G(uids) -n(o_info) -C ca_name -P ca_port " "-s smlid -t(imeout) timeout_ms -m mlid --node-name-map node-name-map ] \n", basename); fprintf(stderr, "\n\tUnicast examples:\n"); fprintf(stderr, "\t\t%s 4 16\t\t\t# show path between lids 4 and 16\n", basename); fprintf(stderr, "\t\t%s -n 4 16\t\t# same, but using simple output format\n", basename); fprintf(stderr, "\t\t%s -G 0x8f1040396522d 0x002c9000100d051\t# use guid addresses\n", basename); fprintf(stderr, "\n\tMulticast example:\n"); fprintf(stderr, "\t\t%s -m 0xc000 4 16\t# show multicast path of mlid 0xc000 between lids 4 and 16\n", basename); exit(-1); } int main(int argc, char **argv) { int mgmt_classes[3] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS}; ib_portid_t my_portid = {0}; ib_portid_t src_portid = {0}; ib_portid_t dest_portid = {0}; ib_portid_t *sm_id = 0, sm_portid = {0}; int dumplevel = 2, dest_type = IB_DEST_LID, multicast = 0, mlid = 0; Node *endnode; int udebug = 0; char *ca = 0; int ca_port = 0; static char const str_opts[] = "C:P:t:s:m:dvfDGnVhu"; static const struct option long_opts[] = { { "C", 1, 0, 'C'}, { "P", 1, 0, 'P'}, { "debug", 0, 0, 'd'}, { "verbose", 0, 0, 'v'}, { "force", 0, 0, 'f'}, { "Direct", 0, 0, 'D'}, { "Guids", 0, 0, 'G'}, { "no_info", 0, 0, 'n'}, { "timeout", 1, 0, 't'}, { "s", 1, 0, 's'}, { "m", 1, 0, 'm'}, { "Version", 0, 0, 'V'}, { "help", 0, 0, 'h'}, { "usage", 0, 0, 'u'}, { "node-name-map", 1, 0, 1}, { } }; argv0 = argv[0]; f = stdout; while (1) { int ch = getopt_long(argc, argv, str_opts, long_opts, NULL); if ( ch == -1 ) break; switch(ch) { case 1: node_name_map_file = strdup(optarg); break; case 'C': ca = optarg; break; case 'P': ca_port = strtoul(optarg, 0, 0); break; case 'd': ibdebug++; madrpc_show_errors(1); umad_debug(udebug); udebug++; break; case 'D': dest_type = IB_DEST_DRPATH; break; case 'G': dest_type = IB_DEST_GUID; break; case 'm': multicast++; mlid = strtoul(optarg, 0, 0); break; case 'f': force++; break; case 'n': dumplevel = 1; break; case 's': if (ib_resolve_portid_str(&sm_portid, optarg, IB_DEST_LID, 0) < 0) IBERROR("can't resolve SM destination port %s", optarg); sm_id = &sm_portid; break; case 't': timeout = strtoul(optarg, 0, 0); madrpc_set_timeout(timeout); break; case 'v': madrpc_show_errors(1); verbose++; break; case 'V': fprintf(stderr, "%s %s\n", argv0, get_build_version() ); exit(-1); default: usage(); break; } } argc -= optind; argv += optind; if (argc < 2) usage(); madrpc_init(ca, ca_port, mgmt_classes, 3); node_name_map = open_node_name_map(node_name_map_file); if (ib_resolve_portid_str(&src_portid, argv[0], dest_type, sm_id) < 0) IBERROR("can't resolve source port %s", argv[0]); if (ib_resolve_portid_str(&dest_portid, argv[1], dest_type, sm_id) < 0) IBERROR("can't resolve destination port %s", argv[1]); if (dest_type == IB_DEST_DRPATH) { if (resolve_lid(&src_portid, NULL) < 0) IBERROR("cannot resolve lid for port \'%s\'", portid2str(&src_portid)); if (resolve_lid(&dest_portid, NULL) < 0) IBERROR("cannot resolve lid for port \'%s\'", portid2str(&dest_portid)); } if (dest_portid.lid == 0 || src_portid.lid == 0) { IBWARN("bad src/dest lid"); usage(); } if (dest_type != IB_DEST_DRPATH) { /* first find a direct path to the src port */ if (find_route(&my_portid, &src_portid, 0) < 0) IBERROR("can't find a route to the src port"); src_portid = my_portid; } if (!multicast) { if (find_route(&src_portid, &dest_portid, dumplevel) < 0) IBERROR("can't find a route from src to dest"); exit(0); } else { if (mlid < 0xc000) IBWARN("invalid MLID; must be 0xc000 or larger"); } if (!(target_portguid = find_target_portguid(&dest_portid))) IBERROR("can't reach target lid %d", dest_portid.lid); if (!(endnode = find_mcpath(&src_portid, mlid))) IBERROR("can't find a multicast route from src to dest"); /* dump multicast path */ dump_mcpath(endnode, dumplevel); close_node_name_map(node_name_map); exit(0); } Index: projects/sendfile/contrib/ofed/management/infiniband-diags/src/saquery.c =================================================================== --- projects/sendfile/contrib/ofed/management/infiniband-diags/src/saquery.c (revision 275355) +++ projects/sendfile/contrib/ofed/management/infiniband-diags/src/saquery.c (revision 275356) @@ -1,1891 +1,1891 @@ /* * Copyright (c) 2006,2007 The Regents of the University of California. * Copyright (c) 2004-2008 Voltaire, Inc. All rights reserved. * Copyright (c) 2002-2005 Mellanox Technologies LTD. All rights reserved. * Copyright (c) 1996-2003 Intel Corporation. All rights reserved. * * Produced at Lawrence Livermore National Laboratory. * Written by Ira Weiny . * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include #include #include #include #include #include #include #include #define _GNU_SOURCE #include #include -#include -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include "ibdiag_common.h" struct query_cmd { const char *name, *alias; ib_net16_t query_type; const char *usage; int (*handler) (const struct query_cmd * q, osm_bind_handle_t h, int argc, char *argv[]); }; char *argv0 = "saquery"; static char *node_name_map_file = NULL; static nn_map_t *node_name_map = NULL; static ib_net64_t smkey = OSM_DEFAULT_SA_KEY; /** * Declare some globals because I don't want this to be too complex. */ #define MAX_PORTS (8) #define DEFAULT_SA_TIMEOUT_MS (1000) osmv_query_res_t result; osm_log_t log_osm; osm_mad_pool_t mad_pool; osm_vendor_t *vendor = NULL; int osm_debug = 0; uint32_t sa_timeout_ms = DEFAULT_SA_TIMEOUT_MS; char *sa_hca_name = NULL; uint32_t sa_port_num = 0; enum { ALL, LID_ONLY, UNIQUE_LID_ONLY, GUID_ONLY, ALL_DESC, NAME_OF_LID, NAME_OF_GUID, } node_print_desc = ALL; char *requested_name = NULL; ib_net16_t requested_lid = 0; int requested_lid_flag = 0; ib_net64_t requested_guid = 0; int requested_guid_flag = 0; static void format_buf(char *in, char *out, unsigned size) { unsigned i; for (i = 0; i < size - 3 && *in; i++) { *out++ = *in; if (*in++ == '\n' && *in) { *out++ = '\t'; *out++ = '\t'; } } *out = '\0'; } /** * Call back for the various record requests. */ static void query_res_cb(osmv_query_res_t * res) { result = *res; } static void print_node_desc(ib_node_record_t * node_record) { ib_node_info_t *p_ni = &(node_record->node_info); ib_node_desc_t *p_nd = &(node_record->node_desc); if (p_ni->node_type == IB_NODE_TYPE_CA) { printf("%6d \"%s\"\n", cl_ntoh16(node_record->lid), clean_nodedesc((char *)p_nd->description)); } } static void print_node_record(ib_node_record_t * node_record) { ib_node_info_t *p_ni = NULL; ib_node_desc_t *p_nd = NULL; char *name; p_ni = &(node_record->node_info); p_nd = &(node_record->node_desc); switch (node_print_desc) { case LID_ONLY: case UNIQUE_LID_ONLY: printf("%d\n", cl_ntoh16(node_record->lid)); return; case GUID_ONLY: printf("0x%016" PRIx64 "\n", cl_ntoh64(p_ni->port_guid)); return; case NAME_OF_LID: case NAME_OF_GUID: name = remap_node_name(node_name_map, cl_ntoh64(p_ni->node_guid), (char *)p_nd->description); printf("%s\n", name); free(name); return; case ALL: default: break; } printf("NodeRecord dump:\n" "\t\tlid.....................0x%X\n" "\t\treserved................0x%X\n" "\t\tbase_version............0x%X\n" "\t\tclass_version...........0x%X\n" "\t\tnode_type...............%s\n" "\t\tnum_ports...............0x%X\n" "\t\tsys_guid................0x%016" PRIx64 "\n" "\t\tnode_guid...............0x%016" PRIx64 "\n" "\t\tport_guid...............0x%016" PRIx64 "\n" "\t\tpartition_cap...........0x%X\n" "\t\tdevice_id...............0x%X\n" "\t\trevision................0x%X\n" "\t\tport_num................0x%X\n" "\t\tvendor_id...............0x%X\n" "\t\tNodeDescription.........%s\n" "", cl_ntoh16(node_record->lid), cl_ntoh16(node_record->resv), p_ni->base_version, p_ni->class_version, ib_get_node_type_str(p_ni->node_type), p_ni->num_ports, cl_ntoh64(p_ni->sys_guid), cl_ntoh64(p_ni->node_guid), cl_ntoh64(p_ni->port_guid), cl_ntoh16(p_ni->partition_cap), cl_ntoh16(p_ni->device_id), cl_ntoh32(p_ni->revision), ib_node_info_get_local_port_num(p_ni), cl_ntoh32(ib_node_info_get_vendor_id(p_ni)), clean_nodedesc((char *)node_record->node_desc.description) ); } static void dump_path_record(void *data) { char gid_str[INET6_ADDRSTRLEN]; char gid_str2[INET6_ADDRSTRLEN]; ib_path_rec_t *p_pr = data; printf("PathRecord dump:\n" "\t\tservice_id..............0x%016" PRIx64 "\n" "\t\tdgid....................%s\n" "\t\tsgid....................%s\n" "\t\tdlid....................0x%X\n" "\t\tslid....................0x%X\n" "\t\thop_flow_raw............0x%X\n" "\t\ttclass..................0x%X\n" "\t\tnum_path_revers.........0x%X\n" "\t\tpkey....................0x%X\n" "\t\tqos_class...............0x%X\n" "\t\tsl......................0x%X\n" "\t\tmtu.....................0x%X\n" "\t\trate....................0x%X\n" "\t\tpkt_life................0x%X\n" "\t\tpreference..............0x%X\n" "\t\tresv2...................0x%X\n" "\t\tresv3...................0x%X\n" "", cl_ntoh64(p_pr->service_id), inet_ntop(AF_INET6, p_pr->dgid.raw, gid_str, sizeof gid_str), inet_ntop(AF_INET6, p_pr->sgid.raw, gid_str2, sizeof gid_str2), cl_ntoh16(p_pr->dlid), cl_ntoh16(p_pr->slid), cl_ntoh32(p_pr->hop_flow_raw), p_pr->tclass, p_pr->num_path, cl_ntoh16(p_pr->pkey), ib_path_rec_qos_class(p_pr), ib_path_rec_sl(p_pr), p_pr->mtu, p_pr->rate, p_pr->pkt_life, p_pr->preference, *(uint32_t *) & p_pr->resv2, *((uint16_t *) & p_pr->resv2 + 2) ); } static void dump_class_port_info(void *data) { char gid_str[INET6_ADDRSTRLEN]; char gid_str2[INET6_ADDRSTRLEN]; ib_class_port_info_t *class_port_info = data; printf("SA ClassPortInfo:\n" "\t\tBase version.............%d\n" "\t\tClass version............%d\n" "\t\tCapability mask..........0x%04X\n" "\t\tCapability mask 2........0x%08X\n" "\t\tResponse time value......0x%02X\n" "\t\tRedirect GID.............%s\n" "\t\tRedirect TC/SL/FL........0x%08X\n" "\t\tRedirect LID.............0x%04X\n" "\t\tRedirect PKey............0x%04X\n" "\t\tRedirect QP..............0x%08X\n" "\t\tRedirect QKey............0x%08X\n" "\t\tTrap GID.................%s\n" "\t\tTrap TC/SL/FL............0x%08X\n" "\t\tTrap LID.................0x%04X\n" "\t\tTrap PKey................0x%04X\n" "\t\tTrap HL/QP...............0x%08X\n" "\t\tTrap QKey................0x%08X\n" "", class_port_info->base_ver, class_port_info->class_ver, cl_ntoh16(class_port_info->cap_mask), ib_class_cap_mask2(class_port_info), ib_class_resp_time_val(class_port_info), inet_ntop(AF_INET6, &(class_port_info->redir_gid), gid_str, sizeof gid_str), cl_ntoh32(class_port_info->redir_tc_sl_fl), cl_ntoh16(class_port_info->redir_lid), cl_ntoh16(class_port_info->redir_pkey), cl_ntoh32(class_port_info->redir_qp), cl_ntoh32(class_port_info->redir_qkey), inet_ntop(AF_INET6, &(class_port_info->trap_gid), gid_str2, sizeof gid_str2), cl_ntoh32(class_port_info->trap_tc_sl_fl), cl_ntoh16(class_port_info->trap_lid), cl_ntoh16(class_port_info->trap_pkey), cl_ntoh32(class_port_info->trap_hop_qp), cl_ntoh32(class_port_info->trap_qkey) ); } static void dump_portinfo_record(void *data) { ib_portinfo_record_t *p_pir = data; const ib_port_info_t *const p_pi = &p_pir->port_info; printf("PortInfoRecord dump:\n" "\t\tEndPortLid..............0x%X\n" "\t\tPortNum.................0x%X\n" "\t\tbase_lid................0x%X\n" "\t\tmaster_sm_base_lid......0x%X\n" "\t\tcapability_mask.........0x%X\n" "", cl_ntoh16(p_pir->lid), p_pir->port_num, cl_ntoh16(p_pi->base_lid), cl_ntoh16(p_pi->master_sm_base_lid), cl_ntoh32(p_pi->capability_mask) ); } static void dump_one_portinfo_record(void *data) { char buf[2048], buf2[4096]; ib_portinfo_record_t *pir = data; ib_port_info_t *pi = &pir->port_info; mad_dump_portinfo(buf, sizeof(buf), pi, sizeof(*pi)); format_buf(buf, buf2, sizeof(buf2)); printf("PortInfoRecord dump:\n" "\tRID:\n" "\t\tEndPortLid..............%u\n" "\t\tPortNum.................0x%x\n" "\t\tReserved................0x%x\n" "\tPortInfo dump:\n\t\t%s", cl_ntoh16(pir->lid), pir->port_num, pir->resv, buf2); } static void dump_multicast_group_record(void *data) { char gid_str[INET6_ADDRSTRLEN]; ib_member_rec_t *p_mcmr = data; uint8_t sl; ib_member_get_sl_flow_hop(p_mcmr->sl_flow_hop, &sl, NULL, NULL); printf("MCMemberRecord group dump:\n" "\t\tMGID....................%s\n" "\t\tMlid....................0x%X\n" "\t\tMtu.....................0x%X\n" "\t\tpkey....................0x%X\n" "\t\tRate....................0x%X\n" "\t\tSL......................0x%X\n" "", inet_ntop(AF_INET6, p_mcmr->mgid.raw, gid_str, sizeof gid_str), cl_ntoh16(p_mcmr->mlid), p_mcmr->mtu, cl_ntoh16(p_mcmr->pkey), p_mcmr->rate, sl); } static void dump_multicast_member_record(void *data) { char gid_str[INET6_ADDRSTRLEN]; char gid_str2[INET6_ADDRSTRLEN]; ib_member_rec_t *p_mcmr = data; uint16_t mlid = cl_ntoh16(p_mcmr->mlid); int i = 0; char *node_name = ""; /* go through the node records searching for a port guid which matches * this port gid interface id. * This gives us a node name to print, if available. */ for (i = 0; i < result.result_cnt; i++) { ib_node_record_t *nr = osmv_get_query_node_rec(result.p_result_madw, i); if (nr->node_info.port_guid == p_mcmr->port_gid.unicast.interface_id) { node_name = clean_nodedesc((char *)nr->node_desc.description); break; } } if (requested_name) { if (strtol(requested_name, NULL, 0) == mlid) { printf("\t\tPortGid.................%s (%s)\n", inet_ntop(AF_INET6, p_mcmr->port_gid.raw, gid_str, sizeof gid_str), node_name); } } else { printf("MCMemberRecord member dump:\n" "\t\tMGID....................%s\n" "\t\tMlid....................0x%X\n" "\t\tPortGid.................%s\n" "\t\tScopeState..............0x%X\n" "\t\tProxyJoin...............0x%X\n" "\t\tNodeDescription.........%s\n" "", inet_ntop(AF_INET6, p_mcmr->mgid.raw, gid_str, sizeof gid_str), cl_ntoh16(p_mcmr->mlid), inet_ntop(AF_INET6, p_mcmr->port_gid.raw, gid_str2, sizeof gid_str2), p_mcmr->scope_state, p_mcmr->proxy_join, node_name); } } static void dump_service_record(void *data) { char gid_str[INET6_ADDRSTRLEN]; char buf_service_key[35]; char buf_service_name[65]; ib_service_record_t *p_sr = data; sprintf(buf_service_key, "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", p_sr->service_key[0], p_sr->service_key[1], p_sr->service_key[2], p_sr->service_key[3], p_sr->service_key[4], p_sr->service_key[5], p_sr->service_key[6], p_sr->service_key[7], p_sr->service_key[8], p_sr->service_key[9], p_sr->service_key[10], p_sr->service_key[11], p_sr->service_key[12], p_sr->service_key[13], p_sr->service_key[14], p_sr->service_key[15]); strncpy(buf_service_name, (char *)p_sr->service_name, 64); buf_service_name[64] = '\0'; printf("ServiceRecord dump:\n" "\t\tServiceID...............0x%016" PRIx64 "\n" "\t\tServiceGID..............%s\n" "\t\tServiceP_Key............0x%X\n" "\t\tServiceLease............0x%X\n" "\t\tServiceKey..............%s\n" "\t\tServiceName.............%s\n" "\t\tServiceData8.1..........0x%X\n" "\t\tServiceData8.2..........0x%X\n" "\t\tServiceData8.3..........0x%X\n" "\t\tServiceData8.4..........0x%X\n" "\t\tServiceData8.5..........0x%X\n" "\t\tServiceData8.6..........0x%X\n" "\t\tServiceData8.7..........0x%X\n" "\t\tServiceData8.8..........0x%X\n" "\t\tServiceData8.9..........0x%X\n" "\t\tServiceData8.10.........0x%X\n" "\t\tServiceData8.11.........0x%X\n" "\t\tServiceData8.12.........0x%X\n" "\t\tServiceData8.13.........0x%X\n" "\t\tServiceData8.14.........0x%X\n" "\t\tServiceData8.15.........0x%X\n" "\t\tServiceData8.16.........0x%X\n" "\t\tServiceData16.1.........0x%X\n" "\t\tServiceData16.2.........0x%X\n" "\t\tServiceData16.3.........0x%X\n" "\t\tServiceData16.4.........0x%X\n" "\t\tServiceData16.5.........0x%X\n" "\t\tServiceData16.6.........0x%X\n" "\t\tServiceData16.7.........0x%X\n" "\t\tServiceData16.8.........0x%X\n" "\t\tServiceData32.1.........0x%X\n" "\t\tServiceData32.2.........0x%X\n" "\t\tServiceData32.3.........0x%X\n" "\t\tServiceData32.4.........0x%X\n" "\t\tServiceData64.1.........0x%016" PRIx64 "\n" "\t\tServiceData64.2.........0x%016" PRIx64 "\n" "", cl_ntoh64(p_sr->service_id), inet_ntop(AF_INET6, p_sr->service_gid.raw, gid_str, sizeof gid_str), cl_ntoh16(p_sr->service_pkey), cl_ntoh32(p_sr->service_lease), buf_service_key, buf_service_name, p_sr->service_data8[0], p_sr->service_data8[1], p_sr->service_data8[2], p_sr->service_data8[3], p_sr->service_data8[4], p_sr->service_data8[5], p_sr->service_data8[6], p_sr->service_data8[7], p_sr->service_data8[8], p_sr->service_data8[9], p_sr->service_data8[10], p_sr->service_data8[11], p_sr->service_data8[12], p_sr->service_data8[13], p_sr->service_data8[14], p_sr->service_data8[15], cl_ntoh16(p_sr->service_data16[0]), cl_ntoh16(p_sr->service_data16[1]), cl_ntoh16(p_sr->service_data16[2]), cl_ntoh16(p_sr->service_data16[3]), cl_ntoh16(p_sr->service_data16[4]), cl_ntoh16(p_sr->service_data16[5]), cl_ntoh16(p_sr->service_data16[6]), cl_ntoh16(p_sr->service_data16[7]), cl_ntoh32(p_sr->service_data32[0]), cl_ntoh32(p_sr->service_data32[1]), cl_ntoh32(p_sr->service_data32[2]), cl_ntoh32(p_sr->service_data32[3]), cl_ntoh64(p_sr->service_data64[0]), cl_ntoh64(p_sr->service_data64[1]) ); } static void dump_inform_info_record(void *data) { char gid_str[INET6_ADDRSTRLEN]; char gid_str2[INET6_ADDRSTRLEN]; ib_inform_info_record_t *p_iir = data; uint32_t qpn; uint8_t resp_time_val; ib_inform_info_get_qpn_resp_time(p_iir->inform_info.g_or_v.generic. qpn_resp_time_val, &qpn, &resp_time_val); if (p_iir->inform_info.is_generic) { printf("InformInfoRecord dump:\n" "\t\tRID\n" "\t\tSubscriberGID...........%s\n" "\t\tSubscriberEnum..........0x%X\n" "\t\tInformInfo dump:\n" "\t\tgid.....................%s\n" "\t\tlid_range_begin.........0x%X\n" "\t\tlid_range_end...........0x%X\n" "\t\tis_generic..............0x%X\n" "\t\tsubscribe...............0x%X\n" "\t\ttrap_type...............0x%X\n" "\t\ttrap_num................%u\n" "\t\tqpn.....................0x%06X\n" "\t\tresp_time_val...........0x%X\n" "\t\tnode_type...............0x%06X\n" "", inet_ntop(AF_INET6, p_iir->subscriber_gid.raw, gid_str, sizeof gid_str), cl_ntoh16(p_iir->subscriber_enum), inet_ntop(AF_INET6, p_iir->inform_info.gid.raw, gid_str2, sizeof gid_str2), cl_ntoh16(p_iir->inform_info.lid_range_begin), cl_ntoh16(p_iir->inform_info.lid_range_end), p_iir->inform_info.is_generic, p_iir->inform_info.subscribe, cl_ntoh16(p_iir->inform_info.trap_type), cl_ntoh16(p_iir->inform_info.g_or_v.generic.trap_num), cl_ntoh32(qpn), resp_time_val, cl_ntoh32(ib_inform_info_get_prod_type (&p_iir->inform_info)) ); } else { printf("InformInfoRecord dump:\n" "\t\tRID\n" "\t\tSubscriberGID...........%s\n" "\t\tSubscriberEnum..........0x%X\n" "\t\tInformInfo dump:\n" "\t\tgid.....................%s\n" "\t\tlid_range_begin.........0x%X\n" "\t\tlid_range_end...........0x%X\n" "\t\tis_generic..............0x%X\n" "\t\tsubscribe...............0x%X\n" "\t\ttrap_type...............0x%X\n" "\t\tdev_id..................0x%X\n" "\t\tqpn.....................0x%06X\n" "\t\tresp_time_val...........0x%X\n" "\t\tvendor_id...............0x%06X\n" "", inet_ntop(AF_INET6, p_iir->subscriber_gid.raw, gid_str, sizeof gid_str), cl_ntoh16(p_iir->subscriber_enum), inet_ntop(AF_INET6, p_iir->inform_info.gid.raw, gid_str2, sizeof gid_str2), cl_ntoh16(p_iir->inform_info.lid_range_begin), cl_ntoh16(p_iir->inform_info.lid_range_end), p_iir->inform_info.is_generic, p_iir->inform_info.subscribe, cl_ntoh16(p_iir->inform_info.trap_type), cl_ntoh16(p_iir->inform_info.g_or_v.vend.dev_id), cl_ntoh32(qpn), resp_time_val, cl_ntoh32(ib_inform_info_get_prod_type (&p_iir->inform_info)) ); } } static void dump_one_link_record(void *data) { ib_link_record_t *lr = data; printf("LinkRecord dump:\n" "\t\tFromLID....................%u\n" "\t\tFromPort...................%u\n" "\t\tToPort.....................%u\n" "\t\tToLID......................%u\n", cl_ntoh16(lr->from_lid), lr->from_port_num, lr->to_port_num, cl_ntoh16(lr->to_lid)); } static void dump_one_slvl_record(void *data) { ib_slvl_table_record_t *slvl = data; ib_slvl_table_t *t = &slvl->slvl_tbl; printf("SL2VLTableRecord dump:\n" "\t\tLID........................%u\n" "\t\tInPort.....................%u\n" "\t\tOutPort....................%u\n" "\t\tSL: 0| 1| 2| 3| 4| 5| 6| 7| 8| 9|10|11|12|13|14|15|\n" "\t\tVL:%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u" "|%2u|%2u|%2u|\n", cl_ntoh16(slvl->lid), slvl->in_port_num, slvl->out_port_num, ib_slvl_table_get(t, 0), ib_slvl_table_get(t, 1), ib_slvl_table_get(t, 2), ib_slvl_table_get(t, 3), ib_slvl_table_get(t, 4), ib_slvl_table_get(t, 5), ib_slvl_table_get(t, 6), ib_slvl_table_get(t, 7), ib_slvl_table_get(t, 8), ib_slvl_table_get(t, 9), ib_slvl_table_get(t, 10), ib_slvl_table_get(t, 11), ib_slvl_table_get(t, 12), ib_slvl_table_get(t, 13), ib_slvl_table_get(t, 14), ib_slvl_table_get(t, 15)); } static void dump_one_vlarb_record(void *data) { ib_vl_arb_table_record_t *vlarb = data; ib_vl_arb_element_t *e = vlarb->vl_arb_tbl.vl_entry; int i; printf("VLArbTableRecord dump:\n" "\t\tLID........................%u\n" "\t\tPort.......................%u\n" "\t\tBlock......................%u\n", cl_ntoh16(vlarb->lid), vlarb->port_num, vlarb->block_num); for (i = 0; i < 32; i += 16) { printf("\t\tVL :%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u|" "%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u|", e[i + 0].vl, e[i + 1].vl, e[i + 2].vl, e[i + 3].vl, e[i + 4].vl, e[i + 5].vl, e[i + 6].vl, e[i + 7].vl, e[i + 8].vl, e[i + 9].vl, e[i + 10].vl, e[i + 11].vl, e[i + 12].vl, e[i + 13].vl, e[i + 14].vl, e[i + 15].vl); printf("\n\t\tWeight:%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u|" "%2u|%2u|%2u|%2u|%2u|%2u|%2u|%2u|", e[i + 0].weight, e[i + 1].weight, e[i + 2].weight, e[i + 3].weight, e[i + 4].weight, e[i + 5].weight, e[i + 6].weight, e[i + 7].weight, e[i + 8].weight, e[i + 9].weight, e[i + 10].weight, e[i + 11].weight, e[i + 12].weight, e[i + 13].weight, e[i + 14].weight, e[i + 15].weight); printf("\n"); } } static void dump_one_pkey_tbl_record(void *data) { ib_pkey_table_record_t *pktr = data; ib_net16_t *p = pktr->pkey_tbl.pkey_entry; int i; printf("PKeyTableRecord dump:\n" "\t\tLID........................%u\n" "\t\tPort.......................%u\n" "\t\tBlock......................%u\n" "\t\tPKey Table:\n", cl_ntoh16(pktr->lid), pktr->port_num, pktr->block_num); for (i = 0; i < 32; i += 8) printf("\t\t0x%04x 0x%04x 0x%04x 0x%04x" " 0x%04x 0x%04x 0x%04x 0x%04x\n", cl_ntoh16(p[i + 0]), cl_ntoh16(p[i + 1]), cl_ntoh16(p[i + 2]), cl_ntoh16(p[i + 3]), cl_ntoh16(p[i + 4]), cl_ntoh16(p[i + 5]), cl_ntoh16(p[i + 6]), cl_ntoh16(p[i + 7])); printf("\n"); } static void dump_one_lft_record(void *data) { ib_lft_record_t *lftr = data; unsigned block = cl_ntoh16(lftr->block_num); int i; printf("LFT Record dump:\n" "\t\tLID........................%u\n" "\t\tBlock......................%u\n" "\t\tLFT:\n\t\tLID\tPort Number\n", cl_ntoh16(lftr->lid), block); for (i = 0; i < 64; i++) printf("\t\t%u\t%u\n", block * 64 + i, lftr->lft[i]); printf("\n"); } static void dump_one_mft_record(void *data) { ib_mft_record_t *mftr = data; unsigned position = cl_ntoh16(mftr->position_block_num) >> 12; unsigned block = cl_ntoh16(mftr->position_block_num) & IB_MCAST_BLOCK_ID_MASK_HO; int i; printf("MFT Record dump:\n" "\t\tLID........................%u\n" "\t\tPosition...................%u\n" "\t\tBlock......................%u\n" "\t\tMFT:\n\t\tMLID\tPort Mask\n", cl_ntoh16(mftr->lid), position, block); for (i = 0; i < IB_MCAST_BLOCK_SIZE; i++) printf("\t\t0x%x\t0x%x\n", IB_LID_MCAST_START_HO + block * 64 + i, cl_ntoh16(mftr->mft[i])); printf("\n"); } static void dump_results(osmv_query_res_t * r, void (*dump_func) (void *)) { int i; for (i = 0; i < r->result_cnt; i++) { void *data = osmv_get_query_result(r->p_result_madw, i); dump_func(data); } } static void return_mad(void) { /* * Return the IB query MAD to the pool as necessary. */ if (result.p_result_madw != NULL) { osm_mad_pool_put(&mad_pool, result.p_result_madw); result.p_result_madw = NULL; } } /** * Get any record(s) */ static ib_api_status_t get_any_records(osm_bind_handle_t h, ib_net16_t attr_id, ib_net32_t attr_mod, ib_net64_t comp_mask, void *attr, ib_net16_t attr_offset, ib_net64_t sm_key) { ib_api_status_t status; osmv_query_req_t req; osmv_user_query_t user; memset(&req, 0, sizeof(req)); memset(&user, 0, sizeof(user)); user.attr_id = attr_id; user.attr_offset = attr_offset; user.attr_mod = attr_mod; user.comp_mask = comp_mask; user.p_attr = attr; req.query_type = OSMV_QUERY_USER_DEFINED; req.timeout_ms = sa_timeout_ms; req.retry_cnt = 1; req.flags = OSM_SA_FLAGS_SYNC; req.query_context = NULL; req.pfn_query_cb = query_res_cb; req.p_query_input = &user; req.sm_key = sm_key; if ((status = osmv_query_sa(h, &req)) != IB_SUCCESS) { fprintf(stderr, "Query SA failed: %s\n", ib_get_err_str(status)); return status; } if (result.status != IB_SUCCESS) { fprintf(stderr, "Query result returned: %s\n", ib_get_err_str(result.status)); return result.status; } return status; } /** * Get all the records available for requested query type. */ static ib_api_status_t get_all_records(osm_bind_handle_t h, ib_net16_t query_id, ib_net16_t attr_offset, int trusted) { return get_any_records(h, query_id, 0, 0, NULL, attr_offset, trusted ? smkey : 0); } /** * return the lid from the node descriptor (name) supplied */ static ib_api_status_t get_lid_from_name(osm_bind_handle_t h, const char *name, ib_net16_t * lid) { int i = 0; ib_node_record_t *node_record = NULL; ib_node_info_t *p_ni = NULL; ib_net16_t attr_offset = ib_get_attr_offset(sizeof(*node_record)); ib_api_status_t status; status = get_all_records(h, IB_MAD_ATTR_NODE_RECORD, attr_offset, 0); if (status != IB_SUCCESS) return (status); for (i = 0; i < result.result_cnt; i++) { node_record = osmv_get_query_node_rec(result.p_result_madw, i); p_ni = &(node_record->node_info); if (name && strncmp(name, (char *)node_record->node_desc.description, sizeof(node_record->node_desc.description)) == 0) { *lid = cl_ntoh16(node_record->lid); break; } } return_mad(); return (status); } static ib_net16_t get_lid(osm_bind_handle_t h, const char *name) { ib_net16_t rc_lid = 0; if (!name) return (0); if (isalpha(name[0])) assert(get_lid_from_name(h, name, &rc_lid) == IB_SUCCESS); else rc_lid = atoi(name); if (rc_lid == 0) fprintf(stderr, "Failed to find lid for \"%s\"\n", name); return (rc_lid); } static int parse_lid_and_ports(osm_bind_handle_t h, char *str, int *lid, int *port1, int *port2) { char *p, *e; if (port1) *port1 = -1; if (port2) *port2 = -1; p = strchr(str, '/'); if (p) *p = '\0'; if (lid) *lid = get_lid(h, str); if (!p) return 0; str = p + 1; p = strchr(str, '/'); if (p) *p = '\0'; if (port1) { *port1 = strtoul(str, &e, 0); if (e == str) *port1 = -1; } if (!p) return 0; str = p + 1; if (port2) { *port2 = strtoul(str, &e, 0); if (e == str) *port2 = -1; } return 0; } /* * Get the portinfo records available with IsSM or IsSMdisabled CapabilityMask bit on. */ static ib_api_status_t get_issm_records(osm_bind_handle_t h, ib_net32_t capability_mask) { ib_portinfo_record_t attr; memset(&attr, 0, sizeof(attr)); attr.port_info.capability_mask = capability_mask; return get_any_records(h, IB_MAD_ATTR_PORTINFO_RECORD, cl_hton32(1 << 31), IB_PIR_COMPMASK_CAPMASK, &attr, ib_get_attr_offset(sizeof(ib_portinfo_record_t)), 0); } static ib_api_status_t print_node_records(osm_bind_handle_t h) { int i = 0; ib_node_record_t *node_record = NULL; ib_net16_t attr_offset = ib_get_attr_offset(sizeof(*node_record)); ib_api_status_t status; status = get_all_records(h, IB_MAD_ATTR_NODE_RECORD, attr_offset, 0); if (status != IB_SUCCESS) return (status); if (node_print_desc == ALL_DESC) { printf(" LID \"name\"\n"); printf("================\n"); } for (i = 0; i < result.result_cnt; i++) { node_record = osmv_get_query_node_rec(result.p_result_madw, i); if (node_print_desc == ALL_DESC) { print_node_desc(node_record); } else if (node_print_desc == NAME_OF_LID) { if (requested_lid == cl_ntoh16(node_record->lid)) { print_node_record(node_record); } } else if (node_print_desc == NAME_OF_GUID) { ib_node_info_t *p_ni = &(node_record->node_info); if (requested_guid == cl_ntoh64(p_ni->port_guid)) { print_node_record(node_record); } } else { if (!requested_name || (strncmp(requested_name, (char *)node_record->node_desc.description, sizeof(node_record->node_desc. description)) == 0)) { print_node_record(node_record); if (node_print_desc == UNIQUE_LID_ONLY) { return_mad(); exit(0); } } } } return_mad(); return (status); } static ib_api_status_t get_print_path_rec_lid(osm_bind_handle_t h, ib_net16_t src_lid, ib_net16_t dst_lid) { osmv_query_req_t req; osmv_lid_pair_t lid_pair; ib_api_status_t status; lid_pair.src_lid = cl_hton16(src_lid); lid_pair.dest_lid = cl_hton16(dst_lid); memset(&req, 0, sizeof(req)); req.query_type = OSMV_QUERY_PATH_REC_BY_LIDS; req.timeout_ms = sa_timeout_ms; req.retry_cnt = 1; req.flags = OSM_SA_FLAGS_SYNC; req.query_context = NULL; req.pfn_query_cb = query_res_cb; req.p_query_input = (void *)&lid_pair; req.sm_key = 0; if ((status = osmv_query_sa(h, &req)) != IB_SUCCESS) { fprintf(stderr, "ERROR: Query SA failed: %s\n", ib_get_err_str(status)); return (status); } if (result.status != IB_SUCCESS) { fprintf(stderr, "ERROR: Query result returned: %s\n", ib_get_err_str(result.status)); return (result.status); } status = result.status; dump_results(&result, dump_path_record); return_mad(); return (status); } static ib_api_status_t get_print_path_rec_gid(osm_bind_handle_t h, const ib_gid_t * src_gid, const ib_gid_t * dst_gid) { osmv_query_req_t req; osmv_gid_pair_t gid_pair; ib_api_status_t status; gid_pair.src_gid = *src_gid; gid_pair.dest_gid = *dst_gid; memset(&req, 0, sizeof(req)); req.query_type = OSMV_QUERY_PATH_REC_BY_GIDS; req.timeout_ms = sa_timeout_ms; req.retry_cnt = 1; req.flags = OSM_SA_FLAGS_SYNC; req.query_context = NULL; req.pfn_query_cb = query_res_cb; req.p_query_input = (void *)&gid_pair; req.sm_key = 0; if ((status = osmv_query_sa(h, &req)) != IB_SUCCESS) { fprintf(stderr, "ERROR: Query SA failed: %s\n", ib_get_err_str(status)); return (status); } if (result.status != IB_SUCCESS) { fprintf(stderr, "ERROR: Query result returned: %s\n", ib_get_err_str(result.status)); return (result.status); } status = result.status; dump_results(&result, dump_path_record); return_mad(); return (status); } static ib_api_status_t get_print_class_port_info(osm_bind_handle_t h) { osmv_query_req_t req; ib_api_status_t status; memset(&req, 0, sizeof(req)); req.query_type = OSMV_QUERY_CLASS_PORT_INFO; req.timeout_ms = sa_timeout_ms; req.retry_cnt = 1; req.flags = OSM_SA_FLAGS_SYNC; req.query_context = NULL; req.pfn_query_cb = query_res_cb; req.p_query_input = NULL; req.sm_key = 0; if ((status = osmv_query_sa(h, &req)) != IB_SUCCESS) { fprintf(stderr, "ERROR: Query SA failed: %s\n", ib_get_err_str(status)); return (status); } if (result.status != IB_SUCCESS) { fprintf(stderr, "ERROR: Query result returned: %s\n", ib_get_err_str(result.status)); return (result.status); } status = result.status; dump_results(&result, dump_class_port_info); return_mad(); return (status); } static int query_path_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_net16_t attr_offset = ib_get_attr_offset(sizeof(ib_path_rec_t)); ib_api_status_t status; status = get_all_records(h, IB_MAD_ATTR_PATH_RECORD, attr_offset, 0); if (status != IB_SUCCESS) return (status); dump_results(&result, dump_path_record); return_mad(); return (status); } static ib_api_status_t print_issm_records(osm_bind_handle_t h) { ib_api_status_t status; /* First, get IsSM records */ status = get_issm_records(h, IB_PORT_CAP_IS_SM); if (status != IB_SUCCESS) return (status); printf("IsSM ports\n"); dump_results(&result, dump_portinfo_record); return_mad(); /* Now, get IsSMdisabled records */ status = get_issm_records(h, IB_PORT_CAP_SM_DISAB); if (status != IB_SUCCESS) return (status); printf("\nIsSMdisabled ports\n"); dump_results(&result, dump_portinfo_record); return_mad(); return (status); } static ib_api_status_t print_multicast_member_records(osm_bind_handle_t h) { osmv_query_res_t mc_group_result; ib_api_status_t status; status = get_all_records(h, IB_MAD_ATTR_MCMEMBER_RECORD, ib_get_attr_offset(sizeof(ib_member_rec_t)), 1); if (status != IB_SUCCESS) return (status); mc_group_result = result; status = get_all_records(h, IB_MAD_ATTR_NODE_RECORD, ib_get_attr_offset(sizeof(ib_node_record_t)), 0); if (status != IB_SUCCESS) goto return_mc; dump_results(&mc_group_result, dump_multicast_member_record); return_mad(); return_mc: /* return_mad for the mc_group_result */ if (mc_group_result.p_result_madw != NULL) { osm_mad_pool_put(&mad_pool, mc_group_result.p_result_madw); mc_group_result.p_result_madw = NULL; } return (status); } static ib_api_status_t print_multicast_group_records(osm_bind_handle_t h) { ib_api_status_t status; status = get_all_records(h, IB_MAD_ATTR_MCMEMBER_RECORD, ib_get_attr_offset(sizeof(ib_member_rec_t)), 0); if (status != IB_SUCCESS) return (status); dump_results(&result, dump_multicast_group_record); return_mad(); return (status); } static int query_class_port_info(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { return get_print_class_port_info(h); } static int query_node_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { return print_node_records(h); } static int query_portinfo_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_portinfo_record_t pir; ib_net64_t comp_mask = 0; int lid = 0, port = -1; ib_api_status_t status; if (argc > 0) parse_lid_and_ports(h, argv[0], &lid, &port, NULL); memset(&pir, 0, sizeof(pir)); if (lid > 0) { pir.lid = cl_hton16(lid); comp_mask |= IB_PIR_COMPMASK_LID; } if (port >= 0) { pir.port_num = cl_hton16(port); comp_mask |= IB_PIR_COMPMASK_PORTNUM; } status = get_any_records(h, IB_MAD_ATTR_PORTINFO_RECORD, 0, comp_mask, &pir, ib_get_attr_offset(sizeof(pir)), 0); if (status != IB_SUCCESS) return status; dump_results(&result, dump_one_portinfo_record); return_mad(); return 0; } static int query_mcmember_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { return print_multicast_member_records(h); } static int query_service_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_net16_t attr_offset = ib_get_attr_offset(sizeof(ib_service_record_t)); ib_api_status_t status; status = get_all_records(h, IB_MAD_ATTR_SERVICE_RECORD, attr_offset, 0); if (status != IB_SUCCESS) return (status); dump_results(&result, dump_service_record); return_mad(); return (status); } static int query_informinfo_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_net16_t attr_offset = ib_get_attr_offset(sizeof(ib_inform_info_record_t)); ib_api_status_t status; status = get_all_records(h, IB_MAD_ATTR_INFORM_INFO_RECORD, attr_offset, 0); if (status != IB_SUCCESS) return (status); dump_results(&result, dump_inform_info_record); return_mad(); return (status); } static int query_link_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_link_record_t lr; ib_net64_t comp_mask = 0; int from_lid = 0, to_lid = 0, from_port = -1, to_port = -1; ib_api_status_t status; if (argc > 0) parse_lid_and_ports(h, argv[0], &from_lid, &from_port, NULL); if (argc > 1) parse_lid_and_ports(h, argv[1], &to_lid, &to_port, NULL); memset(&lr, 0, sizeof(lr)); if (from_lid > 0) { lr.from_lid = cl_hton16(from_lid); comp_mask |= IB_LR_COMPMASK_FROM_LID; } if (from_port >= 0) { lr.from_port_num = from_port; comp_mask |= IB_LR_COMPMASK_FROM_PORT; } if (to_lid > 0) { lr.to_lid = cl_hton16(to_lid); comp_mask |= IB_LR_COMPMASK_TO_LID; } if (to_port >= 0) { lr.to_port_num = to_port; comp_mask |= IB_LR_COMPMASK_TO_PORT; } status = get_any_records(h, IB_MAD_ATTR_LINK_RECORD, 0, comp_mask, &lr, ib_get_attr_offset(sizeof(lr)), 0); if (status != IB_SUCCESS) return status; dump_results(&result, dump_one_link_record); return_mad(); return status; } static int query_sl2vl_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_slvl_table_record_t slvl; ib_net64_t comp_mask = 0; int lid = 0, in_port = -1, out_port = -1; ib_api_status_t status; if (argc > 0) parse_lid_and_ports(h, argv[0], &lid, &in_port, &out_port); memset(&slvl, 0, sizeof(slvl)); if (lid > 0) { slvl.lid = cl_hton16(lid); comp_mask |= IB_SLVL_COMPMASK_LID; } if (in_port >= 0) { slvl.in_port_num = in_port; comp_mask |= IB_SLVL_COMPMASK_IN_PORT; } if (out_port >= 0) { slvl.out_port_num = out_port; comp_mask |= IB_SLVL_COMPMASK_OUT_PORT; } status = get_any_records(h, IB_MAD_ATTR_SLVL_RECORD, 0, comp_mask, &slvl, ib_get_attr_offset(sizeof(slvl)), 0); if (status != IB_SUCCESS) return status; dump_results(&result, dump_one_slvl_record); return_mad(); return status; } static int query_vlarb_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_vl_arb_table_record_t vlarb; ib_net64_t comp_mask = 0; int lid = 0, port = -1, block = -1; ib_api_status_t status; if (argc > 0) parse_lid_and_ports(h, argv[0], &lid, &port, &block); memset(&vlarb, 0, sizeof(vlarb)); if (lid > 0) { vlarb.lid = cl_hton16(lid); comp_mask |= IB_VLA_COMPMASK_LID; } if (port >= 0) { vlarb.port_num = port; comp_mask |= IB_VLA_COMPMASK_OUT_PORT; } if (block >= 0) { vlarb.block_num = block; comp_mask |= IB_VLA_COMPMASK_BLOCK; } status = get_any_records(h, IB_MAD_ATTR_VLARB_RECORD, 0, comp_mask, &vlarb, ib_get_attr_offset(sizeof(vlarb)), 0); if (status != IB_SUCCESS) return status; dump_results(&result, dump_one_vlarb_record); return_mad(); return status; } static int query_pkey_tbl_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_pkey_table_record_t pktr; ib_net64_t comp_mask = 0; int lid = 0, port = -1, block = -1; ib_api_status_t status; if (argc > 0) parse_lid_and_ports(h, argv[0], &lid, &port, &block); memset(&pktr, 0, sizeof(pktr)); if (lid > 0) { pktr.lid = cl_hton16(lid); comp_mask |= IB_PKEY_COMPMASK_LID; } if (port >= 0) { pktr.port_num = port; comp_mask |= IB_PKEY_COMPMASK_PORT; } if (block >= 0) { pktr.block_num = block; comp_mask |= IB_PKEY_COMPMASK_BLOCK; } status = get_any_records(h, IB_MAD_ATTR_PKEY_TBL_RECORD, 0, comp_mask, &pktr, ib_get_attr_offset(sizeof(pktr)), smkey); if (status != IB_SUCCESS) return status; dump_results(&result, dump_one_pkey_tbl_record); return_mad(); return status; } static int query_lft_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_lft_record_t lftr; ib_net64_t comp_mask = 0; int lid = 0, block = -1; ib_api_status_t status; if (argc > 0) parse_lid_and_ports(h, argv[0], &lid, &block, NULL); memset(&lftr, 0, sizeof(lftr)); if (lid > 0) { lftr.lid = cl_hton16(lid); comp_mask |= IB_LFTR_COMPMASK_LID; } if (block >= 0) { lftr.block_num = cl_hton16(block); comp_mask |= IB_LFTR_COMPMASK_BLOCK; } status = get_any_records(h, IB_MAD_ATTR_LFT_RECORD, 0, comp_mask, &lftr, ib_get_attr_offset(sizeof(lftr)), 0); if (status != IB_SUCCESS) return status; dump_results(&result, dump_one_lft_record); return_mad(); return status; } static int query_mft_records(const struct query_cmd *q, osm_bind_handle_t h, int argc, char *argv[]) { ib_mft_record_t mftr; ib_net64_t comp_mask = 0; int lid = 0, block = -1, position = -1; ib_api_status_t status; if (argc > 0) parse_lid_and_ports(h, argv[0], &lid, &position, &block); memset(&mftr, 0, sizeof(mftr)); if (lid > 0) { mftr.lid = cl_hton16(lid); comp_mask |= IB_MFTR_COMPMASK_LID; } if (position >= 0) { mftr.position_block_num = cl_hton16(position << 12); comp_mask |= IB_MFTR_COMPMASK_POSITION; } if (block >= 0) { mftr.position_block_num |= cl_hton16(block & IB_MCAST_BLOCK_ID_MASK_HO); comp_mask |= IB_MFTR_COMPMASK_BLOCK; } status = get_any_records(h, IB_MAD_ATTR_MFT_RECORD, 0, comp_mask, &mftr, ib_get_attr_offset(sizeof(mftr)), 0); if (status != IB_SUCCESS) return status; dump_results(&result, dump_one_mft_record); return_mad(); return status; } static osm_bind_handle_t get_bind_handle(void) { uint32_t i = 0; uint64_t port_guid = (uint64_t) - 1; osm_bind_handle_t h; ib_api_status_t status; ib_port_attr_t attr_array[MAX_PORTS]; uint32_t num_ports = MAX_PORTS; uint32_t ca_name_index = 0; complib_init(); osm_log_construct(&log_osm); if ((status = osm_log_init_v2(&log_osm, TRUE, 0x0001, NULL, 0, TRUE)) != IB_SUCCESS) { fprintf(stderr, "Failed to init osm_log: %s\n", ib_get_err_str(status)); exit(-1); } osm_log_set_level(&log_osm, OSM_LOG_NONE); if (osm_debug) osm_log_set_level(&log_osm, OSM_LOG_DEFAULT_LEVEL); vendor = osm_vendor_new(&log_osm, sa_timeout_ms); osm_mad_pool_construct(&mad_pool); if ((status = osm_mad_pool_init(&mad_pool)) != IB_SUCCESS) { fprintf(stderr, "Failed to init mad pool: %s\n", ib_get_err_str(status)); exit(-1); } if ((status = osm_vendor_get_all_port_attr(vendor, attr_array, &num_ports)) != IB_SUCCESS) { fprintf(stderr, "Failed to get port attributes: %s\n", ib_get_err_str(status)); exit(-1); } for (i = 0; i < num_ports; i++) { if (i > 1 && cl_ntoh64(attr_array[i].port_guid) != (cl_ntoh64(attr_array[i - 1].port_guid) + 1)) ca_name_index++; if (sa_port_num && sa_port_num != attr_array[i].port_num) continue; if (sa_hca_name && strcmp(sa_hca_name, vendor->ca_names[ca_name_index]) != 0) continue; if (attr_array[i].link_state == IB_LINK_ACTIVE) { port_guid = attr_array[i].port_guid; break; } } if (port_guid == (uint64_t) - 1) { fprintf(stderr, "Failed to find active port, check port status with \"ibstat\"\n"); exit(-1); } h = osmv_bind_sa(vendor, &mad_pool, port_guid); if (h == OSM_BIND_INVALID_HANDLE) { fprintf(stderr, "Failed to bind to SA\n"); exit(-1); } return h; } static void clean_up(void) { osm_mad_pool_destroy(&mad_pool); osm_vendor_delete(&vendor); } static const struct query_cmd query_cmds[] = { {"ClassPortInfo", "CPI", IB_MAD_ATTR_CLASS_PORT_INFO, NULL, query_class_port_info}, {"NodeRecord", "NR", IB_MAD_ATTR_NODE_RECORD, NULL, query_node_records}, {"PortInfoRecord", "PIR", IB_MAD_ATTR_PORTINFO_RECORD, "[[lid]/[port]]", query_portinfo_records}, {"SL2VLTableRecord", "SL2VL", IB_MAD_ATTR_SLVL_RECORD, "[[lid]/[in_port]/[out_port]]", query_sl2vl_records}, {"PKeyTableRecord", "PKTR", IB_MAD_ATTR_PKEY_TBL_RECORD, "[[lid]/[port]/[block]]", query_pkey_tbl_records}, {"VLArbitrationTableRecord", "VLAR", IB_MAD_ATTR_VLARB_RECORD, "[[lid]/[port]/[block]]", query_vlarb_records}, {"InformInfoRecord", "IIR", IB_MAD_ATTR_INFORM_INFO_RECORD, NULL, query_informinfo_records}, {"LinkRecord", "LR", IB_MAD_ATTR_LINK_RECORD, "[[from_lid]/[from_port]] [[to_lid]/[to_port]]", query_link_records}, {"ServiceRecord", "SR", IB_MAD_ATTR_SERVICE_RECORD, NULL, query_service_records}, {"PathRecord", "PR", IB_MAD_ATTR_PATH_RECORD, NULL, query_path_records}, {"MCMemberRecord", "MCMR", IB_MAD_ATTR_MCMEMBER_RECORD, NULL, query_mcmember_records}, {"LFTRecord", "LFTR", IB_MAD_ATTR_LFT_RECORD, "[[lid]/[block]]", query_lft_records}, {"MFTRecord", "MFTR", IB_MAD_ATTR_MFT_RECORD, "[[mlid]/[position]/[block]]", query_mft_records}, {0} }; static const struct query_cmd *find_query(const char *name) { const struct query_cmd *q; unsigned len = strlen(name); for (q = query_cmds; q->name; q++) if (!strncasecmp(name, q->name, len) || (q->alias && !strncasecmp(name, q->alias, len))) return q; return NULL; } static const struct query_cmd *find_query_by_type(ib_net16_t type) { const struct query_cmd *q; for (q = query_cmds; q->name; q++) if (q->query_type == type) return q; return NULL; } static void usage(void) { const struct query_cmd *q; fprintf(stderr, "Usage: %s [-h -d -p -N] [--list | -D] [-S -I -L -l -G" " -O -U -c -s -g -m --src-to-dst --sgid-to-dgid " "-C -P -t(imeout) ] [query-name] [ | | ]\n", argv0); fprintf(stderr, " Queries node records by default\n"); fprintf(stderr, " -d enable debugging\n"); fprintf(stderr, " -p get PathRecord info\n"); fprintf(stderr, " -N get NodeRecord info\n"); fprintf(stderr, " --list | -D the node desc of the CA's\n"); fprintf(stderr, " -S get ServiceRecord info\n"); fprintf(stderr, " -I get InformInfoRecord (subscription) info\n"); fprintf(stderr, " -L return the Lids of the name specified\n"); fprintf(stderr, " -l return the unique Lid of the name specified\n"); fprintf(stderr, " -G return the Guids of the name specified\n"); fprintf(stderr, " -O return name for the Lid specified\n"); fprintf(stderr, " -U return name for the Guid specified\n"); fprintf(stderr, " -c get the SA's class port info\n"); fprintf(stderr, " -s return the PortInfoRecords with isSM or " "isSMdisabled capability mask bit on\n"); fprintf(stderr, " -g get multicast group info\n"); fprintf(stderr, " -m get multicast member info\n"); fprintf(stderr, " (if multicast group specified, list member GIDs" " only for group specified\n"); fprintf(stderr, " specified, for example 'saquery -m 0xC000')\n"); fprintf(stderr, " -x get LinkRecord info\n"); fprintf(stderr, " --src-to-dst get a PathRecord for \n" " where src and dst are either node " "names or LIDs\n"); fprintf(stderr, " --sgid-to-dgid get a PathRecord for \n" " where sgid and dgid are addresses in " "IPv6 format\n"); fprintf(stderr, " -C specify the SA query HCA\n"); fprintf(stderr, " -P specify the SA query port\n"); fprintf(stderr, " --smkey specify SM_Key value for the query." " If non-numeric value \n" " (like 'x') is specified then " "saquery will prompt for a value\n"); fprintf(stderr, " -t | --timeout specify the SA query " "response timeout (default %u msec)\n", DEFAULT_SA_TIMEOUT_MS); fprintf(stderr, " --node-name-map specify a node name map\n"); fprintf(stderr, "\n Supported query names (and aliases):\n"); for (q = query_cmds; q->name; q++) fprintf(stderr, " %s (%s) %s\n", q->name, q->alias ? q->alias : "", q->usage ? q->usage : ""); fprintf(stderr, "\n"); exit(-1); } enum saquery_command { SAQUERY_CMD_QUERY, SAQUERY_CMD_NODE_RECORD, SAQUERY_CMD_PATH_RECORD, SAQUERY_CMD_CLASS_PORT_INFO, SAQUERY_CMD_ISSM, SAQUERY_CMD_MCGROUPS, SAQUERY_CMD_MCMEMBERS, }; int main(int argc, char **argv) { int ch = 0; osm_bind_handle_t h; enum saquery_command command = SAQUERY_CMD_QUERY; const struct query_cmd *q = NULL; char *src = NULL, *dst = NULL; char *sgid = NULL, *dgid = NULL; ib_net16_t query_type = 0; ib_net16_t src_lid, dst_lid; ib_api_status_t status; static char const str_opts[] = "pVNDLlGOUcSIsgmxdhP:C:t:"; static const struct option long_opts[] = { {"p", 0, 0, 'p'}, {"Version", 0, 0, 'V'}, {"N", 0, 0, 'N'}, {"L", 0, 0, 'L'}, {"l", 0, 0, 'l'}, {"G", 0, 0, 'G'}, {"O", 0, 0, 'O'}, {"U", 0, 0, 'U'}, {"s", 0, 0, 's'}, {"g", 0, 0, 'g'}, {"m", 0, 0, 'm'}, {"x", 0, 0, 'x'}, {"d", 0, 0, 'd'}, {"c", 0, 0, 'c'}, {"S", 0, 0, 'S'}, {"I", 0, 0, 'I'}, {"P", 1, 0, 'P'}, {"C", 1, 0, 'C'}, {"help", 0, 0, 'h'}, {"list", 0, 0, 'D'}, {"src-to-dst", 1, 0, 1}, {"sgid-to-dgid", 1, 0, 2}, {"timeout", 1, 0, 't'}, {"node-name-map", 1, 0, 3}, {"smkey", 1, 0, 4}, {} }; argv0 = argv[0]; while ((ch = getopt_long(argc, argv, str_opts, long_opts, NULL)) != -1) { switch (ch) { case 1: { char *opt = strdup(optarg); char *ch = strchr(opt, ':'); if (!ch) { fprintf(stderr, "ERROR: --src-to-dst :\n"); usage(); } *ch++ = '\0'; if (*opt) src = strdup(opt); if (*ch) dst = strdup(ch); free(opt); command = SAQUERY_CMD_PATH_RECORD; break; } case 2: { char *opt = strdup(optarg); char *tok1 = strtok(opt, "-"); char *tok2 = strtok(NULL, "\0"); if (tok1 && tok2) { sgid = strdup(tok1); dgid = strdup(tok2); } else { fprintf(stderr, "ERROR: --sgid-to-dgid -\n"); usage(); } free(opt); command = SAQUERY_CMD_PATH_RECORD; break; } case 3: node_name_map_file = strdup(optarg); break; case 4: if (!isxdigit(*optarg) && !(optarg = getpass("SM_Key: "))) { fprintf(stderr, "cannot get SM_Key\n"); usage(); } smkey = cl_hton64(strtoull(optarg, NULL, 0)); break; case 'p': command = SAQUERY_CMD_PATH_RECORD; break; case 'V': fprintf(stderr, "%s %s\n", argv0, get_build_version()); exit(-1); case 'D': node_print_desc = ALL_DESC; break; case 'c': command = SAQUERY_CMD_CLASS_PORT_INFO; break; case 'S': query_type = IB_MAD_ATTR_SERVICE_RECORD; break; case 'I': query_type = IB_MAD_ATTR_INFORM_INFO_RECORD; break; case 'N': command = SAQUERY_CMD_NODE_RECORD; break; case 'L': node_print_desc = LID_ONLY; break; case 'l': node_print_desc = UNIQUE_LID_ONLY; break; case 'G': node_print_desc = GUID_ONLY; break; case 'O': node_print_desc = NAME_OF_LID; break; case 'U': node_print_desc = NAME_OF_GUID; break; case 's': command = SAQUERY_CMD_ISSM; break; case 'g': command = SAQUERY_CMD_MCGROUPS; break; case 'm': command = SAQUERY_CMD_MCMEMBERS; break; case 'x': query_type = IB_MAD_ATTR_LINK_RECORD; break; case 'd': osm_debug = 1; break; case 'C': sa_hca_name = optarg; break; case 'P': sa_port_num = strtoul(optarg, NULL, 0); break; case 't': sa_timeout_ms = strtoul(optarg, NULL, 0); break; case 'h': default: usage(); } } argc -= optind; argv += optind; if (!query_type) { if (!argc || !(q = find_query(argv[0]))) query_type = IB_MAD_ATTR_NODE_RECORD; else { query_type = q->query_type; argc--; argv++; } } if (argc) { if (node_print_desc == NAME_OF_LID) { requested_lid = (ib_net16_t) strtoul(argv[0], NULL, 0); requested_lid_flag++; } else if (node_print_desc == NAME_OF_GUID) { requested_guid = (ib_net64_t) strtoul(argv[0], NULL, 0); requested_guid_flag++; } else { requested_name = argv[0]; } } if ((node_print_desc == LID_ONLY || node_print_desc == UNIQUE_LID_ONLY || node_print_desc == GUID_ONLY) && !requested_name) { fprintf(stderr, "ERROR: name not specified\n"); usage(); } if (node_print_desc == NAME_OF_LID && !requested_lid_flag) { fprintf(stderr, "ERROR: lid not specified\n"); usage(); } if (node_print_desc == NAME_OF_GUID && !requested_guid_flag) { fprintf(stderr, "ERROR: guid not specified\n"); usage(); } /* Note: lid cannot be 0; see infiniband spec 4.1.3 */ if (node_print_desc == NAME_OF_LID && !requested_lid) { fprintf(stderr, "ERROR: lid invalid\n"); usage(); } h = get_bind_handle(); node_name_map = open_node_name_map(node_name_map_file); switch (command) { case SAQUERY_CMD_NODE_RECORD: status = print_node_records(h); break; case SAQUERY_CMD_PATH_RECORD: if (src && dst) { src_lid = get_lid(h, src); dst_lid = get_lid(h, dst); printf("Path record for %s -> %s\n", src, dst); if (src_lid == 0 || dst_lid == 0) { status = IB_UNKNOWN_ERROR; } else { status = get_print_path_rec_lid(h, src_lid, dst_lid); } } else if (sgid && dgid) { struct in6_addr src_addr, dst_addr; if (inet_pton(AF_INET6, sgid, &src_addr) <= 0) { fprintf(stderr, "invalid src gid: %s\n", sgid); exit(-1); } if (inet_pton(AF_INET6, dgid, &dst_addr) <= 0) { fprintf(stderr, "invalid dst gid: %s\n", dgid); exit(-1); } status = get_print_path_rec_gid(h, (ib_gid_t *) & src_addr.s6_addr, (ib_gid_t *) & dst_addr.s6_addr); } else { status = query_path_records(q, h, 0, NULL); } break; case SAQUERY_CMD_CLASS_PORT_INFO: status = get_print_class_port_info(h); break; case SAQUERY_CMD_ISSM: status = print_issm_records(h); break; case SAQUERY_CMD_MCGROUPS: status = print_multicast_group_records(h); break; case SAQUERY_CMD_MCMEMBERS: status = print_multicast_member_records(h); break; default: if ((!q && !(q = find_query_by_type(query_type))) || !q->handler) { fprintf(stderr, "Unknown query type %d\n", ntohs(query_type)); status = IB_UNKNOWN_ERROR; } else status = q->handler(q, h, argc, argv); break; } if (src) free(src); if (dst) free(dst); clean_up(); close_node_name_map(node_name_map); return (status); } Index: projects/sendfile/contrib/ofed/management/infiniband-diags/src/smpquery.c =================================================================== --- projects/sendfile/contrib/ofed/management/infiniband-diags/src/smpquery.c (revision 275355) +++ projects/sendfile/contrib/ofed/management/infiniband-diags/src/smpquery.c (revision 275356) @@ -1,538 +1,538 @@ /* * Copyright (c) 2004-2008 Voltaire Inc. All rights reserved. * * This software is available to you under a choice of one of two * licenses. You may choose to be licensed under the terms of the GNU * General Public License (GPL) Version 2, available from the file * COPYING in the main directory of this source tree, or the * OpenIB.org BSD license below: * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above * copyright notice, this list of conditions and the following * disclaimer. * * - 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. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #if HAVE_CONFIG_H # include #endif /* HAVE_CONFIG_H */ #include #include #include #include #include #include #include #include #define __STDC_FORMAT_MACROS #include #include #include #include -#include +#include #include "ibdiag_common.h" #undef DEBUG #define DEBUG if (verbose>1) IBWARN static int dest_type = IB_DEST_LID; static int verbose; typedef char *(op_fn_t)(ib_portid_t *dest, char **argv, int argc); typedef struct match_rec { char *name; op_fn_t *fn; unsigned opt_portnum; } match_rec_t; static op_fn_t node_desc, node_info, port_info, switch_info, pkey_table, sl2vl_table, vlarb_table, guid_info; static const match_rec_t match_tbl[] = { { "nodeinfo", node_info }, { "nodedesc", node_desc }, { "portinfo", port_info, 1 }, { "switchinfo", switch_info }, { "pkeys", pkey_table, 1 }, { "sl2vl", sl2vl_table, 1 }, { "vlarb", vlarb_table, 1 }, { "guids", guid_info }, {0} }; char *argv0 = "smpquery"; static char *node_name_map_file = NULL; static nn_map_t *node_name_map = NULL; /*******************************************/ static char * node_desc(ib_portid_t *dest, char **argv, int argc) { int node_type, l; uint64_t node_guid; char nd[IB_SMP_DATA_SIZE]; uint8_t data[IB_SMP_DATA_SIZE]; char dots[128]; char *nodename = NULL; if (!smp_query(data, dest, IB_ATTR_NODE_INFO, 0, 0)) return "node info query failed"; mad_decode_field(data, IB_NODE_TYPE_F, &node_type); mad_decode_field(data, IB_NODE_GUID_F, &node_guid); if (!smp_query(nd, dest, IB_ATTR_NODE_DESC, 0, 0)) return "node desc query failed"; nodename = remap_node_name(node_name_map, node_guid, nd); l = strlen(nodename); if (l < 32) { memset(dots, '.', 32 - l); dots[32 - l] = '\0'; } else { dots[0] = '.'; dots[1] = '\0'; } printf("Node Description:%s%s\n", dots, nodename); free(nodename); return 0; } static char * node_info(ib_portid_t *dest, char **argv, int argc) { char buf[2048]; char data[IB_SMP_DATA_SIZE]; if (!smp_query(data, dest, IB_ATTR_NODE_INFO, 0, 0)) return "node info query failed"; mad_dump_nodeinfo(buf, sizeof buf, data, sizeof data); printf("# Node info: %s\n%s", portid2str(dest), buf); return 0; } static char * port_info(ib_portid_t *dest, char **argv, int argc) { char buf[2048]; char data[IB_SMP_DATA_SIZE]; int portnum = 0; if (argc > 0) portnum = strtol(argv[0], 0, 0); if (!smp_query(data, dest, IB_ATTR_PORT_INFO, portnum, 0)) return "port info query failed"; mad_dump_portinfo(buf, sizeof buf, data, sizeof data); printf("# Port info: %s port %d\n%s", portid2str(dest), portnum, buf); return 0; } static char * switch_info(ib_portid_t *dest, char **argv, int argc) { char buf[2048]; char data[IB_SMP_DATA_SIZE]; if (!smp_query(data, dest, IB_ATTR_SWITCH_INFO, 0, 0)) return "switch info query failed"; mad_dump_switchinfo(buf, sizeof buf, data, sizeof data); printf("# Switch info: %s\n%s", portid2str(dest), buf); return 0; } static char * pkey_table(ib_portid_t *dest, char **argv, int argc) { uint8_t data[IB_SMP_DATA_SIZE]; uint32_t i, j, k; uint16_t *p; unsigned mod; int n, t, phy_ports; int portnum = 0; if (argc > 0) portnum = strtol(argv[0], 0, 0); /* Get the partition capacity */ if (!smp_query(data, dest, IB_ATTR_NODE_INFO, 0, 0)) return "node info query failed"; mad_decode_field(data, IB_NODE_TYPE_F, &t); mad_decode_field(data, IB_NODE_NPORTS_F, &phy_ports); if (portnum > phy_ports) return "invalid port number"; if ((t == IB_NODE_SWITCH) && (portnum != 0)) { if (!smp_query(data, dest, IB_ATTR_SWITCH_INFO, 0, 0)) return "switch info failed"; mad_decode_field(data, IB_SW_PARTITION_ENFORCE_CAP_F, &n); } else mad_decode_field(data, IB_NODE_PARTITION_CAP_F, &n); for (i = 0; i < (n + 31) / 32; i++) { mod = i | (portnum << 16); if (!smp_query(data, dest, IB_ATTR_PKEY_TBL, mod, 0)) return "pkey table query failed"; if (i + 1 == (n + 31) / 32) k = ((n + 7 - i * 32) / 8) * 8; else k = 32; p = (uint16_t *) data; for (j = 0; j < k; j += 8, p += 8) { printf("%4u: 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x 0x%04x\n", (i * 32) + j, ntohs(p[0]), ntohs(p[1]), ntohs(p[2]), ntohs(p[3]), ntohs(p[4]), ntohs(p[5]), ntohs(p[6]), ntohs(p[7])); } } printf("%d pkeys capacity for this port\n", n); return 0; } static char *sl2vl_dump_table_entry(ib_portid_t *dest, int in, int out) { char buf[2048]; char data[IB_SMP_DATA_SIZE]; int portnum = (in << 8) | out; if (!smp_query(data, dest, IB_ATTR_SLVL_TABLE, portnum, 0)) return "slvl query failed"; mad_dump_sltovl(buf, sizeof buf, data, sizeof data); printf("ports: in %2d, out %2d: ", in, out); printf("%s", buf); return 0; } static char * sl2vl_table(ib_portid_t *dest, char **argv, int argc) { uint8_t data[IB_SMP_DATA_SIZE]; int type, num_ports, portnum = 0; int i; char *ret; if (argc > 0) portnum = strtol(argv[0], 0, 0); if (!smp_query(data, dest, IB_ATTR_NODE_INFO, 0, 0)) return "node info query failed"; mad_decode_field(data, IB_NODE_TYPE_F, &type); mad_decode_field(data, IB_NODE_NPORTS_F, &num_ports); if (portnum > num_ports) return "invalid port number"; printf("# SL2VL table: %s\n", portid2str(dest)); printf("# SL: |"); for (i = 0 ; i < 16 ; i++) printf("%2d|", i); printf("\n"); if (type != IB_NODE_SWITCH) return sl2vl_dump_table_entry(dest, 0, 0); for (i = 0 ; i <= num_ports ; i++) { ret = sl2vl_dump_table_entry(dest, i, portnum); if (ret) return ret; } return 0; } static char *vlarb_dump_table_entry(ib_portid_t *dest, int portnum, int offset, unsigned cap) { char buf[2048]; char data[IB_SMP_DATA_SIZE]; if (!smp_query(data, dest, IB_ATTR_VL_ARBITRATION, (offset << 16) | portnum, 0)) return "vl arb query failed"; mad_dump_vlarbitration(buf, sizeof(buf), data, cap * 2); printf("%s", buf); return 0; } static char *vlarb_dump_table(ib_portid_t *dest, int portnum, char *name, int offset, int cap) { char *ret; printf("# %s priority VL Arbitration Table:", name); ret = vlarb_dump_table_entry(dest, portnum, offset, cap < 32 ? cap : 32); if (!ret && cap > 32) ret = vlarb_dump_table_entry(dest, portnum, offset + 1, cap - 32); return ret; } static char * vlarb_table(ib_portid_t *dest, char **argv, int argc) { uint8_t data[IB_SMP_DATA_SIZE]; int portnum = 0; int type, enhsp0, lowcap, highcap; char *ret = 0; if (argc > 0) portnum = strtol(argv[0], 0, 0); /* port number of 0 could mean SP0 or port MAD arrives on */ if (portnum == 0) { if (!smp_query(data, dest, IB_ATTR_NODE_INFO, 0, 0)) return "node info query failed"; mad_decode_field(data, IB_NODE_TYPE_F, &type); if (type == IB_NODE_SWITCH) { if (!smp_query(data, dest, IB_ATTR_SWITCH_INFO, 0, 0)) return "switch info query failed"; mad_decode_field(data, IB_SW_ENHANCED_PORT0_F, &enhsp0); if (!enhsp0) { printf("# No VLArbitration tables (BSP0): %s port %d\n", portid2str(dest), 0); return 0; } } } if (!smp_query(data, dest, IB_ATTR_PORT_INFO, portnum, 0)) return "port info query failed"; mad_decode_field(data, IB_PORT_VL_ARBITRATION_LOW_CAP_F, &lowcap); mad_decode_field(data, IB_PORT_VL_ARBITRATION_HIGH_CAP_F,&highcap); printf("# VLArbitration tables: %s port %d LowCap %d HighCap %d\n", portid2str(dest), portnum, lowcap, highcap); if (lowcap > 0) ret = vlarb_dump_table(dest, portnum, "Low", 1, lowcap); if (!ret && highcap > 0) ret = vlarb_dump_table(dest, portnum, "High", 3, highcap); return ret; } static char * guid_info(ib_portid_t *dest, char **argv, int argc) { uint8_t data[IB_SMP_DATA_SIZE]; uint32_t i, j, k; uint64_t *p; unsigned mod; int n; /* Get the guid capacity */ if (!smp_query(data, dest, IB_ATTR_PORT_INFO, 0, 0)) return "port info failed"; mad_decode_field(data, IB_PORT_GUID_CAP_F, &n); for (i = 0; i < (n + 7) / 8; i++) { mod = i; if (!smp_query(data, dest, IB_ATTR_GUID_INFO, mod, 0)) return "guid info query failed"; if (i + 1 == (n + 7) / 8) k = ((n + 1 - i * 8) / 2) * 2; else k = 8; p = (uint64_t *) data; for (j = 0; j < k; j += 2, p += 2) { printf("%4u: 0x%016"PRIx64" 0x%016"PRIx64"\n", (i * 8) + j, ntohll(p[0]), ntohll(p[1])); } } printf("%d guids capacity for this port\n", n); return 0; } static op_fn_t * match_op(char *name) { const match_rec_t *r; for (r = match_tbl; r->name; r++) if (!strcmp(r->name, name)) return r->fn; return 0; } static void usage(void) { char *basename; const match_rec_t *r; if (!(basename = strrchr(argv0, '/'))) basename = argv0; else basename++; fprintf(stderr, "Usage: %s [-d(ebug) -e(rr_show) -v(erbose) -D(irect) -G(uid) -s smlid -V(ersion) -C ca_name -P ca_port " "-t(imeout) timeout_ms --node-name-map node-name-map] [op params]\n", basename); fprintf(stderr, "\tsupported ops:\n"); for (r = match_tbl ; r->name ; r++) { fprintf(stderr, "\t\t%s %s\n", r->name, r->opt_portnum ? " []" : ""); } fprintf(stderr, "\n\texamples:\n"); fprintf(stderr, "\t\t%s portinfo 3 1\t\t\t\t# portinfo by lid, with port modifier\n", basename); fprintf(stderr, "\t\t%s -G switchinfo 0x2C9000100D051 1\t# switchinfo by guid\n", basename); fprintf(stderr, "\t\t%s -D nodeinfo 0\t\t\t\t# nodeinfo by direct route\n", basename); fprintf(stderr, "\t\t%s -c nodeinfo 6 0,12\t\t\t# nodeinfo by combined route\n", basename); exit(-1); } int main(int argc, char **argv) { int mgmt_classes[3] = {IB_SMI_CLASS, IB_SMI_DIRECT_CLASS, IB_SA_CLASS}; ib_portid_t portid = {0}; ib_portid_t *sm_id = 0, sm_portid = {0}; extern int ibdebug; int timeout = 0, udebug = 0; char *ca = 0; int ca_port = 0; char *err; op_fn_t *fn; static char const str_opts[] = "C:P:t:s:devDcGVhu"; static const struct option long_opts[] = { { "C", 1, 0, 'C'}, { "P", 1, 0, 'P'}, { "debug", 0, 0, 'd'}, { "err_show", 0, 0, 'e'}, { "verbose", 0, 0, 'v'}, { "Direct", 0, 0, 'D'}, { "combined", 0, 0, 'c'}, { "Guid", 0, 0, 'G'}, { "smlid", 1, 0, 's'}, { "timeout", 1, 0, 't'}, { "node-name-map", 1, 0, 1}, { "Version", 0, 0, 'V'}, { "help", 0, 0, 'h'}, { "usage", 0, 0, 'u'}, { } }; argv0 = argv[0]; while (1) { int ch = getopt_long(argc, argv, str_opts, long_opts, NULL); if ( ch == -1 ) break; switch(ch) { case 1: node_name_map_file = strdup(optarg); break; case 'd': ibdebug++; madrpc_show_errors(1); umad_debug(udebug); udebug++; break; case 'e': madrpc_show_errors(1); break; case 'D': dest_type = IB_DEST_DRPATH; break; case 'c': dest_type = IB_DEST_DRSLID; break; case 'G': dest_type = IB_DEST_GUID; break; case 'C': ca = optarg; break; case 'P': ca_port = strtoul(optarg, 0, 0); break; case 's': if (ib_resolve_portid_str(&sm_portid, optarg, IB_DEST_LID, 0) < 0) IBERROR("can't resolve SM destination port %s", optarg); sm_id = &sm_portid; break; case 't': timeout = strtoul(optarg, 0, 0); madrpc_set_timeout(timeout); break; case 'v': verbose++; break; case 'V': fprintf(stderr, "%s %s\n", argv0, get_build_version() ); exit(-1); default: usage(); break; } } argc -= optind; argv += optind; if (argc < 2) usage(); if (!(fn = match_op(argv[0]))) IBERROR("operation '%s' not supported", argv[0]); madrpc_init(ca, ca_port, mgmt_classes, 3); node_name_map = open_node_name_map(node_name_map_file); if (dest_type != IB_DEST_DRSLID) { if (ib_resolve_portid_str(&portid, argv[1], dest_type, sm_id) < 0) IBERROR("can't resolve destination port %s", argv[1]); if ((err = fn(&portid, argv+2, argc-2))) IBERROR("operation %s: %s", argv[0], err); } else { char concat[64]; memset(concat, 0, 64); snprintf(concat, sizeof(concat), "%s %s", argv[1], argv[2]); if (ib_resolve_portid_str(&portid, concat, dest_type, sm_id) < 0) IBERROR("can't resolve destination port %s", concat); if ((err = fn(&portid, argv+3, argc-3))) IBERROR("operation %s: %s", argv[0], err); } close_node_name_map(node_name_map); exit(0); } Index: projects/sendfile/contrib/ofed/usr.bin/Makefile.inc =================================================================== --- projects/sendfile/contrib/ofed/usr.bin/Makefile.inc (revision 275355) +++ projects/sendfile/contrib/ofed/usr.bin/Makefile.inc (revision 275356) @@ -1,4 +1,9 @@ DIAGPATH= ${.CURDIR}/../../management/infiniband-diags BINDIR?= /usr/bin CFLAGS+= -I${.CURDIR}/../../include/infiniband +CFLAGS+= -I${.CURDIR}/../../include CFLAGS+= -I${.CURDIR}/../../management/opensm/include/ +CFLAGS+= -I${.CURDIR}/../../management/opensm +CFLAGS+= -I${.CURDIR}/../../management/libibcommon/include +CFLAGS+= -I${.CURDIR}/../../management/libibumad/include +CFLAGS+= -I${.CURDIR}/../../management/libibmad/include Index: projects/sendfile/sys/dev/virtio/block/virtio_blk.c =================================================================== --- projects/sendfile/sys/dev/virtio/block/virtio_blk.c (revision 275355) +++ projects/sendfile/sys/dev/virtio/block/virtio_blk.c (revision 275356) @@ -1,1405 +1,1410 @@ /*- * Copyright (c) 2011, Bryan Venteicher * 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 unmodified, 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. */ /* Driver for VirtIO block devices. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "virtio_if.h" struct vtblk_request { struct virtio_blk_outhdr vbr_hdr; struct bio *vbr_bp; uint8_t vbr_ack; - TAILQ_ENTRY(vtblk_request) vbr_link; }; enum vtblk_cache_mode { VTBLK_CACHE_WRITETHROUGH, VTBLK_CACHE_WRITEBACK, VTBLK_CACHE_MAX }; struct vtblk_softc { device_t vtblk_dev; struct mtx vtblk_mtx; uint64_t vtblk_features; uint32_t vtblk_flags; #define VTBLK_FLAG_INDIRECT 0x0001 #define VTBLK_FLAG_READONLY 0x0002 #define VTBLK_FLAG_DETACH 0x0004 #define VTBLK_FLAG_SUSPEND 0x0008 #define VTBLK_FLAG_DUMPING 0x0010 #define VTBLK_FLAG_BARRIER 0x0020 #define VTBLK_FLAG_WC_CONFIG 0x0040 struct virtqueue *vtblk_vq; struct sglist *vtblk_sglist; struct disk *vtblk_disk; struct bio_queue_head vtblk_bioq; TAILQ_HEAD(, vtblk_request) vtblk_req_free; TAILQ_HEAD(, vtblk_request) vtblk_req_ready; struct vtblk_request *vtblk_req_ordered; int vtblk_max_nsegs; int vtblk_request_count; enum vtblk_cache_mode vtblk_write_cache; struct vtblk_request vtblk_dump_request; }; static struct virtio_feature_desc vtblk_feature_desc[] = { { VIRTIO_BLK_F_BARRIER, "HostBarrier" }, { VIRTIO_BLK_F_SIZE_MAX, "MaxSegSize" }, { VIRTIO_BLK_F_SEG_MAX, "MaxNumSegs" }, { VIRTIO_BLK_F_GEOMETRY, "DiskGeometry" }, { VIRTIO_BLK_F_RO, "ReadOnly" }, { VIRTIO_BLK_F_BLK_SIZE, "BlockSize" }, { VIRTIO_BLK_F_SCSI, "SCSICmds" }, { VIRTIO_BLK_F_WCE, "WriteCache" }, { VIRTIO_BLK_F_TOPOLOGY, "Topology" }, { VIRTIO_BLK_F_CONFIG_WCE, "ConfigWCE" }, { 0, NULL } }; static int vtblk_modevent(module_t, int, void *); static int vtblk_probe(device_t); static int vtblk_attach(device_t); static int vtblk_detach(device_t); static int vtblk_suspend(device_t); static int vtblk_resume(device_t); static int vtblk_shutdown(device_t); static int vtblk_config_change(device_t); static int vtblk_open(struct disk *); static int vtblk_close(struct disk *); static int vtblk_ioctl(struct disk *, u_long, void *, int, struct thread *); static int vtblk_dump(void *, void *, vm_offset_t, off_t, size_t); static void vtblk_strategy(struct bio *); static void vtblk_negotiate_features(struct vtblk_softc *); +static void vtblk_setup_features(struct vtblk_softc *); static int vtblk_maximum_segments(struct vtblk_softc *, struct virtio_blk_config *); static int vtblk_alloc_virtqueue(struct vtblk_softc *); static void vtblk_resize_disk(struct vtblk_softc *, uint64_t); -static void vtblk_set_write_cache(struct vtblk_softc *, int); -static int vtblk_write_cache_enabled(struct vtblk_softc *sc, - struct virtio_blk_config *); -static int vtblk_write_cache_sysctl(SYSCTL_HANDLER_ARGS); static void vtblk_alloc_disk(struct vtblk_softc *, struct virtio_blk_config *); static void vtblk_create_disk(struct vtblk_softc *); -static int vtblk_quiesce(struct vtblk_softc *); -static void vtblk_startio(struct vtblk_softc *); -static struct vtblk_request * vtblk_bio_request(struct vtblk_softc *); -static int vtblk_execute_request(struct vtblk_softc *, +static int vtblk_request_prealloc(struct vtblk_softc *); +static void vtblk_request_free(struct vtblk_softc *); +static struct vtblk_request * + vtblk_request_dequeue(struct vtblk_softc *); +static void vtblk_request_enqueue(struct vtblk_softc *, struct vtblk_request *); +static struct vtblk_request * + vtblk_request_next_ready(struct vtblk_softc *); +static void vtblk_request_requeue_ready(struct vtblk_softc *, + struct vtblk_request *); +static struct vtblk_request * + vtblk_request_next(struct vtblk_softc *); +static struct vtblk_request * + vtblk_request_bio(struct vtblk_softc *); +static int vtblk_request_execute(struct vtblk_softc *, + struct vtblk_request *); +static int vtblk_request_error(struct vtblk_request *); -static void vtblk_vq_intr(void *); +static void vtblk_queue_completed(struct vtblk_softc *, + struct bio_queue *); +static void vtblk_done_completed(struct vtblk_softc *, + struct bio_queue *); +static void vtblk_drain_vq(struct vtblk_softc *, int); +static void vtblk_drain(struct vtblk_softc *); -static void vtblk_stop(struct vtblk_softc *); +static void vtblk_startio(struct vtblk_softc *); +static void vtblk_bio_done(struct vtblk_softc *, struct bio *, int); static void vtblk_read_config(struct vtblk_softc *, struct virtio_blk_config *); -static void vtblk_get_ident(struct vtblk_softc *); -static void vtblk_prepare_dump(struct vtblk_softc *); -static int vtblk_write_dump(struct vtblk_softc *, void *, off_t, size_t); -static int vtblk_flush_dump(struct vtblk_softc *); +static void vtblk_ident(struct vtblk_softc *); static int vtblk_poll_request(struct vtblk_softc *, struct vtblk_request *); +static int vtblk_quiesce(struct vtblk_softc *); +static void vtblk_vq_intr(void *); +static void vtblk_stop(struct vtblk_softc *); -static void vtblk_finish_completed(struct vtblk_softc *); -static void vtblk_drain_vq(struct vtblk_softc *, int); -static void vtblk_drain(struct vtblk_softc *); +static void vtblk_dump_prepare(struct vtblk_softc *); +static int vtblk_dump_write(struct vtblk_softc *, void *, off_t, size_t); +static int vtblk_dump_flush(struct vtblk_softc *); -static int vtblk_alloc_requests(struct vtblk_softc *); -static void vtblk_free_requests(struct vtblk_softc *); -static struct vtblk_request * vtblk_dequeue_request(struct vtblk_softc *); -static void vtblk_enqueue_request(struct vtblk_softc *, - struct vtblk_request *); +static void vtblk_set_write_cache(struct vtblk_softc *, int); +static int vtblk_write_cache_enabled(struct vtblk_softc *sc, + struct virtio_blk_config *); +static int vtblk_write_cache_sysctl(SYSCTL_HANDLER_ARGS); -static struct vtblk_request * vtblk_dequeue_ready(struct vtblk_softc *); -static void vtblk_enqueue_ready(struct vtblk_softc *, - struct vtblk_request *); - -static int vtblk_request_error(struct vtblk_request *); -static void vtblk_finish_bio(struct bio *, int); - static void vtblk_setup_sysctl(struct vtblk_softc *); static int vtblk_tunable_int(struct vtblk_softc *, const char *, int); /* Tunables. */ static int vtblk_no_ident = 0; TUNABLE_INT("hw.vtblk.no_ident", &vtblk_no_ident); static int vtblk_writecache_mode = -1; TUNABLE_INT("hw.vtblk.writecache_mode", &vtblk_writecache_mode); /* Features desired/implemented by this driver. */ #define VTBLK_FEATURES \ (VIRTIO_BLK_F_BARRIER | \ VIRTIO_BLK_F_SIZE_MAX | \ VIRTIO_BLK_F_SEG_MAX | \ VIRTIO_BLK_F_GEOMETRY | \ VIRTIO_BLK_F_RO | \ VIRTIO_BLK_F_BLK_SIZE | \ VIRTIO_BLK_F_WCE | \ VIRTIO_BLK_F_CONFIG_WCE | \ VIRTIO_RING_F_INDIRECT_DESC) #define VTBLK_MTX(_sc) &(_sc)->vtblk_mtx #define VTBLK_LOCK_INIT(_sc, _name) \ mtx_init(VTBLK_MTX((_sc)), (_name), \ "VirtIO Block Lock", MTX_DEF) #define VTBLK_LOCK(_sc) mtx_lock(VTBLK_MTX((_sc))) #define VTBLK_UNLOCK(_sc) mtx_unlock(VTBLK_MTX((_sc))) #define VTBLK_LOCK_DESTROY(_sc) mtx_destroy(VTBLK_MTX((_sc))) #define VTBLK_LOCK_ASSERT(_sc) mtx_assert(VTBLK_MTX((_sc)), MA_OWNED) #define VTBLK_LOCK_ASSERT_NOTOWNED(_sc) \ mtx_assert(VTBLK_MTX((_sc)), MA_NOTOWNED) #define VTBLK_DISK_NAME "vtbd" #define VTBLK_QUIESCE_TIMEOUT (30 * hz) /* * Each block request uses at least two segments - one for the header * and one for the status. */ #define VTBLK_MIN_SEGMENTS 2 static device_method_t vtblk_methods[] = { /* Device methods. */ DEVMETHOD(device_probe, vtblk_probe), DEVMETHOD(device_attach, vtblk_attach), DEVMETHOD(device_detach, vtblk_detach), DEVMETHOD(device_suspend, vtblk_suspend), DEVMETHOD(device_resume, vtblk_resume), DEVMETHOD(device_shutdown, vtblk_shutdown), /* VirtIO methods. */ DEVMETHOD(virtio_config_change, vtblk_config_change), DEVMETHOD_END }; static driver_t vtblk_driver = { "vtblk", vtblk_methods, sizeof(struct vtblk_softc) }; static devclass_t vtblk_devclass; DRIVER_MODULE(virtio_blk, virtio_mmio, vtblk_driver, vtblk_devclass, vtblk_modevent, 0); DRIVER_MODULE(virtio_blk, virtio_pci, vtblk_driver, vtblk_devclass, vtblk_modevent, 0); MODULE_VERSION(virtio_blk, 1); MODULE_DEPEND(virtio_blk, virtio, 1, 1, 1); static int vtblk_modevent(module_t mod, int type, void *unused) { int error; error = 0; switch (type) { case MOD_LOAD: case MOD_QUIESCE: case MOD_UNLOAD: case MOD_SHUTDOWN: break; default: error = EOPNOTSUPP; break; } return (error); } static int vtblk_probe(device_t dev) { if (virtio_get_device_type(dev) != VIRTIO_ID_BLOCK) return (ENXIO); device_set_desc(dev, "VirtIO Block Adapter"); return (BUS_PROBE_DEFAULT); } static int vtblk_attach(device_t dev) { struct vtblk_softc *sc; struct virtio_blk_config blkcfg; int error; + virtio_set_feature_desc(dev, vtblk_feature_desc); + sc = device_get_softc(dev); sc->vtblk_dev = dev; - VTBLK_LOCK_INIT(sc, device_get_nameunit(dev)); - bioq_init(&sc->vtblk_bioq); TAILQ_INIT(&sc->vtblk_req_free); TAILQ_INIT(&sc->vtblk_req_ready); - virtio_set_feature_desc(dev, vtblk_feature_desc); - vtblk_negotiate_features(sc); - - if (virtio_with_feature(dev, VIRTIO_RING_F_INDIRECT_DESC)) - sc->vtblk_flags |= VTBLK_FLAG_INDIRECT; - if (virtio_with_feature(dev, VIRTIO_BLK_F_RO)) - sc->vtblk_flags |= VTBLK_FLAG_READONLY; - if (virtio_with_feature(dev, VIRTIO_BLK_F_BARRIER)) - sc->vtblk_flags |= VTBLK_FLAG_BARRIER; - if (virtio_with_feature(dev, VIRTIO_BLK_F_CONFIG_WCE)) - sc->vtblk_flags |= VTBLK_FLAG_WC_CONFIG; - vtblk_setup_sysctl(sc); + vtblk_setup_features(sc); - /* Get local copy of config. */ vtblk_read_config(sc, &blkcfg); /* * With the current sglist(9) implementation, it is not easy * for us to support a maximum segment size as adjacent * segments are coalesced. For now, just make sure it's larger * than the maximum supported transfer size. */ if (virtio_with_feature(dev, VIRTIO_BLK_F_SIZE_MAX)) { if (blkcfg.size_max < MAXPHYS) { error = ENOTSUP; device_printf(dev, "host requires unsupported " "maximum segment size feature\n"); goto fail; } } sc->vtblk_max_nsegs = vtblk_maximum_segments(sc, &blkcfg); if (sc->vtblk_max_nsegs <= VTBLK_MIN_SEGMENTS) { error = EINVAL; device_printf(dev, "fewer than minimum number of segments " "allowed: %d\n", sc->vtblk_max_nsegs); goto fail; } sc->vtblk_sglist = sglist_alloc(sc->vtblk_max_nsegs, M_NOWAIT); if (sc->vtblk_sglist == NULL) { error = ENOMEM; device_printf(dev, "cannot allocate sglist\n"); goto fail; } error = vtblk_alloc_virtqueue(sc); if (error) { device_printf(dev, "cannot allocate virtqueue\n"); goto fail; } - error = vtblk_alloc_requests(sc); + error = vtblk_request_prealloc(sc); if (error) { device_printf(dev, "cannot preallocate requests\n"); goto fail; } vtblk_alloc_disk(sc, &blkcfg); error = virtio_setup_intr(dev, INTR_TYPE_BIO | INTR_ENTROPY); if (error) { device_printf(dev, "cannot setup virtqueue interrupt\n"); goto fail; } vtblk_create_disk(sc); virtqueue_enable_intr(sc->vtblk_vq); fail: if (error) vtblk_detach(dev); return (error); } static int vtblk_detach(device_t dev) { struct vtblk_softc *sc; sc = device_get_softc(dev); VTBLK_LOCK(sc); sc->vtblk_flags |= VTBLK_FLAG_DETACH; if (device_is_attached(dev)) vtblk_stop(sc); VTBLK_UNLOCK(sc); vtblk_drain(sc); if (sc->vtblk_disk != NULL) { disk_destroy(sc->vtblk_disk); sc->vtblk_disk = NULL; } if (sc->vtblk_sglist != NULL) { sglist_free(sc->vtblk_sglist); sc->vtblk_sglist = NULL; } VTBLK_LOCK_DESTROY(sc); return (0); } static int vtblk_suspend(device_t dev) { struct vtblk_softc *sc; int error; sc = device_get_softc(dev); VTBLK_LOCK(sc); sc->vtblk_flags |= VTBLK_FLAG_SUSPEND; /* XXX BMV: virtio_stop(), etc needed here? */ error = vtblk_quiesce(sc); if (error) sc->vtblk_flags &= ~VTBLK_FLAG_SUSPEND; VTBLK_UNLOCK(sc); return (error); } static int vtblk_resume(device_t dev) { struct vtblk_softc *sc; sc = device_get_softc(dev); VTBLK_LOCK(sc); /* XXX BMV: virtio_reinit(), etc needed here? */ sc->vtblk_flags &= ~VTBLK_FLAG_SUSPEND; vtblk_startio(sc); VTBLK_UNLOCK(sc); return (0); } static int vtblk_shutdown(device_t dev) { return (0); } static int vtblk_config_change(device_t dev) { struct vtblk_softc *sc; struct virtio_blk_config blkcfg; uint64_t capacity; sc = device_get_softc(dev); vtblk_read_config(sc, &blkcfg); /* Capacity is always in 512-byte units. */ capacity = blkcfg.capacity * 512; if (sc->vtblk_disk->d_mediasize != capacity) vtblk_resize_disk(sc, capacity); return (0); } static int vtblk_open(struct disk *dp) { struct vtblk_softc *sc; if ((sc = dp->d_drv1) == NULL) return (ENXIO); return (sc->vtblk_flags & VTBLK_FLAG_DETACH ? ENXIO : 0); } static int vtblk_close(struct disk *dp) { struct vtblk_softc *sc; if ((sc = dp->d_drv1) == NULL) return (ENXIO); return (0); } static int vtblk_ioctl(struct disk *dp, u_long cmd, void *addr, int flag, struct thread *td) { struct vtblk_softc *sc; if ((sc = dp->d_drv1) == NULL) return (ENXIO); return (ENOTTY); } static int vtblk_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { struct disk *dp; struct vtblk_softc *sc; int error; dp = arg; if ((sc = dp->d_drv1) == NULL) return (ENXIO); VTBLK_LOCK(sc); if ((sc->vtblk_flags & VTBLK_FLAG_DUMPING) == 0) { - vtblk_prepare_dump(sc); + vtblk_dump_prepare(sc); sc->vtblk_flags |= VTBLK_FLAG_DUMPING; } if (length > 0) - error = vtblk_write_dump(sc, virtual, offset, length); + error = vtblk_dump_write(sc, virtual, offset, length); else if (virtual == NULL && offset == 0) - error = vtblk_flush_dump(sc); + error = vtblk_dump_flush(sc); else { error = EINVAL; sc->vtblk_flags &= ~VTBLK_FLAG_DUMPING; } VTBLK_UNLOCK(sc); return (error); } static void vtblk_strategy(struct bio *bp) { struct vtblk_softc *sc; if ((sc = bp->bio_disk->d_drv1) == NULL) { - vtblk_finish_bio(bp, EINVAL); + vtblk_bio_done(NULL, bp, EINVAL); return; } /* * Fail any write if RO. Unfortunately, there does not seem to * be a better way to report our readonly'ness to GEOM above. */ if (sc->vtblk_flags & VTBLK_FLAG_READONLY && (bp->bio_cmd == BIO_WRITE || bp->bio_cmd == BIO_FLUSH)) { - vtblk_finish_bio(bp, EROFS); + vtblk_bio_done(sc, bp, EROFS); return; } -#ifdef INVARIANTS - /* - * Prevent read/write buffers spanning too many segments from - * getting into the queue. This should only trip if d_maxsize - * was incorrectly set. - */ - if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { - int nsegs, max_nsegs; + VTBLK_LOCK(sc); - nsegs = sglist_count(bp->bio_data, bp->bio_bcount); - max_nsegs = sc->vtblk_max_nsegs - VTBLK_MIN_SEGMENTS; - - KASSERT(nsegs <= max_nsegs, - ("%s: bio %p spanned too many segments: %d, max: %d", - __func__, bp, nsegs, max_nsegs)); + if (sc->vtblk_flags & VTBLK_FLAG_DETACH) { + VTBLK_UNLOCK(sc); + vtblk_bio_done(sc, bp, ENXIO); + return; } -#endif - VTBLK_LOCK(sc); - if (sc->vtblk_flags & VTBLK_FLAG_DETACH) - vtblk_finish_bio(bp, ENXIO); - else { - bioq_insert_tail(&sc->vtblk_bioq, bp); + bioq_insert_tail(&sc->vtblk_bioq, bp); + vtblk_startio(sc); - if ((sc->vtblk_flags & VTBLK_FLAG_SUSPEND) == 0) - vtblk_startio(sc); - } VTBLK_UNLOCK(sc); } static void vtblk_negotiate_features(struct vtblk_softc *sc) { device_t dev; uint64_t features; dev = sc->vtblk_dev; features = VTBLK_FEATURES; sc->vtblk_features = virtio_negotiate_features(dev, features); } +static void +vtblk_setup_features(struct vtblk_softc *sc) +{ + device_t dev; + + dev = sc->vtblk_dev; + + vtblk_negotiate_features(sc); + + if (virtio_with_feature(dev, VIRTIO_RING_F_INDIRECT_DESC)) + sc->vtblk_flags |= VTBLK_FLAG_INDIRECT; + if (virtio_with_feature(dev, VIRTIO_BLK_F_RO)) + sc->vtblk_flags |= VTBLK_FLAG_READONLY; + if (virtio_with_feature(dev, VIRTIO_BLK_F_BARRIER)) + sc->vtblk_flags |= VTBLK_FLAG_BARRIER; + if (virtio_with_feature(dev, VIRTIO_BLK_F_CONFIG_WCE)) + sc->vtblk_flags |= VTBLK_FLAG_WC_CONFIG; +} + static int vtblk_maximum_segments(struct vtblk_softc *sc, struct virtio_blk_config *blkcfg) { device_t dev; int nsegs; dev = sc->vtblk_dev; nsegs = VTBLK_MIN_SEGMENTS; if (virtio_with_feature(dev, VIRTIO_BLK_F_SEG_MAX)) { nsegs += MIN(blkcfg->seg_max, MAXPHYS / PAGE_SIZE + 1); if (sc->vtblk_flags & VTBLK_FLAG_INDIRECT) nsegs = MIN(nsegs, VIRTIO_MAX_INDIRECT); } else nsegs += 1; return (nsegs); } static int vtblk_alloc_virtqueue(struct vtblk_softc *sc) { device_t dev; struct vq_alloc_info vq_info; dev = sc->vtblk_dev; VQ_ALLOC_INFO_INIT(&vq_info, sc->vtblk_max_nsegs, vtblk_vq_intr, sc, &sc->vtblk_vq, "%s request", device_get_nameunit(dev)); return (virtio_alloc_virtqueues(dev, 0, 1, &vq_info)); } static void vtblk_resize_disk(struct vtblk_softc *sc, uint64_t new_capacity) { device_t dev; struct disk *dp; int error; dev = sc->vtblk_dev; dp = sc->vtblk_disk; dp->d_mediasize = new_capacity; if (bootverbose) { device_printf(dev, "resized to %juMB (%ju %u byte sectors)\n", (uintmax_t) dp->d_mediasize >> 20, (uintmax_t) dp->d_mediasize / dp->d_sectorsize, dp->d_sectorsize); } error = disk_resize(dp, M_NOWAIT); if (error) { device_printf(dev, "disk_resize(9) failed, error: %d\n", error); } } static void -vtblk_set_write_cache(struct vtblk_softc *sc, int wc) -{ - - /* Set either writeback (1) or writethrough (0) mode. */ - virtio_write_dev_config_1(sc->vtblk_dev, - offsetof(struct virtio_blk_config, writeback), wc); -} - -static int -vtblk_write_cache_enabled(struct vtblk_softc *sc, - struct virtio_blk_config *blkcfg) -{ - int wc; - - if (sc->vtblk_flags & VTBLK_FLAG_WC_CONFIG) { - wc = vtblk_tunable_int(sc, "writecache_mode", - vtblk_writecache_mode); - if (wc >= 0 && wc < VTBLK_CACHE_MAX) - vtblk_set_write_cache(sc, wc); - else - wc = blkcfg->writeback; - } else - wc = virtio_with_feature(sc->vtblk_dev, VIRTIO_BLK_F_WCE); - - return (wc); -} - -static int -vtblk_write_cache_sysctl(SYSCTL_HANDLER_ARGS) -{ - struct vtblk_softc *sc; - int wc, error; - - sc = oidp->oid_arg1; - wc = sc->vtblk_write_cache; - - error = sysctl_handle_int(oidp, &wc, 0, req); - if (error || req->newptr == NULL) - return (error); - if ((sc->vtblk_flags & VTBLK_FLAG_WC_CONFIG) == 0) - return (EPERM); - if (wc < 0 || wc >= VTBLK_CACHE_MAX) - return (EINVAL); - - VTBLK_LOCK(sc); - sc->vtblk_write_cache = wc; - vtblk_set_write_cache(sc, sc->vtblk_write_cache); - VTBLK_UNLOCK(sc); - - return (0); -} - -static void vtblk_alloc_disk(struct vtblk_softc *sc, struct virtio_blk_config *blkcfg) { device_t dev; struct disk *dp; dev = sc->vtblk_dev; sc->vtblk_disk = dp = disk_alloc(); dp->d_open = vtblk_open; dp->d_close = vtblk_close; dp->d_ioctl = vtblk_ioctl; dp->d_strategy = vtblk_strategy; dp->d_name = VTBLK_DISK_NAME; dp->d_unit = device_get_unit(dev); dp->d_drv1 = sc; - dp->d_flags = DISKFLAG_CANFLUSHCACHE | DISKFLAG_UNMAPPED_BIO; + dp->d_flags = DISKFLAG_CANFLUSHCACHE | DISKFLAG_UNMAPPED_BIO | + DISKFLAG_DIRECT_COMPLETION; dp->d_hba_vendor = virtio_get_vendor(dev); dp->d_hba_device = virtio_get_device(dev); dp->d_hba_subvendor = virtio_get_subvendor(dev); dp->d_hba_subdevice = virtio_get_subdevice(dev); if ((sc->vtblk_flags & VTBLK_FLAG_READONLY) == 0) dp->d_dump = vtblk_dump; /* Capacity is always in 512-byte units. */ dp->d_mediasize = blkcfg->capacity * 512; if (virtio_with_feature(dev, VIRTIO_BLK_F_BLK_SIZE)) dp->d_sectorsize = blkcfg->blk_size; else dp->d_sectorsize = 512; /* * The VirtIO maximum I/O size is given in terms of segments. * However, FreeBSD limits I/O size by logical buffer size, not * by physically contiguous pages. Therefore, we have to assume * no pages are contiguous. This may impose an artificially low * maximum I/O size. But in practice, since QEMU advertises 128 * segments, this gives us a maximum IO size of 125 * PAGE_SIZE, * which is typically greater than MAXPHYS. Eventually we should * just advertise MAXPHYS and split buffers that are too big. * * Note we must subtract one additional segment in case of non * page aligned buffers. */ dp->d_maxsize = (sc->vtblk_max_nsegs - VTBLK_MIN_SEGMENTS - 1) * PAGE_SIZE; if (dp->d_maxsize < PAGE_SIZE) dp->d_maxsize = PAGE_SIZE; /* XXX */ if (virtio_with_feature(dev, VIRTIO_BLK_F_GEOMETRY)) { dp->d_fwsectors = blkcfg->geometry.sectors; dp->d_fwheads = blkcfg->geometry.heads; } if (virtio_with_feature(dev, VIRTIO_BLK_F_TOPOLOGY)) { dp->d_stripesize = dp->d_sectorsize * (1 << blkcfg->topology.physical_block_exp); dp->d_stripeoffset = (dp->d_stripesize - blkcfg->topology.alignment_offset * dp->d_sectorsize) % dp->d_stripesize; } if (vtblk_write_cache_enabled(sc, blkcfg) != 0) sc->vtblk_write_cache = VTBLK_CACHE_WRITEBACK; else sc->vtblk_write_cache = VTBLK_CACHE_WRITETHROUGH; } static void vtblk_create_disk(struct vtblk_softc *sc) { struct disk *dp; dp = sc->vtblk_disk; - /* - * Retrieving the identification string must be done after - * the virtqueue interrupt is setup otherwise it will hang. - */ - vtblk_get_ident(sc); + vtblk_ident(sc); device_printf(sc->vtblk_dev, "%juMB (%ju %u byte sectors)\n", (uintmax_t) dp->d_mediasize >> 20, (uintmax_t) dp->d_mediasize / dp->d_sectorsize, dp->d_sectorsize); disk_create(dp, DISK_VERSION); } static int -vtblk_quiesce(struct vtblk_softc *sc) +vtblk_request_prealloc(struct vtblk_softc *sc) { - int error; + struct vtblk_request *req; + int i, nreqs; - error = 0; + nreqs = virtqueue_size(sc->vtblk_vq); - VTBLK_LOCK_ASSERT(sc); + /* + * Preallocate sufficient requests to keep the virtqueue full. Each + * request consumes VTBLK_MIN_SEGMENTS or more descriptors so reduce + * the number allocated when indirect descriptors are not available. + */ + if ((sc->vtblk_flags & VTBLK_FLAG_INDIRECT) == 0) + nreqs /= VTBLK_MIN_SEGMENTS; - while (!virtqueue_empty(sc->vtblk_vq)) { - if (mtx_sleep(&sc->vtblk_vq, VTBLK_MTX(sc), PRIBIO, "vtblkq", - VTBLK_QUIESCE_TIMEOUT) == EWOULDBLOCK) { - error = EBUSY; - break; - } + for (i = 0; i < nreqs; i++) { + req = malloc(sizeof(struct vtblk_request), M_DEVBUF, M_NOWAIT); + if (req == NULL) + return (ENOMEM); + + MPASS(sglist_count(&req->vbr_hdr, sizeof(req->vbr_hdr)) == 1); + MPASS(sglist_count(&req->vbr_ack, sizeof(req->vbr_ack)) == 1); + + sc->vtblk_request_count++; + vtblk_request_enqueue(sc, req); } - return (error); + return (0); } static void -vtblk_startio(struct vtblk_softc *sc) +vtblk_request_free(struct vtblk_softc *sc) { - struct virtqueue *vq; struct vtblk_request *req; - int enq; - vq = sc->vtblk_vq; - enq = 0; + MPASS(TAILQ_EMPTY(&sc->vtblk_req_ready)); - VTBLK_LOCK_ASSERT(sc); + while ((req = vtblk_request_dequeue(sc)) != NULL) { + sc->vtblk_request_count--; + free(req, M_DEVBUF); + } - while (!virtqueue_full(vq)) { - if ((req = vtblk_dequeue_ready(sc)) == NULL) - req = vtblk_bio_request(sc); - if (req == NULL) - break; + KASSERT(sc->vtblk_request_count == 0, + ("%s: leaked %d requests", __func__, sc->vtblk_request_count)); +} - if (vtblk_execute_request(sc, req) != 0) { - vtblk_enqueue_ready(sc, req); - break; - } +static struct vtblk_request * +vtblk_request_dequeue(struct vtblk_softc *sc) +{ + struct vtblk_request *req; - enq++; + req = TAILQ_FIRST(&sc->vtblk_req_free); + if (req != NULL) { + TAILQ_REMOVE(&sc->vtblk_req_free, req, vbr_link); + bzero(req, sizeof(struct vtblk_request)); } - if (enq > 0) - virtqueue_notify(vq); + return (req); } +static void +vtblk_request_enqueue(struct vtblk_softc *sc, struct vtblk_request *req) +{ + + TAILQ_INSERT_HEAD(&sc->vtblk_req_free, req, vbr_link); +} + static struct vtblk_request * -vtblk_bio_request(struct vtblk_softc *sc) +vtblk_request_next_ready(struct vtblk_softc *sc) { + struct vtblk_request *req; + + req = TAILQ_FIRST(&sc->vtblk_req_ready); + if (req != NULL) + TAILQ_REMOVE(&sc->vtblk_req_ready, req, vbr_link); + + return (req); +} + +static void +vtblk_request_requeue_ready(struct vtblk_softc *sc, struct vtblk_request *req) +{ + + /* NOTE: Currently, there will be at most one request in the queue. */ + TAILQ_INSERT_HEAD(&sc->vtblk_req_ready, req, vbr_link); +} + +static struct vtblk_request * +vtblk_request_next(struct vtblk_softc *sc) +{ + struct vtblk_request *req; + + req = vtblk_request_next_ready(sc); + if (req != NULL) + return (req); + + return (vtblk_request_bio(sc)); +} + +static struct vtblk_request * +vtblk_request_bio(struct vtblk_softc *sc) +{ struct bio_queue_head *bioq; struct vtblk_request *req; struct bio *bp; bioq = &sc->vtblk_bioq; if (bioq_first(bioq) == NULL) return (NULL); - req = vtblk_dequeue_request(sc); + req = vtblk_request_dequeue(sc); if (req == NULL) return (NULL); bp = bioq_takefirst(bioq); req->vbr_bp = bp; req->vbr_ack = -1; req->vbr_hdr.ioprio = 1; switch (bp->bio_cmd) { case BIO_FLUSH: req->vbr_hdr.type = VIRTIO_BLK_T_FLUSH; break; case BIO_READ: req->vbr_hdr.type = VIRTIO_BLK_T_IN; req->vbr_hdr.sector = bp->bio_offset / 512; break; case BIO_WRITE: req->vbr_hdr.type = VIRTIO_BLK_T_OUT; req->vbr_hdr.sector = bp->bio_offset / 512; break; default: panic("%s: bio with unhandled cmd: %d", __func__, bp->bio_cmd); } + if (bp->bio_flags & BIO_ORDERED) + req->vbr_hdr.type |= VIRTIO_BLK_T_BARRIER; + return (req); } static int -vtblk_execute_request(struct vtblk_softc *sc, struct vtblk_request *req) +vtblk_request_execute(struct vtblk_softc *sc, struct vtblk_request *req) { struct virtqueue *vq; struct sglist *sg; struct bio *bp; int ordered, readable, writable, error; vq = sc->vtblk_vq; sg = sc->vtblk_sglist; bp = req->vbr_bp; ordered = 0; writable = 0; - VTBLK_LOCK_ASSERT(sc); - /* - * Wait until the ordered request completes before - * executing subsequent requests. + * Some hosts (such as bhyve) do not implement the barrier feature, + * so we emulate it in the driver by allowing the barrier request + * to be the only one in flight. */ - if (sc->vtblk_req_ordered != NULL) - return (EBUSY); - - if (bp->bio_flags & BIO_ORDERED) { - if ((sc->vtblk_flags & VTBLK_FLAG_BARRIER) == 0) { - /* - * This request will be executed once all - * the in-flight requests are completed. - */ + if ((sc->vtblk_flags & VTBLK_FLAG_BARRIER) == 0) { + if (sc->vtblk_req_ordered != NULL) + return (EBUSY); + if (bp->bio_flags & BIO_ORDERED) { if (!virtqueue_empty(vq)) return (EBUSY); ordered = 1; - } else - req->vbr_hdr.type |= VIRTIO_BLK_T_BARRIER; + req->vbr_hdr.type &= ~VIRTIO_BLK_T_BARRIER; + } } sglist_reset(sg); sglist_append(sg, &req->vbr_hdr, sizeof(struct virtio_blk_outhdr)); if (bp->bio_cmd == BIO_READ || bp->bio_cmd == BIO_WRITE) { error = sglist_append_bio(sg, bp); if (error || sg->sg_nseg == sg->sg_maxseg) { - panic("%s: data buffer too big bio:%p error:%d", + panic("%s: bio %p data buffer too big %d", __func__, bp, error); } /* BIO_READ means the host writes into our buffer. */ if (bp->bio_cmd == BIO_READ) writable = sg->sg_nseg - 1; } writable++; sglist_append(sg, &req->vbr_ack, sizeof(uint8_t)); readable = sg->sg_nseg - writable; error = virtqueue_enqueue(vq, req, sg, readable, writable); if (error == 0 && ordered) sc->vtblk_req_ordered = req; return (error); } +static int +vtblk_request_error(struct vtblk_request *req) +{ + int error; + + switch (req->vbr_ack) { + case VIRTIO_BLK_S_OK: + error = 0; + break; + case VIRTIO_BLK_S_UNSUPP: + error = ENOTSUP; + break; + default: + error = EIO; + break; + } + + return (error); +} + static void -vtblk_vq_intr(void *xsc) +vtblk_queue_completed(struct vtblk_softc *sc, struct bio_queue *queue) { - struct vtblk_softc *sc; + struct vtblk_request *req; + struct bio *bp; + + while ((req = virtqueue_dequeue(sc->vtblk_vq, NULL)) != NULL) { + if (sc->vtblk_req_ordered != NULL) { + MPASS(sc->vtblk_req_ordered == req); + sc->vtblk_req_ordered = NULL; + } + + bp = req->vbr_bp; + bp->bio_error = vtblk_request_error(req); + TAILQ_INSERT_TAIL(queue, bp, bio_queue); + + vtblk_request_enqueue(sc, req); + } +} + +static void +vtblk_done_completed(struct vtblk_softc *sc, struct bio_queue *queue) +{ + struct bio *bp, *tmp; + + TAILQ_FOREACH_SAFE(bp, queue, bio_queue, tmp) { + if (bp->bio_error != 0) + disk_err(bp, "hard error", -1, 1); + vtblk_bio_done(sc, bp, bp->bio_error); + } +} + +static void +vtblk_drain_vq(struct vtblk_softc *sc, int skip_done) +{ struct virtqueue *vq; + struct vtblk_request *req; + int last; - sc = xsc; vq = sc->vtblk_vq; + last = 0; -again: - VTBLK_LOCK(sc); - if (sc->vtblk_flags & VTBLK_FLAG_DETACH) { - VTBLK_UNLOCK(sc); - return; + while ((req = virtqueue_drain(vq, &last)) != NULL) { + if (!skip_done) + vtblk_bio_done(sc, req->vbr_bp, ENXIO); + + vtblk_request_enqueue(sc, req); } - vtblk_finish_completed(sc); + sc->vtblk_req_ordered = NULL; + KASSERT(virtqueue_empty(vq), ("virtqueue not empty")); +} - if ((sc->vtblk_flags & VTBLK_FLAG_SUSPEND) == 0) - vtblk_startio(sc); - else - wakeup(&sc->vtblk_vq); +static void +vtblk_drain(struct vtblk_softc *sc) +{ + struct bio_queue queue; + struct bio_queue_head *bioq; + struct vtblk_request *req; + struct bio *bp; - if (virtqueue_enable_intr(vq) != 0) { - virtqueue_disable_intr(vq); - VTBLK_UNLOCK(sc); - goto again; + bioq = &sc->vtblk_bioq; + TAILQ_INIT(&queue); + + if (sc->vtblk_vq != NULL) { + vtblk_queue_completed(sc, &queue); + vtblk_done_completed(sc, &queue); + + vtblk_drain_vq(sc, 0); } - VTBLK_UNLOCK(sc); + while ((req = vtblk_request_next_ready(sc)) != NULL) { + vtblk_bio_done(sc, req->vbr_bp, ENXIO); + vtblk_request_enqueue(sc, req); + } + + while (bioq_first(bioq) != NULL) { + bp = bioq_takefirst(bioq); + vtblk_bio_done(sc, bp, ENXIO); + } + + vtblk_request_free(sc); } static void -vtblk_stop(struct vtblk_softc *sc) +vtblk_startio(struct vtblk_softc *sc) { + struct virtqueue *vq; + struct vtblk_request *req; + int enq; - virtqueue_disable_intr(sc->vtblk_vq); - virtio_stop(sc->vtblk_dev); + VTBLK_LOCK_ASSERT(sc); + vq = sc->vtblk_vq; + enq = 0; + + if (sc->vtblk_flags & VTBLK_FLAG_SUSPEND) + return; + + while (!virtqueue_full(vq)) { + req = vtblk_request_next(sc); + if (req == NULL) + break; + + if (vtblk_request_execute(sc, req) != 0) { + vtblk_request_requeue_ready(sc, req); + break; + } + + enq++; + } + + if (enq > 0) + virtqueue_notify(vq); } +static void +vtblk_bio_done(struct vtblk_softc *sc, struct bio *bp, int error) +{ + + /* Because of GEOM direct dispatch, we cannot hold any locks. */ + if (sc != NULL) + VTBLK_LOCK_ASSERT_NOTOWNED(sc); + + if (error) { + bp->bio_resid = bp->bio_bcount; + bp->bio_error = error; + bp->bio_flags |= BIO_ERROR; + } + + biodone(bp); +} + #define VTBLK_GET_CONFIG(_dev, _feature, _field, _cfg) \ if (virtio_with_feature(_dev, _feature)) { \ virtio_read_device_config(_dev, \ offsetof(struct virtio_blk_config, _field), \ &(_cfg)->_field, sizeof((_cfg)->_field)); \ } static void vtblk_read_config(struct vtblk_softc *sc, struct virtio_blk_config *blkcfg) { device_t dev; dev = sc->vtblk_dev; bzero(blkcfg, sizeof(struct virtio_blk_config)); /* The capacity is always available. */ virtio_read_device_config(dev, offsetof(struct virtio_blk_config, capacity), &blkcfg->capacity, sizeof(blkcfg->capacity)); /* Read the configuration if the feature was negotiated. */ VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_SIZE_MAX, size_max, blkcfg); VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_SEG_MAX, seg_max, blkcfg); VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_GEOMETRY, geometry, blkcfg); VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_BLK_SIZE, blk_size, blkcfg); VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_TOPOLOGY, topology, blkcfg); VTBLK_GET_CONFIG(dev, VIRTIO_BLK_F_CONFIG_WCE, writeback, blkcfg); } #undef VTBLK_GET_CONFIG static void -vtblk_get_ident(struct vtblk_softc *sc) +vtblk_ident(struct vtblk_softc *sc) { struct bio buf; struct disk *dp; struct vtblk_request *req; int len, error; dp = sc->vtblk_disk; len = MIN(VIRTIO_BLK_ID_BYTES, DISK_IDENT_SIZE); if (vtblk_tunable_int(sc, "no_ident", vtblk_no_ident) != 0) return; - req = vtblk_dequeue_request(sc); + req = vtblk_request_dequeue(sc); if (req == NULL) return; req->vbr_ack = -1; req->vbr_hdr.type = VIRTIO_BLK_T_GET_ID; req->vbr_hdr.ioprio = 1; req->vbr_hdr.sector = 0; req->vbr_bp = &buf; bzero(&buf, sizeof(struct bio)); buf.bio_cmd = BIO_READ; buf.bio_data = dp->d_ident; buf.bio_bcount = len; VTBLK_LOCK(sc); error = vtblk_poll_request(sc, req); VTBLK_UNLOCK(sc); - vtblk_enqueue_request(sc, req); + vtblk_request_enqueue(sc, req); if (error) { device_printf(sc->vtblk_dev, "error getting device identifier: %d\n", error); } } -static void -vtblk_prepare_dump(struct vtblk_softc *sc) -{ - device_t dev; - struct virtqueue *vq; - - dev = sc->vtblk_dev; - vq = sc->vtblk_vq; - - vtblk_stop(sc); - - /* - * Drain all requests caught in-flight in the virtqueue, - * skipping biodone(). When dumping, only one request is - * outstanding at a time, and we just poll the virtqueue - * for the response. - */ - vtblk_drain_vq(sc, 1); - - if (virtio_reinit(dev, sc->vtblk_features) != 0) { - panic("%s: cannot reinit VirtIO block device during dump", - device_get_nameunit(dev)); - } - - virtqueue_disable_intr(vq); - virtio_reinit_complete(dev); -} - static int -vtblk_write_dump(struct vtblk_softc *sc, void *virtual, off_t offset, - size_t length) -{ - struct bio buf; - struct vtblk_request *req; - - req = &sc->vtblk_dump_request; - req->vbr_ack = -1; - req->vbr_hdr.type = VIRTIO_BLK_T_OUT; - req->vbr_hdr.ioprio = 1; - req->vbr_hdr.sector = offset / 512; - - req->vbr_bp = &buf; - bzero(&buf, sizeof(struct bio)); - - buf.bio_cmd = BIO_WRITE; - buf.bio_data = virtual; - buf.bio_bcount = length; - - return (vtblk_poll_request(sc, req)); -} - -static int -vtblk_flush_dump(struct vtblk_softc *sc) -{ - struct bio buf; - struct vtblk_request *req; - - req = &sc->vtblk_dump_request; - req->vbr_ack = -1; - req->vbr_hdr.type = VIRTIO_BLK_T_FLUSH; - req->vbr_hdr.ioprio = 1; - req->vbr_hdr.sector = 0; - - req->vbr_bp = &buf; - bzero(&buf, sizeof(struct bio)); - - buf.bio_cmd = BIO_FLUSH; - - return (vtblk_poll_request(sc, req)); -} - -static int vtblk_poll_request(struct vtblk_softc *sc, struct vtblk_request *req) { struct virtqueue *vq; int error; vq = sc->vtblk_vq; if (!virtqueue_empty(vq)) return (EBUSY); - error = vtblk_execute_request(sc, req); + error = vtblk_request_execute(sc, req); if (error) return (error); virtqueue_notify(vq); virtqueue_poll(vq, NULL); error = vtblk_request_error(req); if (error && bootverbose) { device_printf(sc->vtblk_dev, "%s: IO error: %d\n", __func__, error); } return (error); } -static void -vtblk_finish_completed(struct vtblk_softc *sc) +static int +vtblk_quiesce(struct vtblk_softc *sc) { - struct vtblk_request *req; - struct bio *bp; int error; - while ((req = virtqueue_dequeue(sc->vtblk_vq, NULL)) != NULL) { - bp = req->vbr_bp; + VTBLK_LOCK_ASSERT(sc); + error = 0; - if (sc->vtblk_req_ordered != NULL) { - /* This should be the only outstanding request. */ - MPASS(sc->vtblk_req_ordered == req); - sc->vtblk_req_ordered = NULL; + while (!virtqueue_empty(sc->vtblk_vq)) { + if (mtx_sleep(&sc->vtblk_vq, VTBLK_MTX(sc), PRIBIO, "vtblkq", + VTBLK_QUIESCE_TIMEOUT) == EWOULDBLOCK) { + error = EBUSY; + break; } - - error = vtblk_request_error(req); - if (error) - disk_err(bp, "hard error", -1, 1); - - vtblk_finish_bio(bp, error); - vtblk_enqueue_request(sc, req); } + + return (error); } static void -vtblk_drain_vq(struct vtblk_softc *sc, int skip_done) +vtblk_vq_intr(void *xsc) { + struct vtblk_softc *sc; struct virtqueue *vq; - struct vtblk_request *req; - int last; + struct bio_queue queue; + sc = xsc; vq = sc->vtblk_vq; - last = 0; + TAILQ_INIT(&queue); - while ((req = virtqueue_drain(vq, &last)) != NULL) { - if (!skip_done) - vtblk_finish_bio(req->vbr_bp, ENXIO); + VTBLK_LOCK(sc); - vtblk_enqueue_request(sc, req); - } +again: + if (sc->vtblk_flags & VTBLK_FLAG_DETACH) + goto out; - sc->vtblk_req_ordered = NULL; - KASSERT(virtqueue_empty(vq), ("virtqueue not empty")); -} + vtblk_queue_completed(sc, &queue); + vtblk_startio(sc); -static void -vtblk_drain(struct vtblk_softc *sc) -{ - struct bio_queue_head *bioq; - struct vtblk_request *req; - struct bio *bp; - - bioq = &sc->vtblk_bioq; - - if (sc->vtblk_vq != NULL) { - vtblk_finish_completed(sc); - vtblk_drain_vq(sc, 0); + if (virtqueue_enable_intr(vq) != 0) { + virtqueue_disable_intr(vq); + goto again; } - while ((req = vtblk_dequeue_ready(sc)) != NULL) { - vtblk_finish_bio(req->vbr_bp, ENXIO); - vtblk_enqueue_request(sc, req); - } + if (sc->vtblk_flags & VTBLK_FLAG_SUSPEND) + wakeup(&sc->vtblk_vq); - while (bioq_first(bioq) != NULL) { - bp = bioq_takefirst(bioq); - vtblk_finish_bio(bp, ENXIO); - } - - vtblk_free_requests(sc); +out: + VTBLK_UNLOCK(sc); + vtblk_done_completed(sc, &queue); } -#ifdef INVARIANTS static void -vtblk_request_invariants(struct vtblk_request *req) +vtblk_stop(struct vtblk_softc *sc) { - int hdr_nsegs, ack_nsegs; - hdr_nsegs = sglist_count(&req->vbr_hdr, sizeof(req->vbr_hdr)); - ack_nsegs = sglist_count(&req->vbr_ack, sizeof(req->vbr_ack)); - - KASSERT(hdr_nsegs == 1, ("request header crossed page boundary")); - KASSERT(ack_nsegs == 1, ("request ack crossed page boundary")); + virtqueue_disable_intr(sc->vtblk_vq); + virtio_stop(sc->vtblk_dev); } -#endif -static int -vtblk_alloc_requests(struct vtblk_softc *sc) +static void +vtblk_dump_prepare(struct vtblk_softc *sc) { - struct vtblk_request *req; - int i, nreqs; + device_t dev; + struct virtqueue *vq; - nreqs = virtqueue_size(sc->vtblk_vq); + dev = sc->vtblk_dev; + vq = sc->vtblk_vq; + vtblk_stop(sc); + /* - * Preallocate sufficient requests to keep the virtqueue full. Each - * request consumes VTBLK_MIN_SEGMENTS or more descriptors so reduce - * the number allocated when indirect descriptors are not available. + * Drain all requests caught in-flight in the virtqueue, + * skipping biodone(). When dumping, only one request is + * outstanding at a time, and we just poll the virtqueue + * for the response. */ - if ((sc->vtblk_flags & VTBLK_FLAG_INDIRECT) == 0) - nreqs /= VTBLK_MIN_SEGMENTS; + vtblk_drain_vq(sc, 1); - for (i = 0; i < nreqs; i++) { - req = malloc(sizeof(struct vtblk_request), M_DEVBUF, M_NOWAIT); - if (req == NULL) - return (ENOMEM); - -#ifdef INVARIANTS - vtblk_request_invariants(req); -#endif - - sc->vtblk_request_count++; - vtblk_enqueue_request(sc, req); + if (virtio_reinit(dev, sc->vtblk_features) != 0) { + panic("%s: cannot reinit VirtIO block device during dump", + device_get_nameunit(dev)); } - return (0); + virtqueue_disable_intr(vq); + virtio_reinit_complete(dev); } -static void -vtblk_free_requests(struct vtblk_softc *sc) +static int +vtblk_dump_write(struct vtblk_softc *sc, void *virtual, off_t offset, + size_t length) { + struct bio buf; struct vtblk_request *req; - KASSERT(TAILQ_EMPTY(&sc->vtblk_req_ready), - ("%s: ready requests left on queue", __func__)); + req = &sc->vtblk_dump_request; + req->vbr_ack = -1; + req->vbr_hdr.type = VIRTIO_BLK_T_OUT; + req->vbr_hdr.ioprio = 1; + req->vbr_hdr.sector = offset / 512; - while ((req = vtblk_dequeue_request(sc)) != NULL) { - sc->vtblk_request_count--; - free(req, M_DEVBUF); - } + req->vbr_bp = &buf; + bzero(&buf, sizeof(struct bio)); - KASSERT(sc->vtblk_request_count == 0, - ("%s: leaked %d requests", __func__, sc->vtblk_request_count)); + buf.bio_cmd = BIO_WRITE; + buf.bio_data = virtual; + buf.bio_bcount = length; + + return (vtblk_poll_request(sc, req)); } -static struct vtblk_request * -vtblk_dequeue_request(struct vtblk_softc *sc) +static int +vtblk_dump_flush(struct vtblk_softc *sc) { + struct bio buf; struct vtblk_request *req; - req = TAILQ_FIRST(&sc->vtblk_req_free); - if (req != NULL) - TAILQ_REMOVE(&sc->vtblk_req_free, req, vbr_link); + req = &sc->vtblk_dump_request; + req->vbr_ack = -1; + req->vbr_hdr.type = VIRTIO_BLK_T_FLUSH; + req->vbr_hdr.ioprio = 1; + req->vbr_hdr.sector = 0; - return (req); -} + req->vbr_bp = &buf; + bzero(&buf, sizeof(struct bio)); -static void -vtblk_enqueue_request(struct vtblk_softc *sc, struct vtblk_request *req) -{ + buf.bio_cmd = BIO_FLUSH; - bzero(req, sizeof(struct vtblk_request)); - TAILQ_INSERT_HEAD(&sc->vtblk_req_free, req, vbr_link); + return (vtblk_poll_request(sc, req)); } -static struct vtblk_request * -vtblk_dequeue_ready(struct vtblk_softc *sc) -{ - struct vtblk_request *req; - - req = TAILQ_FIRST(&sc->vtblk_req_ready); - if (req != NULL) - TAILQ_REMOVE(&sc->vtblk_req_ready, req, vbr_link); - - return (req); -} - static void -vtblk_enqueue_ready(struct vtblk_softc *sc, struct vtblk_request *req) +vtblk_set_write_cache(struct vtblk_softc *sc, int wc) { - TAILQ_INSERT_HEAD(&sc->vtblk_req_ready, req, vbr_link); + /* Set either writeback (1) or writethrough (0) mode. */ + virtio_write_dev_config_1(sc->vtblk_dev, + offsetof(struct virtio_blk_config, writeback), wc); } static int -vtblk_request_error(struct vtblk_request *req) +vtblk_write_cache_enabled(struct vtblk_softc *sc, + struct virtio_blk_config *blkcfg) { - int error; + int wc; - switch (req->vbr_ack) { - case VIRTIO_BLK_S_OK: - error = 0; - break; - case VIRTIO_BLK_S_UNSUPP: - error = ENOTSUP; - break; - default: - error = EIO; - break; - } + if (sc->vtblk_flags & VTBLK_FLAG_WC_CONFIG) { + wc = vtblk_tunable_int(sc, "writecache_mode", + vtblk_writecache_mode); + if (wc >= 0 && wc < VTBLK_CACHE_MAX) + vtblk_set_write_cache(sc, wc); + else + wc = blkcfg->writeback; + } else + wc = virtio_with_feature(sc->vtblk_dev, VIRTIO_BLK_F_WCE); - return (error); + return (wc); } -static void -vtblk_finish_bio(struct bio *bp, int error) +static int +vtblk_write_cache_sysctl(SYSCTL_HANDLER_ARGS) { + struct vtblk_softc *sc; + int wc, error; - if (error) { - bp->bio_resid = bp->bio_bcount; - bp->bio_error = error; - bp->bio_flags |= BIO_ERROR; - } + sc = oidp->oid_arg1; + wc = sc->vtblk_write_cache; - biodone(bp); + error = sysctl_handle_int(oidp, &wc, 0, req); + if (error || req->newptr == NULL) + return (error); + if ((sc->vtblk_flags & VTBLK_FLAG_WC_CONFIG) == 0) + return (EPERM); + if (wc < 0 || wc >= VTBLK_CACHE_MAX) + return (EINVAL); + + VTBLK_LOCK(sc); + sc->vtblk_write_cache = wc; + vtblk_set_write_cache(sc, sc->vtblk_write_cache); + VTBLK_UNLOCK(sc); + + return (0); } static void vtblk_setup_sysctl(struct vtblk_softc *sc) { device_t dev; struct sysctl_ctx_list *ctx; struct sysctl_oid *tree; struct sysctl_oid_list *child; dev = sc->vtblk_dev; ctx = device_get_sysctl_ctx(dev); tree = device_get_sysctl_tree(dev); child = SYSCTL_CHILDREN(tree); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "writecache_mode", CTLTYPE_INT | CTLFLAG_RW, sc, 0, vtblk_write_cache_sysctl, "I", "Write cache mode (writethrough (0) or writeback (1))"); } static int vtblk_tunable_int(struct vtblk_softc *sc, const char *knob, int def) { char path[64]; snprintf(path, sizeof(path), "hw.vtblk.%d.%s", device_get_unit(sc->vtblk_dev), knob); TUNABLE_INT_FETCH(path, &def); return (def); } Index: projects/sendfile/sys/kern/subr_taskqueue.c =================================================================== --- projects/sendfile/sys/kern/subr_taskqueue.c (revision 275355) +++ projects/sendfile/sys/kern/subr_taskqueue.c (revision 275356) @@ -1,721 +1,721 @@ /*- * Copyright (c) 2000 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. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static MALLOC_DEFINE(M_TASKQUEUE, "taskqueue", "Task Queues"); static void *taskqueue_giant_ih; static void *taskqueue_ih; static void taskqueue_fast_enqueue(void *); static void taskqueue_swi_enqueue(void *); static void taskqueue_swi_giant_enqueue(void *); struct taskqueue_busy { struct task *tb_running; TAILQ_ENTRY(taskqueue_busy) tb_link; }; struct taskqueue { STAILQ_HEAD(, task) tq_queue; taskqueue_enqueue_fn tq_enqueue; void *tq_context; TAILQ_HEAD(, taskqueue_busy) tq_active; struct mtx tq_mutex; struct thread **tq_threads; int tq_tcount; int tq_spin; int tq_flags; int tq_callouts; taskqueue_callback_fn tq_callbacks[TASKQUEUE_NUM_CALLBACKS]; void *tq_cb_contexts[TASKQUEUE_NUM_CALLBACKS]; }; #define TQ_FLAGS_ACTIVE (1 << 0) #define TQ_FLAGS_BLOCKED (1 << 1) #define TQ_FLAGS_UNLOCKED_ENQUEUE (1 << 2) #define DT_CALLOUT_ARMED (1 << 0) #define TQ_LOCK(tq) \ do { \ if ((tq)->tq_spin) \ mtx_lock_spin(&(tq)->tq_mutex); \ else \ mtx_lock(&(tq)->tq_mutex); \ } while (0) #define TQ_ASSERT_LOCKED(tq) mtx_assert(&(tq)->tq_mutex, MA_OWNED) #define TQ_UNLOCK(tq) \ do { \ if ((tq)->tq_spin) \ mtx_unlock_spin(&(tq)->tq_mutex); \ else \ mtx_unlock(&(tq)->tq_mutex); \ } while (0) #define TQ_ASSERT_UNLOCKED(tq) mtx_assert(&(tq)->tq_mutex, MA_NOTOWNED) void _timeout_task_init(struct taskqueue *queue, struct timeout_task *timeout_task, int priority, task_fn_t func, void *context) { TASK_INIT(&timeout_task->t, priority, func, context); callout_init_mtx(&timeout_task->c, &queue->tq_mutex, CALLOUT_RETURNUNLOCKED); timeout_task->q = queue; timeout_task->f = 0; } static __inline int TQ_SLEEP(struct taskqueue *tq, void *p, struct mtx *m, int pri, const char *wm, int t) { if (tq->tq_spin) return (msleep_spin(p, m, wm, t)); return (msleep(p, m, pri, wm, t)); } static struct taskqueue * _taskqueue_create(const char *name __unused, int mflags, taskqueue_enqueue_fn enqueue, void *context, int mtxflags, const char *mtxname) { struct taskqueue *queue; queue = malloc(sizeof(struct taskqueue), M_TASKQUEUE, mflags | M_ZERO); if (!queue) return NULL; STAILQ_INIT(&queue->tq_queue); TAILQ_INIT(&queue->tq_active); queue->tq_enqueue = enqueue; queue->tq_context = context; queue->tq_spin = (mtxflags & MTX_SPIN) != 0; queue->tq_flags |= TQ_FLAGS_ACTIVE; if (enqueue == taskqueue_fast_enqueue || enqueue == taskqueue_swi_enqueue || enqueue == taskqueue_swi_giant_enqueue || enqueue == taskqueue_thread_enqueue) queue->tq_flags |= TQ_FLAGS_UNLOCKED_ENQUEUE; mtx_init(&queue->tq_mutex, mtxname, NULL, mtxflags); return queue; } struct taskqueue * taskqueue_create(const char *name, int mflags, taskqueue_enqueue_fn enqueue, void *context) { return _taskqueue_create(name, mflags, enqueue, context, MTX_DEF, "taskqueue"); } void taskqueue_set_callback(struct taskqueue *queue, enum taskqueue_callback_type cb_type, taskqueue_callback_fn callback, void *context) { KASSERT(((cb_type >= TASKQUEUE_CALLBACK_TYPE_MIN) && (cb_type <= TASKQUEUE_CALLBACK_TYPE_MAX)), ("Callback type %d not valid, must be %d-%d", cb_type, TASKQUEUE_CALLBACK_TYPE_MIN, TASKQUEUE_CALLBACK_TYPE_MAX)); KASSERT((queue->tq_callbacks[cb_type] == NULL), ("Re-initialization of taskqueue callback?")); queue->tq_callbacks[cb_type] = callback; queue->tq_cb_contexts[cb_type] = context; } /* * Signal a taskqueue thread to terminate. */ static void taskqueue_terminate(struct thread **pp, struct taskqueue *tq) { while (tq->tq_tcount > 0 || tq->tq_callouts > 0) { wakeup(tq); TQ_SLEEP(tq, pp, &tq->tq_mutex, PWAIT, "taskqueue_destroy", 0); } } void taskqueue_free(struct taskqueue *queue) { TQ_LOCK(queue); queue->tq_flags &= ~TQ_FLAGS_ACTIVE; taskqueue_terminate(queue->tq_threads, queue); KASSERT(TAILQ_EMPTY(&queue->tq_active), ("Tasks still running?")); KASSERT(queue->tq_callouts == 0, ("Armed timeout tasks")); mtx_destroy(&queue->tq_mutex); free(queue->tq_threads, M_TASKQUEUE); free(queue, M_TASKQUEUE); } static int taskqueue_enqueue_locked(struct taskqueue *queue, struct task *task) { struct task *ins; struct task *prev; /* * Count multiple enqueues. */ if (task->ta_pending) { if (task->ta_pending < USHRT_MAX) task->ta_pending++; TQ_UNLOCK(queue); return (0); } /* * Optimise the case when all tasks have the same priority. */ prev = STAILQ_LAST(&queue->tq_queue, task, ta_link); if (!prev || prev->ta_priority >= task->ta_priority) { STAILQ_INSERT_TAIL(&queue->tq_queue, task, ta_link); } else { prev = NULL; for (ins = STAILQ_FIRST(&queue->tq_queue); ins; prev = ins, ins = STAILQ_NEXT(ins, ta_link)) if (ins->ta_priority < task->ta_priority) break; if (prev) STAILQ_INSERT_AFTER(&queue->tq_queue, prev, task, ta_link); else STAILQ_INSERT_HEAD(&queue->tq_queue, task, ta_link); } task->ta_pending = 1; if ((queue->tq_flags & TQ_FLAGS_UNLOCKED_ENQUEUE) != 0) TQ_UNLOCK(queue); if ((queue->tq_flags & TQ_FLAGS_BLOCKED) == 0) queue->tq_enqueue(queue->tq_context); if ((queue->tq_flags & TQ_FLAGS_UNLOCKED_ENQUEUE) == 0) TQ_UNLOCK(queue); /* Return with lock released. */ return (0); } int taskqueue_enqueue(struct taskqueue *queue, struct task *task) { int res; TQ_LOCK(queue); res = taskqueue_enqueue_locked(queue, task); /* The lock is released inside. */ return (res); } static void taskqueue_timeout_func(void *arg) { struct taskqueue *queue; struct timeout_task *timeout_task; timeout_task = arg; queue = timeout_task->q; KASSERT((timeout_task->f & DT_CALLOUT_ARMED) != 0, ("Stray timeout")); timeout_task->f &= ~DT_CALLOUT_ARMED; queue->tq_callouts--; taskqueue_enqueue_locked(timeout_task->q, &timeout_task->t); /* The lock is released inside. */ } int taskqueue_enqueue_timeout(struct taskqueue *queue, struct timeout_task *timeout_task, int ticks) { int res; TQ_LOCK(queue); KASSERT(timeout_task->q == NULL || timeout_task->q == queue, ("Migrated queue")); KASSERT(!queue->tq_spin, ("Timeout for spin-queue")); timeout_task->q = queue; res = timeout_task->t.ta_pending; if (ticks == 0) { taskqueue_enqueue_locked(queue, &timeout_task->t); /* The lock is released inside. */ } else { if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) { res++; } else { queue->tq_callouts++; timeout_task->f |= DT_CALLOUT_ARMED; if (ticks < 0) ticks = -ticks; /* Ignore overflow. */ } if (ticks > 0) { callout_reset(&timeout_task->c, ticks, taskqueue_timeout_func, timeout_task); } TQ_UNLOCK(queue); } return (res); } static void taskqueue_drain_running(struct taskqueue *queue) { while (!TAILQ_EMPTY(&queue->tq_active)) TQ_SLEEP(queue, &queue->tq_active, &queue->tq_mutex, PWAIT, "-", 0); } void taskqueue_block(struct taskqueue *queue) { TQ_LOCK(queue); queue->tq_flags |= TQ_FLAGS_BLOCKED; TQ_UNLOCK(queue); } void taskqueue_unblock(struct taskqueue *queue) { TQ_LOCK(queue); queue->tq_flags &= ~TQ_FLAGS_BLOCKED; if (!STAILQ_EMPTY(&queue->tq_queue)) queue->tq_enqueue(queue->tq_context); TQ_UNLOCK(queue); } static void taskqueue_run_locked(struct taskqueue *queue) { struct taskqueue_busy tb; struct task *task; int pending; TQ_ASSERT_LOCKED(queue); tb.tb_running = NULL; TAILQ_INSERT_TAIL(&queue->tq_active, &tb, tb_link); while (STAILQ_FIRST(&queue->tq_queue)) { /* * Carefully remove the first task from the queue and * zero its pending count. */ task = STAILQ_FIRST(&queue->tq_queue); STAILQ_REMOVE_HEAD(&queue->tq_queue, ta_link); pending = task->ta_pending; task->ta_pending = 0; tb.tb_running = task; TQ_UNLOCK(queue); task->ta_func(task->ta_context, pending); TQ_LOCK(queue); tb.tb_running = NULL; wakeup(task); } TAILQ_REMOVE(&queue->tq_active, &tb, tb_link); if (TAILQ_EMPTY(&queue->tq_active)) wakeup(&queue->tq_active); } void taskqueue_run(struct taskqueue *queue) { TQ_LOCK(queue); taskqueue_run_locked(queue); TQ_UNLOCK(queue); } static int task_is_running(struct taskqueue *queue, struct task *task) { struct taskqueue_busy *tb; TQ_ASSERT_LOCKED(queue); TAILQ_FOREACH(tb, &queue->tq_active, tb_link) { if (tb->tb_running == task) return (1); } return (0); } static int taskqueue_cancel_locked(struct taskqueue *queue, struct task *task, u_int *pendp) { if (task->ta_pending > 0) STAILQ_REMOVE(&queue->tq_queue, task, task, ta_link); if (pendp != NULL) *pendp = task->ta_pending; task->ta_pending = 0; return (task_is_running(queue, task) ? EBUSY : 0); } int taskqueue_cancel(struct taskqueue *queue, struct task *task, u_int *pendp) { int error; TQ_LOCK(queue); error = taskqueue_cancel_locked(queue, task, pendp); TQ_UNLOCK(queue); return (error); } int taskqueue_cancel_timeout(struct taskqueue *queue, struct timeout_task *timeout_task, u_int *pendp) { u_int pending, pending1; int error; TQ_LOCK(queue); pending = !!callout_stop(&timeout_task->c); error = taskqueue_cancel_locked(queue, &timeout_task->t, &pending1); if ((timeout_task->f & DT_CALLOUT_ARMED) != 0) { timeout_task->f &= ~DT_CALLOUT_ARMED; queue->tq_callouts--; } TQ_UNLOCK(queue); if (pendp != NULL) *pendp = pending + pending1; return (error); } void taskqueue_drain(struct taskqueue *queue, struct task *task) { if (!queue->tq_spin) WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, __func__); TQ_LOCK(queue); while (task->ta_pending != 0 || task_is_running(queue, task)) TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0); TQ_UNLOCK(queue); } void taskqueue_drain_all(struct taskqueue *queue) { struct task *task; if (!queue->tq_spin) WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, __func__); TQ_LOCK(queue); task = STAILQ_LAST(&queue->tq_queue, task, ta_link); if (task != NULL) while (task->ta_pending != 0) TQ_SLEEP(queue, task, &queue->tq_mutex, PWAIT, "-", 0); taskqueue_drain_running(queue); KASSERT(STAILQ_EMPTY(&queue->tq_queue), ("taskqueue queue is not empty after draining")); TQ_UNLOCK(queue); } void taskqueue_drain_timeout(struct taskqueue *queue, struct timeout_task *timeout_task) { callout_drain(&timeout_task->c); taskqueue_drain(queue, &timeout_task->t); } static void taskqueue_swi_enqueue(void *context) { swi_sched(taskqueue_ih, 0); } static void taskqueue_swi_run(void *dummy) { taskqueue_run(taskqueue_swi); } static void taskqueue_swi_giant_enqueue(void *context) { swi_sched(taskqueue_giant_ih, 0); } static void taskqueue_swi_giant_run(void *dummy) { taskqueue_run(taskqueue_swi_giant); } static int _taskqueue_start_threads(struct taskqueue **tqp, int count, int pri, cpuset_t *mask, const char *ktname) { struct thread *td; struct taskqueue *tq; int i, error; if (count <= 0) return (EINVAL); tq = *tqp; tq->tq_threads = malloc(sizeof(struct thread *) * count, M_TASKQUEUE, M_NOWAIT | M_ZERO); if (tq->tq_threads == NULL) { printf("%s: no memory for %s threads\n", __func__, ktname); return (ENOMEM); } for (i = 0; i < count; i++) { if (count == 1) error = kthread_add(taskqueue_thread_loop, tqp, NULL, &tq->tq_threads[i], RFSTOPPED, 0, "%s", ktname); else error = kthread_add(taskqueue_thread_loop, tqp, NULL, &tq->tq_threads[i], RFSTOPPED, 0, "%s_%d", ktname, i); if (error) { /* should be ok to continue, taskqueue_free will dtrt */ printf("%s: kthread_add(%s): error %d", __func__, ktname, error); tq->tq_threads[i] = NULL; /* paranoid */ } else tq->tq_tcount++; } for (i = 0; i < count; i++) { if (tq->tq_threads[i] == NULL) continue; td = tq->tq_threads[i]; if (mask) { error = cpuset_setthread(td->td_tid, mask); /* * Failing to pin is rarely an actual fatal error; * it'll just affect performance. */ if (error) printf("%s: curthread=%llu: can't pin; " "error=%d\n", __func__, (unsigned long long) td->td_tid, error); } thread_lock(td); sched_prio(td, pri); sched_add(td, SRQ_BORING); thread_unlock(td); } return (0); } int taskqueue_start_threads(struct taskqueue **tqp, int count, int pri, const char *name, ...) { char ktname[MAXCOMLEN + 1]; va_list ap; va_start(ap, name); vsnprintf(ktname, sizeof(ktname), name, ap); va_end(ap); return (_taskqueue_start_threads(tqp, count, pri, NULL, ktname)); } int taskqueue_start_threads_pinned(struct taskqueue **tqp, int count, int pri, int cpu_id, const char *name, ...) { char ktname[MAXCOMLEN + 1]; va_list ap; cpuset_t mask; va_start(ap, name); vsnprintf(ktname, sizeof(ktname), name, ap); va_end(ap); /* * In case someone passes in NOCPU, just fall back to the * default behaviour of "don't pin". */ if (cpu_id != NOCPU) { CPU_ZERO(&mask); CPU_SET(cpu_id, &mask); } return (_taskqueue_start_threads(tqp, count, pri, cpu_id == NOCPU ? NULL : &mask, ktname)); } static inline void taskqueue_run_callback(struct taskqueue *tq, enum taskqueue_callback_type cb_type) { taskqueue_callback_fn tq_callback; TQ_ASSERT_UNLOCKED(tq); tq_callback = tq->tq_callbacks[cb_type]; if (tq_callback != NULL) tq_callback(tq->tq_cb_contexts[cb_type]); } void taskqueue_thread_loop(void *arg) { struct taskqueue **tqp, *tq; tqp = arg; tq = *tqp; taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_INIT); TQ_LOCK(tq); while ((tq->tq_flags & TQ_FLAGS_ACTIVE) != 0) { taskqueue_run_locked(tq); /* * Because taskqueue_run() can drop tq_mutex, we need to * check if the TQ_FLAGS_ACTIVE flag wasn't removed in the * meantime, which means we missed a wakeup. */ if ((tq->tq_flags & TQ_FLAGS_ACTIVE) == 0) break; TQ_SLEEP(tq, tq, &tq->tq_mutex, 0, "-", 0); } taskqueue_run_locked(tq); /* * This thread is on its way out, so just drop the lock temporarily * in order to call the shutdown callback. This allows the callback * to look at the taskqueue, even just before it dies. */ TQ_UNLOCK(tq); taskqueue_run_callback(tq, TASKQUEUE_CALLBACK_TYPE_SHUTDOWN); TQ_LOCK(tq); /* rendezvous with thread that asked us to terminate */ tq->tq_tcount--; wakeup_one(tq->tq_threads); TQ_UNLOCK(tq); kthread_exit(); } void taskqueue_thread_enqueue(void *context) { struct taskqueue **tqp, *tq; tqp = context; tq = *tqp; wakeup_one(tq); } TASKQUEUE_DEFINE(swi, taskqueue_swi_enqueue, NULL, swi_add(NULL, "task queue", taskqueue_swi_run, NULL, SWI_TQ, - INTR_MPSAFE, &taskqueue_ih)); + INTR_MPSAFE, &taskqueue_ih)); TASKQUEUE_DEFINE(swi_giant, taskqueue_swi_giant_enqueue, NULL, swi_add(NULL, "Giant taskq", taskqueue_swi_giant_run, - NULL, SWI_TQ_GIANT, 0, &taskqueue_giant_ih)); + NULL, SWI_TQ_GIANT, 0, &taskqueue_giant_ih)); TASKQUEUE_DEFINE_THREAD(thread); struct taskqueue * taskqueue_create_fast(const char *name, int mflags, taskqueue_enqueue_fn enqueue, void *context) { return _taskqueue_create(name, mflags, enqueue, context, MTX_SPIN, "fast_taskqueue"); } /* NB: for backwards compatibility */ int taskqueue_enqueue_fast(struct taskqueue *queue, struct task *task) { return taskqueue_enqueue(queue, task); } static void *taskqueue_fast_ih; static void taskqueue_fast_enqueue(void *context) { swi_sched(taskqueue_fast_ih, 0); } static void taskqueue_fast_run(void *dummy) { taskqueue_run(taskqueue_fast); } TASKQUEUE_FAST_DEFINE(fast, taskqueue_fast_enqueue, NULL, swi_add(NULL, "fast taskq", taskqueue_fast_run, NULL, SWI_TQ_FAST, INTR_MPSAFE, &taskqueue_fast_ih)); int taskqueue_member(struct taskqueue *queue, struct thread *td) { int i, j, ret = 0; for (i = 0, j = 0; ; i++) { if (queue->tq_threads[i] == NULL) continue; if (queue->tq_threads[i] == td) { ret = 1; break; } if (++j >= queue->tq_tcount) break; } return (ret); } Index: projects/sendfile/sys/vm/uma_core.c =================================================================== --- projects/sendfile/sys/vm/uma_core.c (revision 275355) +++ projects/sendfile/sys/vm/uma_core.c (revision 275356) @@ -1,3598 +1,3627 @@ /*- * Copyright (c) 2002-2005, 2009, 2013 Jeffrey Roberson * Copyright (c) 2004, 2005 Bosko Milekic * Copyright (c) 2004-2006 Robert N. M. Watson * 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 unmodified, 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. */ /* * uma_core.c Implementation of the Universal Memory allocator * * This allocator is intended to replace the multitude of similar object caches * in the standard FreeBSD kernel. The intent is to be flexible as well as * effecient. A primary design goal is to return unused memory to the rest of * the system. This will make the system as a whole more flexible due to the * ability to move memory to subsystems which most need it instead of leaving * pools of reserved memory unused. * * The basic ideas stem from similar slab/zone based allocators whose algorithms * are well known. * */ /* * TODO: * - Improve memory usage for large allocations * - Investigate cache size adjustments */ #include __FBSDID("$FreeBSD$"); /* I should really use ktr.. */ /* #define UMA_DEBUG 1 #define UMA_DEBUG_ALLOC 1 #define UMA_DEBUG_ALLOC_1 1 */ #include "opt_ddb.h" #include "opt_param.h" #include "opt_vm.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DEBUG_MEMGUARD #include #endif /* * This is the zone and keg from which all zones are spawned. The idea is that * even the zone & keg heads are allocated from the allocator, so we use the * bss section to bootstrap us. */ static struct uma_keg masterkeg; static struct uma_zone masterzone_k; static struct uma_zone masterzone_z; static uma_zone_t kegs = &masterzone_k; static uma_zone_t zones = &masterzone_z; /* This is the zone from which all of uma_slab_t's are allocated. */ static uma_zone_t slabzone; static uma_zone_t slabrefzone; /* With refcounters (for UMA_ZONE_REFCNT) */ /* * The initial hash tables come out of this zone so they can be allocated * prior to malloc coming up. */ static uma_zone_t hashzone; /* The boot-time adjusted value for cache line alignment. */ int uma_align_cache = 64 - 1; static MALLOC_DEFINE(M_UMAHASH, "UMAHash", "UMA Hash Buckets"); /* * Are we allowed to allocate buckets? */ static int bucketdisable = 1; /* Linked list of all kegs in the system */ static LIST_HEAD(,uma_keg) uma_kegs = LIST_HEAD_INITIALIZER(uma_kegs); /* Linked list of all cache-only zones in the system */ static LIST_HEAD(,uma_zone) uma_cachezones = LIST_HEAD_INITIALIZER(uma_cachezones); /* This RW lock protects the keg list */ static struct rwlock_padalign uma_rwlock; /* Linked list of boot time pages */ static LIST_HEAD(,uma_slab) uma_boot_pages = LIST_HEAD_INITIALIZER(uma_boot_pages); /* This mutex protects the boot time pages list */ static struct mtx_padalign uma_boot_pages_mtx; +static struct sx uma_drain_lock; + /* Is the VM done starting up? */ static int booted = 0; #define UMA_STARTUP 1 #define UMA_STARTUP2 2 /* * Only mbuf clusters use ref zones. Just provide enough references * to support the one user. New code should not use the ref facility. */ static const u_int uma_max_ipers_ref = PAGE_SIZE / MCLBYTES; /* * This is the handle used to schedule events that need to happen * outside of the allocation fast path. */ static struct callout uma_callout; #define UMA_TIMEOUT 20 /* Seconds for callout interval. */ /* * This structure is passed as the zone ctor arg so that I don't have to create * a special allocation function just for zones. */ struct uma_zctor_args { const char *name; size_t size; uma_ctor ctor; uma_dtor dtor; uma_init uminit; uma_fini fini; uma_import import; uma_release release; void *arg; uma_keg_t keg; int align; uint32_t flags; }; struct uma_kctor_args { uma_zone_t zone; size_t size; uma_init uminit; uma_fini fini; int align; uint32_t flags; }; struct uma_bucket_zone { uma_zone_t ubz_zone; char *ubz_name; int ubz_entries; /* Number of items it can hold. */ int ubz_maxsize; /* Maximum allocation size per-item. */ }; /* * Compute the actual number of bucket entries to pack them in power * of two sizes for more efficient space utilization. */ #define BUCKET_SIZE(n) \ (((sizeof(void *) * (n)) - sizeof(struct uma_bucket)) / sizeof(void *)) #define BUCKET_MAX BUCKET_SIZE(256) struct uma_bucket_zone bucket_zones[] = { { NULL, "4 Bucket", BUCKET_SIZE(4), 4096 }, { NULL, "6 Bucket", BUCKET_SIZE(6), 3072 }, { NULL, "8 Bucket", BUCKET_SIZE(8), 2048 }, { NULL, "12 Bucket", BUCKET_SIZE(12), 1536 }, { NULL, "16 Bucket", BUCKET_SIZE(16), 1024 }, { NULL, "32 Bucket", BUCKET_SIZE(32), 512 }, { NULL, "64 Bucket", BUCKET_SIZE(64), 256 }, { NULL, "128 Bucket", BUCKET_SIZE(128), 128 }, { NULL, "256 Bucket", BUCKET_SIZE(256), 64 }, { NULL, NULL, 0} }; /* * Flags and enumerations to be passed to internal functions. */ enum zfreeskip { SKIP_NONE = 0, SKIP_DTOR, SKIP_FINI }; /* Prototypes.. */ static void *noobj_alloc(uma_zone_t, int, uint8_t *, int); static void *page_alloc(uma_zone_t, int, uint8_t *, int); static void *startup_alloc(uma_zone_t, int, uint8_t *, int); static void page_free(void *, int, uint8_t); static uma_slab_t keg_alloc_slab(uma_keg_t, uma_zone_t, int); static void cache_drain(uma_zone_t); static void bucket_drain(uma_zone_t, uma_bucket_t); static void bucket_cache_drain(uma_zone_t zone); static int keg_ctor(void *, int, void *, int); static void keg_dtor(void *, int, void *); static int zone_ctor(void *, int, void *, int); static void zone_dtor(void *, int, void *); static int zero_init(void *, int, int); static void keg_small_init(uma_keg_t keg); static void keg_large_init(uma_keg_t keg); static void zone_foreach(void (*zfunc)(uma_zone_t)); static void zone_timeout(uma_zone_t zone); static int hash_alloc(struct uma_hash *); static int hash_expand(struct uma_hash *, struct uma_hash *); static void hash_free(struct uma_hash *hash); static void uma_timeout(void *); static void uma_startup3(void); static void *zone_alloc_item(uma_zone_t, void *, int); static void zone_free_item(uma_zone_t, void *, void *, enum zfreeskip); static void bucket_enable(void); static void bucket_init(void); static uma_bucket_t bucket_alloc(uma_zone_t zone, void *, int); static void bucket_free(uma_zone_t zone, uma_bucket_t, void *); static void bucket_zone_drain(void); static uma_bucket_t zone_alloc_bucket(uma_zone_t zone, void *, int flags); static uma_slab_t zone_fetch_slab(uma_zone_t zone, uma_keg_t last, int flags); static uma_slab_t zone_fetch_slab_multi(uma_zone_t zone, uma_keg_t last, int flags); static void *slab_alloc_item(uma_keg_t keg, uma_slab_t slab); static void slab_free_item(uma_keg_t keg, uma_slab_t slab, void *item); static uma_keg_t uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit, uma_fini fini, int align, uint32_t flags); static int zone_import(uma_zone_t zone, void **bucket, int max, int flags); static void zone_release(uma_zone_t zone, void **bucket, int cnt); static void uma_zero_item(void *item, uma_zone_t zone); void uma_print_zone(uma_zone_t); void uma_print_stats(void); static int sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS); static int sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS); SYSINIT(uma_startup3, SI_SUB_VM_CONF, SI_ORDER_SECOND, uma_startup3, NULL); SYSCTL_PROC(_vm, OID_AUTO, zone_count, CTLFLAG_RD|CTLTYPE_INT, 0, 0, sysctl_vm_zone_count, "I", "Number of UMA zones"); SYSCTL_PROC(_vm, OID_AUTO, zone_stats, CTLFLAG_RD|CTLTYPE_STRUCT, 0, 0, sysctl_vm_zone_stats, "s,struct uma_type_header", "Zone Stats"); static int zone_warnings = 1; SYSCTL_INT(_vm, OID_AUTO, zone_warnings, CTLFLAG_RWTUN, &zone_warnings, 0, "Warn when UMA zones becomes full"); /* * This routine checks to see whether or not it's safe to enable buckets. */ static void bucket_enable(void) { bucketdisable = vm_page_count_min(); } /* * Initialize bucket_zones, the array of zones of buckets of various sizes. * * For each zone, calculate the memory required for each bucket, consisting * of the header and an array of pointers. */ static void bucket_init(void) { struct uma_bucket_zone *ubz; int size; int i; for (i = 0, ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++) { size = roundup(sizeof(struct uma_bucket), sizeof(void *)); size += sizeof(void *) * ubz->ubz_entries; ubz->ubz_zone = uma_zcreate(ubz->ubz_name, size, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZONE_MTXCLASS | UMA_ZFLAG_BUCKET); } } /* * Given a desired number of entries for a bucket, return the zone from which * to allocate the bucket. */ static struct uma_bucket_zone * bucket_zone_lookup(int entries) { struct uma_bucket_zone *ubz; for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++) if (ubz->ubz_entries >= entries) return (ubz); ubz--; return (ubz); } static int bucket_select(int size) { struct uma_bucket_zone *ubz; ubz = &bucket_zones[0]; if (size > ubz->ubz_maxsize) return MAX((ubz->ubz_maxsize * ubz->ubz_entries) / size, 1); for (; ubz->ubz_entries != 0; ubz++) if (ubz->ubz_maxsize < size) break; ubz--; return (ubz->ubz_entries); } static uma_bucket_t bucket_alloc(uma_zone_t zone, void *udata, int flags) { struct uma_bucket_zone *ubz; uma_bucket_t bucket; /* * This is to stop us from allocating per cpu buckets while we're * running out of vm.boot_pages. Otherwise, we would exhaust the * boot pages. This also prevents us from allocating buckets in * low memory situations. */ if (bucketdisable) return (NULL); /* * To limit bucket recursion we store the original zone flags * in a cookie passed via zalloc_arg/zfree_arg. This allows the * NOVM flag to persist even through deep recursions. We also * store ZFLAG_BUCKET once we have recursed attempting to allocate * a bucket for a bucket zone so we do not allow infinite bucket * recursion. This cookie will even persist to frees of unused * buckets via the allocation path or bucket allocations in the * free path. */ if ((zone->uz_flags & UMA_ZFLAG_BUCKET) == 0) udata = (void *)(uintptr_t)zone->uz_flags; else { if ((uintptr_t)udata & UMA_ZFLAG_BUCKET) return (NULL); udata = (void *)((uintptr_t)udata | UMA_ZFLAG_BUCKET); } if ((uintptr_t)udata & UMA_ZFLAG_CACHEONLY) flags |= M_NOVM; ubz = bucket_zone_lookup(zone->uz_count); if (ubz->ubz_zone == zone && (ubz + 1)->ubz_entries != 0) ubz++; bucket = uma_zalloc_arg(ubz->ubz_zone, udata, flags); if (bucket) { #ifdef INVARIANTS bzero(bucket->ub_bucket, sizeof(void *) * ubz->ubz_entries); #endif bucket->ub_cnt = 0; bucket->ub_entries = ubz->ubz_entries; } return (bucket); } static void bucket_free(uma_zone_t zone, uma_bucket_t bucket, void *udata) { struct uma_bucket_zone *ubz; KASSERT(bucket->ub_cnt == 0, ("bucket_free: Freeing a non free bucket.")); if ((zone->uz_flags & UMA_ZFLAG_BUCKET) == 0) udata = (void *)(uintptr_t)zone->uz_flags; ubz = bucket_zone_lookup(bucket->ub_entries); uma_zfree_arg(ubz->ubz_zone, bucket, udata); } static void bucket_zone_drain(void) { struct uma_bucket_zone *ubz; for (ubz = &bucket_zones[0]; ubz->ubz_entries != 0; ubz++) zone_drain(ubz->ubz_zone); } static void zone_log_warning(uma_zone_t zone) { static const struct timeval warninterval = { 300, 0 }; if (!zone_warnings || zone->uz_warning == NULL) return; if (ratecheck(&zone->uz_ratecheck, &warninterval)) printf("[zone: %s] %s\n", zone->uz_name, zone->uz_warning); } static void zone_foreach_keg(uma_zone_t zone, void (*kegfn)(uma_keg_t)) { uma_klink_t klink; LIST_FOREACH(klink, &zone->uz_kegs, kl_link) kegfn(klink->kl_keg); } /* * Routine called by timeout which is used to fire off some time interval * based calculations. (stats, hash size, etc.) * * Arguments: * arg Unused * * Returns: * Nothing */ static void uma_timeout(void *unused) { bucket_enable(); zone_foreach(zone_timeout); /* Reschedule this event */ callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL); } /* * Routine to perform timeout driven calculations. This expands the * hashes and does per cpu statistics aggregation. * * Returns nothing. */ static void keg_timeout(uma_keg_t keg) { KEG_LOCK(keg); /* * Expand the keg hash table. * * This is done if the number of slabs is larger than the hash size. * What I'm trying to do here is completely reduce collisions. This * may be a little aggressive. Should I allow for two collisions max? */ if (keg->uk_flags & UMA_ZONE_HASH && keg->uk_pages / keg->uk_ppera >= keg->uk_hash.uh_hashsize) { struct uma_hash newhash; struct uma_hash oldhash; int ret; /* * This is so involved because allocating and freeing * while the keg lock is held will lead to deadlock. * I have to do everything in stages and check for * races. */ newhash = keg->uk_hash; KEG_UNLOCK(keg); ret = hash_alloc(&newhash); KEG_LOCK(keg); if (ret) { if (hash_expand(&keg->uk_hash, &newhash)) { oldhash = keg->uk_hash; keg->uk_hash = newhash; } else oldhash = newhash; KEG_UNLOCK(keg); hash_free(&oldhash); return; } } KEG_UNLOCK(keg); } static void zone_timeout(uma_zone_t zone) { zone_foreach_keg(zone, &keg_timeout); } /* * Allocate and zero fill the next sized hash table from the appropriate * backing store. * * Arguments: * hash A new hash structure with the old hash size in uh_hashsize * * Returns: * 1 on sucess and 0 on failure. */ static int hash_alloc(struct uma_hash *hash) { int oldsize; int alloc; oldsize = hash->uh_hashsize; /* We're just going to go to a power of two greater */ if (oldsize) { hash->uh_hashsize = oldsize * 2; alloc = sizeof(hash->uh_slab_hash[0]) * hash->uh_hashsize; hash->uh_slab_hash = (struct slabhead *)malloc(alloc, M_UMAHASH, M_NOWAIT); } else { alloc = sizeof(hash->uh_slab_hash[0]) * UMA_HASH_SIZE_INIT; hash->uh_slab_hash = zone_alloc_item(hashzone, NULL, M_WAITOK); hash->uh_hashsize = UMA_HASH_SIZE_INIT; } if (hash->uh_slab_hash) { bzero(hash->uh_slab_hash, alloc); hash->uh_hashmask = hash->uh_hashsize - 1; return (1); } return (0); } /* * Expands the hash table for HASH zones. This is done from zone_timeout * to reduce collisions. This must not be done in the regular allocation * path, otherwise, we can recurse on the vm while allocating pages. * * Arguments: * oldhash The hash you want to expand * newhash The hash structure for the new table * * Returns: * Nothing * * Discussion: */ static int hash_expand(struct uma_hash *oldhash, struct uma_hash *newhash) { uma_slab_t slab; int hval; int i; if (!newhash->uh_slab_hash) return (0); if (oldhash->uh_hashsize >= newhash->uh_hashsize) return (0); /* * I need to investigate hash algorithms for resizing without a * full rehash. */ for (i = 0; i < oldhash->uh_hashsize; i++) while (!SLIST_EMPTY(&oldhash->uh_slab_hash[i])) { slab = SLIST_FIRST(&oldhash->uh_slab_hash[i]); SLIST_REMOVE_HEAD(&oldhash->uh_slab_hash[i], us_hlink); hval = UMA_HASH(newhash, slab->us_data); SLIST_INSERT_HEAD(&newhash->uh_slab_hash[hval], slab, us_hlink); } return (1); } /* * Free the hash bucket to the appropriate backing store. * * Arguments: * slab_hash The hash bucket we're freeing * hashsize The number of entries in that hash bucket * * Returns: * Nothing */ static void hash_free(struct uma_hash *hash) { if (hash->uh_slab_hash == NULL) return; if (hash->uh_hashsize == UMA_HASH_SIZE_INIT) zone_free_item(hashzone, hash->uh_slab_hash, NULL, SKIP_NONE); else free(hash->uh_slab_hash, M_UMAHASH); } /* * Frees all outstanding items in a bucket * * Arguments: * zone The zone to free to, must be unlocked. * bucket The free/alloc bucket with items, cpu queue must be locked. * * Returns: * Nothing */ static void bucket_drain(uma_zone_t zone, uma_bucket_t bucket) { int i; if (bucket == NULL) return; if (zone->uz_fini) for (i = 0; i < bucket->ub_cnt; i++) zone->uz_fini(bucket->ub_bucket[i], zone->uz_size); zone->uz_release(zone->uz_arg, bucket->ub_bucket, bucket->ub_cnt); bucket->ub_cnt = 0; } /* * Drains the per cpu caches for a zone. * * NOTE: This may only be called while the zone is being turn down, and not * during normal operation. This is necessary in order that we do not have * to migrate CPUs to drain the per-CPU caches. * * Arguments: * zone The zone to drain, must be unlocked. * * Returns: * Nothing */ static void cache_drain(uma_zone_t zone) { uma_cache_t cache; int cpu; /* * XXX: It is safe to not lock the per-CPU caches, because we're * tearing down the zone anyway. I.e., there will be no further use * of the caches at this point. * * XXX: It would good to be able to assert that the zone is being * torn down to prevent improper use of cache_drain(). * * XXX: We lock the zone before passing into bucket_cache_drain() as * it is used elsewhere. Should the tear-down path be made special * there in some form? */ CPU_FOREACH(cpu) { cache = &zone->uz_cpu[cpu]; bucket_drain(zone, cache->uc_allocbucket); bucket_drain(zone, cache->uc_freebucket); if (cache->uc_allocbucket != NULL) bucket_free(zone, cache->uc_allocbucket, NULL); if (cache->uc_freebucket != NULL) bucket_free(zone, cache->uc_freebucket, NULL); cache->uc_allocbucket = cache->uc_freebucket = NULL; } ZONE_LOCK(zone); bucket_cache_drain(zone); ZONE_UNLOCK(zone); } static void cache_shrink(uma_zone_t zone) { if (zone->uz_flags & UMA_ZFLAG_INTERNAL) return; ZONE_LOCK(zone); zone->uz_count = (zone->uz_count_min + zone->uz_count) / 2; ZONE_UNLOCK(zone); } static void cache_drain_safe_cpu(uma_zone_t zone) { uma_cache_t cache; uma_bucket_t b1, b2; if (zone->uz_flags & UMA_ZFLAG_INTERNAL) return; b1 = b2 = NULL; ZONE_LOCK(zone); critical_enter(); cache = &zone->uz_cpu[curcpu]; if (cache->uc_allocbucket) { if (cache->uc_allocbucket->ub_cnt != 0) LIST_INSERT_HEAD(&zone->uz_buckets, cache->uc_allocbucket, ub_link); else b1 = cache->uc_allocbucket; cache->uc_allocbucket = NULL; } if (cache->uc_freebucket) { if (cache->uc_freebucket->ub_cnt != 0) LIST_INSERT_HEAD(&zone->uz_buckets, cache->uc_freebucket, ub_link); else b2 = cache->uc_freebucket; cache->uc_freebucket = NULL; } critical_exit(); ZONE_UNLOCK(zone); if (b1) bucket_free(zone, b1, NULL); if (b2) bucket_free(zone, b2, NULL); } /* * Safely drain per-CPU caches of a zone(s) to alloc bucket. * This is an expensive call because it needs to bind to all CPUs * one by one and enter a critical section on each of them in order * to safely access their cache buckets. * Zone lock must not be held on call this function. */ static void cache_drain_safe(uma_zone_t zone) { int cpu; /* * Polite bucket sizes shrinking was not enouth, shrink aggressively. */ if (zone) cache_shrink(zone); else zone_foreach(cache_shrink); CPU_FOREACH(cpu) { thread_lock(curthread); sched_bind(curthread, cpu); thread_unlock(curthread); if (zone) cache_drain_safe_cpu(zone); else zone_foreach(cache_drain_safe_cpu); } thread_lock(curthread); sched_unbind(curthread); thread_unlock(curthread); } /* * Drain the cached buckets from a zone. Expects a locked zone on entry. */ static void bucket_cache_drain(uma_zone_t zone) { uma_bucket_t bucket; /* * Drain the bucket queues and free the buckets, we just keep two per * cpu (alloc/free). */ while ((bucket = LIST_FIRST(&zone->uz_buckets)) != NULL) { LIST_REMOVE(bucket, ub_link); ZONE_UNLOCK(zone); bucket_drain(zone, bucket); bucket_free(zone, bucket, NULL); ZONE_LOCK(zone); } /* * Shrink further bucket sizes. Price of single zone lock collision * is probably lower then price of global cache drain. */ if (zone->uz_count > zone->uz_count_min) zone->uz_count--; } static void keg_free_slab(uma_keg_t keg, uma_slab_t slab, int start) { uint8_t *mem; int i; uint8_t flags; mem = slab->us_data; flags = slab->us_flags; i = start; if (keg->uk_fini != NULL) { for (i--; i > -1; i--) keg->uk_fini(slab->us_data + (keg->uk_rsize * i), keg->uk_size); } if (keg->uk_flags & UMA_ZONE_OFFPAGE) zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE); #ifdef UMA_DEBUG printf("%s: Returning %d bytes.\n", keg->uk_name, PAGE_SIZE * keg->uk_ppera); #endif keg->uk_freef(mem, PAGE_SIZE * keg->uk_ppera, flags); } /* * Frees pages from a keg back to the system. This is done on demand from * the pageout daemon. * * Returns nothing. */ static void keg_drain(uma_keg_t keg) { struct slabhead freeslabs = { 0 }; uma_slab_t slab; uma_slab_t n; /* * We don't want to take pages from statically allocated kegs at this * time */ if (keg->uk_flags & UMA_ZONE_NOFREE || keg->uk_freef == NULL) return; #ifdef UMA_DEBUG printf("%s free items: %u\n", keg->uk_name, keg->uk_free); #endif KEG_LOCK(keg); if (keg->uk_free == 0) goto finished; slab = LIST_FIRST(&keg->uk_free_slab); while (slab) { n = LIST_NEXT(slab, us_link); /* We have no where to free these to */ if (slab->us_flags & UMA_SLAB_BOOT) { slab = n; continue; } LIST_REMOVE(slab, us_link); keg->uk_pages -= keg->uk_ppera; keg->uk_free -= keg->uk_ipers; if (keg->uk_flags & UMA_ZONE_HASH) UMA_HASH_REMOVE(&keg->uk_hash, slab, slab->us_data); SLIST_INSERT_HEAD(&freeslabs, slab, us_hlink); slab = n; } finished: KEG_UNLOCK(keg); while ((slab = SLIST_FIRST(&freeslabs)) != NULL) { SLIST_REMOVE(&freeslabs, slab, uma_slab, us_hlink); keg_free_slab(keg, slab, keg->uk_ipers); } } static void zone_drain_wait(uma_zone_t zone, int waitok) { /* * Set draining to interlock with zone_dtor() so we can release our * locks as we go. Only dtor() should do a WAITOK call since it * is the only call that knows the structure will still be available * when it wakes up. */ ZONE_LOCK(zone); while (zone->uz_flags & UMA_ZFLAG_DRAINING) { if (waitok == M_NOWAIT) goto out; msleep(zone, zone->uz_lockptr, PVM, "zonedrain", 1); } zone->uz_flags |= UMA_ZFLAG_DRAINING; bucket_cache_drain(zone); ZONE_UNLOCK(zone); /* * The DRAINING flag protects us from being freed while * we're running. Normally the uma_rwlock would protect us but we * must be able to release and acquire the right lock for each keg. */ zone_foreach_keg(zone, &keg_drain); ZONE_LOCK(zone); zone->uz_flags &= ~UMA_ZFLAG_DRAINING; wakeup(zone); out: ZONE_UNLOCK(zone); } void zone_drain(uma_zone_t zone) { zone_drain_wait(zone, M_NOWAIT); } /* * Allocate a new slab for a keg. This does not insert the slab onto a list. * * Arguments: * wait Shall we wait? * * Returns: * The slab that was allocated or NULL if there is no memory and the * caller specified M_NOWAIT. */ static uma_slab_t keg_alloc_slab(uma_keg_t keg, uma_zone_t zone, int wait) { uma_slabrefcnt_t slabref; uma_alloc allocf; uma_slab_t slab; uint8_t *mem; uint8_t flags; int i; mtx_assert(&keg->uk_lock, MA_OWNED); slab = NULL; mem = NULL; #ifdef UMA_DEBUG printf("alloc_slab: Allocating a new slab for %s\n", keg->uk_name); #endif allocf = keg->uk_allocf; KEG_UNLOCK(keg); if (keg->uk_flags & UMA_ZONE_OFFPAGE) { slab = zone_alloc_item(keg->uk_slabzone, NULL, wait); if (slab == NULL) goto out; } /* * This reproduces the old vm_zone behavior of zero filling pages the * first time they are added to a zone. * * Malloced items are zeroed in uma_zalloc. */ if ((keg->uk_flags & UMA_ZONE_MALLOC) == 0) wait |= M_ZERO; else wait &= ~M_ZERO; if (keg->uk_flags & UMA_ZONE_NODUMP) wait |= M_NODUMP; /* zone is passed for legacy reasons. */ mem = allocf(zone, keg->uk_ppera * PAGE_SIZE, &flags, wait); if (mem == NULL) { if (keg->uk_flags & UMA_ZONE_OFFPAGE) zone_free_item(keg->uk_slabzone, slab, NULL, SKIP_NONE); slab = NULL; goto out; } /* Point the slab into the allocated memory */ if (!(keg->uk_flags & UMA_ZONE_OFFPAGE)) slab = (uma_slab_t )(mem + keg->uk_pgoff); if (keg->uk_flags & UMA_ZONE_VTOSLAB) for (i = 0; i < keg->uk_ppera; i++) vsetslab((vm_offset_t)mem + (i * PAGE_SIZE), slab); slab->us_keg = keg; slab->us_data = mem; slab->us_freecount = keg->uk_ipers; slab->us_flags = flags; BIT_FILL(SLAB_SETSIZE, &slab->us_free); #ifdef INVARIANTS BIT_ZERO(SLAB_SETSIZE, &slab->us_debugfree); #endif if (keg->uk_flags & UMA_ZONE_REFCNT) { slabref = (uma_slabrefcnt_t)slab; for (i = 0; i < keg->uk_ipers; i++) slabref->us_refcnt[i] = 0; } if (keg->uk_init != NULL) { for (i = 0; i < keg->uk_ipers; i++) if (keg->uk_init(slab->us_data + (keg->uk_rsize * i), keg->uk_size, wait) != 0) break; if (i != keg->uk_ipers) { keg_free_slab(keg, slab, i); slab = NULL; goto out; } } out: KEG_LOCK(keg); if (slab != NULL) { if (keg->uk_flags & UMA_ZONE_HASH) UMA_HASH_INSERT(&keg->uk_hash, slab, mem); keg->uk_pages += keg->uk_ppera; keg->uk_free += keg->uk_ipers; } return (slab); } /* * This function is intended to be used early on in place of page_alloc() so * that we may use the boot time page cache to satisfy allocations before * the VM is ready. */ static void * startup_alloc(uma_zone_t zone, int bytes, uint8_t *pflag, int wait) { uma_keg_t keg; uma_slab_t tmps; int pages, check_pages; keg = zone_first_keg(zone); pages = howmany(bytes, PAGE_SIZE); check_pages = pages - 1; KASSERT(pages > 0, ("startup_alloc can't reserve 0 pages\n")); /* * Check our small startup cache to see if it has pages remaining. */ mtx_lock(&uma_boot_pages_mtx); /* First check if we have enough room. */ tmps = LIST_FIRST(&uma_boot_pages); while (tmps != NULL && check_pages-- > 0) tmps = LIST_NEXT(tmps, us_link); if (tmps != NULL) { /* * It's ok to lose tmps references. The last one will * have tmps->us_data pointing to the start address of * "pages" contiguous pages of memory. */ while (pages-- > 0) { tmps = LIST_FIRST(&uma_boot_pages); LIST_REMOVE(tmps, us_link); } mtx_unlock(&uma_boot_pages_mtx); *pflag = tmps->us_flags; return (tmps->us_data); } mtx_unlock(&uma_boot_pages_mtx); if (booted < UMA_STARTUP2) panic("UMA: Increase vm.boot_pages"); /* * Now that we've booted reset these users to their real allocator. */ #ifdef UMA_MD_SMALL_ALLOC keg->uk_allocf = (keg->uk_ppera > 1) ? page_alloc : uma_small_alloc; #else keg->uk_allocf = page_alloc; #endif return keg->uk_allocf(zone, bytes, pflag, wait); } /* * Allocates a number of pages from the system * * Arguments: * bytes The number of bytes requested * wait Shall we wait? * * Returns: * A pointer to the alloced memory or possibly * NULL if M_NOWAIT is set. */ static void * page_alloc(uma_zone_t zone, int bytes, uint8_t *pflag, int wait) { void *p; /* Returned page */ *pflag = UMA_SLAB_KMEM; p = (void *) kmem_malloc(kmem_arena, bytes, wait); return (p); } /* * Allocates a number of pages from within an object * * Arguments: * bytes The number of bytes requested * wait Shall we wait? * * Returns: * A pointer to the alloced memory or possibly * NULL if M_NOWAIT is set. */ static void * noobj_alloc(uma_zone_t zone, int bytes, uint8_t *flags, int wait) { TAILQ_HEAD(, vm_page) alloctail; u_long npages; vm_offset_t retkva, zkva; vm_page_t p, p_next; uma_keg_t keg; TAILQ_INIT(&alloctail); keg = zone_first_keg(zone); npages = howmany(bytes, PAGE_SIZE); while (npages > 0) { p = vm_page_alloc(NULL, 0, VM_ALLOC_INTERRUPT | VM_ALLOC_WIRED | VM_ALLOC_NOOBJ); if (p != NULL) { /* * Since the page does not belong to an object, its * listq is unused. */ TAILQ_INSERT_TAIL(&alloctail, p, listq); npages--; continue; } if (wait & M_WAITOK) { VM_WAIT; continue; } /* * Page allocation failed, free intermediate pages and * exit. */ TAILQ_FOREACH_SAFE(p, &alloctail, listq, p_next) { vm_page_unwire(p, PQ_INACTIVE); vm_page_free(p); } return (NULL); } *flags = UMA_SLAB_PRIV; zkva = keg->uk_kva + atomic_fetchadd_long(&keg->uk_offset, round_page(bytes)); retkva = zkva; TAILQ_FOREACH(p, &alloctail, listq) { pmap_qenter(zkva, &p, 1); zkva += PAGE_SIZE; } return ((void *)retkva); } /* * Frees a number of pages to the system * * Arguments: * mem A pointer to the memory to be freed * size The size of the memory being freed * flags The original p->us_flags field * * Returns: * Nothing */ static void page_free(void *mem, int size, uint8_t flags) { struct vmem *vmem; if (flags & UMA_SLAB_KMEM) vmem = kmem_arena; else if (flags & UMA_SLAB_KERNEL) vmem = kernel_arena; else panic("UMA: page_free used with invalid flags %d", flags); kmem_free(vmem, (vm_offset_t)mem, size); } /* * Zero fill initializer * * Arguments/Returns follow uma_init specifications */ static int zero_init(void *mem, int size, int flags) { bzero(mem, size); return (0); } /* * Finish creating a small uma keg. This calculates ipers, and the keg size. * * Arguments * keg The zone we should initialize * * Returns * Nothing */ static void keg_small_init(uma_keg_t keg) { u_int rsize; u_int memused; u_int wastedspace; u_int shsize; if (keg->uk_flags & UMA_ZONE_PCPU) { u_int ncpus = mp_ncpus ? mp_ncpus : MAXCPU; keg->uk_slabsize = sizeof(struct pcpu); keg->uk_ppera = howmany(ncpus * sizeof(struct pcpu), PAGE_SIZE); } else { keg->uk_slabsize = UMA_SLAB_SIZE; keg->uk_ppera = 1; } /* * Calculate the size of each allocation (rsize) according to * alignment. If the requested size is smaller than we have * allocation bits for we round it up. */ rsize = keg->uk_size; if (rsize < keg->uk_slabsize / SLAB_SETSIZE) rsize = keg->uk_slabsize / SLAB_SETSIZE; if (rsize & keg->uk_align) rsize = (rsize & ~keg->uk_align) + (keg->uk_align + 1); keg->uk_rsize = rsize; KASSERT((keg->uk_flags & UMA_ZONE_PCPU) == 0 || keg->uk_rsize < sizeof(struct pcpu), ("%s: size %u too large", __func__, keg->uk_rsize)); if (keg->uk_flags & UMA_ZONE_REFCNT) rsize += sizeof(uint32_t); if (keg->uk_flags & UMA_ZONE_OFFPAGE) shsize = 0; else shsize = sizeof(struct uma_slab); keg->uk_ipers = (keg->uk_slabsize - shsize) / rsize; KASSERT(keg->uk_ipers > 0 && keg->uk_ipers <= SLAB_SETSIZE, ("%s: keg->uk_ipers %u", __func__, keg->uk_ipers)); memused = keg->uk_ipers * rsize + shsize; wastedspace = keg->uk_slabsize - memused; /* * We can't do OFFPAGE if we're internal or if we've been * asked to not go to the VM for buckets. If we do this we * may end up going to the VM for slabs which we do not * want to do if we're UMA_ZFLAG_CACHEONLY as a result * of UMA_ZONE_VM, which clearly forbids it. */ if ((keg->uk_flags & UMA_ZFLAG_INTERNAL) || (keg->uk_flags & UMA_ZFLAG_CACHEONLY)) return; /* * See if using an OFFPAGE slab will limit our waste. Only do * this if it permits more items per-slab. * * XXX We could try growing slabsize to limit max waste as well. * Historically this was not done because the VM could not * efficiently handle contiguous allocations. */ if ((wastedspace >= keg->uk_slabsize / UMA_MAX_WASTE) && (keg->uk_ipers < (keg->uk_slabsize / keg->uk_rsize))) { keg->uk_ipers = keg->uk_slabsize / keg->uk_rsize; KASSERT(keg->uk_ipers > 0 && keg->uk_ipers <= SLAB_SETSIZE, ("%s: keg->uk_ipers %u", __func__, keg->uk_ipers)); #ifdef UMA_DEBUG printf("UMA decided we need offpage slab headers for " "keg: %s, calculated wastedspace = %d, " "maximum wasted space allowed = %d, " "calculated ipers = %d, " "new wasted space = %d\n", keg->uk_name, wastedspace, keg->uk_slabsize / UMA_MAX_WASTE, keg->uk_ipers, keg->uk_slabsize - keg->uk_ipers * keg->uk_rsize); #endif keg->uk_flags |= UMA_ZONE_OFFPAGE; } if ((keg->uk_flags & UMA_ZONE_OFFPAGE) && (keg->uk_flags & UMA_ZONE_VTOSLAB) == 0) keg->uk_flags |= UMA_ZONE_HASH; } /* * Finish creating a large (> UMA_SLAB_SIZE) uma kegs. Just give in and do * OFFPAGE for now. When I can allow for more dynamic slab sizes this will be * more complicated. * * Arguments * keg The keg we should initialize * * Returns * Nothing */ static void keg_large_init(uma_keg_t keg) { u_int shsize; KASSERT(keg != NULL, ("Keg is null in keg_large_init")); KASSERT((keg->uk_flags & UMA_ZFLAG_CACHEONLY) == 0, ("keg_large_init: Cannot large-init a UMA_ZFLAG_CACHEONLY keg")); KASSERT((keg->uk_flags & UMA_ZONE_PCPU) == 0, ("%s: Cannot large-init a UMA_ZONE_PCPU keg", __func__)); keg->uk_ppera = howmany(keg->uk_size, PAGE_SIZE); keg->uk_slabsize = keg->uk_ppera * PAGE_SIZE; keg->uk_ipers = 1; keg->uk_rsize = keg->uk_size; /* We can't do OFFPAGE if we're internal, bail out here. */ if (keg->uk_flags & UMA_ZFLAG_INTERNAL) return; /* Check whether we have enough space to not do OFFPAGE. */ if ((keg->uk_flags & UMA_ZONE_OFFPAGE) == 0) { shsize = sizeof(struct uma_slab); if (keg->uk_flags & UMA_ZONE_REFCNT) shsize += keg->uk_ipers * sizeof(uint32_t); if (shsize & UMA_ALIGN_PTR) shsize = (shsize & ~UMA_ALIGN_PTR) + (UMA_ALIGN_PTR + 1); if ((PAGE_SIZE * keg->uk_ppera) - keg->uk_rsize < shsize) keg->uk_flags |= UMA_ZONE_OFFPAGE; } if ((keg->uk_flags & UMA_ZONE_OFFPAGE) && (keg->uk_flags & UMA_ZONE_VTOSLAB) == 0) keg->uk_flags |= UMA_ZONE_HASH; } static void keg_cachespread_init(uma_keg_t keg) { int alignsize; int trailer; int pages; int rsize; KASSERT((keg->uk_flags & UMA_ZONE_PCPU) == 0, ("%s: Cannot cachespread-init a UMA_ZONE_PCPU keg", __func__)); alignsize = keg->uk_align + 1; rsize = keg->uk_size; /* * We want one item to start on every align boundary in a page. To * do this we will span pages. We will also extend the item by the * size of align if it is an even multiple of align. Otherwise, it * would fall on the same boundary every time. */ if (rsize & keg->uk_align) rsize = (rsize & ~keg->uk_align) + alignsize; if ((rsize & alignsize) == 0) rsize += alignsize; trailer = rsize - keg->uk_size; pages = (rsize * (PAGE_SIZE / alignsize)) / PAGE_SIZE; pages = MIN(pages, (128 * 1024) / PAGE_SIZE); keg->uk_rsize = rsize; keg->uk_ppera = pages; keg->uk_slabsize = UMA_SLAB_SIZE; keg->uk_ipers = ((pages * PAGE_SIZE) + trailer) / rsize; keg->uk_flags |= UMA_ZONE_OFFPAGE | UMA_ZONE_VTOSLAB; KASSERT(keg->uk_ipers <= SLAB_SETSIZE, ("%s: keg->uk_ipers too high(%d) increase max_ipers", __func__, keg->uk_ipers)); } /* * Keg header ctor. This initializes all fields, locks, etc. And inserts * the keg onto the global keg list. * * Arguments/Returns follow uma_ctor specifications * udata Actually uma_kctor_args */ static int keg_ctor(void *mem, int size, void *udata, int flags) { struct uma_kctor_args *arg = udata; uma_keg_t keg = mem; uma_zone_t zone; bzero(keg, size); keg->uk_size = arg->size; keg->uk_init = arg->uminit; keg->uk_fini = arg->fini; keg->uk_align = arg->align; keg->uk_free = 0; keg->uk_reserve = 0; keg->uk_pages = 0; keg->uk_flags = arg->flags; keg->uk_allocf = page_alloc; keg->uk_freef = page_free; keg->uk_slabzone = NULL; /* * The master zone is passed to us at keg-creation time. */ zone = arg->zone; keg->uk_name = zone->uz_name; if (arg->flags & UMA_ZONE_VM) keg->uk_flags |= UMA_ZFLAG_CACHEONLY; if (arg->flags & UMA_ZONE_ZINIT) keg->uk_init = zero_init; if (arg->flags & UMA_ZONE_REFCNT || arg->flags & UMA_ZONE_MALLOC) keg->uk_flags |= UMA_ZONE_VTOSLAB; if (arg->flags & UMA_ZONE_PCPU) #ifdef SMP keg->uk_flags |= UMA_ZONE_OFFPAGE; #else keg->uk_flags &= ~UMA_ZONE_PCPU; #endif if (keg->uk_flags & UMA_ZONE_CACHESPREAD) { keg_cachespread_init(keg); } else if (keg->uk_flags & UMA_ZONE_REFCNT) { if (keg->uk_size > (UMA_SLAB_SIZE - sizeof(struct uma_slab_refcnt) - sizeof(uint32_t))) keg_large_init(keg); else keg_small_init(keg); } else { if (keg->uk_size > (UMA_SLAB_SIZE - sizeof(struct uma_slab))) keg_large_init(keg); else keg_small_init(keg); } if (keg->uk_flags & UMA_ZONE_OFFPAGE) { if (keg->uk_flags & UMA_ZONE_REFCNT) { if (keg->uk_ipers > uma_max_ipers_ref) panic("Too many ref items per zone: %d > %d\n", keg->uk_ipers, uma_max_ipers_ref); keg->uk_slabzone = slabrefzone; } else keg->uk_slabzone = slabzone; } /* * If we haven't booted yet we need allocations to go through the * startup cache until the vm is ready. */ if (keg->uk_ppera == 1) { #ifdef UMA_MD_SMALL_ALLOC keg->uk_allocf = uma_small_alloc; keg->uk_freef = uma_small_free; if (booted < UMA_STARTUP) keg->uk_allocf = startup_alloc; #else if (booted < UMA_STARTUP2) keg->uk_allocf = startup_alloc; #endif } else if (booted < UMA_STARTUP2 && (keg->uk_flags & UMA_ZFLAG_INTERNAL)) keg->uk_allocf = startup_alloc; /* * Initialize keg's lock */ KEG_LOCK_INIT(keg, (arg->flags & UMA_ZONE_MTXCLASS)); /* * If we're putting the slab header in the actual page we need to * figure out where in each page it goes. This calculates a right * justified offset into the memory on an ALIGN_PTR boundary. */ if (!(keg->uk_flags & UMA_ZONE_OFFPAGE)) { u_int totsize; /* Size of the slab struct and free list */ totsize = sizeof(struct uma_slab); /* Size of the reference counts. */ if (keg->uk_flags & UMA_ZONE_REFCNT) totsize += keg->uk_ipers * sizeof(uint32_t); if (totsize & UMA_ALIGN_PTR) totsize = (totsize & ~UMA_ALIGN_PTR) + (UMA_ALIGN_PTR + 1); keg->uk_pgoff = (PAGE_SIZE * keg->uk_ppera) - totsize; /* * The only way the following is possible is if with our * UMA_ALIGN_PTR adjustments we are now bigger than * UMA_SLAB_SIZE. I haven't checked whether this is * mathematically possible for all cases, so we make * sure here anyway. */ totsize = keg->uk_pgoff + sizeof(struct uma_slab); if (keg->uk_flags & UMA_ZONE_REFCNT) totsize += keg->uk_ipers * sizeof(uint32_t); if (totsize > PAGE_SIZE * keg->uk_ppera) { printf("zone %s ipers %d rsize %d size %d\n", zone->uz_name, keg->uk_ipers, keg->uk_rsize, keg->uk_size); panic("UMA slab won't fit."); } } if (keg->uk_flags & UMA_ZONE_HASH) hash_alloc(&keg->uk_hash); #ifdef UMA_DEBUG printf("UMA: %s(%p) size %d(%d) flags %#x ipers %d ppera %d out %d free %d\n", zone->uz_name, zone, keg->uk_size, keg->uk_rsize, keg->uk_flags, keg->uk_ipers, keg->uk_ppera, (keg->uk_ipers * keg->uk_pages) - keg->uk_free, keg->uk_free); #endif LIST_INSERT_HEAD(&keg->uk_zones, zone, uz_link); rw_wlock(&uma_rwlock); LIST_INSERT_HEAD(&uma_kegs, keg, uk_link); rw_wunlock(&uma_rwlock); return (0); } /* * Zone header ctor. This initializes all fields, locks, etc. * * Arguments/Returns follow uma_ctor specifications * udata Actually uma_zctor_args */ static int zone_ctor(void *mem, int size, void *udata, int flags) { struct uma_zctor_args *arg = udata; uma_zone_t zone = mem; uma_zone_t z; uma_keg_t keg; bzero(zone, size); zone->uz_name = arg->name; zone->uz_ctor = arg->ctor; zone->uz_dtor = arg->dtor; zone->uz_slab = zone_fetch_slab; zone->uz_init = NULL; zone->uz_fini = NULL; zone->uz_allocs = 0; zone->uz_frees = 0; zone->uz_fails = 0; zone->uz_sleeps = 0; zone->uz_count = 0; zone->uz_count_min = 0; zone->uz_flags = 0; zone->uz_warning = NULL; timevalclear(&zone->uz_ratecheck); keg = arg->keg; ZONE_LOCK_INIT(zone, (arg->flags & UMA_ZONE_MTXCLASS)); /* * This is a pure cache zone, no kegs. */ if (arg->import) { if (arg->flags & UMA_ZONE_VM) arg->flags |= UMA_ZFLAG_CACHEONLY; zone->uz_flags = arg->flags; zone->uz_size = arg->size; zone->uz_import = arg->import; zone->uz_release = arg->release; zone->uz_arg = arg->arg; zone->uz_lockptr = &zone->uz_lock; rw_wlock(&uma_rwlock); LIST_INSERT_HEAD(&uma_cachezones, zone, uz_link); rw_wunlock(&uma_rwlock); goto out; } /* * Use the regular zone/keg/slab allocator. */ zone->uz_import = (uma_import)zone_import; zone->uz_release = (uma_release)zone_release; zone->uz_arg = zone; if (arg->flags & UMA_ZONE_SECONDARY) { KASSERT(arg->keg != NULL, ("Secondary zone on zero'd keg")); zone->uz_init = arg->uminit; zone->uz_fini = arg->fini; zone->uz_lockptr = &keg->uk_lock; zone->uz_flags |= UMA_ZONE_SECONDARY; rw_wlock(&uma_rwlock); ZONE_LOCK(zone); LIST_FOREACH(z, &keg->uk_zones, uz_link) { if (LIST_NEXT(z, uz_link) == NULL) { LIST_INSERT_AFTER(z, zone, uz_link); break; } } ZONE_UNLOCK(zone); rw_wunlock(&uma_rwlock); } else if (keg == NULL) { if ((keg = uma_kcreate(zone, arg->size, arg->uminit, arg->fini, arg->align, arg->flags)) == NULL) return (ENOMEM); } else { struct uma_kctor_args karg; int error; /* We should only be here from uma_startup() */ karg.size = arg->size; karg.uminit = arg->uminit; karg.fini = arg->fini; karg.align = arg->align; karg.flags = arg->flags; karg.zone = zone; error = keg_ctor(arg->keg, sizeof(struct uma_keg), &karg, flags); if (error) return (error); } /* * Link in the first keg. */ zone->uz_klink.kl_keg = keg; LIST_INSERT_HEAD(&zone->uz_kegs, &zone->uz_klink, kl_link); zone->uz_lockptr = &keg->uk_lock; zone->uz_size = keg->uk_size; zone->uz_flags |= (keg->uk_flags & (UMA_ZONE_INHERIT | UMA_ZFLAG_INHERIT)); /* * Some internal zones don't have room allocated for the per cpu * caches. If we're internal, bail out here. */ if (keg->uk_flags & UMA_ZFLAG_INTERNAL) { KASSERT((zone->uz_flags & UMA_ZONE_SECONDARY) == 0, ("Secondary zone requested UMA_ZFLAG_INTERNAL")); return (0); } out: if ((arg->flags & UMA_ZONE_MAXBUCKET) == 0) zone->uz_count = bucket_select(zone->uz_size); else zone->uz_count = BUCKET_MAX; zone->uz_count_min = zone->uz_count; return (0); } /* * Keg header dtor. This frees all data, destroys locks, frees the hash * table and removes the keg from the global list. * * Arguments/Returns follow uma_dtor specifications * udata unused */ static void keg_dtor(void *arg, int size, void *udata) { uma_keg_t keg; keg = (uma_keg_t)arg; KEG_LOCK(keg); if (keg->uk_free != 0) { printf("Freed UMA keg (%s) was not empty (%d items). " " Lost %d pages of memory.\n", keg->uk_name ? keg->uk_name : "", keg->uk_free, keg->uk_pages); } KEG_UNLOCK(keg); hash_free(&keg->uk_hash); KEG_LOCK_FINI(keg); } /* * Zone header dtor. * * Arguments/Returns follow uma_dtor specifications * udata unused */ static void zone_dtor(void *arg, int size, void *udata) { uma_klink_t klink; uma_zone_t zone; uma_keg_t keg; zone = (uma_zone_t)arg; keg = zone_first_keg(zone); if (!(zone->uz_flags & UMA_ZFLAG_INTERNAL)) cache_drain(zone); rw_wlock(&uma_rwlock); LIST_REMOVE(zone, uz_link); rw_wunlock(&uma_rwlock); /* * XXX there are some races here where * the zone can be drained but zone lock * released and then refilled before we * remove it... we dont care for now */ zone_drain_wait(zone, M_WAITOK); /* * Unlink all of our kegs. */ while ((klink = LIST_FIRST(&zone->uz_kegs)) != NULL) { klink->kl_keg = NULL; LIST_REMOVE(klink, kl_link); if (klink == &zone->uz_klink) continue; free(klink, M_TEMP); } /* * We only destroy kegs from non secondary zones. */ if (keg != NULL && (zone->uz_flags & UMA_ZONE_SECONDARY) == 0) { rw_wlock(&uma_rwlock); LIST_REMOVE(keg, uk_link); rw_wunlock(&uma_rwlock); zone_free_item(kegs, keg, NULL, SKIP_NONE); } ZONE_LOCK_FINI(zone); } /* * Traverses every zone in the system and calls a callback * * Arguments: * zfunc A pointer to a function which accepts a zone * as an argument. * * Returns: * Nothing */ static void zone_foreach(void (*zfunc)(uma_zone_t)) { uma_keg_t keg; uma_zone_t zone; rw_rlock(&uma_rwlock); LIST_FOREACH(keg, &uma_kegs, uk_link) { LIST_FOREACH(zone, &keg->uk_zones, uz_link) zfunc(zone); } rw_runlock(&uma_rwlock); } /* Public functions */ /* See uma.h */ void uma_startup(void *bootmem, int boot_pages) { struct uma_zctor_args args; uma_slab_t slab; u_int slabsize; int i; #ifdef UMA_DEBUG printf("Creating uma keg headers zone and keg.\n"); #endif rw_init(&uma_rwlock, "UMA lock"); /* "manually" create the initial zone */ memset(&args, 0, sizeof(args)); args.name = "UMA Kegs"; args.size = sizeof(struct uma_keg); args.ctor = keg_ctor; args.dtor = keg_dtor; args.uminit = zero_init; args.fini = NULL; args.keg = &masterkeg; args.align = 32 - 1; args.flags = UMA_ZFLAG_INTERNAL; /* The initial zone has no Per cpu queues so it's smaller */ zone_ctor(kegs, sizeof(struct uma_zone), &args, M_WAITOK); #ifdef UMA_DEBUG printf("Filling boot free list.\n"); #endif for (i = 0; i < boot_pages; i++) { slab = (uma_slab_t)((uint8_t *)bootmem + (i * UMA_SLAB_SIZE)); slab->us_data = (uint8_t *)slab; slab->us_flags = UMA_SLAB_BOOT; LIST_INSERT_HEAD(&uma_boot_pages, slab, us_link); } mtx_init(&uma_boot_pages_mtx, "UMA boot pages", NULL, MTX_DEF); #ifdef UMA_DEBUG printf("Creating uma zone headers zone and keg.\n"); #endif args.name = "UMA Zones"; args.size = sizeof(struct uma_zone) + (sizeof(struct uma_cache) * (mp_maxid + 1)); args.ctor = zone_ctor; args.dtor = zone_dtor; args.uminit = zero_init; args.fini = NULL; args.keg = NULL; args.align = 32 - 1; args.flags = UMA_ZFLAG_INTERNAL; /* The initial zone has no Per cpu queues so it's smaller */ zone_ctor(zones, sizeof(struct uma_zone), &args, M_WAITOK); #ifdef UMA_DEBUG printf("Initializing pcpu cache locks.\n"); #endif #ifdef UMA_DEBUG printf("Creating slab and hash zones.\n"); #endif /* Now make a zone for slab headers */ slabzone = uma_zcreate("UMA Slabs", sizeof(struct uma_slab), NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL); /* * We also create a zone for the bigger slabs with reference * counts in them, to accomodate UMA_ZONE_REFCNT zones. */ slabsize = sizeof(struct uma_slab_refcnt); slabsize += uma_max_ipers_ref * sizeof(uint32_t); slabrefzone = uma_zcreate("UMA RCntSlabs", slabsize, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL); hashzone = uma_zcreate("UMA Hash", sizeof(struct slabhead *) * UMA_HASH_SIZE_INIT, NULL, NULL, NULL, NULL, UMA_ALIGN_PTR, UMA_ZFLAG_INTERNAL); bucket_init(); booted = UMA_STARTUP; #ifdef UMA_DEBUG printf("UMA startup complete.\n"); #endif } /* see uma.h */ void uma_startup2(void) { booted = UMA_STARTUP2; bucket_enable(); + sx_init(&uma_drain_lock, "umadrain"); #ifdef UMA_DEBUG printf("UMA startup2 complete.\n"); #endif } /* * Initialize our callout handle * */ static void uma_startup3(void) { #ifdef UMA_DEBUG printf("Starting callout.\n"); #endif callout_init(&uma_callout, CALLOUT_MPSAFE); callout_reset(&uma_callout, UMA_TIMEOUT * hz, uma_timeout, NULL); #ifdef UMA_DEBUG printf("UMA startup3 complete.\n"); #endif } static uma_keg_t uma_kcreate(uma_zone_t zone, size_t size, uma_init uminit, uma_fini fini, int align, uint32_t flags) { struct uma_kctor_args args; args.size = size; args.uminit = uminit; args.fini = fini; args.align = (align == UMA_ALIGN_CACHE) ? uma_align_cache : align; args.flags = flags; args.zone = zone; return (zone_alloc_item(kegs, &args, M_WAITOK)); } /* See uma.h */ void uma_set_align(int align) { if (align != UMA_ALIGN_CACHE) uma_align_cache = align; } /* See uma.h */ uma_zone_t uma_zcreate(const char *name, size_t size, uma_ctor ctor, uma_dtor dtor, uma_init uminit, uma_fini fini, int align, uint32_t flags) { struct uma_zctor_args args; + uma_zone_t res; + bool locked; /* This stuff is essential for the zone ctor */ memset(&args, 0, sizeof(args)); args.name = name; args.size = size; args.ctor = ctor; args.dtor = dtor; args.uminit = uminit; args.fini = fini; args.align = align; args.flags = flags; args.keg = NULL; - return (zone_alloc_item(zones, &args, M_WAITOK)); + if (booted < UMA_STARTUP2) { + locked = false; + } else { + sx_slock(&uma_drain_lock); + locked = true; + } + res = zone_alloc_item(zones, &args, M_WAITOK); + if (locked) + sx_sunlock(&uma_drain_lock); + return (res); } /* See uma.h */ uma_zone_t uma_zsecond_create(char *name, uma_ctor ctor, uma_dtor dtor, uma_init zinit, uma_fini zfini, uma_zone_t master) { struct uma_zctor_args args; uma_keg_t keg; + uma_zone_t res; + bool locked; keg = zone_first_keg(master); memset(&args, 0, sizeof(args)); args.name = name; args.size = keg->uk_size; args.ctor = ctor; args.dtor = dtor; args.uminit = zinit; args.fini = zfini; args.align = keg->uk_align; args.flags = keg->uk_flags | UMA_ZONE_SECONDARY; args.keg = keg; + if (booted < UMA_STARTUP2) { + locked = false; + } else { + sx_slock(&uma_drain_lock); + locked = true; + } /* XXX Attaches only one keg of potentially many. */ - return (zone_alloc_item(zones, &args, M_WAITOK)); + res = zone_alloc_item(zones, &args, M_WAITOK); + if (locked) + sx_sunlock(&uma_drain_lock); + return (res); } /* See uma.h */ uma_zone_t uma_zcache_create(char *name, int size, uma_ctor ctor, uma_dtor dtor, uma_init zinit, uma_fini zfini, uma_import zimport, uma_release zrelease, void *arg, int flags) { struct uma_zctor_args args; memset(&args, 0, sizeof(args)); args.name = name; args.size = size; args.ctor = ctor; args.dtor = dtor; args.uminit = zinit; args.fini = zfini; args.import = zimport; args.release = zrelease; args.arg = arg; args.align = 0; args.flags = flags; return (zone_alloc_item(zones, &args, M_WAITOK)); } static void zone_lock_pair(uma_zone_t a, uma_zone_t b) { if (a < b) { ZONE_LOCK(a); mtx_lock_flags(b->uz_lockptr, MTX_DUPOK); } else { ZONE_LOCK(b); mtx_lock_flags(a->uz_lockptr, MTX_DUPOK); } } static void zone_unlock_pair(uma_zone_t a, uma_zone_t b) { ZONE_UNLOCK(a); ZONE_UNLOCK(b); } int uma_zsecond_add(uma_zone_t zone, uma_zone_t master) { uma_klink_t klink; uma_klink_t kl; int error; error = 0; klink = malloc(sizeof(*klink), M_TEMP, M_WAITOK | M_ZERO); zone_lock_pair(zone, master); /* * zone must use vtoslab() to resolve objects and must already be * a secondary. */ if ((zone->uz_flags & (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY)) != (UMA_ZONE_VTOSLAB | UMA_ZONE_SECONDARY)) { error = EINVAL; goto out; } /* * The new master must also use vtoslab(). */ if ((zone->uz_flags & UMA_ZONE_VTOSLAB) != UMA_ZONE_VTOSLAB) { error = EINVAL; goto out; } /* * Both must either be refcnt, or not be refcnt. */ if ((zone->uz_flags & UMA_ZONE_REFCNT) != (master->uz_flags & UMA_ZONE_REFCNT)) { error = EINVAL; goto out; } /* * The underlying object must be the same size. rsize * may be different. */ if (master->uz_size != zone->uz_size) { error = E2BIG; goto out; } /* * Put it at the end of the list. */ klink->kl_keg = zone_first_keg(master); LIST_FOREACH(kl, &zone->uz_kegs, kl_link) { if (LIST_NEXT(kl, kl_link) == NULL) { LIST_INSERT_AFTER(kl, klink, kl_link); break; } } klink = NULL; zone->uz_flags |= UMA_ZFLAG_MULTI; zone->uz_slab = zone_fetch_slab_multi; out: zone_unlock_pair(zone, master); if (klink != NULL) free(klink, M_TEMP); return (error); } /* See uma.h */ void uma_zdestroy(uma_zone_t zone) { + sx_slock(&uma_drain_lock); zone_free_item(zones, zone, NULL, SKIP_NONE); + sx_sunlock(&uma_drain_lock); } /* See uma.h */ void * uma_zalloc_arg(uma_zone_t zone, void *udata, int flags) { void *item; uma_cache_t cache; uma_bucket_t bucket; int lockfail; int cpu; #if 0 /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ /* The entropy here is desirable, but the harvesting is expensive */ random_harvest(&(zone->uz_name), sizeof(void *), 1, RANDOM_UMA_ALLOC); #endif /* This is the fast path allocation */ #ifdef UMA_DEBUG_ALLOC_1 printf("Allocating one item from %s(%p)\n", zone->uz_name, zone); #endif CTR3(KTR_UMA, "uma_zalloc_arg thread %x zone %s flags %d", curthread, zone->uz_name, flags); if (flags & M_WAITOK) { WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, "uma_zalloc_arg: zone \"%s\"", zone->uz_name); } #ifdef DEBUG_MEMGUARD if (memguard_cmp_zone(zone)) { item = memguard_alloc(zone->uz_size, flags); if (item != NULL) { /* * Avoid conflict with the use-after-free * protecting infrastructure from INVARIANTS. */ if (zone->uz_init != NULL && zone->uz_init != mtrash_init && zone->uz_init(item, zone->uz_size, flags) != 0) return (NULL); if (zone->uz_ctor != NULL && zone->uz_ctor != mtrash_ctor && zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) { zone->uz_fini(item, zone->uz_size); return (NULL); } #if 0 /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ /* The entropy here is desirable, but the harvesting is expensive */ random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC); #endif return (item); } /* This is unfortunate but should not be fatal. */ } #endif /* * If possible, allocate from the per-CPU cache. There are two * requirements for safe access to the per-CPU cache: (1) the thread * accessing the cache must not be preempted or yield during access, * and (2) the thread must not migrate CPUs without switching which * cache it accesses. We rely on a critical section to prevent * preemption and migration. We release the critical section in * order to acquire the zone mutex if we are unable to allocate from * the current cache; when we re-acquire the critical section, we * must detect and handle migration if it has occurred. */ critical_enter(); cpu = curcpu; cache = &zone->uz_cpu[cpu]; zalloc_start: bucket = cache->uc_allocbucket; if (bucket != NULL && bucket->ub_cnt > 0) { bucket->ub_cnt--; item = bucket->ub_bucket[bucket->ub_cnt]; #ifdef INVARIANTS bucket->ub_bucket[bucket->ub_cnt] = NULL; #endif KASSERT(item != NULL, ("uma_zalloc: Bucket pointer mangled.")); cache->uc_allocs++; critical_exit(); if (zone->uz_ctor != NULL && zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) { atomic_add_long(&zone->uz_fails, 1); zone_free_item(zone, item, udata, SKIP_DTOR); return (NULL); } #ifdef INVARIANTS uma_dbg_alloc(zone, NULL, item); #endif if (flags & M_ZERO) uma_zero_item(item, zone); #if 0 /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ /* The entropy here is desirable, but the harvesting is expensive */ random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC); #endif return (item); } /* * We have run out of items in our alloc bucket. * See if we can switch with our free bucket. */ bucket = cache->uc_freebucket; if (bucket != NULL && bucket->ub_cnt > 0) { #ifdef UMA_DEBUG_ALLOC printf("uma_zalloc: Swapping empty with alloc.\n"); #endif cache->uc_freebucket = cache->uc_allocbucket; cache->uc_allocbucket = bucket; goto zalloc_start; } /* * Discard any empty allocation bucket while we hold no locks. */ bucket = cache->uc_allocbucket; cache->uc_allocbucket = NULL; critical_exit(); if (bucket != NULL) bucket_free(zone, bucket, udata); /* Short-circuit for zones without buckets and low memory. */ if (zone->uz_count == 0 || bucketdisable) goto zalloc_item; /* * Attempt to retrieve the item from the per-CPU cache has failed, so * we must go back to the zone. This requires the zone lock, so we * must drop the critical section, then re-acquire it when we go back * to the cache. Since the critical section is released, we may be * preempted or migrate. As such, make sure not to maintain any * thread-local state specific to the cache from prior to releasing * the critical section. */ lockfail = 0; if (ZONE_TRYLOCK(zone) == 0) { /* Record contention to size the buckets. */ ZONE_LOCK(zone); lockfail = 1; } critical_enter(); cpu = curcpu; cache = &zone->uz_cpu[cpu]; /* * Since we have locked the zone we may as well send back our stats. */ atomic_add_long(&zone->uz_allocs, cache->uc_allocs); atomic_add_long(&zone->uz_frees, cache->uc_frees); cache->uc_allocs = 0; cache->uc_frees = 0; /* See if we lost the race to fill the cache. */ if (cache->uc_allocbucket != NULL) { ZONE_UNLOCK(zone); goto zalloc_start; } /* * Check the zone's cache of buckets. */ if ((bucket = LIST_FIRST(&zone->uz_buckets)) != NULL) { KASSERT(bucket->ub_cnt != 0, ("uma_zalloc_arg: Returning an empty bucket.")); LIST_REMOVE(bucket, ub_link); cache->uc_allocbucket = bucket; ZONE_UNLOCK(zone); goto zalloc_start; } /* We are no longer associated with this CPU. */ critical_exit(); /* * We bump the uz count when the cache size is insufficient to * handle the working set. */ if (lockfail && zone->uz_count < BUCKET_MAX) zone->uz_count++; ZONE_UNLOCK(zone); /* * Now lets just fill a bucket and put it on the free list. If that * works we'll restart the allocation from the begining and it * will use the just filled bucket. */ bucket = zone_alloc_bucket(zone, udata, flags); if (bucket != NULL) { ZONE_LOCK(zone); critical_enter(); cpu = curcpu; cache = &zone->uz_cpu[cpu]; /* * See if we lost the race or were migrated. Cache the * initialized bucket to make this less likely or claim * the memory directly. */ if (cache->uc_allocbucket == NULL) cache->uc_allocbucket = bucket; else LIST_INSERT_HEAD(&zone->uz_buckets, bucket, ub_link); ZONE_UNLOCK(zone); goto zalloc_start; } /* * We may not be able to get a bucket so return an actual item. */ #ifdef UMA_DEBUG printf("uma_zalloc_arg: Bucketzone returned NULL\n"); #endif zalloc_item: item = zone_alloc_item(zone, udata, flags); #if 0 /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ /* The entropy here is desirable, but the harvesting is expensive */ random_harvest(&item, sizeof(void *), 1, RANDOM_UMA_ALLOC); #endif return (item); } static uma_slab_t keg_fetch_slab(uma_keg_t keg, uma_zone_t zone, int flags) { uma_slab_t slab; int reserve; mtx_assert(&keg->uk_lock, MA_OWNED); slab = NULL; reserve = 0; if ((flags & M_USE_RESERVE) == 0) reserve = keg->uk_reserve; for (;;) { /* * Find a slab with some space. Prefer slabs that are partially * used over those that are totally full. This helps to reduce * fragmentation. */ if (keg->uk_free > reserve) { if (!LIST_EMPTY(&keg->uk_part_slab)) { slab = LIST_FIRST(&keg->uk_part_slab); } else { slab = LIST_FIRST(&keg->uk_free_slab); LIST_REMOVE(slab, us_link); LIST_INSERT_HEAD(&keg->uk_part_slab, slab, us_link); } MPASS(slab->us_keg == keg); return (slab); } /* * M_NOVM means don't ask at all! */ if (flags & M_NOVM) break; if (keg->uk_maxpages && keg->uk_pages >= keg->uk_maxpages) { keg->uk_flags |= UMA_ZFLAG_FULL; /* * If this is not a multi-zone, set the FULL bit. * Otherwise slab_multi() takes care of it. */ if ((zone->uz_flags & UMA_ZFLAG_MULTI) == 0) { zone->uz_flags |= UMA_ZFLAG_FULL; zone_log_warning(zone); } if (flags & M_NOWAIT) break; zone->uz_sleeps++; msleep(keg, &keg->uk_lock, PVM, "keglimit", 0); continue; } slab = keg_alloc_slab(keg, zone, flags); /* * If we got a slab here it's safe to mark it partially used * and return. We assume that the caller is going to remove * at least one item. */ if (slab) { MPASS(slab->us_keg == keg); LIST_INSERT_HEAD(&keg->uk_part_slab, slab, us_link); return (slab); } /* * We might not have been able to get a slab but another cpu * could have while we were unlocked. Check again before we * fail. */ flags |= M_NOVM; } return (slab); } static uma_slab_t zone_fetch_slab(uma_zone_t zone, uma_keg_t keg, int flags) { uma_slab_t slab; if (keg == NULL) { keg = zone_first_keg(zone); KEG_LOCK(keg); } for (;;) { slab = keg_fetch_slab(keg, zone, flags); if (slab) return (slab); if (flags & (M_NOWAIT | M_NOVM)) break; } KEG_UNLOCK(keg); return (NULL); } /* * uma_zone_fetch_slab_multi: Fetches a slab from one available keg. Returns * with the keg locked. On NULL no lock is held. * * The last pointer is used to seed the search. It is not required. */ static uma_slab_t zone_fetch_slab_multi(uma_zone_t zone, uma_keg_t last, int rflags) { uma_klink_t klink; uma_slab_t slab; uma_keg_t keg; int flags; int empty; int full; /* * Don't wait on the first pass. This will skip limit tests * as well. We don't want to block if we can find a provider * without blocking. */ flags = (rflags & ~M_WAITOK) | M_NOWAIT; /* * Use the last slab allocated as a hint for where to start * the search. */ if (last != NULL) { slab = keg_fetch_slab(last, zone, flags); if (slab) return (slab); KEG_UNLOCK(last); } /* * Loop until we have a slab incase of transient failures * while M_WAITOK is specified. I'm not sure this is 100% * required but we've done it for so long now. */ for (;;) { empty = 0; full = 0; /* * Search the available kegs for slabs. Be careful to hold the * correct lock while calling into the keg layer. */ LIST_FOREACH(klink, &zone->uz_kegs, kl_link) { keg = klink->kl_keg; KEG_LOCK(keg); if ((keg->uk_flags & UMA_ZFLAG_FULL) == 0) { slab = keg_fetch_slab(keg, zone, flags); if (slab) return (slab); } if (keg->uk_flags & UMA_ZFLAG_FULL) full++; else empty++; KEG_UNLOCK(keg); } if (rflags & (M_NOWAIT | M_NOVM)) break; flags = rflags; /* * All kegs are full. XXX We can't atomically check all kegs * and sleep so just sleep for a short period and retry. */ if (full && !empty) { ZONE_LOCK(zone); zone->uz_flags |= UMA_ZFLAG_FULL; zone->uz_sleeps++; zone_log_warning(zone); msleep(zone, zone->uz_lockptr, PVM, "zonelimit", hz/100); zone->uz_flags &= ~UMA_ZFLAG_FULL; ZONE_UNLOCK(zone); continue; } } return (NULL); } static void * slab_alloc_item(uma_keg_t keg, uma_slab_t slab) { void *item; uint8_t freei; MPASS(keg == slab->us_keg); mtx_assert(&keg->uk_lock, MA_OWNED); freei = BIT_FFS(SLAB_SETSIZE, &slab->us_free) - 1; BIT_CLR(SLAB_SETSIZE, freei, &slab->us_free); item = slab->us_data + (keg->uk_rsize * freei); slab->us_freecount--; keg->uk_free--; /* Move this slab to the full list */ if (slab->us_freecount == 0) { LIST_REMOVE(slab, us_link); LIST_INSERT_HEAD(&keg->uk_full_slab, slab, us_link); } return (item); } static int zone_import(uma_zone_t zone, void **bucket, int max, int flags) { uma_slab_t slab; uma_keg_t keg; int i; slab = NULL; keg = NULL; /* Try to keep the buckets totally full */ for (i = 0; i < max; ) { if ((slab = zone->uz_slab(zone, keg, flags)) == NULL) break; keg = slab->us_keg; while (slab->us_freecount && i < max) { bucket[i++] = slab_alloc_item(keg, slab); if (keg->uk_free <= keg->uk_reserve) break; } /* Don't grab more than one slab at a time. */ flags &= ~M_WAITOK; flags |= M_NOWAIT; } if (slab != NULL) KEG_UNLOCK(keg); return i; } static uma_bucket_t zone_alloc_bucket(uma_zone_t zone, void *udata, int flags) { uma_bucket_t bucket; int max; /* Don't wait for buckets, preserve caller's NOVM setting. */ bucket = bucket_alloc(zone, udata, M_NOWAIT | (flags & M_NOVM)); if (bucket == NULL) return (NULL); max = MIN(bucket->ub_entries, zone->uz_count); bucket->ub_cnt = zone->uz_import(zone->uz_arg, bucket->ub_bucket, max, flags); /* * Initialize the memory if necessary. */ if (bucket->ub_cnt != 0 && zone->uz_init != NULL) { int i; for (i = 0; i < bucket->ub_cnt; i++) if (zone->uz_init(bucket->ub_bucket[i], zone->uz_size, flags) != 0) break; /* * If we couldn't initialize the whole bucket, put the * rest back onto the freelist. */ if (i != bucket->ub_cnt) { zone->uz_release(zone->uz_arg, &bucket->ub_bucket[i], bucket->ub_cnt - i); #ifdef INVARIANTS bzero(&bucket->ub_bucket[i], sizeof(void *) * (bucket->ub_cnt - i)); #endif bucket->ub_cnt = i; } } if (bucket->ub_cnt == 0) { bucket_free(zone, bucket, udata); atomic_add_long(&zone->uz_fails, 1); return (NULL); } return (bucket); } /* * Allocates a single item from a zone. * * Arguments * zone The zone to alloc for. * udata The data to be passed to the constructor. * flags M_WAITOK, M_NOWAIT, M_ZERO. * * Returns * NULL if there is no memory and M_NOWAIT is set * An item if successful */ static void * zone_alloc_item(uma_zone_t zone, void *udata, int flags) { void *item; item = NULL; #ifdef UMA_DEBUG_ALLOC printf("INTERNAL: Allocating one item from %s(%p)\n", zone->uz_name, zone); #endif if (zone->uz_import(zone->uz_arg, &item, 1, flags) != 1) goto fail; atomic_add_long(&zone->uz_allocs, 1); /* * We have to call both the zone's init (not the keg's init) * and the zone's ctor. This is because the item is going from * a keg slab directly to the user, and the user is expecting it * to be both zone-init'd as well as zone-ctor'd. */ if (zone->uz_init != NULL) { if (zone->uz_init(item, zone->uz_size, flags) != 0) { zone_free_item(zone, item, udata, SKIP_FINI); goto fail; } } if (zone->uz_ctor != NULL) { if (zone->uz_ctor(item, zone->uz_size, udata, flags) != 0) { zone_free_item(zone, item, udata, SKIP_DTOR); goto fail; } } #ifdef INVARIANTS uma_dbg_alloc(zone, NULL, item); #endif if (flags & M_ZERO) uma_zero_item(item, zone); return (item); fail: atomic_add_long(&zone->uz_fails, 1); return (NULL); } /* See uma.h */ void uma_zfree_arg(uma_zone_t zone, void *item, void *udata) { uma_cache_t cache; uma_bucket_t bucket; int lockfail; int cpu; #if 0 /* XXX: FIX!! Do not enable this in CURRENT!! MarkM */ /* The entropy here is desirable, but the harvesting is expensive */ struct entropy { const void *uz_name; const void *item; } entropy; entropy.uz_name = zone->uz_name; entropy.item = item; random_harvest(&entropy, sizeof(struct entropy), 2, RANDOM_UMA_ALLOC); #endif #ifdef UMA_DEBUG_ALLOC_1 printf("Freeing item %p to %s(%p)\n", item, zone->uz_name, zone); #endif CTR2(KTR_UMA, "uma_zfree_arg thread %x zone %s", curthread, zone->uz_name); /* uma_zfree(..., NULL) does nothing, to match free(9). */ if (item == NULL) return; #ifdef DEBUG_MEMGUARD if (is_memguard_addr(item)) { if (zone->uz_dtor != NULL && zone->uz_dtor != mtrash_dtor) zone->uz_dtor(item, zone->uz_size, udata); if (zone->uz_fini != NULL && zone->uz_fini != mtrash_fini) zone->uz_fini(item, zone->uz_size); memguard_free(item); return; } #endif #ifdef INVARIANTS if (zone->uz_flags & UMA_ZONE_MALLOC) uma_dbg_free(zone, udata, item); else uma_dbg_free(zone, NULL, item); #endif if (zone->uz_dtor != NULL) zone->uz_dtor(item, zone->uz_size, udata); /* * The race here is acceptable. If we miss it we'll just have to wait * a little longer for the limits to be reset. */ if (zone->uz_flags & UMA_ZFLAG_FULL) goto zfree_item; /* * If possible, free to the per-CPU cache. There are two * requirements for safe access to the per-CPU cache: (1) the thread * accessing the cache must not be preempted or yield during access, * and (2) the thread must not migrate CPUs without switching which * cache it accesses. We rely on a critical section to prevent * preemption and migration. We release the critical section in * order to acquire the zone mutex if we are unable to free to the * current cache; when we re-acquire the critical section, we must * detect and handle migration if it has occurred. */ zfree_restart: critical_enter(); cpu = curcpu; cache = &zone->uz_cpu[cpu]; zfree_start: /* * Try to free into the allocbucket first to give LIFO ordering * for cache-hot datastructures. Spill over into the freebucket * if necessary. Alloc will swap them if one runs dry. */ bucket = cache->uc_allocbucket; if (bucket == NULL || bucket->ub_cnt >= bucket->ub_entries) bucket = cache->uc_freebucket; if (bucket != NULL && bucket->ub_cnt < bucket->ub_entries) { KASSERT(bucket->ub_bucket[bucket->ub_cnt] == NULL, ("uma_zfree: Freeing to non free bucket index.")); bucket->ub_bucket[bucket->ub_cnt] = item; bucket->ub_cnt++; cache->uc_frees++; critical_exit(); return; } /* * We must go back the zone, which requires acquiring the zone lock, * which in turn means we must release and re-acquire the critical * section. Since the critical section is released, we may be * preempted or migrate. As such, make sure not to maintain any * thread-local state specific to the cache from prior to releasing * the critical section. */ critical_exit(); if (zone->uz_count == 0 || bucketdisable) goto zfree_item; lockfail = 0; if (ZONE_TRYLOCK(zone) == 0) { /* Record contention to size the buckets. */ ZONE_LOCK(zone); lockfail = 1; } critical_enter(); cpu = curcpu; cache = &zone->uz_cpu[cpu]; /* * Since we have locked the zone we may as well send back our stats. */ atomic_add_long(&zone->uz_allocs, cache->uc_allocs); atomic_add_long(&zone->uz_frees, cache->uc_frees); cache->uc_allocs = 0; cache->uc_frees = 0; bucket = cache->uc_freebucket; if (bucket != NULL && bucket->ub_cnt < bucket->ub_entries) { ZONE_UNLOCK(zone); goto zfree_start; } cache->uc_freebucket = NULL; /* Can we throw this on the zone full list? */ if (bucket != NULL) { #ifdef UMA_DEBUG_ALLOC printf("uma_zfree: Putting old bucket on the free list.\n"); #endif /* ub_cnt is pointing to the last free item */ KASSERT(bucket->ub_cnt != 0, ("uma_zfree: Attempting to insert an empty bucket onto the full list.\n")); LIST_INSERT_HEAD(&zone->uz_buckets, bucket, ub_link); } /* We are no longer associated with this CPU. */ critical_exit(); /* * We bump the uz count when the cache size is insufficient to * handle the working set. */ if (lockfail && zone->uz_count < BUCKET_MAX) zone->uz_count++; ZONE_UNLOCK(zone); #ifdef UMA_DEBUG_ALLOC printf("uma_zfree: Allocating new free bucket.\n"); #endif bucket = bucket_alloc(zone, udata, M_NOWAIT); if (bucket) { critical_enter(); cpu = curcpu; cache = &zone->uz_cpu[cpu]; if (cache->uc_freebucket == NULL) { cache->uc_freebucket = bucket; goto zfree_start; } /* * We lost the race, start over. We have to drop our * critical section to free the bucket. */ critical_exit(); bucket_free(zone, bucket, udata); goto zfree_restart; } /* * If nothing else caught this, we'll just do an internal free. */ zfree_item: zone_free_item(zone, item, udata, SKIP_DTOR); return; } static void slab_free_item(uma_keg_t keg, uma_slab_t slab, void *item) { uint8_t freei; mtx_assert(&keg->uk_lock, MA_OWNED); MPASS(keg == slab->us_keg); /* Do we need to remove from any lists? */ if (slab->us_freecount+1 == keg->uk_ipers) { LIST_REMOVE(slab, us_link); LIST_INSERT_HEAD(&keg->uk_free_slab, slab, us_link); } else if (slab->us_freecount == 0) { LIST_REMOVE(slab, us_link); LIST_INSERT_HEAD(&keg->uk_part_slab, slab, us_link); } /* Slab management. */ freei = ((uintptr_t)item - (uintptr_t)slab->us_data) / keg->uk_rsize; BIT_SET(SLAB_SETSIZE, freei, &slab->us_free); slab->us_freecount++; /* Keg statistics. */ keg->uk_free++; } static void zone_release(uma_zone_t zone, void **bucket, int cnt) { void *item; uma_slab_t slab; uma_keg_t keg; uint8_t *mem; int clearfull; int i; clearfull = 0; keg = zone_first_keg(zone); KEG_LOCK(keg); for (i = 0; i < cnt; i++) { item = bucket[i]; if (!(zone->uz_flags & UMA_ZONE_VTOSLAB)) { mem = (uint8_t *)((uintptr_t)item & (~UMA_SLAB_MASK)); if (zone->uz_flags & UMA_ZONE_HASH) { slab = hash_sfind(&keg->uk_hash, mem); } else { mem += keg->uk_pgoff; slab = (uma_slab_t)mem; } } else { slab = vtoslab((vm_offset_t)item); if (slab->us_keg != keg) { KEG_UNLOCK(keg); keg = slab->us_keg; KEG_LOCK(keg); } } slab_free_item(keg, slab, item); if (keg->uk_flags & UMA_ZFLAG_FULL) { if (keg->uk_pages < keg->uk_maxpages) { keg->uk_flags &= ~UMA_ZFLAG_FULL; clearfull = 1; } /* * We can handle one more allocation. Since we're * clearing ZFLAG_FULL, wake up all procs blocked * on pages. This should be uncommon, so keeping this * simple for now (rather than adding count of blocked * threads etc). */ wakeup(keg); } } KEG_UNLOCK(keg); if (clearfull) { ZONE_LOCK(zone); zone->uz_flags &= ~UMA_ZFLAG_FULL; wakeup(zone); ZONE_UNLOCK(zone); } } /* * Frees a single item to any zone. * * Arguments: * zone The zone to free to * item The item we're freeing * udata User supplied data for the dtor * skip Skip dtors and finis */ static void zone_free_item(uma_zone_t zone, void *item, void *udata, enum zfreeskip skip) { #ifdef INVARIANTS if (skip == SKIP_NONE) { if (zone->uz_flags & UMA_ZONE_MALLOC) uma_dbg_free(zone, udata, item); else uma_dbg_free(zone, NULL, item); } #endif if (skip < SKIP_DTOR && zone->uz_dtor) zone->uz_dtor(item, zone->uz_size, udata); if (skip < SKIP_FINI && zone->uz_fini) zone->uz_fini(item, zone->uz_size); atomic_add_long(&zone->uz_frees, 1); zone->uz_release(zone->uz_arg, &item, 1); } /* See uma.h */ int uma_zone_set_max(uma_zone_t zone, int nitems) { uma_keg_t keg; keg = zone_first_keg(zone); if (keg == NULL) return (0); KEG_LOCK(keg); keg->uk_maxpages = (nitems / keg->uk_ipers) * keg->uk_ppera; if (keg->uk_maxpages * keg->uk_ipers < nitems) keg->uk_maxpages += keg->uk_ppera; nitems = keg->uk_maxpages * keg->uk_ipers; KEG_UNLOCK(keg); return (nitems); } /* See uma.h */ int uma_zone_get_max(uma_zone_t zone) { int nitems; uma_keg_t keg; keg = zone_first_keg(zone); if (keg == NULL) return (0); KEG_LOCK(keg); nitems = keg->uk_maxpages * keg->uk_ipers; KEG_UNLOCK(keg); return (nitems); } /* See uma.h */ void uma_zone_set_warning(uma_zone_t zone, const char *warning) { ZONE_LOCK(zone); zone->uz_warning = warning; ZONE_UNLOCK(zone); } /* See uma.h */ int uma_zone_get_cur(uma_zone_t zone) { int64_t nitems; u_int i; ZONE_LOCK(zone); nitems = zone->uz_allocs - zone->uz_frees; CPU_FOREACH(i) { /* * See the comment in sysctl_vm_zone_stats() regarding the * safety of accessing the per-cpu caches. With the zone lock * held, it is safe, but can potentially result in stale data. */ nitems += zone->uz_cpu[i].uc_allocs - zone->uz_cpu[i].uc_frees; } ZONE_UNLOCK(zone); return (nitems < 0 ? 0 : nitems); } /* See uma.h */ void uma_zone_set_init(uma_zone_t zone, uma_init uminit) { uma_keg_t keg; keg = zone_first_keg(zone); KASSERT(keg != NULL, ("uma_zone_set_init: Invalid zone type")); KEG_LOCK(keg); KASSERT(keg->uk_pages == 0, ("uma_zone_set_init on non-empty keg")); keg->uk_init = uminit; KEG_UNLOCK(keg); } /* See uma.h */ void uma_zone_set_fini(uma_zone_t zone, uma_fini fini) { uma_keg_t keg; keg = zone_first_keg(zone); KASSERT(keg != NULL, ("uma_zone_set_init: Invalid zone type")); KEG_LOCK(keg); KASSERT(keg->uk_pages == 0, ("uma_zone_set_fini on non-empty keg")); keg->uk_fini = fini; KEG_UNLOCK(keg); } /* See uma.h */ void uma_zone_set_zinit(uma_zone_t zone, uma_init zinit) { ZONE_LOCK(zone); KASSERT(zone_first_keg(zone)->uk_pages == 0, ("uma_zone_set_zinit on non-empty keg")); zone->uz_init = zinit; ZONE_UNLOCK(zone); } /* See uma.h */ void uma_zone_set_zfini(uma_zone_t zone, uma_fini zfini) { ZONE_LOCK(zone); KASSERT(zone_first_keg(zone)->uk_pages == 0, ("uma_zone_set_zfini on non-empty keg")); zone->uz_fini = zfini; ZONE_UNLOCK(zone); } /* See uma.h */ /* XXX uk_freef is not actually used with the zone locked */ void uma_zone_set_freef(uma_zone_t zone, uma_free freef) { uma_keg_t keg; keg = zone_first_keg(zone); KASSERT(keg != NULL, ("uma_zone_set_init: Invalid zone type")); KEG_LOCK(keg); keg->uk_freef = freef; KEG_UNLOCK(keg); } /* See uma.h */ /* XXX uk_allocf is not actually used with the zone locked */ void uma_zone_set_allocf(uma_zone_t zone, uma_alloc allocf) { uma_keg_t keg; keg = zone_first_keg(zone); KEG_LOCK(keg); keg->uk_allocf = allocf; KEG_UNLOCK(keg); } /* See uma.h */ void uma_zone_reserve(uma_zone_t zone, int items) { uma_keg_t keg; keg = zone_first_keg(zone); if (keg == NULL) return; KEG_LOCK(keg); keg->uk_reserve = items; KEG_UNLOCK(keg); return; } /* See uma.h */ int uma_zone_reserve_kva(uma_zone_t zone, int count) { uma_keg_t keg; vm_offset_t kva; int pages; keg = zone_first_keg(zone); if (keg == NULL) return (0); pages = count / keg->uk_ipers; if (pages * keg->uk_ipers < count) pages++; #ifdef UMA_MD_SMALL_ALLOC if (keg->uk_ppera > 1) { #else if (1) { #endif kva = kva_alloc(pages * UMA_SLAB_SIZE); if (kva == 0) return (0); } else kva = 0; KEG_LOCK(keg); keg->uk_kva = kva; keg->uk_offset = 0; keg->uk_maxpages = pages; #ifdef UMA_MD_SMALL_ALLOC keg->uk_allocf = (keg->uk_ppera > 1) ? noobj_alloc : uma_small_alloc; #else keg->uk_allocf = noobj_alloc; #endif keg->uk_flags |= UMA_ZONE_NOFREE; KEG_UNLOCK(keg); return (1); } /* See uma.h */ void uma_prealloc(uma_zone_t zone, int items) { int slabs; uma_slab_t slab; uma_keg_t keg; keg = zone_first_keg(zone); if (keg == NULL) return; KEG_LOCK(keg); slabs = items / keg->uk_ipers; if (slabs * keg->uk_ipers < items) slabs++; while (slabs > 0) { slab = keg_alloc_slab(keg, zone, M_WAITOK); if (slab == NULL) break; MPASS(slab->us_keg == keg); LIST_INSERT_HEAD(&keg->uk_free_slab, slab, us_link); slabs--; } KEG_UNLOCK(keg); } /* See uma.h */ uint32_t * uma_find_refcnt(uma_zone_t zone, void *item) { uma_slabrefcnt_t slabref; uma_slab_t slab; uma_keg_t keg; uint32_t *refcnt; int idx; slab = vtoslab((vm_offset_t)item & (~UMA_SLAB_MASK)); slabref = (uma_slabrefcnt_t)slab; keg = slab->us_keg; KASSERT(keg->uk_flags & UMA_ZONE_REFCNT, ("uma_find_refcnt(): zone possibly not UMA_ZONE_REFCNT")); idx = ((uintptr_t)item - (uintptr_t)slab->us_data) / keg->uk_rsize; refcnt = &slabref->us_refcnt[idx]; return refcnt; } /* See uma.h */ void uma_reclaim(void) { #ifdef UMA_DEBUG printf("UMA: vm asked us to release pages!\n"); #endif + sx_xlock(&uma_drain_lock); bucket_enable(); zone_foreach(zone_drain); if (vm_page_count_min()) { cache_drain_safe(NULL); zone_foreach(zone_drain); } /* * Some slabs may have been freed but this zone will be visited early * we visit again so that we can free pages that are empty once other * zones are drained. We have to do the same for buckets. */ zone_drain(slabzone); zone_drain(slabrefzone); bucket_zone_drain(); + sx_xunlock(&uma_drain_lock); } /* See uma.h */ int uma_zone_exhausted(uma_zone_t zone) { int full; ZONE_LOCK(zone); full = (zone->uz_flags & UMA_ZFLAG_FULL); ZONE_UNLOCK(zone); return (full); } int uma_zone_exhausted_nolock(uma_zone_t zone) { return (zone->uz_flags & UMA_ZFLAG_FULL); } void * uma_large_malloc(int size, int wait) { void *mem; uma_slab_t slab; uint8_t flags; slab = zone_alloc_item(slabzone, NULL, wait); if (slab == NULL) return (NULL); mem = page_alloc(NULL, size, &flags, wait); if (mem) { vsetslab((vm_offset_t)mem, slab); slab->us_data = mem; slab->us_flags = flags | UMA_SLAB_MALLOC; slab->us_size = size; } else { zone_free_item(slabzone, slab, NULL, SKIP_NONE); } return (mem); } void uma_large_free(uma_slab_t slab) { page_free(slab->us_data, slab->us_size, slab->us_flags); zone_free_item(slabzone, slab, NULL, SKIP_NONE); } static void uma_zero_item(void *item, uma_zone_t zone) { if (zone->uz_flags & UMA_ZONE_PCPU) { for (int i = 0; i < mp_ncpus; i++) bzero(zpcpu_get_cpu(item, i), zone->uz_size); } else bzero(item, zone->uz_size); } void uma_print_stats(void) { zone_foreach(uma_print_zone); } static void slab_print(uma_slab_t slab) { printf("slab: keg %p, data %p, freecount %d\n", slab->us_keg, slab->us_data, slab->us_freecount); } static void cache_print(uma_cache_t cache) { printf("alloc: %p(%d), free: %p(%d)\n", cache->uc_allocbucket, cache->uc_allocbucket?cache->uc_allocbucket->ub_cnt:0, cache->uc_freebucket, cache->uc_freebucket?cache->uc_freebucket->ub_cnt:0); } static void uma_print_keg(uma_keg_t keg) { uma_slab_t slab; printf("keg: %s(%p) size %d(%d) flags %#x ipers %d ppera %d " "out %d free %d limit %d\n", keg->uk_name, keg, keg->uk_size, keg->uk_rsize, keg->uk_flags, keg->uk_ipers, keg->uk_ppera, (keg->uk_ipers * keg->uk_pages) - keg->uk_free, keg->uk_free, (keg->uk_maxpages / keg->uk_ppera) * keg->uk_ipers); printf("Part slabs:\n"); LIST_FOREACH(slab, &keg->uk_part_slab, us_link) slab_print(slab); printf("Free slabs:\n"); LIST_FOREACH(slab, &keg->uk_free_slab, us_link) slab_print(slab); printf("Full slabs:\n"); LIST_FOREACH(slab, &keg->uk_full_slab, us_link) slab_print(slab); } void uma_print_zone(uma_zone_t zone) { uma_cache_t cache; uma_klink_t kl; int i; printf("zone: %s(%p) size %d flags %#x\n", zone->uz_name, zone, zone->uz_size, zone->uz_flags); LIST_FOREACH(kl, &zone->uz_kegs, kl_link) uma_print_keg(kl->kl_keg); CPU_FOREACH(i) { cache = &zone->uz_cpu[i]; printf("CPU %d Cache:\n", i); cache_print(cache); } } #ifdef DDB /* * Generate statistics across both the zone and its per-cpu cache's. Return * desired statistics if the pointer is non-NULL for that statistic. * * Note: does not update the zone statistics, as it can't safely clear the * per-CPU cache statistic. * * XXXRW: Following the uc_allocbucket and uc_freebucket pointers here isn't * safe from off-CPU; we should modify the caches to track this information * directly so that we don't have to. */ static void uma_zone_sumstat(uma_zone_t z, int *cachefreep, uint64_t *allocsp, uint64_t *freesp, uint64_t *sleepsp) { uma_cache_t cache; uint64_t allocs, frees, sleeps; int cachefree, cpu; allocs = frees = sleeps = 0; cachefree = 0; CPU_FOREACH(cpu) { cache = &z->uz_cpu[cpu]; if (cache->uc_allocbucket != NULL) cachefree += cache->uc_allocbucket->ub_cnt; if (cache->uc_freebucket != NULL) cachefree += cache->uc_freebucket->ub_cnt; allocs += cache->uc_allocs; frees += cache->uc_frees; } allocs += z->uz_allocs; frees += z->uz_frees; sleeps += z->uz_sleeps; if (cachefreep != NULL) *cachefreep = cachefree; if (allocsp != NULL) *allocsp = allocs; if (freesp != NULL) *freesp = frees; if (sleepsp != NULL) *sleepsp = sleeps; } #endif /* DDB */ static int sysctl_vm_zone_count(SYSCTL_HANDLER_ARGS) { uma_keg_t kz; uma_zone_t z; int count; count = 0; rw_rlock(&uma_rwlock); LIST_FOREACH(kz, &uma_kegs, uk_link) { LIST_FOREACH(z, &kz->uk_zones, uz_link) count++; } rw_runlock(&uma_rwlock); return (sysctl_handle_int(oidp, &count, 0, req)); } static int sysctl_vm_zone_stats(SYSCTL_HANDLER_ARGS) { struct uma_stream_header ush; struct uma_type_header uth; struct uma_percpu_stat ups; uma_bucket_t bucket; struct sbuf sbuf; uma_cache_t cache; uma_klink_t kl; uma_keg_t kz; uma_zone_t z; uma_keg_t k; int count, error, i; error = sysctl_wire_old_buffer(req, 0); if (error != 0) return (error); sbuf_new_for_sysctl(&sbuf, NULL, 128, req); count = 0; rw_rlock(&uma_rwlock); LIST_FOREACH(kz, &uma_kegs, uk_link) { LIST_FOREACH(z, &kz->uk_zones, uz_link) count++; } /* * Insert stream header. */ bzero(&ush, sizeof(ush)); ush.ush_version = UMA_STREAM_VERSION; ush.ush_maxcpus = (mp_maxid + 1); ush.ush_count = count; (void)sbuf_bcat(&sbuf, &ush, sizeof(ush)); LIST_FOREACH(kz, &uma_kegs, uk_link) { LIST_FOREACH(z, &kz->uk_zones, uz_link) { bzero(&uth, sizeof(uth)); ZONE_LOCK(z); strlcpy(uth.uth_name, z->uz_name, UTH_MAX_NAME); uth.uth_align = kz->uk_align; uth.uth_size = kz->uk_size; uth.uth_rsize = kz->uk_rsize; LIST_FOREACH(kl, &z->uz_kegs, kl_link) { k = kl->kl_keg; uth.uth_maxpages += k->uk_maxpages; uth.uth_pages += k->uk_pages; uth.uth_keg_free += k->uk_free; uth.uth_limit = (k->uk_maxpages / k->uk_ppera) * k->uk_ipers; } /* * A zone is secondary is it is not the first entry * on the keg's zone list. */ if ((z->uz_flags & UMA_ZONE_SECONDARY) && (LIST_FIRST(&kz->uk_zones) != z)) uth.uth_zone_flags = UTH_ZONE_SECONDARY; LIST_FOREACH(bucket, &z->uz_buckets, ub_link) uth.uth_zone_free += bucket->ub_cnt; uth.uth_allocs = z->uz_allocs; uth.uth_frees = z->uz_frees; uth.uth_fails = z->uz_fails; uth.uth_sleeps = z->uz_sleeps; (void)sbuf_bcat(&sbuf, &uth, sizeof(uth)); /* * While it is not normally safe to access the cache * bucket pointers while not on the CPU that owns the * cache, we only allow the pointers to be exchanged * without the zone lock held, not invalidated, so * accept the possible race associated with bucket * exchange during monitoring. */ for (i = 0; i < (mp_maxid + 1); i++) { bzero(&ups, sizeof(ups)); if (kz->uk_flags & UMA_ZFLAG_INTERNAL) goto skip; if (CPU_ABSENT(i)) goto skip; cache = &z->uz_cpu[i]; if (cache->uc_allocbucket != NULL) ups.ups_cache_free += cache->uc_allocbucket->ub_cnt; if (cache->uc_freebucket != NULL) ups.ups_cache_free += cache->uc_freebucket->ub_cnt; ups.ups_allocs = cache->uc_allocs; ups.ups_frees = cache->uc_frees; skip: (void)sbuf_bcat(&sbuf, &ups, sizeof(ups)); } ZONE_UNLOCK(z); } } rw_runlock(&uma_rwlock); error = sbuf_finish(&sbuf); sbuf_delete(&sbuf); return (error); } int sysctl_handle_uma_zone_max(SYSCTL_HANDLER_ARGS) { uma_zone_t zone = *(uma_zone_t *)arg1; int error, max, old; old = max = uma_zone_get_max(zone); error = sysctl_handle_int(oidp, &max, 0, req); if (error || !req->newptr) return (error); if (max < old) return (EINVAL); uma_zone_set_max(zone, max); return (0); } int sysctl_handle_uma_zone_cur(SYSCTL_HANDLER_ARGS) { uma_zone_t zone = *(uma_zone_t *)arg1; int cur; cur = uma_zone_get_cur(zone); return (sysctl_handle_int(oidp, &cur, 0, req)); } #ifdef DDB DB_SHOW_COMMAND(uma, db_show_uma) { uint64_t allocs, frees, sleeps; uma_bucket_t bucket; uma_keg_t kz; uma_zone_t z; int cachefree; db_printf("%18s %8s %8s %8s %12s %8s %8s\n", "Zone", "Size", "Used", "Free", "Requests", "Sleeps", "Bucket"); LIST_FOREACH(kz, &uma_kegs, uk_link) { LIST_FOREACH(z, &kz->uk_zones, uz_link) { if (kz->uk_flags & UMA_ZFLAG_INTERNAL) { allocs = z->uz_allocs; frees = z->uz_frees; sleeps = z->uz_sleeps; cachefree = 0; } else uma_zone_sumstat(z, &cachefree, &allocs, &frees, &sleeps); if (!((z->uz_flags & UMA_ZONE_SECONDARY) && (LIST_FIRST(&kz->uk_zones) != z))) cachefree += kz->uk_free; LIST_FOREACH(bucket, &z->uz_buckets, ub_link) cachefree += bucket->ub_cnt; db_printf("%18s %8ju %8jd %8d %12ju %8ju %8u\n", z->uz_name, (uintmax_t)kz->uk_size, (intmax_t)(allocs - frees), cachefree, (uintmax_t)allocs, sleeps, z->uz_count); if (db_pager_quit) return; } } } DB_SHOW_COMMAND(umacache, db_show_umacache) { uint64_t allocs, frees; uma_bucket_t bucket; uma_zone_t z; int cachefree; db_printf("%18s %8s %8s %8s %12s %8s\n", "Zone", "Size", "Used", "Free", "Requests", "Bucket"); LIST_FOREACH(z, &uma_cachezones, uz_link) { uma_zone_sumstat(z, &cachefree, &allocs, &frees, NULL); LIST_FOREACH(bucket, &z->uz_buckets, ub_link) cachefree += bucket->ub_cnt; db_printf("%18s %8ju %8jd %8d %12ju %8u\n", z->uz_name, (uintmax_t)z->uz_size, (intmax_t)(allocs - frees), cachefree, (uintmax_t)allocs, z->uz_count); if (db_pager_quit) return; } } #endif Index: projects/sendfile/sys =================================================================== --- projects/sendfile/sys (revision 275355) +++ projects/sendfile/sys (revision 275356) Property changes on: projects/sendfile/sys ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head/sys:r275330-275355 Index: projects/sendfile/tools/tools/shlib-compat/shlib-compat.py =================================================================== --- projects/sendfile/tools/tools/shlib-compat/shlib-compat.py (revision 275355) +++ projects/sendfile/tools/tools/shlib-compat/shlib-compat.py (revision 275356) @@ -1,1097 +1,1156 @@ #!/usr/bin/env python #- # Copyright (c) 2010 Gleb Kurtsou # 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$ import os import sys import re import optparse class Config(object): version = '0.1' # controlled by user verbose = 0 dump = False no_dump = False version_filter = None symbol_filter = None alias_prefixes = [] # misc opts objdump = 'objdump' dwarfdump = 'dwarfdump' # debug cmpcache_enabled = True dwarfcache_enabled = True w_alias = True w_cached = False w_symbol = True class FileConfig(object): filename = None out = sys.stdout def init(self, outname): if outname and outname != '-': self.out = open(outname, "w") origfile = FileConfig() newfile = FileConfig() + exclude_sym_default = [ + '^__bss_start$', + '^_edata$', + '^_end$', + '^_fini$', + '^_init$', + ] + @classmethod def init(cls): cls.version_filter = StrFilter() cls.symbol_filter = StrFilter() class App(object): result_code = 0 def warn(cond, msg): if cond: print >> sys.stderr, "WARN: " + msg # {{{ misc class StrFilter(object): def __init__(self): self.exclude = [] self.include = [] def compile(self): self.re_exclude = [ re.compile(x) for x in self.exclude ] self.re_include = [ re.compile(x) for x in self.include ] def match(self, s): if len(self.re_include): matched = False for r in self.re_include: if r.match(s): matched = True break if not matched: return False for r in self.re_exclude: if r.match(s): return False return True class Cache(object): class CacheStats(object): def __init__(self): self.hit = 0 self.miss = 0 def show(self, name): total = self.hit + self.miss if total == 0: ratio = '(undef)' else: ratio = '%f' % (self.hit/float(total)) return '%s cache stats: hit: %d; miss: %d; ratio: %s' % \ (name, self.hit, self.miss, ratio) def __init__(self, enabled=True, stats=None): self.enabled = enabled self.items = {} if stats == None: self.stats = Cache.CacheStats() else: self.stats = stats def get(self, id): if self.enabled and self.items.has_key(id): self.stats.hit += 1 return self.items[id] else: self.stats.miss += 1 return None def put(self, id, obj): if self.enabled: if self.items.has_key(id) and obj is not self.items[id]: #raise ValueError("Item is already cached: %d (%s, %s)" % # (id, self.items[id], obj)) warn(Config.w_cached, "Item is already cached: %d (%s, %s)" % \ (id, self.items[id], obj)) self.items[id] = obj def replace(self, id, obj): if self.enabled: assert self.items.has_key(id) self.items[id] = obj class ListDiff(object): def __init__(self, orig, new): self.orig = set(orig) self.new = set(new) self.common = self.orig & self.new self.added = self.new - self.common self.removed = self.orig - self.common class PrettyPrinter(object): def __init__(self): self.stack = [] def run_nested(self, obj): ex = obj._pp_ex(self) self.stack.append(ex) def run(self, obj): self._result = obj._pp(self) return self._result def nested(self): return sorted(set(self.stack)) def result(self): return self._result; # }}} #{{{ symbols and version maps class Symbol(object): def __init__(self, name, offset, version, lib): self.name = name self.offset = offset self.version = version self.lib = lib self.definition = None @property def name_ver(self): return self.name + '@' + self.version def __repr__(self): return "Symbol(%s, 0x%x, %s)" % (self.name, self.offset, self.version) class CommonSymbol(object): def __init__(self, origsym, newsym): if origsym.name != newsym.name or origsym.version != newsym.version: raise RuntimeError("Symbols have different names: %s", [origsym, newsym]) self.origsym = origsym self.newsym = newsym self.name = newsym.name self.version = newsym.version def __repr__(self): return "CommonSymbol(%s, %s)" % (self.name, self.version) class SymbolAlias(object): def __init__(self, alias, prefix, offset): assert alias.startswith(prefix) self.alias = alias self.name = alias[len(prefix):] self.offset = offset def __repr__(self): return "SymbolAlias(%s, 0x%x)" % (self.alias, self.offset) class VersionMap(object): def __init__(self, name): self.name = name self.symbols = {} def append(self, symbol): if (self.symbols.has_key(symbol.name)): raise ValueError("Symbol is already defined %s@%s" % (symbol.name, self.name)) self.symbols[symbol.name] = symbol def names(self): return self.symbols.keys() def __repr__(self): return repr(self.symbols.values()) # }}} # {{{ types and definitions class Def(object): _is_alias = False def __init__(self, id, name, **kwargs): self.id = id self.name = name self.attrs = kwargs def __getattr__(self, attr): if not self.attrs.has_key(attr): raise AttributeError('%s in %s' % (attr, str(self))) return self.attrs[attr] def _name_opt(self, default=''): if not self.name: return default return self.name def _alias(self): if self._is_alias: return self.type._alias() return self def __cmp__(self, other): # TODO assert 'self' and 'other' belong to different libraries #print 'cmp defs: %s, %s' % (self, other) a = self._alias() try: b = other._alias() except AttributeError: return 1 r = cmp(a.__class__, b.__class__) if r == 0: if a.id != 0 and b.id != 0: ind = (long(a.id) << 32) + b.id r = Dwarf.cmpcache.get(ind) if r != None: return r else: ind = 0 r = cmp(a.attrs, b.attrs) if ind != 0: Dwarf.cmpcache.put(ind, r) else: r = 0 #raise RuntimeError('Comparing different classes: %s, %s' % # (a.__class__.__name__, b.__class__.__name__)) return r def __repr__(self): p = [] if hasattr(self, 'name'): p.append("name=%s" % self.name) for (k, v) in self.attrs.items(): if isinstance(v, Def): v = v.__class__.__name__ + '(...)' p.append("%s=%s" % (k, v)) return self.__class__.__name__ + '(' + ', '.join(p) + ')' def _mapval(self, param, vals): if param not in vals.keys(): raise NotImplementedError("Invalid value '%s': %s" % (param, str(self))) return vals[param] def _pp_ex(self, pp): raise NotImplementedError('Extended pretty print not implemeted: %s' % str(self)) def _pp(self, pp): raise NotImplementedError('Pretty print not implemeted: %s' % str(self)) class AnonymousDef(Def): def __init__(self, id, **kwargs): Def.__init__(self, id, None, **kwargs) class Void(AnonymousDef): _instance = None def __new__(cls, *args, **kwargs): if not cls._instance: cls._instance = super(Void, cls).__new__( cls, *args, **kwargs) return cls._instance def __init__(self): AnonymousDef.__init__(self, 0) def _pp(self, pp): return "void" class VarArgs(AnonymousDef): def _pp(self, pp): return "..." class PointerDef(AnonymousDef): def _pp(self, pp): t = pp.run(self.type) return "%s*" % (t,) class BaseTypeDef(Def): inttypes = ['DW_ATE_signed', 'DW_ATE_unsigned', 'DW_ATE_unsigned_char'] def _pp(self, pp): if self.encoding in self.inttypes: sign = '' if self.encoding == 'DW_ATE_signed' else 'u' - bits = int(self.byte_size) * 8 + bits = int(self.byte_size, 0) * 8 return '%sint%s_t' % (sign, bits) - elif self.encoding == 'DW_ATE_signed_char' and int(self.byte_size) == 1: + elif self.encoding == 'DW_ATE_signed_char' and int(self.byte_size, 0) == 1: return 'char'; + elif self.encoding == 'DW_ATE_boolean' and int(self.byte_size, 0) == 1: + return 'bool'; elif self.encoding == 'DW_ATE_float': - return self._mapval(self.byte_size, { - '16': 'long double', - '8': 'double', - '4': 'float', + return self._mapval(int(self.byte_size, 0), { + 16: 'long double', + 8: 'double', + 4: 'float', }) raise NotImplementedError('Invalid encoding: %s' % self) class TypeAliasDef(Def): _is_alias = True def _pp(self, pp): alias = self._alias() # push typedef name if self.name and not alias.name: alias.name = 'T(%s)' % self.name # return type with modifiers return self.type._pp(pp) class EnumerationTypeDef(Def): def _pp(self, pp): return 'enum ' + self._name_opt('UNKNOWN') class ConstTypeDef(AnonymousDef): _is_alias = True def _pp(self, pp): return 'const ' + self.type._pp(pp) class VolatileTypeDef(AnonymousDef): _is_alias = True def _pp(self, pp): return 'volatile ' + self.type._pp(pp) +class RestrictTypeDef(AnonymousDef): + _is_alias = True + def _pp(self, pp): + return 'restrict ' + self.type._pp(pp) + class ArrayDef(AnonymousDef): def _pp(self, pp): t = pp.run(self.type) assert len(self.subranges) == 1 try: sz = int(self.subranges[0].upper_bound) + 1 except ValueError: s = re.sub(r'\(.+\)', '', self.subranges[0].upper_bound) sz = int(s) + 1 return '%s[%s]' % (t, sz) class ArraySubrangeDef(AnonymousDef): pass class FunctionDef(Def): def _pp(self, pp): result = pp.run(self.result) if not self.params: params = "void" else: params = ', '.join([ pp.run(x) for x in self.params ]) return "%s %s(%s);" % (result, self.name, params) class FunctionTypeDef(Def): def _pp(self, pp): result = pp.run(self.result) if not self.params: params = "void" else: params = ', '.join([ pp.run(x) for x in self.params ]) return "F(%s, %s, (%s))" % (self._name_opt(), result, params) class ParameterDef(Def): def _pp(self, pp): t = pp.run(self.type) return "%s %s" % (t, self._name_opt()) +class VariableDef(Def): + def _pp(self, pp): + t = pp.run(self.type) + return "%s %s" % (t, self._name_opt()) + # TODO class StructForwardDef(Def): pass class IncompleteDef(Def): def update(self, complete, cache=None): self.complete = complete complete.incomplete = self if cache != None: cached = cache.get(self.id) if cached != None and isinstance(cached, IncompleteDef): cache.replace(self.id, complete) class StructIncompleteDef(IncompleteDef): def _pp(self, pp): return "struct %s" % (self.name,) class UnionIncompleteDef(IncompleteDef): def _pp(self, pp): return "union %s" % (self.name,) class StructDef(Def): def _pp_ex(self, pp, suffix=';'): members = [ pp.run(x) for x in self.members ] return "struct %s { %s }%s" % \ (self._name_opt(), ' '.join(members), suffix) def _pp(self, pp): if self.name: pp.run_nested(self) return "struct %s" % (self.name,) else: return self._pp_ex(pp, suffix='') class UnionDef(Def): def _pp_ex(self, pp, suffix=';'): members = [ pp.run(x) for x in self.members ] return "union %s { %s }%s" % \ (self._name_opt(), ' '.join(members), suffix) def _pp(self, pp): if self.name: pp.run_nested(self) return "union %s" % (self.name,) else: return self._pp_ex(pp, suffix='') class MemberDef(Def): def _pp(self, pp): t = pp.run(self.type) if self.bit_size: bits = ":%s" % self.bit_size else: bits = "" return "%s %s%s;" % (t, self._name_opt(), bits) class Dwarf(object): cmpcache = Cache(enabled=Config.cmpcache_enabled) def __init__(self, dump): self.dump = dump def _build_optarg_type(self, praw): type = praw.optarg('type', Void()) if type != Void(): type = self.buildref(praw.unit, type) return type def build_subprogram(self, raw): if raw.optname == None: raw.setname('SUBPROGRAM_NONAME_' + raw.arg('low_pc')); params = [ self.build(x) for x in raw.nested ] result = self._build_optarg_type(raw) return FunctionDef(raw.id, raw.name, params=params, result=result) + def build_variable(self, raw): + type = self._build_optarg_type(raw) + return VariableDef(raw.id, raw.optname, type=type) + def build_subroutine_type(self, raw): params = [ self.build(x) for x in raw.nested ] result = self._build_optarg_type(raw) return FunctionTypeDef(raw.id, raw.optname, params=params, result=result) def build_formal_parameter(self, raw): type = self._build_optarg_type(raw) return ParameterDef(raw.id, raw.optname, type=type) def build_pointer_type(self, raw): type = self._build_optarg_type(raw) return PointerDef(raw.id, type=type) def build_member(self, raw): type = self.buildref(raw.unit, raw.arg('type')) return MemberDef(raw.id, raw.name, type=type, bit_size=raw.optarg('bit_size', None)) def build_structure_type(self, raw): incomplete = raw.unit.incomplete.get(raw.id) if incomplete == None: incomplete = StructIncompleteDef(raw.id, raw.optname) raw.unit.incomplete.put(raw.id, incomplete) else: return incomplete members = [ self.build(x) for x in raw.nested ] byte_size = raw.optarg('byte_size', None) if byte_size == None: obj = StructForwardDef(raw.id, raw.name, members=members, forcename=raw.name) obj = StructDef(raw.id, raw.optname, members=members, byte_size=byte_size) incomplete.update(obj, cache=raw.unit.cache) return obj def build_union_type(self, raw): incomplete = raw.unit.incomplete.get(raw.id) if incomplete == None: incomplete = UnionIncompleteDef(raw.id, raw.optname) raw.unit.incomplete.put(raw.id, incomplete) else: return incomplete members = [ self.build(x) for x in raw.nested ] byte_size = raw.optarg('byte_size', None) obj = UnionDef(raw.id, raw.optname, members=members, byte_size=byte_size) obj.incomplete = incomplete incomplete.complete = obj return obj def build_typedef(self, raw): type = self._build_optarg_type(raw) return TypeAliasDef(raw.id, raw.name, type=type) def build_const_type(self, raw): type = self._build_optarg_type(raw) return ConstTypeDef(raw.id, type=type) def build_volatile_type(self, raw): type = self._build_optarg_type(raw) return VolatileTypeDef(raw.id, type=type) + def build_restrict_type(self, raw): + type = self._build_optarg_type(raw) + return RestrictTypeDef(raw.id, type=type) + def build_enumeration_type(self, raw): # TODO handle DW_TAG_enumerator ??? return EnumerationTypeDef(raw.id, name=raw.optname, byte_size=raw.arg('byte_size')) def build_base_type(self, raw): return BaseTypeDef(raw.id, raw.optname, byte_size=raw.arg('byte_size'), encoding=raw.arg('encoding')) def build_array_type(self, raw): type = self.buildref(raw.unit, raw.arg('type')) subranges = [ self.build(x) for x in raw.nested ] return ArrayDef(raw.id, type=type, subranges=subranges) def build_subrange_type(self, raw): type = self.buildref(raw.unit, raw.arg('type')) return ArraySubrangeDef(raw.id, type=type, upper_bound=raw.optarg('upper_bound', 0)) def build_unspecified_parameters(self, raw): return VarArgs(raw.id) def _get_id(self, id): try: return int(id) except ValueError: if (id.startswith('<') and id.endswith('>')): - return int(id[1:-1]) + return int(id[1:-1], 0) else: raise ValueError("Invalid dwarf id: %s" % id) def build(self, raw): obj = raw.unit.cache.get(raw.id) if obj != None: return obj builder_name = raw.tag.replace('DW_TAG_', 'build_') try: builder = getattr(self, builder_name) except AttributeError: raise AttributeError("Unknown dwarf tag: %s" % raw) obj = builder(raw) raw.unit.cache.put(obj.id, obj) return obj def buildref(self, unit, id): id = self._get_id(id) raw = unit.tags[id] obj = self.build(raw) return obj # }}} class Shlib(object): def __init__(self, libfile): self.libfile = libfile self.versions = {} self.alias_syms = {} def parse_objdump(self): objdump = ObjdumpParser(self.libfile) objdump.run() for p in objdump.dynamic_symbols: vername = p['ver'] if vername.startswith('(') and vername.endswith(')'): vername = vername[1:-1] if not Config.version_filter.match(vername): continue if not Config.symbol_filter.match(p['symbol']): continue sym = Symbol(p['symbol'], p['offset'], vername, self) if not self.versions.has_key(vername): self.versions[vername] = VersionMap(vername) self.versions[vername].append(sym) if Config.alias_prefixes: self.local_offsetmap = objdump.local_offsetmap for p in objdump.local_symbols: for prefix in Config.alias_prefixes: if not p['symbol'].startswith(prefix): continue alias = SymbolAlias(p['symbol'], prefix, p['offset']) if self.alias_syms.has_key(alias.name): prevalias = self.alias_syms[alias.name] if alias.name != prevalias.name or \ alias.offset != prevalias.offset: warn(Config.w_alias, "Symbol alias is " \ "already defined: %s: %s at %08x -- %s at %08x" % \ (alias.alias, alias.name, alias.offset, prevalias.name, prevalias.offset)) self.alias_syms[alias.name] = alias def parse_dwarfdump(self): dwarfdump = DwarfdumpParser(self.libfile) def lookup(sym): raw = None try: raw = dwarfdump.offsetmap[sym.offset] except: try: localnames = self.local_offsetmap[sym.offset] localnames.sort(key=lambda x: -len(x)) for localname in localnames: if not self.alias_syms.has_key(localname): continue alias = self.alias_syms[localname] raw = dwarfdump.offsetmap[alias.offset] break except: pass return raw dwarfdump.run() dwarf = Dwarf(dwarfdump) for ver in self.versions.values(): for sym in ver.symbols.values(): raw = lookup(sym); if not raw: warn(Config.w_symbol, "Symbol %s (%s) not found at offset 0x%x" % \ (sym.name_ver, self.libfile, sym.offset)) continue if Config.verbose >= 3: print "Parsing symbol %s (%s)" % (sym.name_ver, self.libfile) sym.definition = dwarf.build(raw) def parse(self): if not os.path.isfile(self.libfile): print >> sys.stderr, ("No such file: %s" % self.libfile) sys.exit(1) self.parse_objdump() self.parse_dwarfdump() # {{{ parsers class Parser(object): def __init__(self, proc): self.proc = proc self.parser = self.parse_begin def run(self): fd = os.popen(self.proc, 'r') while True: line = fd.readline() if (not line): break line = line.strip() if (line): self.parser(line) err = fd.close() if err: print >> sys.stderr, ("Execution failed: %s" % self.proc) sys.exit(2) def parse_begin(self, line): print(line) class ObjdumpParser(Parser): re_header = re.compile('(?P\w*)\s*SYMBOL TABLE:') re_local_symbol = re.compile('(?P[0-9a-fA-F]+)\s+(?P\w+)\s+(?P\w+)\s+(?P
[^\s]+)\s+(?P[0-9a-fA-F]+)\s*(?P[^\s]*)') re_lame_symbol = re.compile('(?P[0-9a-fA-F]+)\s+(?P\w+)\s+\*[A-Z]+\*') re_dynamic_symbol = re.compile('(?P[0-9a-fA-F]+)\s+(?P\w+)\s+(?P\w+)\s+(?P
[^\s]+)\s+(?P[0-9a-fA-F]+)\s*(?P[^\s]*)\s*(?P[^\s]*)') def __init__(self, libfile): Parser.__init__(self, "%s -wtT %s" % (Config.objdump, libfile)) self.dynamic_symbols = [] self.local_symbols = [] self.local_offsetmap = {} def parse_begin(self, line): self.parse_header(line) def add_symbol(self, table, symbol, offsetmap = None): offset = int(symbol['offset'], 16); symbol['offset'] = offset if (offset == 0): return table.append(symbol) if offsetmap != None: if not offsetmap.has_key(offset): offsetmap[offset] = [symbol['symbol']] else: offsetmap[offset].append(symbol['symbol']) def parse_header(self, line): m = self.re_header.match(line) if (m): table = m.group('table') if (table == "DYNAMIC"): self.parser = self.parse_dynamic elif table == '': self.parser = self.parse_local else: raise ValueError("Invalid symbol table: %s" % table) return True return False def parse_local(self, line): if (self.parse_header(line)): return if (self.re_lame_symbol.match(line)): return m = self.re_local_symbol.match(line) if (not m): return #raise ValueError("Invalid symbol definition: %s" % line) p = m.groupdict() if (p['symbol'] and p['symbol'].find('@') == -1): self.add_symbol(self.local_symbols, p, self.local_offsetmap); def parse_dynamic(self, line): if (self.parse_header(line)): return if (self.re_lame_symbol.match(line)): return m = self.re_dynamic_symbol.match(line) if (not m): raise ValueError("Invalid symbol definition: %s" % line) p = m.groupdict() if (p['symbol'] and p['ver']): self.add_symbol(self.dynamic_symbols, p); class DwarfdumpParser(Parser): tagcache_stats = Cache.CacheStats() class Unit(object): def __init__(self): self.cache = Cache(enabled=Config.dwarfcache_enabled, stats=DwarfdumpParser.tagcache_stats) self.incomplete = Cache() self.tags = {} class Tag(object): def __init__(self, unit, data): self.unit = unit - self.id = int(data['id']) + self.id = int(data['id'], 0) self.level = int(data['level']) self.tag = data['tag'] self.args = {} self.nested = [] @property def name(self): return self.arg('name') @property def optname(self): return self.optarg('name', None) def setname(self, name): self.args['DW_AT_name'] = name def arg(self, a): name = 'DW_AT_' + a try: return self.args[name] except KeyError: raise KeyError("Argument '%s' not found in %s: %s" % (name, self, self.args)) def optarg(self, a, default): try: return self.arg(a) except KeyError: return default def __repr__(self): return "Tag(%d, %d, %s)" % (self.level, self.id, self.tag) - re_header = re.compile('<(?P\d+)><(?P\d+\+*\d*)><(?P\w+)>') + re_header = re.compile('<(?P\d+)><(?P[0xX0-9a-fA-F]+(?:\+(0[xX])?[0-9a-fA-F]+)?)><(?P\w+)>') re_argname = re.compile('(?P\w+)<') re_argunknown = re.compile('<[^<>]+>') skip_tags = set([ 'DW_TAG_lexical_block', 'DW_TAG_inlined_subroutine', 'DW_TAG_label', 'DW_TAG_variable', ]) + external_tags = set([ + 'DW_TAG_variable', + ]) + def __init__(self, libfile): Parser.__init__(self, "%s -di %s" % (Config.dwarfdump, libfile)) self.current_unit = None self.offsetmap = {} self.stack = [] def parse_begin(self, line): if line == '.debug_info': self.parser = self.parse_debuginfo else: raise ValueError("Invalid dwarfdump header: %s" % line) def parse_argvalue(self, args): assert args.startswith('<') i = 1 cnt = 1 while i < len(args) and args[i]: if args[i] == '<': cnt += 1 elif args[i] == '>': cnt -= 1 if cnt == 0: break i = i + 1 value = args[1:i] args = args[i+1:] return (args, value) def parse_arg(self, tag, args): m = self.re_argname.match(args) if not m: m = self.re_argunknown.match(args) if not m: raise ValueError("Invalid dwarfdump: couldn't parse arguments: %s" % args) args = args[len(m.group(0)):].lstrip() return args argname = m.group('arg') args = args[len(argname):] value = [] while len(args) > 0 and args.startswith('<'): (args, v) = self.parse_argvalue(args) value.append(v) args = args.lstrip() if len(value) == 1: value = value[0] tag.args[argname] = value return args def parse_debuginfo(self, line): m = self.re_header.match(line) if not m: raise ValueError("Invalid dwarfdump: %s" % line) if m.group('level') == '0': self.current_unit = DwarfdumpParser.Unit() return tag = DwarfdumpParser.Tag(self.current_unit, m.groupdict()) args = line[len(m.group(0)):].lstrip() while args: args = self.parse_arg(tag, args) tag.unit.tags[tag.id] = tag - if tag.args.has_key('DW_AT_low_pc') and \ - tag.tag not in DwarfdumpParser.skip_tags: - offset = int(tag.args['DW_AT_low_pc'], 16) + def parse_offset(tag): + if tag.args.has_key('DW_AT_low_pc'): + return int(tag.args['DW_AT_low_pc'], 16) + elif tag.args.has_key('DW_AT_location'): + location = tag.args['DW_AT_location'] + if location.startswith('DW_OP_addr'): + return int(location.replace('DW_OP_addr', ''), 16) + return None + offset = parse_offset(tag) + if offset is not None and \ + (tag.tag not in DwarfdumpParser.skip_tags or \ + (tag.args.has_key('DW_AT_external') and \ + tag.tag in DwarfdumpParser.external_tags)): if self.offsetmap.has_key(offset): raise ValueError("Dwarf dump parse error: " + "symbol is aleady defined at offset 0x%x" % offset) self.offsetmap[offset] = tag if len(self.stack) > 0: prev = self.stack.pop() while prev.level >= tag.level and len(self.stack) > 0: prev = self.stack.pop() if prev.level < tag.level: assert prev.level == tag.level - 1 # TODO check DW_AT_sibling ??? if tag.tag not in DwarfdumpParser.skip_tags: prev.nested.append(tag) self.stack.append(prev) self.stack.append(tag) assert len(self.stack) == tag.level # }}} def list_str(l): l = [ str(x) for x in l ] l.sort() return ', '.join(l) def names_ver_str(vername, names): return list_str([ x + "@" + vername for x in names ]) def common_symbols(origlib, newlib): result = [] verdiff = ListDiff(origlib.versions.keys(), newlib.versions.keys()) if Config.verbose >= 1: print 'Original versions: ', list_str(verdiff.orig) print 'New versions: ', list_str(verdiff.new) for vername in verdiff.added: print 'Added version: ', vername print ' Added symbols: ', \ names_ver_str(vername, newlib.versions[vername].names()) for vername in verdiff.removed: print 'Removed version: ', vername print ' Removed symbols: ', \ names_ver_str(vername, origlib.versions[vername].names()) added = [] removed = [] for vername in verdiff.common: origver = origlib.versions[vername] newver = newlib.versions[vername] namediff = ListDiff(origver.names(), newver.names()) if namediff.added: added.append(names_ver_str(vername, namediff.added)) if namediff.removed: removed.append(names_ver_str(vername, namediff.removed)) commonver = VersionMap(vername) result.append(commonver) for n in namediff.common: sym = CommonSymbol(origver.symbols[n], newver.symbols[n]) commonver.append(sym) if added: print 'Added symbols:' for i in added: print ' ', i if removed: print 'Removed symbols:' for i in removed: print ' ', i return result def cmp_symbols(commonver): for ver in commonver: names = ver.names(); names.sort() for symname in names: sym = ver.symbols[symname] - match = sym.origsym.definition == sym.newsym.definition + missing = sym.origsym.definition is None or sym.newsym.definition is None + match = not missing and sym.origsym.definition == sym.newsym.definition if not match: App.result_code = 1 if Config.verbose >= 1 or not match: + if missing: + print '%s: missing definition' % \ + (sym.origsym.name_ver,) + continue print '%s: definitions %smatch' % \ (sym.origsym.name_ver, "" if match else "mis") if Config.dump or (not match and not Config.no_dump): for x in [(sym.origsym, Config.origfile), (sym.newsym, Config.newfile)]: xsym = x[0] xout = x[1].out if not xsym.definition: print >> xout, '\n// Definition not found: %s %s' % \ (xsym.name_ver, xsym.lib.libfile) continue print >> xout, '\n// Definitions mismatch: %s %s' % \ (xsym.name_ver, xsym.lib.libfile) pp = PrettyPrinter() pp.run(xsym.definition) for i in pp.nested(): print >> xout, i print >> xout, pp.result() def dump_symbols(commonver): class SymbolDump(object): def __init__(self, io_conf): self.io_conf = io_conf self.pp = PrettyPrinter() self.res = [] def run(self, sym): r = self.pp.run(sym.definition) self.res.append('/* %s@%s */ %s' % (sym.name, sym.version, r)) def finish(self): print >> self.io_conf.out, '\n// Symbol dump: version %s, library %s' % \ (ver.name, self.io_conf.filename) for i in self.pp.nested(): print >> self.io_conf.out, i print >> self.io_conf.out, '' for i in self.res: print >> self.io_conf.out, i for ver in commonver: names = sorted(ver.names()); d_orig = SymbolDump(Config.origfile) d_new = SymbolDump(Config.newfile) for symname in names: sym = ver.symbols[symname] if not sym.origsym.definition or not sym.newsym.definition: # XXX warn(Config.w_symbol, 'Missing symbol definition: %s@%s' % \ (symname, ver.name)) continue d_orig.run(sym.origsym) d_new.run(sym.newsym) d_orig.finish() d_new.finish() if __name__ == '__main__': Config.init() parser = optparse.OptionParser(usage="usage: %prog origlib newlib", version="%prog " + Config.version) parser.add_option('-v', '--verbose', action='count', help="verbose mode, may be specified several times") parser.add_option('--alias-prefix', action='append', help="name prefix to try for symbol alias lookup", metavar="STR") parser.add_option('--dump', action='store_true', help="dump symbol definitions") parser.add_option('--no-dump', action='store_true', help="disable dump for mismatched symbols") parser.add_option('--out-orig', action='store', help="result output file for original library", metavar="ORIGFILE") parser.add_option('--out-new', action='store', help="result output file for new library", metavar="NEWFILE") + parser.add_option('--dwarfdump', action='store', + help="path to dwarfdump executable", metavar="DWARFDUMP") + parser.add_option('--objdump', action='store', + help="path to objdump executable", metavar="OBJDUMP") parser.add_option('--exclude-ver', action='append', metavar="RE") parser.add_option('--include-ver', action='append', metavar="RE") parser.add_option('--exclude-sym', action='append', metavar="RE") parser.add_option('--include-sym', action='append', metavar="RE") + parser.add_option('--no-exclude-sym-default', action='store_true', + help="don't exclude special symbols like _init, _end, __bss_start") for opt in ['alias', 'cached', 'symbol']: parser.add_option("--w-" + opt, action="store_true", dest="w_" + opt) parser.add_option("--w-no-" + opt, action="store_false", dest="w_" + opt) (opts, args) = parser.parse_args() if len(args) != 2: parser.print_help() sys.exit(-1) + if opts.dwarfdump: + Config.dwarfdump = opts.dwarfdump + if opts.objdump: + Config.objdump = opts.objdump if opts.out_orig: Config.origfile.init(opts.out_orig) if opts.out_new: Config.newfile.init(opts.out_new) if opts.no_dump: Config.dump = False Config.no_dump = True if opts.dump: Config.dump = True Config.no_dump = False Config.verbose = 1 if opts.verbose: Config.verbose = opts.verbose if opts.alias_prefix: Config.alias_prefixes = opts.alias_prefix Config.alias_prefixes.sort(key=lambda x: -len(x)) for (k, v) in ({ '_sym': Config.symbol_filter, '_ver': Config.version_filter }).items(): for a in [ 'exclude', 'include' ]: opt = getattr(opts, a + k) if opt: getattr(v, a).extend(opt) + if not opts.no_exclude_sym_default: + Config.symbol_filter.exclude.extend(Config.exclude_sym_default) Config.version_filter.compile() Config.symbol_filter.compile() for w in ['w_alias', 'w_cached', 'w_symbol']: if hasattr(opts, w): v = getattr(opts, w) if v != None: setattr(Config, w, v) (Config.origfile.filename, Config.newfile.filename) = (args[0], args[1]) origlib = Shlib(Config.origfile.filename) origlib.parse() newlib = Shlib(Config.newfile.filename) newlib.parse() commonver = common_symbols(origlib, newlib) if Config.dump: dump_symbols(commonver) cmp_symbols(commonver) if Config.verbose >= 4: print Dwarf.cmpcache.stats.show('Cmp') print DwarfdumpParser.tagcache_stats.show('Dwarf tag') sys.exit(App.result_code) Index: projects/sendfile/tools/tools/shlib-compat/test/Makefile.inc =================================================================== --- projects/sendfile/tools/tools/shlib-compat/test/Makefile.inc (revision 275355) +++ projects/sendfile/tools/tools/shlib-compat/test/Makefile.inc (revision 275356) @@ -1,13 +1,15 @@ # $FreeBSD$ LIB= test${TESTNUM} SHLIB_MAJOR= 0 SRCS+= test.c WARNS?= 3 DEBUG_FLAGS?= -g VERSION_DEF= ${.CURDIR}/../Versions.def SYMBOL_MAPS= ${.CURDIR}/Symbol.map + +MK_DEBUG_FILES= yes Index: projects/sendfile/tools/tools/shlib-compat/test/regress.sh =================================================================== --- projects/sendfile/tools/tools/shlib-compat/test/regress.sh (revision 275355) +++ projects/sendfile/tools/tools/shlib-compat/test/regress.sh (revision 275356) @@ -1,16 +1,16 @@ #!/bin/sh # $FreeBSD$ -run() { ../shlib-compat.py --no-dump -vv libtest$1/libtest$1.so.0.debug libtest$2/libtest$2.so.0.debug; } +run() { ../shlib-compat.py --no-dump -vv libtest$1/libtest$1.so.0.full libtest$2/libtest$2.so.0.full; } echo 1..9 REGRESSION_START($1) REGRESSION_TEST(`1-1', `run 1 1') REGRESSION_TEST(`1-2', `run 1 2') REGRESSION_TEST(`1-3', `run 1 3') REGRESSION_TEST(`2-1', `run 2 1') REGRESSION_TEST(`2-2', `run 2 2') REGRESSION_TEST(`2-3', `run 2 3') REGRESSION_TEST(`3-1', `run 3 1') REGRESSION_TEST(`3-2', `run 3 2') REGRESSION_TEST(`3-3', `run 3 3') REGRESSION_END() Index: projects/sendfile/tools/tools/sysbuild/sysbuild.sh =================================================================== --- projects/sendfile/tools/tools/sysbuild/sysbuild.sh (revision 275355) +++ projects/sendfile/tools/tools/sysbuild/sysbuild.sh (revision 275356) @@ -1,638 +1,637 @@ #!/bin/sh # # Copyright (c) 1994-2009 Poul-Henning Kamp. # 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$ # set -e exec < /dev/null if [ `uname -m` = "i386" -o `uname -m` = "amd64" ] ; then TARGET_PART=`df / | sed ' 1d s/[ ].*// s,/dev/,, s,s1a,s3a, s,s2a,s1a, s,s3a,s2a, '` FREEBSD_PART=`sed -n \ -e 's/#.*//' \ -e '/[ ]\/freebsd[ ]/!d' \ -e 's/[ ].*//p' \ /etc/fstab` # Calculate a suggested gpart command TARGET_DISK=`expr ${TARGET_PART} : '\(.*\)s[12]a$' || true` TARGET_SLICE=`expr ${TARGET_PART} : '.*s\([12]\)a$' || true` GPART_SUGGESTION="gpart set -a active -i $TARGET_SLICE /dev/$TARGET_DISK" unset TARGET_DISK TARGET_SLICE else TARGET_PART=unknown FREEBSD_PART=unknown GPART_SUGGESTION=unknown fi # Relative to /freebsd PORTS_PATH=ports SRC_PATH=src # OBJ_PATH=obj # Name of kernel KERNCONF=GENERIC # srcconf #SRCCONF="SRCCONF=/usr/src/src.conf" # -j arg to make(1) ncpu=`sysctl -n kern.smp.cpus` if [ $ncpu -gt 1 ] ; then JARG="-j $ncpu" fi # serial console ? SERCONS=false # Remotely mounted distfiles # REMOTEDISTFILES=fs:/rdonly/distfiles # Proxy #FTP_PROXY=http://127.0.0.1:3128/ #HTTP_PROXY=http://127.0.0.1:3128/ #export FTP_PROXY HTTP_PROXY PORTS_WE_WANT=' ' PORTS_OPTS="BATCH=YES A4=yes" CONFIGFILES=' ' SBMNT="/mnt.sysbuild" cleanup() ( ) before_ports() ( ) before_ports_chroot() ( ) final_root() ( ) final_chroot() ( ) ####################################################################### # -P is a pretty neat way to clean junk out from your ports dist-files: # # mkdir /freebsd/ports/distfiles.old # mv /freebsd/ports/distfiles/* /freebsd/ports/distfiles.old # sh sysbuild.sh -c $yourconfig -P /freebsd/ports/distfiles.old # rm -rf /freebsd/ports/distfiles.old # # Unfortunately bsd.ports.mk does not attempt to use a hard-link so # while this runs you need diskspace for both your old and your "new" # distfiles. # ####################################################################### usage () { ( echo "Usage: $0 [-b/-k/-w] [-c config_file]" echo " -b suppress builds (both kernel and world)" echo " -k suppress buildkernel" echo " -w suppress buildworld" echo " -p used cached packages" echo " -P prefetch ports" echo " -c specify config file" ) 1>&2 exit 2 } ####################################################################### ####################################################################### if [ ! -f $0 ] ; then echo "Must be able to access self ($0)" 1>&2 exit 1 fi if grep -q 'Magic String: 0`0nQT40W%l,CX&' $0 ; then true else echo "self ($0) does not contain magic string" 1>&2 exit 1 fi ####################################################################### set -e log_it() ( a="$*" set `cat /tmp/_sb_log` TX=`date +%s` echo "$1 $TX" > /tmp/_sb_log DT=`expr $TX - $1 || true` DL=`expr $TX - $2 || true` echo -n "### `date +%H:%M:%S`" printf " ### %5d ### %5d ### %s\n" $DT $DL "$a" ) ####################################################################### ports_recurse() ( t=$1 shift if [ "x$t" = "x." ] ; then true > /tmp/_.plist true > /tmp/_.plist.tdone echo 'digraph {' > /tmp/_.plist.dot fi if grep -q "^$t\$" /tmp/_.plist.tdone ; then return fi echo "$t" >> /tmp/_.plist.tdone for d do if [ ! -d $d ] ; then echo "Missing port $d" 1>&2 continue fi if [ ! -f $d/Makefile ] ; then echo "Missing port $d" 1>&2 continue fi if [ "x$t" != "x." ] ; then echo "\"$t\" -> \"$d\"" >> /tmp/_.plist.dot fi if grep -q "^$d\$" /tmp/_.plist ; then true elif grep -q "^$d\$" /tmp/_.plist.tdone ; then true else ( cd $d ports_recurse $d `make -V _DEPEND_DIRS ${PORTS_OPTS}` ) echo "$d" >> /tmp/_.plist fi done if [ "x$t" = "x." ] ; then echo '}' >> /tmp/_.plist.dot fi ) ports_build() ( ports_recurse . $PORTS_WE_WANT # Now build & install them for p in `cat /tmp/_.plist` do b=`echo $p | tr / _` t=`echo $p | sed 's,/usr/ports/,,'` pn=`cd $p && make package-name` - if [ "x$p" == "x/usr/ports/ports-mgmt/pkg" -o \ - "x$p" == "x/freebsd/ports/ports-mgmt/pkg" ] ; then + if [ "x`basename $p`" == "xpkg" ] ; then log_it "Very Special: $t ($pn)" ( cd $p make clean ${PORTS_OPTS} make all ${PORTS_OPTS} make install ${PORTS_OPTS} ) > _.$b 2>&1 < /dev/null continue fi if pkg info $pn > /dev/null 2>&1 ; then log_it "Already installed: $t ($pn)" continue fi if [ "x${PKG_DIR}" != "x" -a -f ${PKG_DIR}/$pn.txz ] ; then if [ "x$use_pkg" = "x-p" ] ; then log_it "Install $t ($pn)" ( set +e pkg add ${PKG_DIR}/$pn.txz || true ) > _.$b 2>&1 < /dev/null continue fi fi miss=`(cd $p ; make missing ${PORTS_OPTS}) || true` if [ "x${miss}" != "x" ] ; then log_it "MISSING for $p:" $miss continue fi log_it "build $pn ($p)" ( set +e cd $p make clean ${PORTS_OPTS} if make install ${PORTS_OPTS} ; then if [ "x${PKG_DIR}" != "x" ] ; then make package ${PORTS_OPTS} fi else log_it FAIL build $p fi make clean ) > _.$b 2>&1 < /dev/null done ) ports_prefetch() ( ( set +x ldir=$1 true > /${ldir}/_.prefetch echo "Building /tmp/_.plist" >> /${ldir}/_.prefetch ports_recurse . $PORTS_WE_WANT echo "Completed /tmp/_.plist" >> /${ldir}/_.prefetch # Now checksump/fetch them for p in `cat /tmp/_.plist` do b=`echo $p | tr / _` ( cd $p if make checksum $PORTS_OPTS ; then rm -f /${ldir}/_.prefetch.$b echo "OK $p" >> /${ldir}/_.prefetch exit 0 fi make distclean make checksum $PORTS_OPTS || true if make checksum $PORTS_OPTS > /dev/null 2>&1 ; then rm -f /${ldir}/_.prefetch.$b echo "OK $p" >> /${ldir}/_.prefetch else echo "BAD $p" >> /${ldir}/_.prefetch fi ) > /${ldir}/_.prefetch.$b 2>&1 done echo "Done" >> /${ldir}/_.prefetch ) ) ####################################################################### do_world=true do_kernel=true do_prefetch=false use_pkg="" c_arg="" set +e args=`getopt bc:hkpP:w $*` if [ $? -ne 0 ] ; then usage fi set -e set -- $args for i do case "$i" in -b) shift; do_world=false do_kernel=false ;; -c) c_arg=$2 if [ ! -f "$c_arg" ] ; then echo "Cannot read $c_arg" 1>&2 usage fi . "$2" shift shift ;; -h) usage ;; -k) shift; do_kernel=false ;; -p) shift; use_pkg="-p" ;; -P) shift; do_prefetch=true distfile_cache=$1 shift; ;; -w) shift; do_world=false ;; --) shift break; ;; esac done ####################################################################### if [ "x$1" = "xchroot_script" ] ; then set -e shift before_ports_chroot ports_build exit 0 fi if [ "x$1" = "xfinal_chroot" ] ; then final_chroot exit 0 fi if [ $# -gt 0 ] ; then echo "$0: Extraneous arguments supplied" usage fi ####################################################################### T0=`date +%s` echo $T0 $T0 > /tmp/_sb_log [ ! -d ${SBMNT} ] && mkdir -p ${SBMNT} if $do_prefetch ; then rm -rf /tmp/sysbuild/ports mkdir -p /tmp/sysbuild/ports ln -s ${distfile_cache} /tmp/sysbuild/ports/distfiles export PORTS_OPTS=CD_MOUNTPTS=/tmp/sysbuild ports_prefetch /tmp exit 0 fi log_it Unmount everything ( ( cleanup ) umount /freebsd/distfiles || true umount ${SBMNT}/freebsd/distfiles || true umount ${FREEBSD_PART} || true umount ${SBMNT}/freebsd || true umount ${SBMNT}/dev || true umount ${SBMNT} || true umount /dev/${TARGET_PART} || true ) # > /dev/null 2>&1 log_it Prepare running image mkdir -p /freebsd mount ${FREEBSD_PART} /freebsd ####################################################################### if [ ! -d /freebsd/${PORTS_PATH} ] ; then echo PORTS_PATH does not exist 1>&2 exit 1 fi if [ ! -d /freebsd/${SRC_PATH} ] ; then echo SRC_PATH does not exist 1>&2 exit 1 fi log_it TARGET_PART $TARGET_PART sleep 5 rm -rf /usr/ports ln -s /freebsd/${PORTS_PATH} /usr/ports rm -rf /usr/src ln -s /freebsd/${SRC_PATH} /usr/src if $do_world ; then if [ "x${OBJ_PATH}" != "x" ] ; then rm -rf /usr/obj mkdir -p /freebsd/${OBJ_PATH} ln -s /freebsd/${OBJ_PATH} /usr/obj else rm -rf /usr/obj mkdir -p /usr/obj fi fi ####################################################################### for i in ${PORTS_WE_WANT} do if [ ! -d $i ] ; then echo "Port $i not found" 1>&2 exit 2 fi done export PORTS_WE_WANT export PORTS_OPTS ####################################################################### log_it Prepare destination partition newfs -t -E -O2 -U /dev/${TARGET_PART} > /dev/null mount /dev/${TARGET_PART} ${SBMNT} mkdir -p ${SBMNT}/dev mount -t devfs devfs ${SBMNT}/dev if [ "x${REMOTEDISTFILES}" != "x" ] ; then rm -rf /freebsd/${PORTS_PATH}/distfiles ln -s /freebsd/distfiles /freebsd/${PORTS_PATH}/distfiles mkdir -p /freebsd/distfiles mount ${REMOTEDISTFILES} /freebsd/distfiles fi log_it copy ports config files (cd / ; find var/db/ports -print | cpio -dumpv ${SBMNT} > /dev/null 2>&1) log_it "Start prefetch of ports distfiles" ports_prefetch ${SBMNT} & if $do_world ; then ( cd /usr/src log_it "Buildworld" make ${JARG} -s buildworld ${SRCCONF} > ${SBMNT}/_.bw 2>&1 ) fi if $do_kernel ; then ( cd /usr/src log_it "Buildkernel" make ${JARG} -s buildkernel KERNCONF=$KERNCONF > ${SBMNT}/_.bk 2>&1 ) fi log_it Installworld (cd /usr/src && make ${JARG} installworld DESTDIR=${SBMNT} ${SRCCONF} ) \ > ${SBMNT}/_.iw 2>&1 log_it distribution (cd /usr/src/etc && make -m /usr/src/share/mk distribution DESTDIR=${SBMNT} ${SRCCONF} ) \ > ${SBMNT}/_.dist 2>&1 log_it Installkernel (cd /usr/src && make ${JARG} installkernel DESTDIR=${SBMNT} KERNCONF=$KERNCONF ) \ > ${SBMNT}/_.ik 2>&1 if [ "x${OBJ_PATH}" != "x" ] ; then rmdir ${SBMNT}/usr/obj ln -s /freebsd/${OBJ_PATH} ${SBMNT}/usr/obj fi log_it Wait for ports prefetch log_it "(Tail ${SBMNT}/_.prefetch for progress)" wait log_it Move filesystems if [ "x${REMOTEDISTFILES}" != "x" ] ; then umount /freebsd/distfiles fi umount ${FREEBSD_PART} || true mkdir -p ${SBMNT}/freebsd mount ${FREEBSD_PART} ${SBMNT}/freebsd if [ "x${REMOTEDISTFILES}" != "x" ] ; then mount ${REMOTEDISTFILES} ${SBMNT}/freebsd/distfiles fi rm -rf ${SBMNT}/usr/ports || true ln -s /freebsd/${PORTS_PATH} ${SBMNT}/usr/ports rm -rf ${SBMNT}/usr/src || true ln -s /freebsd/${SRC_PATH} ${SBMNT}/usr/src log_it Build and install ports # Make sure fetching will work in the chroot if [ -f /etc/resolv.conf ] ; then log_it copy resolv.conf cp /etc/resolv.conf ${SBMNT}/etc chflags schg ${SBMNT}/etc/resolv.conf fi if [ -f /etc/localtime ] ; then log_it copy localtime cp /etc/localtime ${SBMNT}/etc fi log_it ldconfig in chroot chroot ${SBMNT} sh /etc/rc.d/ldconfig start log_it before_ports ( before_ports ) log_it fixing fstab sed "/[ ]\/[ ]/s;^[^ ]*[ ];/dev/${TARGET_PART} ;" \ /etc/fstab > ${SBMNT}/etc/fstab log_it build ports cp $0 ${SBMNT}/root cp /tmp/_sb_log ${SBMNT}/tmp b=`basename $0` if [ "x$c_arg" != "x" ] ; then cp $c_arg ${SBMNT}/root chroot ${SBMNT} sh /root/$0 -c /root/`basename $c_arg` $use_pkg chroot_script else chroot ${SBMNT} sh /root/$0 $use_pkg chroot_script fi cp ${SBMNT}/tmp/_sb_log /tmp log_it create all mountpoints grep -v '^[ ]*#' ${SBMNT}/etc/fstab | while read a b c do mkdir -p ${SBMNT}/$b done if [ "x$SERCONS" != "xfalse" ] ; then log_it serial console echo " -h" > ${SBMNT}/boot.config sed -i "" -e /ttyd0/s/off/on/ ${SBMNT}/etc/ttys sed -i "" -e /ttyu0/s/off/on/ ${SBMNT}/etc/ttys sed -i "" -e '/^ttyv[0-8]/s/ on/ off/' ${SBMNT}/etc/ttys fi log_it move dist config files "(expect warnings)" ( cd ${SBMNT} mkdir root/configfiles_dist find ${CONFIGFILES} -print | cpio -dumpv root/configfiles_dist ) log_it copy live config files (cd / && find ${CONFIGFILES} -print | cpio -dumpv ${SBMNT}) log_it final_root ( final_root ) log_it final_chroot cp /tmp/_sb_log ${SBMNT}/tmp if [ "x$c_arg" != "x" ] ; then chroot ${SBMNT} sh /root/$0 -c /root/`basename $c_arg` final_chroot else chroot ${SBMNT} sh /root/$0 final_chroot fi cp ${SBMNT}/tmp/_sb_log /tmp log_it "Check these messages (if any):" grep '^Stop' ${SBMNT}/_* || true log_it DONE echo "Now you probably want to:" echo " $GPART_SUGGESTION" echo " shutdown -r now" Index: projects/sendfile =================================================================== --- projects/sendfile (revision 275355) +++ projects/sendfile (revision 275356) Property changes on: projects/sendfile ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r275330-275355