%{ /* Copyright (C) 2001, 2002, 2003, 2004, 2005, 2008 Red Hat, Inc. This file is part of elfutils. Written by Ulrich Drepper <drepper@redhat.com>, 2001. 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 3 of the License, or (at your option) any later version. elfutils is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>. */ #ifdef HAVE_CONFIG_H # include <config.h> #endif #include <assert.h> #include <ctype.h> #include <elf.h> #include <error.h> #include <inttypes.h> #include <libintl.h> #include <stdbool.h> #include <stdio.h> #include <string.h> #include <system.h> #include <ld.h> #include "ldscript.h" /* We sure use no threads to read the stream, so use the _unlocked variants of the functions. */ #undef getc #define getc(s) getc_unlocked (s) #undef ferror #define ferror(s) ferror_unlocked (s) #undef fread #define fread(b, m, n, s) fread_unlocked (b, m, n, s) #undef fwrite #define fwrite(b, m, n, s) fwrite_unlocked (b, m, n, s) /* ECHO must be redefined since the default implementation ignores the return value of fwrite_unlocked. */ #define ECHO do { size_t n__ __attribute__ ((unused)) \ = fwrite (yytext, yyleng, 1, yyout); } while (0) /* Defined in ld.c. */ extern int ld_scan_version_script; #define MAX_PREPDEPTH 20 static enum prepstate { prep_normal, skip_if, skip_to_endif } prepstate[MAX_PREPDEPTH]; static int prepdepth; static void eat_comment (void); static void eat_to_eol (bool empty); static int attrib_convert (int c); static void push_state (enum prepstate); static int pop_state (void); static int handle_ifdef (void); static void invalid_char (int ch); %} ID [a-zA-Z0-9_.*?][a-zA-Z0-9_.*?-]* FILENAMECHAR1 [a-zA-Z0-9_/.\\~] FILENAMECHAR [^][{}[:space:]():;]+ HEX 0[xX][0-9a-fA-F]+[kKmM]? OCT 0[0-7]*[kKmM]? DEC [0-9]+[kKmM]? WHITE [[:space:]]+ %option yylineno %option never-interactive %option noyywrap %x IGNORE %% if (unlikely (ld_scan_version_script)) { ld_scan_version_script = -1; return kVERSION_SCRIPT; } ^"#"ifdef/[[:space:]] { BEGIN (handle_ifdef ()); } ^"#"else/[[:space:]\n] { eat_to_eol (true); push_state (skip_to_endif); BEGIN (IGNORE); } ^"#"elifdef/[[:space:]] { eat_to_eol (false); push_state (skip_to_endif); BEGIN (IGNORE); } ^"#"endif/[[:space:]\n] { eat_to_eol (true) ; } <IGNORE>^"#"ifdef/[[:space:]\n] { eat_to_eol (false); push_state (skip_to_endif); } <IGNORE>^"#"else/[[:space:]\n] { eat_to_eol (true); assert (prepdepth > 0); if (prepstate[prepdepth - 1] == skip_if) { /* Back to normal processing. */ assert (prepdepth == 1); BEGIN (pop_state ()); } } <IGNORE>^"#"elifdef/[[:space:]] { assert (prepdepth > 0); if (prepstate[prepdepth - 1] == skip_if) { /* Maybe this symbol is defined. */ pop_state (); BEGIN (handle_ifdef ()); } } <IGNORE>^"#"endif/[[:space:]\n] { eat_to_eol (true); BEGIN (pop_state ()); } <IGNORE>.|\n { /* nothing */ } "/*" { eat_comment (); } ALIGN { return kALIGN; } AS_NEEDED { return kAS_NEEDED; } ENTRY { return kENTRY; } EXCLUDE_FILE { return kEXCLUDE_FILE; } "global:" { return kGLOBAL; } GROUP { return kGROUP; } INPUT { return kINPUT; } INTERP { return kINTERP; } KEEP { return kKEEP; } "local:" { return kLOCAL; } OUTPUT_FORMAT { return kOUTPUT_FORMAT; } PAGESIZE { return kPAGESIZE; } PROVIDE { return kPROVIDE; } SEARCH_DIR { return kSEARCH_DIR; } SEGMENT { return kSEGMENT; } SIZEOF_HEADERS { return kSIZEOF_HEADERS; } SORT { return kSORT; } VERSION { return kVERSION; } "["([RWX]){0,3}"]" { unsigned int cnt = 1 ; ldlval.num = 0; while (cnt < yyleng - 1) ldlval.num |= attrib_convert (yytext[cnt++]); return kMODE; } "{" { return '{'; } "}" { return '}'; } "(" { return '('; } ")" { return ')'; } ":" { return ':'; } ";" { return ';'; } "=" { return '='; } "+" { ldlval.op = exp_plus; return kADD_OP; } "-" { ldlval.op = exp_minus; return kADD_OP; } "*" { return '*'; } "/" { ldlval.op = exp_div; return kMUL_OP; } "%" { ldlval.op = exp_mod; return kMUL_OP; } "&" { return '&'; } "|" { return '|'; } "," { return ','; } {HEX}|{OCT}|{DEC} { char *endp; ldlval.num = strtoumax (yytext, &endp, 0); if (*endp != '\0') { if (tolower (*endp) == 'k') ldlval.num *= 1024; else { assert (tolower (*endp) == 'm'); ldlval.num *= 1024 * 1024; } } return kNUM; } {ID} { ldlval.str = obstack_strndup (&ld_state.smem, yytext, yyleng); return kID; } {FILENAMECHAR1}{FILENAMECHAR} { ldlval.str = obstack_strndup (&ld_state.smem, yytext, yyleng); return kFILENAME; } {WHITE} { /* IGNORE */ } . { invalid_char (*yytext); } %% static void eat_comment (void) { while (1) { int c = input (); while (c != '*' && c != EOF) c = input (); if (c == '*') { c = input (); while (c == '*') c = input (); if (c == '/') break; } if (c == EOF) { /* XXX Use the setjmp buffer and signal EOF in comment */ error (0, 0, gettext ("EOF in comment")); break; } } } static void eat_to_eol (bool empty) { bool warned = false; while (1) { int c = input (); if (c == EOF) break; if (c == '\n') { ++yylineno; break; } if (empty && ! isspace (c) && ! warned) { error (0, 0, gettext ("%d: garbage at end of line"), yylineno); warned = true; } } } static int attrib_convert (int c) { if (c == 'X') return PF_X; if (c == 'W') return PF_W; assert (c == 'R'); return PF_R; } static void push_state (enum prepstate state) { if (prepdepth >= MAX_PREPDEPTH) error (EXIT_FAILURE, 0, gettext ("%d: conditionals nested too deep"), yylineno); prepstate[prepdepth++] = state; } static int pop_state (void) { if (prepdepth == 0) error (0, 0, gettext ("%d: unexpected #endif"), yylineno); else --prepdepth; return prepdepth == 0 ? INITIAL : IGNORE; } static int handle_ifdef (void) { char idbuf[50]; char *id = idbuf; size_t idlen = 0; size_t idmax = sizeof (idbuf); bool ignore_ws = true; bool defined = false; int result; while (1) { int c = input (); if (isspace (c) && ignore_ws) continue; if (c != '_' && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (idlen == 0 || c < '0' || c > '9')) { unput (c); break; } if (idlen == idmax) { char *newp = (char *) alloca (idmax *= 2); id = memcpy (newp, id, idlen); } id[idlen++] = c; ignore_ws = false; } /* XXX Compare in a better way. */ if (idlen == 6 && strncmp (id, "SHARED", 6) == 0) defined = ld_state.file_type == dso_file_type; if (defined) result = INITIAL; else { push_state (skip_if); result = IGNORE; } return result; } static void invalid_char (int ch) { error (0, 0, (isascii (ch) ? gettext ("invalid character '%c' at line %d; ignored") : gettext ("invalid character '\\%o' at line %d; ignored")), ch, yylineno); } // Local Variables: // mode: C // End: