blob: 835c2dc16f596b22322ea09647187c77fd37264e [file] [log] [blame]
%{
/* 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: