| %{ |
| /* Copyright (C) 2001, 2002, 2003, 2004 Red Hat, Inc. |
| Written by Ulrich Drepper <drepper@redhat.com>, 2001. |
| |
| This program is Open Source software; you can redistribute it and/or |
| modify it under the terms of the Open Software License version 1.0 as |
| published by the Open Source Initiative. |
| |
| You should have received a copy of the Open Software License along |
| with this program; if not, you may obtain a copy of the Open Software |
| License version 1.0 from http://www.opensource.org/licenses/osl.php or |
| by writing the Open Source Initiative c/o Lawrence Rosen, Esq., |
| 3001 King Ranch Road, Ukiah, CA 95482. */ |
| |
| #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) |
| |
| /* 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_.*?]+ |
| 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; } |
| 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}"]" { 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: |