1233 lines (1104 with data), 26.7 kB
%{
/*-
* Copyright (c) 2010-2013 Kai Wang
* 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 "ld.h"
#include "ld_arch.h"
#include "ld_options.h"
#include "ld_output.h"
#include "ld_script.h"
#include "ld_file.h"
#include "ld_path.h"
#include "ld_exp.h"
ELFTC_VCSID("$Id$");
struct yy_buffer_state;
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#ifndef YY_BUF_SIZE
#define YY_BUF_SIZE 16384
#endif
extern int yylex(void);
extern int yyparse(void);
extern YY_BUFFER_STATE yy_create_buffer(FILE *file, int size);
extern YY_BUFFER_STATE yy_scan_string(char *yy_str);
extern void yy_switch_to_buffer(YY_BUFFER_STATE b);
extern void yy_delete_buffer(YY_BUFFER_STATE b);
extern int lineno;
extern FILE *yyin;
extern struct ld *ld;
static void yyerror(const char *s);
static void _init_script(void);
static struct ld_script_cmd_head ldss_c, ldso_c;
%}
%token T_ABSOLUTE
%token T_ADDR
%token T_ALIGN
%token T_ALIGNOF
%token T_ASSERT
%token T_AS_NEEDED
%token T_AT
%token T_BIND
%token T_BLOCK
%token T_BYTE
%token T_CONSTANT
%token T_CONSTRUCTORS
%token T_CREATE_OBJECT_SYMBOLS
%token T_DATA_SEGMENT_ALIGN
%token T_DATA_SEGMENT_END
%token T_DATA_SEGMENT_RELRO_END
%token T_DEFINED
%token T_ENTRY
%token T_EXCLUDE_FILE
%token T_EXTERN
%token T_FILEHDR
%token T_FILL
%token T_FLAGS
%token T_FLOAT
%token T_FORCE_COMMON_ALLOCATION
%token T_GROUP
%token T_HLL
%token T_INCLUDE
%token T_INHIBIT_COMMON_ALLOCATION
%token T_INPUT
%token T_KEEP
%token T_LENGTH
%token T_LOADADDR
%token T_LONG
%token T_MAP
%token T_MAX
%token T_MEMORY
%token T_MIN
%token T_NEXT
%token T_NOCROSSREFS
%token T_NOFLOAT
%token T_OPTION
%token T_ORIGIN
%token T_OUTPUT
%token T_OUTPUT_ARCH
%token T_OUTPUT_FORMAT
%token T_PHDRS
%token T_PROVIDE
%token T_PROVIDE_HIDDEN
%token T_QUAD
%token T_REGION_ALIAS
%token T_SEARCH_DIR
%token T_SECTIONS
%token T_SEGMENT_START
%token T_SHORT
%token T_SIZEOF
%token T_SIZEOF_HEADERS
%token T_SORT_BY_ALIGNMENT
%token T_SORT_BY_NAME
%token T_SPECIAL
%token T_SQUAD
%token T_STARTUP
%token T_SUBALIGN
%token T_SYSLIB
%token T_TARGET
%token T_TRUNCATE
%token T_VER_EXTERN
%token T_VER_GLOBAL
%token T_VER_LOCAL
%token T_LSHIFT_E
%token T_RSHIFT_E
%token T_LSHIFT
%token T_RSHIFT
%token T_EQ
%token T_NE
%token T_GE
%token T_LE
%token T_ADD_E
%token T_SUB_E
%token T_MUL_E
%token T_DIV_E
%token T_AND_E
%token T_OR_E
%token T_LOGICAL_AND
%token T_LOGICAL_OR
%right '=' T_AND_E T_OR_E T_MUL_E T_DIV_E T_ADD_E T_SUB_E T_LSHIFT_E T_RSHIFT_E
%right '?' ':'
%left T_LOGICAL_OR
%left T_LOGICAL_AND
%left '|'
%left '&'
%left T_EQ T_NE T_GE T_LE '>' '<'
%left T_LSHIFT T_RSHIFT
%left '+' '-'
%left '*' '/' '%'
%left UNARY
%token <num> T_NUM
%token <str> T_COMMONPAGESIZE
%token <str> T_COPY
%token <str> T_DSECT
%token <str> T_IDENT
%token <str> T_INFO
%token <str> T_MAXPAGESIZE
%token <str> T_MEMORY_ATTR
%token <str> T_NOLOAD
%token <str> T_ONLY_IF_RO
%token <str> T_ONLY_IF_RW
%token <str> T_OVERLAY
%token <str> T_STRING
%token <str> T_WILDCARD
%type <assign> assignment
%type <assign> provide_assignment
%type <assign> provide_hidden_assignment
%type <assign> simple_assignment
%type <cmd> assert_command
%type <cmd> entry_command
%type <cmd> ldscript_command
%type <cmd> output_section_command
%type <cmd> sections_command
%type <cmd> sections_sub_command
%type <exp> expression
%type <exp> function
%type <exp> constant
%type <exp> variable
%type <exp> absolute_function
%type <exp> addr_function
%type <exp> align_function
%type <exp> alignof_function
%type <exp> block_function
%type <exp> data_segment_align_function
%type <exp> data_segment_end_function
%type <exp> data_segment_relro_end_function
%type <exp> defined_function
%type <exp> length_function
%type <exp> loadaddr_function
%type <exp> max_function
%type <exp> min_function
%type <exp> next_function
%type <exp> origin_function
%type <exp> output_section_addr
%type <exp> output_section_align
%type <exp> output_section_fillexp
%type <exp> output_section_lma
%type <exp> output_section_subalign
%type <exp> overlay_vma
%type <exp> phdr_at
%type <exp> segment_start_function
%type <exp> sizeof_function
%type <exp> sizeof_headers_function
%type <input_file> input_file
%type <input_section> input_section
%type <input_section> input_section_desc
%type <input_section> input_section_desc_no_keep
%type <list> as_needed_list
%type <list> ident_list
%type <list> ident_list_nosep
%type <list> input_file_list
%type <list> output_section_addr_and_type
%type <list> output_section_phdr
%type <list> overlay_section_list
%type <list> wildcard_list
%type <num> assign_op
%type <num> data_type
%type <num> output_section_keywords
%type <num> overlay_nocref
%type <num> phdr_filehdr
%type <num> phdr_flags
%type <num> phdr_phdrs
%type <output_data> output_section_data
%type <output_desc> output_sections_desc
%type <overlay_desc> overlay_desc
%type <overlay_section> overlay_section
%type <phdr> phdr
%type <region> memory_region
%type <str> ident
%type <str> memory_attr
%type <str> output_section_constraint
%type <str> output_section_lma_region
%type <str> output_section_region
%type <str> output_section_type
%type <str> output_section_type_keyword
%type <str> symbolic_constant
%type <str> wildcard
%type <wildcard> wildcard_sort
%type <version_entry> version_entry
%type <version_entry_head> version_block
%type <version_entry_head> version_entry_list
%type <version_entry_head> extern_block
%type <str> version_dependency
%union {
struct ld_exp *exp;
struct ld_script_assign *assign;
struct ld_script_cmd *cmd;
struct ld_script_list *list;
struct ld_script_input_file *input_file;
struct ld_script_phdr *phdr;
struct ld_script_region *region;
struct ld_script_sections_output *output_desc;
struct ld_script_sections_output_data *output_data;
struct ld_script_sections_output_input *input_section;
struct ld_script_sections_overlay *overlay_desc;
struct ld_script_sections_overlay_section *overlay_section;
struct ld_script_version_entry *version_entry;
struct ld_script_version_entry_head *version_entry_head;
struct ld_wildcard *wildcard;
char *str;
int64_t num;
}
%%
script
: ldscript
|
;
ldscript
: ldscript_command {
if ($1 != NULL)
ld_script_cmd_insert(&ld->ld_scp->lds_c, $1);
}
| ldscript ldscript_command {
if ($2 != NULL)
ld_script_cmd_insert(&ld->ld_scp->lds_c, $2);
}
;
expression
: expression '+' expression {
$$ = ld_exp_binary(ld, LEOP_ADD, $1, $3);
}
| expression '-' expression {
$$ = ld_exp_binary(ld, LEOP_SUBSTRACT, $1, $3);
}
| expression '*' expression {
$$ = ld_exp_binary(ld, LEOP_MUL, $1, $3);
}
| expression '/' expression {
$$ = ld_exp_binary(ld, LEOP_DIV, $1, $3);
}
| expression '%' expression {
$$ = ld_exp_binary(ld, LEOP_MOD, $1, $3);
}
| expression '&' expression {
$$ = ld_exp_binary(ld, LEOP_AND, $1, $3);
}
| expression '|' expression {
$$ = ld_exp_binary(ld, LEOP_OR, $1, $3);
}
| expression '>' expression {
$$ = ld_exp_binary(ld, LEOP_GREATER, $1, $3);
}
| expression '<' expression {
$$ = ld_exp_binary(ld, LEOP_LESSER, $1, $3);
}
| expression T_EQ expression {
$$ = ld_exp_binary(ld, LEOP_EQUAL, $1, $3);
}
| expression T_NE expression {
$$ = ld_exp_binary(ld, LEOP_NE, $1, $3);
}
| expression T_GE expression {
$$ = ld_exp_binary(ld, LEOP_GE, $1, $3);
}
| expression T_LE expression {
$$ = ld_exp_binary(ld, LEOP_LE, $1, $3);
}
| expression T_LSHIFT expression {
$$ = ld_exp_binary(ld, LEOP_LSHIFT, $1, $3);
}
| expression T_RSHIFT expression {
$$ = ld_exp_binary(ld, LEOP_RSHIFT, $1, $3);
}
| expression T_LOGICAL_AND expression {
$$ = ld_exp_binary(ld, LEOP_LOGICAL_AND, $1, $3);
}
| expression T_LOGICAL_OR expression {
$$ = ld_exp_binary(ld, LEOP_LOGICAL_OR, $1, $3);
}
| '!' expression %prec UNARY {
$$ = ld_exp_unary(ld, LEOP_NOT, $2);
}
| '-' expression %prec UNARY {
$$ = ld_exp_unary(ld, LEOP_MINUS, $2);
}
| '~' expression %prec UNARY {
$$ = ld_exp_unary(ld, LEOP_NEGATION, $2);
}
| expression '?' expression ':' expression {
$$ = ld_exp_trinary(ld, $1, $3, $5);
}
| simple_assignment {
$$ = ld_exp_assign(ld, $1);
}
| function
| constant
| variable
| '(' expression ')' { $$ = $2; $$->le_par = 1; }
;
function
: absolute_function
| addr_function
| align_function
| alignof_function
| block_function
| data_segment_align_function
| data_segment_end_function
| data_segment_relro_end_function
| defined_function
| length_function
| loadaddr_function
| max_function
| min_function
| next_function
| origin_function
| segment_start_function
| sizeof_function
| sizeof_headers_function
;
absolute_function
: T_ABSOLUTE '(' expression ')' {
$$ = ld_exp_unary(ld, LEOP_ABS, $3);
}
;
addr_function
: T_ADDR '(' ident ')' {
$$ = ld_exp_unary(ld, LEOP_ADDR, ld_exp_name(ld, $3));
}
;
align_function
: T_ALIGN '(' expression ')' {
$$ = ld_exp_unary(ld, LEOP_ALIGN, $3);
}
| T_ALIGN '(' expression ',' expression ')' {
$$ = ld_exp_binary(ld, LEOP_ALIGN, $3, $5);
}
;
alignof_function
: T_ALIGNOF '(' ident ')' {
$$ = ld_exp_unary(ld, LEOP_ALIGNOF, ld_exp_name(ld, $3));
}
;
block_function
: T_BLOCK '(' expression ')' {
$$ = ld_exp_unary(ld, LEOP_BLOCK, $3);
}
;
data_segment_align_function
: T_DATA_SEGMENT_ALIGN '(' expression ',' expression ')' {
$$ = ld_exp_binary(ld, LEOP_DSA, $3, $5);
}
;
data_segment_end_function
: T_DATA_SEGMENT_END '(' expression ')' {
$$ = ld_exp_unary(ld, LEOP_DSE, $3);
}
;
data_segment_relro_end_function
: T_DATA_SEGMENT_RELRO_END '(' expression ',' expression ')' {
$$ = ld_exp_binary(ld, LEOP_DSRE, $3, $5);
}
;
defined_function
: T_DEFINED '(' ident ')' {
$$ = ld_exp_unary(ld, LEOP_DEFINED, ld_exp_symbol(ld, $3));
}
;
length_function
: T_LENGTH '(' ident ')' {
$$ = ld_exp_unary(ld, LEOP_LENGTH, ld_exp_name(ld, $3));
}
;
loadaddr_function
: T_LOADADDR '(' ident ')' {
$$ = ld_exp_unary(ld, LEOP_LOADADDR, ld_exp_name(ld, $3));
}
;
max_function
: T_MAX '(' expression ',' expression ')' {
$$ = ld_exp_binary(ld, LEOP_MAX, $3, $5);
}
;
min_function
: T_MIN '(' expression ',' expression ')' {
$$ = ld_exp_binary(ld, LEOP_MIN, $3, $5);
}
;
next_function
: T_NEXT '(' expression ')' {
$$ = ld_exp_unary(ld, LEOP_NEXT, $3);
}
;
origin_function
: T_ORIGIN '(' ident ')' {
$$ = ld_exp_unary(ld, LEOP_ORIGIN, ld_exp_name(ld, $3));
}
;
segment_start_function
: T_SEGMENT_START '(' ident ',' expression ')' {
$$ = ld_exp_binary(ld, LEOP_MIN, ld_exp_name(ld, $3), $5);
}
;
sizeof_function
: T_SIZEOF '(' ident ')' {
$$ = ld_exp_unary(ld, LEOP_SIZEOF, ld_exp_name(ld, $3));
}
;
sizeof_headers_function
: T_SIZEOF_HEADERS {
$$ = ld_exp_sizeof_headers(ld);
}
;
constant
: T_NUM {
$$ = ld_exp_constant(ld, $1);
}
| symbolic_constant {
$$ = ld_exp_symbolic_constant(ld, $1);
}
;
symbolic_constant
: T_CONSTANT '(' T_COMMONPAGESIZE ')' { $$ = $3; }
| T_CONSTANT '(' T_MAXPAGESIZE ')' { $$ = $3; }
;
ldscript_command
: assert_command
| assignment {
if (*$1->lda_var->le_name == '.')
ld_fatal(ld, "variable . can only be used inside"
" SECTIONS command");
$$ = ld_script_cmd(ld, LSC_ASSIGN, $1);
}
| entry_command
| extern_command { $$ = NULL; }
| force_common_allocation_command { $$ = NULL; }
| group_command { $$ = NULL; }
| inhibit_common_allocation_command { $$ = NULL; }
| input_command { $$ = NULL; }
| memory_command { $$ = NULL; }
| nocrossrefs_command { $$ = NULL; }
| output_command { $$ = NULL; }
| output_arch_command { $$ = NULL; }
| output_format_command { $$ = NULL; }
| phdrs_command { $$ = NULL; }
| region_alias_command { $$ = NULL; }
| search_dir_command { $$ = NULL; }
| sections_command
| startup_command { $$ = NULL; }
| target_command { $$ = NULL; }
| version_script_node { $$ = NULL; }
| ';' { $$ = NULL; }
;
assignment
: simple_assignment
| provide_assignment
| provide_hidden_assignment
;
simple_assignment
: variable assign_op expression %prec '=' {
$$ = ld_script_assign(ld, $1, $2, $3, 0, 0);
}
;
provide_assignment
: T_PROVIDE '(' variable '=' expression ')' {
$$ = ld_script_assign(ld, $3, LSAOP_E, $5, 1, 0);
}
;
provide_hidden_assignment
: T_PROVIDE_HIDDEN '(' variable '=' expression ')' {
$$ = ld_script_assign(ld, $3, LSAOP_E, $5, 1, 1);
}
;
assign_op
: T_LSHIFT_E { $$ = LSAOP_LSHIFT_E; }
| T_RSHIFT_E { $$ = LSAOP_RSHIFT_E; }
| T_ADD_E { $$ = LSAOP_ADD_E; }
| T_SUB_E { $$ = LSAOP_SUB_E; }
| T_MUL_E { $$ = LSAOP_MUL_E; }
| T_DIV_E { $$ = LSAOP_DIV_E; }
| T_AND_E { $$ = LSAOP_AND_E; }
| T_OR_E { $$ = LSAOP_OR_E; }
| '=' { $$ = LSAOP_E; }
;
assert_command
: T_ASSERT '(' expression ',' T_STRING ')' {
$$ = ld_script_assert(ld, $3, $5);
}
;
entry_command
: T_ENTRY '(' ident ')' {
$$ = ld_script_cmd(ld, LSC_ENTRY, $3);
}
;
extern_command
: T_EXTERN '(' ident_list_nosep ')' { ld_script_extern(ld, $3); }
;
force_common_allocation_command
: T_FORCE_COMMON_ALLOCATION { ld->ld_common_alloc = 1; }
;
group_command
: T_GROUP '(' input_file_list ')' {
ld_script_group(ld, ld_script_list_reverse($3));
}
;
inhibit_common_allocation_command
: T_INHIBIT_COMMON_ALLOCATION { ld->ld_common_no_alloc = 1; }
;
input_command
: T_INPUT '(' input_file_list ')' {
ld_script_input(ld, ld_script_list_reverse($3));
}
;
memory_command
: T_MEMORY '{' memory_region_list '}'
;
memory_region_list
: memory_region {
STAILQ_INSERT_TAIL(&ld->ld_scp->lds_r, $1, ldsr_next);
}
| memory_region_list memory_region {
STAILQ_INSERT_TAIL(&ld->ld_scp->lds_r, $2, ldsr_next);
}
;
memory_region
: ident memory_attr ':' T_ORIGIN '=' expression ',' T_LENGTH '='
expression {
ld_script_region(ld, $1, $2, $6, $10);
}
;
memory_attr
: T_MEMORY_ATTR
| { $$ = NULL; }
;
nocrossrefs_command
: T_NOCROSSREFS '(' ident_list_nosep ')' {
ld_script_nocrossrefs(ld, $3);
}
;
output_command
: T_OUTPUT '(' ident ')' {
if (ld->ld_output == NULL)
ld->ld_output_file = $3;
else
free($3);
}
;
output_arch_command
: T_OUTPUT_ARCH '(' ident ')' {
ld_arch_set(ld, $3);
free($3);
}
;
output_format_command
: T_OUTPUT_FORMAT '(' ident ')' {
ld_output_format(ld, $3, $3, $3);
}
| T_OUTPUT_FORMAT '(' ident ',' ident ',' ident ')' {
ld_output_format(ld, $3, $5, $7);
}
;
phdrs_command
: T_PHDRS '{' phdr_list '}'
;
phdr_list
: phdr {
STAILQ_INSERT_TAIL(&ld->ld_scp->lds_p, $1, ldsp_next);
}
| phdr_list phdr {
STAILQ_INSERT_TAIL(&ld->ld_scp->lds_p, $2, ldsp_next);
}
phdr
: ident ident phdr_filehdr phdr_phdrs phdr_at phdr_flags ';' {
$$ = ld_script_phdr(ld, $1, $2, $3, $4, $5, $6);
}
;
phdr_filehdr
: T_FILEHDR { $$ = 1; }
| { $$ = 0; }
;
phdr_phdrs
: T_PHDRS { $$ = 1; }
| { $$ = 0; }
;
phdr_at
: T_AT '(' expression ')' { $$ = $3; }
| { $$ = NULL; }
;
phdr_flags
: T_FLAGS '(' T_NUM ')' { $$ = $3; }
| { $$ = 0; }
;
region_alias_command
: T_REGION_ALIAS '(' ident ',' ident ')' {
ld_script_region_alias(ld, $3, $5);
}
;
search_dir_command
: T_SEARCH_DIR '(' ident ')' {
ld_path_add(ld, $3, LPT_L);
free($3);
}
;
sections_command
: T_SECTIONS '{' sections_command_list '}' {
struct ld_script_sections *ldss;
ldss = malloc(sizeof(struct ld_script_sections));
if (ldss == NULL)
ld_fatal_std(ld, "malloc");
memcpy(&ldss->ldss_c, &ldss_c, sizeof(ldss_c));
$$ = ld_script_cmd(ld, LSC_SECTIONS, ldss);
STAILQ_INIT(&ldss_c);
}
;
sections_command_list
: sections_sub_command {
if ($1 != NULL)
ld_script_cmd_insert(&ldss_c, $1);
}
| sections_command_list sections_sub_command {
if ($2 != NULL)
ld_script_cmd_insert(&ldss_c, $2);
}
;
sections_sub_command
: entry_command
| assignment {
$$ = ld_script_cmd(ld, LSC_ASSIGN, $1);
}
| output_sections_desc {
$$ = ld_script_cmd(ld, LSC_SECTIONS_OUTPUT, $1);
}
| overlay_desc {
$$ = ld_script_cmd(ld, LSC_SECTIONS_OVERLAY, $1);
}
| ';' { $$ = NULL; }
;
output_sections_desc
: ident output_section_addr_and_type ':' {
/* Remember the name of last output section, needed later for assignment. */
ld->ld_scp->lds_base_os_name = $1;
}
output_section_lma
output_section_align
output_section_subalign
output_section_constraint
'{' output_section_command_list '}'
output_section_region
output_section_lma_region
output_section_phdr
output_section_fillexp {
$$ = calloc(1, sizeof(struct ld_script_sections_output));
if ($$ == NULL)
ld_fatal_std(ld, "calloc");
$$->ldso_name = $1;
$$->ldso_vma = $2->ldl_entry;
$$->ldso_type = $2->ldl_next->ldl_entry;
$$->ldso_lma = $5;
$$->ldso_align = $6;
$$->ldso_subalign = $7;
$$->ldso_constraint = $8;
memcpy(&$$->ldso_c, &ldso_c, sizeof(ldso_c));
$$->ldso_region = $12;
$$->ldso_lma_region = $13;
$$->ldso_phdr = ld_script_list_reverse($14);
$$->ldso_fill = $15;
STAILQ_INIT(&ldso_c);
ld->ld_scp->lds_base_os_name = 0;
ld->ld_scp->lds_last_os_name = $1;
}
;
output_section_addr_and_type
: output_section_addr output_section_type {
$$ = ld_script_list(ld, NULL, $2);
$$ = ld_script_list(ld, $$, $1);
}
| output_section_type {
$$ = ld_script_list(ld, NULL, NULL);
$$ = ld_script_list(ld, $$, $1);
}
;
output_section_addr
: expression
;
output_section_type
: '(' output_section_type_keyword ')' { $$ = $2; }
| '(' ')' { $$ = NULL; }
| { $$ = NULL; }
;
output_section_type_keyword
: T_COPY
| T_DSECT
| T_INFO
| T_NOLOAD
| T_OVERLAY
;
output_section_lma
: T_AT '(' expression ')' { $$ = $3; }
| { $$ = NULL; }
;
output_section_align
: T_ALIGN '(' expression ')' { $$ = $3; }
| { $$ = NULL; }
;
output_section_subalign
: T_SUBALIGN '(' expression ')' { $$ = $3; }
| { $$ = NULL; }
;
output_section_constraint
: T_ONLY_IF_RO
| T_ONLY_IF_RW
| { $$ = NULL; }
;
output_section_command_list
: output_section_command {
if ($1 != NULL)
ld_script_cmd_insert(&ldso_c, $1);
}
| output_section_command_list output_section_command {
if ($2 != NULL)
ld_script_cmd_insert(&ldso_c, $2);
}
;
output_section_command
: assignment {
$$ = ld_script_cmd(ld, LSC_ASSIGN, $1);
}
| input_section_desc {
$$ = ld_script_cmd(ld, LSC_SECTIONS_OUTPUT_INPUT, $1);
}
| output_section_data {
$$ = ld_script_cmd(ld, LSC_SECTIONS_OUTPUT_DATA, $1);
}
| output_section_keywords {
$$ = ld_script_cmd(ld, LSC_SECTIONS_OUTPUT_KEYWORD,
(void *) (uintptr_t) $1);
}
| ';' { $$ = NULL; }
;
input_section_desc
: input_section_desc_no_keep {
$1->ldoi_keep = 0;
$$ = $1;
}
| T_KEEP '(' input_section_desc_no_keep ')' {
$3->ldoi_keep = 0;
$$ = $3;
}
;
input_section_desc_no_keep
: wildcard_sort input_section {
$2->ldoi_ar = NULL;
$2->ldoi_file = $1;
$$ = $2;
}
| wildcard_sort ':' wildcard_sort input_section {
$4->ldoi_ar = $1;
$4->ldoi_ar = $3;
$$ = $4;
}
;
input_section
: '(' T_EXCLUDE_FILE '(' wildcard_list ')' wildcard_list ')' {
$$ = calloc(1, sizeof(struct ld_script_sections_output_input));
if ($$ == NULL)
ld_fatal_std(ld, "calloc");
$$->ldoi_exclude = ld_script_list_reverse($4);
$$->ldoi_sec = ld_script_list_reverse($6);
}
| '(' wildcard_list ')' {
$$ = calloc(1, sizeof(struct ld_script_sections_output_input));
if ($$ == NULL)
ld_fatal_std(ld, "calloc");
$$->ldoi_exclude = NULL;
$$->ldoi_sec = ld_script_list_reverse($2);
}
;
output_section_data
: data_type '(' expression ')' {
$$ = calloc(1, sizeof(struct ld_script_sections_output_data));
if ($$ == NULL)
ld_fatal_std(ld, "calloc");
$$->ldod_type = $1;
$$->ldod_exp = $3;
}
;
data_type
: T_BYTE { $$ = LSODT_BYTE; }
| T_SHORT { $$ = LSODT_SHORT; }
| T_LONG { $$ = LSODT_LONG; }
| T_QUAD { $$ = LSODT_QUAD; }
| T_SQUAD { $$ = LSODT_SQUAD; }
| T_FILL { $$ = LSODT_FILL; }
;
output_section_keywords
: T_CREATE_OBJECT_SYMBOLS {
$$ = LSOK_CREATE_OBJECT_SYMBOLS;
}
| T_CONSTRUCTORS {
$$ = LSOK_CONSTRUCTORS;
}
| T_SORT_BY_NAME '(' T_CONSTRUCTORS ')' {
$$ = LSOK_CONSTRUCTORS_SORT_BY_NAME;
}
;
output_section_region
: '>' ident { $$ = $2; }
| { $$ = NULL; }
;
output_section_lma_region
: T_AT '>' ident { $$ = $3; }
| { $$ = NULL; }
;
output_section_phdr
: output_section_phdr ':' ident {
$$ = ld_script_list(ld, $$, $3);
}
| { $$ = NULL; }
;
output_section_fillexp
: '=' expression { $$ = $2; }
| { $$ = NULL; }
;
overlay_desc
: T_OVERLAY
overlay_vma ':'
overlay_nocref
output_section_lma
'{' overlay_section_list '}'
output_section_region
output_section_phdr
output_section_fillexp {
$$ = calloc(1, sizeof(struct ld_script_sections_overlay));
if ($$ == NULL)
ld_fatal_std(ld, "calloc");
$$->ldso_vma = $2;
$$->ldso_nocrossref = !!$4;
$$->ldso_lma = $5;
$$->ldso_s = $7;
$$->ldso_region = $9;
$$->ldso_phdr = $10;
$$->ldso_fill = $11;
}
;
overlay_vma
: expression
| { $$ = NULL; }
;
overlay_nocref
: T_NOCROSSREFS { $$ = 1; }
| { $$ = 0; }
;
overlay_section_list
: overlay_section {
$$ = ld_script_list(ld, NULL, $1);
}
| overlay_section_list overlay_section {
$$ = ld_script_list(ld, $1, $2);
}
;
overlay_section
: ident
'{' output_section_command_list '}'
output_section_phdr
output_section_fillexp {
$$ = calloc(1,
sizeof(struct ld_script_sections_overlay_section));
if ($$ == NULL)
ld_fatal_std(ld, "calloc");
$$->ldos_name = $1;
memcpy(&$$->ldos_c, &ldso_c, sizeof(ldso_c));
$$->ldos_phdr = $5;
$$->ldos_fill = $6;
STAILQ_INIT(&ldso_c);
}
;
startup_command
: T_STARTUP '(' ident ')' {
ld_file_add_first(ld, $3, LFT_UNKNOWN);
free($3);
}
;
target_command
: T_TARGET '(' ident ')'
;
version_script_node
: ident extern_block version_dependency ';' {
ld_script_version_add_node(ld, $1, $2, $3);
}
| ident version_block version_dependency ';' {
ld_script_version_add_node(ld, $1, $2, $3);
}
| extern_block version_dependency ';' {
ld_script_version_add_node(ld, NULL, $1, $2);
}
| version_block version_dependency ';' {
ld_script_version_add_node(ld, NULL, $1, $2);
}
;
extern_block
: T_VER_EXTERN T_STRING version_block {
ld_script_version_set_lang(ld, $3, $2);
$$ = $3;
}
;
version_block
: '{' version_entry_list '}' {
$$ = $2;
ld->ld_state.ls_version_local = 0;
}
;
version_entry_list
: version_entry {
$$ = ld_script_version_link_entry(ld, NULL, $1);
}
| version_entry_list version_entry {
$$ = ld_script_version_link_entry(ld, $1, $2);
}
;
version_entry
: T_VER_GLOBAL {
ld->ld_state.ls_version_local = 0;
$$ = NULL;
}
| T_VER_LOCAL {
ld->ld_state.ls_version_local = 1;
$$ = NULL;
}
| wildcard ';' {
$$ = ld_script_version_alloc_entry(ld, $1, NULL);
}
| extern_block ';' {
$$ = ld_script_version_alloc_entry(ld, NULL, $1);
}
;
version_dependency
: ident
| { $$ = NULL; }
;
ident
: T_IDENT
| T_STRING
;
variable
: ident { $$ = ld_exp_symbol(ld, $1); }
| '.' { $$ = ld_exp_symbol(ld, "."); }
;
wildcard
: ident
| T_WILDCARD
| '*' { $$ = strdup("*"); }
| '?' { $$ = strdup("?"); }
;
wildcard_sort
: wildcard {
$$ = ld_wildcard_alloc(ld);
$$->lw_name = $1;
$$->lw_sort = LWS_NONE;
}
| T_SORT_BY_NAME '(' wildcard ')' {
$$ = ld_wildcard_alloc(ld);
$$->lw_name = $3;
$$->lw_sort = LWS_NAME;
}
| T_SORT_BY_NAME '(' T_SORT_BY_NAME '(' wildcard ')' ')' {
$$ = ld_wildcard_alloc(ld);
$$->lw_name = $5;
$$->lw_sort = LWS_NAME;
}
| T_SORT_BY_NAME '(' T_SORT_BY_ALIGNMENT '(' wildcard ')' ')' {
$$ = ld_wildcard_alloc(ld);
$$->lw_name = $5;
$$->lw_sort = LWS_NAME_ALIGN;
}
| T_SORT_BY_ALIGNMENT '(' wildcard ')' {
$$ = ld_wildcard_alloc(ld);
$$->lw_name = $3;
$$->lw_sort = LWS_ALIGN;
}
| T_SORT_BY_ALIGNMENT '(' T_SORT_BY_NAME '(' wildcard ')' ')' {
$$ = ld_wildcard_alloc(ld);
$$->lw_name = $5;
$$->lw_sort = LWS_ALIGN_NAME;
}
| T_SORT_BY_ALIGNMENT '(' T_SORT_BY_ALIGNMENT '(' wildcard ')' ')' {
$$ = ld_wildcard_alloc(ld);
$$->lw_name = $5;
$$->lw_sort = LWS_ALIGN;
}
;
ident_list
: ident { $$ = ld_script_list(ld, NULL, $1); }
| ident_list separator ident { $$ = ld_script_list(ld, $1, $3); }
;
ident_list_nosep
: ident { $$ = ld_script_list(ld, NULL, $1); }
| ident_list_nosep ident { $$ = ld_script_list(ld, $1, $2); }
;
input_file_list
: input_file { $$ = ld_script_list(ld, NULL, $1); }
| input_file_list separator input_file { $$ = ld_script_list(ld, $1, $3); }
;
input_file
: ident { $$ = ld_script_input_file(ld, 0, $1); }
| as_needed_list { $$ = ld_script_input_file(ld, 1, $1); }
;
as_needed_list
: T_AS_NEEDED '(' ident_list ')' { $$ = $3; }
;
wildcard_list
: wildcard_sort { $$ = ld_script_list(ld, NULL, $1); }
| wildcard_list wildcard_sort { $$ = ld_script_list(ld, $1, $2); }
;
separator
: ','
|
;
%%
/* ARGSUSED */
static void
yyerror(const char *s)
{
(void) s;
errx(1, "Syntax error in ld script, line %d\n", lineno);
}
static void
_init_script(void)
{
STAILQ_INIT(&ldss_c);
STAILQ_INIT(&ldso_c);
}
void
ld_script_parse(const char *name)
{
YY_BUFFER_STATE b;
_init_script();
if ((yyin = fopen(name, "r")) == NULL)
ld_fatal_std(ld, "fopen %s name failed", name);
b = yy_create_buffer(yyin, YY_BUF_SIZE);
yy_switch_to_buffer(b);
if (yyparse() < 0)
ld_fatal(ld, "unable to parse linker script %s", name);
yy_delete_buffer(b);
}
void
ld_script_parse_internal(void)
{
YY_BUFFER_STATE b;
_init_script();
assert(ld->ld_arch != NULL && ld->ld_arch->script != NULL);
b = yy_scan_string(ld->ld_arch->script);
yy_switch_to_buffer(b);
if (yyparse() < 0)
ld_fatal(ld, "unable to parse internal linker script");
yy_delete_buffer(b);
}