| /* Mach-O object file format |
| Copyright 2009 Free Software Foundation, Inc. |
| |
| This file is part of GAS, the GNU Assembler. |
| |
| GAS 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, |
| or (at your option) any later version. |
| |
| GAS 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 GAS; see the file COPYING. If not, write to the Free |
| Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA |
| 02110-1301, USA. */ |
| |
| #define OBJ_HEADER "obj-macho.h" |
| |
| #include "as.h" |
| #include "subsegs.h" |
| #include "symbols.h" |
| #include "write.h" |
| #include "mach-o.h" |
| #include "mach-o/loader.h" |
| |
| static void |
| obj_mach_o_weak (int ignore ATTRIBUTE_UNUSED) |
| { |
| char *name; |
| int c; |
| symbolS *symbolP; |
| |
| do |
| { |
| /* Get symbol name. */ |
| name = input_line_pointer; |
| c = get_symbol_end (); |
| symbolP = symbol_find_or_make (name); |
| S_SET_WEAK (symbolP); |
| *input_line_pointer = c; |
| SKIP_WHITESPACE (); |
| |
| if (c != ',') |
| break; |
| input_line_pointer++; |
| SKIP_WHITESPACE (); |
| } |
| while (*input_line_pointer != '\n'); |
| demand_empty_rest_of_line (); |
| } |
| |
| /* Parse: |
| .section segname,sectname[,type[,attribute[,sizeof_stub]]] |
| */ |
| |
| static void |
| obj_mach_o_section (int ignore ATTRIBUTE_UNUSED) |
| { |
| char *p; |
| char *segname; |
| char *sectname; |
| char c; |
| int sectype = BFD_MACH_O_S_REGULAR; |
| unsigned int secattr = 0; |
| offsetT sizeof_stub = 0; |
| const char *name; |
| flagword oldflags, flags; |
| asection *sec; |
| |
| /* Parse segment name. */ |
| if (!is_name_beginner (*input_line_pointer)) |
| { |
| as_bad (_("missing segment name")); |
| ignore_rest_of_line (); |
| return; |
| } |
| p = input_line_pointer; |
| c = get_symbol_end (); |
| segname = alloca (input_line_pointer - p + 1); |
| strcpy (segname, p); |
| *input_line_pointer = c; |
| |
| if (*input_line_pointer != ',') |
| { |
| as_bad (_("missing comma after segment name")); |
| ignore_rest_of_line (); |
| return; |
| } |
| input_line_pointer++; |
| |
| /* Parse section name. */ |
| if (!is_name_beginner (*input_line_pointer)) |
| { |
| as_bad (_("missing section name")); |
| ignore_rest_of_line (); |
| return; |
| } |
| p = input_line_pointer; |
| c = get_symbol_end (); |
| sectname = alloca (input_line_pointer - p + 1); |
| strcpy (sectname, p); |
| *input_line_pointer = c; |
| |
| /* Parse type. */ |
| if (*input_line_pointer == ',') |
| { |
| input_line_pointer++; |
| if (!is_name_beginner (*input_line_pointer)) |
| { |
| as_bad (_("missing section type name")); |
| ignore_rest_of_line (); |
| return; |
| } |
| p = input_line_pointer; |
| c = get_symbol_end (); |
| |
| sectype = bfd_mach_o_get_section_type_from_name (p); |
| if (sectype == -1) |
| { |
| as_bad (_("unknown or invalid section type '%s'"), p); |
| sectype = BFD_MACH_O_S_REGULAR; |
| } |
| *input_line_pointer = c; |
| |
| /* Parse attributes. */ |
| if (*input_line_pointer == ',') |
| { |
| do |
| { |
| int attr; |
| |
| input_line_pointer++; |
| |
| if (!is_name_beginner (*input_line_pointer)) |
| { |
| as_bad (_("missing section attribute identifier")); |
| ignore_rest_of_line (); |
| break; |
| } |
| p = input_line_pointer; |
| c = get_symbol_end (); |
| |
| attr = bfd_mach_o_get_section_attribute_from_name (p); |
| if (attr == -1) |
| as_bad (_("unknown or invalid section attribute '%s'"), p); |
| else |
| secattr |= attr; |
| |
| *input_line_pointer = c; |
| } |
| while (*input_line_pointer == '+'); |
| |
| /* Parse sizeof_stub. */ |
| if (*input_line_pointer == ',') |
| { |
| if (sectype != BFD_MACH_O_S_SYMBOL_STUBS) |
| as_bad (_("unexpected sizeof_stub expression")); |
| |
| sizeof_stub = get_absolute_expression (); |
| } |
| else if (sectype == BFD_MACH_O_S_SYMBOL_STUBS) |
| as_bad (_("missing sizeof_stub expression")); |
| } |
| } |
| demand_empty_rest_of_line (); |
| |
| bfd_mach_o_normalize_section_name (segname, sectname, &name, &flags); |
| if (name == NULL) |
| { |
| /* There is no normal BFD section name for this section. Create one. |
| The name created doesn't really matter as it will never be written |
| on disk. */ |
| size_t seglen = strlen (segname); |
| size_t sectlen = strlen (sectname); |
| char *n; |
| |
| n = xmalloc (seglen + 1 + sectlen + 1); |
| memcpy (n, segname, seglen); |
| n[seglen] = '.'; |
| memcpy (n + seglen + 1, sectname, sectlen); |
| n[seglen + 1 + sectlen] = 0; |
| name = n; |
| } |
| |
| #ifdef md_flush_pending_output |
| md_flush_pending_output (); |
| #endif |
| |
| /* Sub-segments don't exists as is on Mach-O. */ |
| sec = subseg_new (name, 0); |
| |
| oldflags = bfd_get_section_flags (stdoutput, sec); |
| if (oldflags == SEC_NO_FLAGS) |
| { |
| bfd_mach_o_section *msect; |
| |
| if (! bfd_set_section_flags (stdoutput, sec, flags)) |
| as_warn (_("error setting flags for \"%s\": %s"), |
| bfd_section_name (stdoutput, sec), |
| bfd_errmsg (bfd_get_error ())); |
| msect = bfd_mach_o_get_mach_o_section (sec); |
| strncpy (msect->segname, segname, sizeof (msect->segname)); |
| msect->segname[16] = 0; |
| strncpy (msect->sectname, sectname, sizeof (msect->sectname)); |
| msect->sectname[16] = 0; |
| msect->flags = secattr | sectype; |
| msect->reserved2 = sizeof_stub; |
| } |
| else if (flags != SEC_NO_FLAGS) |
| { |
| if (flags != oldflags) |
| as_warn (_("Ignoring changed section attributes for %s"), name); |
| } |
| } |
| |
| struct known_section |
| { |
| const char *name; |
| unsigned int flags; |
| }; |
| |
| static const struct known_section known_sections[] = |
| { |
| /* 0 */ { NULL, 0}, |
| /* 1 */ { ".cstring", BFD_MACH_O_S_CSTRING_LITERALS } |
| }; |
| |
| static void |
| obj_mach_o_known_section (int sect_index) |
| { |
| const struct known_section *sect = &known_sections[sect_index]; |
| asection *old_sec; |
| segT sec; |
| |
| #ifdef md_flush_pending_output |
| md_flush_pending_output (); |
| #endif |
| |
| old_sec = bfd_get_section_by_name (stdoutput, sect->name); |
| if (old_sec) |
| { |
| /* Section already present. */ |
| sec = old_sec; |
| subseg_set (sec, 0); |
| } |
| else |
| { |
| bfd_mach_o_section *msect; |
| |
| sec = subseg_force_new (sect->name, 0); |
| |
| /* Set default flags. */ |
| msect = bfd_mach_o_get_mach_o_section (sec); |
| msect->flags = sect->flags; |
| } |
| } |
| |
| /* Called from read.c:s_comm after we've parsed .comm symbol, size. |
| Parse a possible alignment value. */ |
| |
| static symbolS * |
| obj_mach_o_common_parse (int ignore ATTRIBUTE_UNUSED, |
| symbolS *symbolP, addressT size) |
| { |
| addressT align = 0; |
| |
| if (*input_line_pointer == ',') |
| { |
| align = parse_align (0); |
| if (align == (addressT) -1) |
| return NULL; |
| } |
| |
| S_SET_VALUE (symbolP, size); |
| S_SET_EXTERNAL (symbolP); |
| S_SET_SEGMENT (symbolP, bfd_com_section_ptr); |
| |
| symbol_get_bfdsym (symbolP)->flags |= BSF_OBJECT; |
| |
| return symbolP; |
| } |
| |
| static void |
| obj_mach_o_comm (int ignore ATTRIBUTE_UNUSED) |
| { |
| s_comm_internal (ignore, obj_mach_o_common_parse); |
| } |
| |
| static void |
| obj_mach_o_subsections_via_symbols (int arg ATTRIBUTE_UNUSED) |
| { |
| /* Currently ignore it. */ |
| demand_empty_rest_of_line (); |
| } |
| |
| const pseudo_typeS mach_o_pseudo_table[] = |
| { |
| { "weak", obj_mach_o_weak, 0}, |
| { "section", obj_mach_o_section, 0}, |
| { "cstring", obj_mach_o_known_section, 1}, |
| { "lcomm", s_lcomm, 1 }, |
| { "comm", obj_mach_o_comm, 0 }, |
| { "subsections_via_symbols", obj_mach_o_subsections_via_symbols, 0 }, |
| |
| {NULL, NULL, 0} |
| }; |