[PATCH] enhance table-based eh support for AIX
Olivier Hainque
hainque@adacore.com
Fri Aug 1 13:11:00 GMT 2008
Hello,
On top of the collect-scan symbol kind filtering capability suggested
here
http://gcc.gnu.org/ml/gcc-patches/2008-07/msg01957.html
This is a resubmission of the enhancement suggestions to improve
support for table-based eh on AIX, initially sent as a preview of our
current internal changes a while ago and approved pending extra doc
and testing results at
http://gcc.gnu.org/ml/gcc-patches/2008-06/msg01966.html
Testing revealed a couple of issues, addressed by this new version,
which passes bootstrap and regression testing for languages=all,ada on
ppc-aix and x86_64-linux.
As ada is currently configured to perform front-end sjlj exceptions,
we also checked that table-based eh is working, with both an FSF
mainline build with Ada eh configuration adjusted and a run on our gcc
4.3 internal tree.
The functional principle is the same as in the initial submission:
in collect2, when searching for static ctors to prevent their
garbage collection, arrange not to reference all the frame tables
from every object and lib early (to prevent always dragging them
all),
in the compiler, arrange to link the tables to their associated
functions, so that they come together when a function is linked in,
in collect2, perform a first pass link and scan to find the eh
tables as on other targets.
On the collect2 side, the first pass link and scan control is now
achieved on all targets by manipulations on a simple scan-filter mask
instead of the previous tricky "ifdef COLLECT_EXPORT_LIST" sequences.
This makes it easier to understand/follow IMO and allows greater
flexibilty.
In particular, this helps the resolution of a problem revealed by the
testing of the initial patch: that we still need to grab all the
tables in the shared_obj case, which has its own specific processing
scheme. This is now pretty easily achieved by controlling the early
scan filter on this condition.
On the compiler side, the initial table/code link implementation was
confused, using current_function_decl outside the FDE loop in
output_call_frame_info. Wrong in-principle this actually didn't work
with -ffunction-sections.
The patch attached here handles this by emitting the links from within
the FDE loop, using fde->decl to get at the function declaration,
switch to the proper section, then back into the frame table
section. This requires a bit of care for targets like AIX that emit
frame tables in the data section, to make sure
switch_to_eh_frame_section emits the table identifying label the first
time only.
Finally, this version includes the (short) documentation of the DWARF2
macro introduced to emit the tables/functions links.
I'll be offline for two weeks starting tomorrow, submitting this now
as a not too distant followup on the previous pieces and to reflect
the stage of our testing and effort on this topic.
Thanks in advance,
Olivier
2008-08-01 Olivier Hainque <hainque@adacore.com>
* collect2.c (DO_COLLECT_EXPORT_LIST): New internal macro,
always defined. Reflect definition or absence of such for
COLLECT_EXPORT_LIST. Readability helper.
(scanfilter enum): Add SCAN_NOTHING.
(main): Reorganize the first pass link control to let AIX
drag only the needed frame tables in executables. Prevent
frame tables collection during the scan aimed at static ctors.
Pre-link and scan for frame tables later to compensate.
* doc/tm.texi (ASM_OUTPUT_DWARF_TABLE_REF): New macro.
A C statement to issue assembly directives that create a reference
to the given DWARF table identifier label from the current function
section.
* dwarf2out.c (switch_to_eh_frame_section): Add a BACK argument
to differentiate first time section entry. Only emit a .data
tables start identifier label the first time around.
(switch_to_frame_table_section): New function. Helper for
output_call_frame_info to switch possibly BACK into the eh_frame
or the debug_frame section depending on FOR_EH.
(output_call_frame_info): Use helper to first enter the proper
frame section. Use ASM_OUTPUT_DWARF_TABLE_REF when defined to
emit a link to the frame table start label from each function
section.
* config/rs6000/rs6000.c (rs6000_aix_asm_output_dwarf_table_ref):
New function. Implementation of ASM_OUTPUT_DWARF_TABLE_REF.
* config/rs6000/rs6000-protos.h: Declare it.
* config/rs6000/aix.h (ASM_OUTPUT_DWARF_TABLE_REF): Define.
-------------- next part --------------
*** collect2.c.ori Fri Jul 25 12:04:06 2008
--- collect2.c Fri Jul 25 14:40:07 2008
*************** int do_collecting = 1;
*** 139,144 ****
--- 139,155 ----
int do_collecting = 0;
#endif
+ /* Cook up an always defined indication of whether we proceed the
+ "EXPORT_LIST" way, much simpler to include in conditional expression
+ and helping readability by preventing the systematic need of #ifdef
+ conditionals. */
+
+ #ifdef COLLECT_EXPORT_LIST
+ #define DO_COLLECT_EXPORT_LIST 1
+ #else
+ #define DO_COLLECT_EXPORT_LIST 0
+ #endif
+
/* Nonzero if we should suppress the automatic demangling of identifiers
in linker error messages. Set from COLLECT_NO_DEMANGLE. */
int no_demangle;
*************** typedef enum {
*** 293,298 ****
--- 304,311 ----
/* ... and which kinds of symbols are to be considered. */
typedef enum {
+ SCAN_NOTHING = 0,
+
SCAN_CTOR = 1 << SYM_CTOR,
SCAN_DTOR = 1 << SYM_DTOR,
SCAN_INIT = 1 << SYM_INIT,
*************** main (int argc, char **argv)
*** 849,854 ****
--- 862,876 ----
const char **c_ptr;
char **ld1_argv;
const char **ld1;
+
+ /* The kinds of symbols we will have to consider when scanning the
+ outcome of a first pass link. This is ALL to start with, then might
+ be adjusted before getting to the first pass link per se, typically on
+ AIX where we perform an early scan of objects and libraries to fetch
+ the list of global ctors/dtors and make sure they are not garbage
+ collected. */
+ scanfilter ld1_filter = SCAN_ALL;
+
char **ld2_argv;
const char **ld2;
char **object_lst;
*************** main (int argc, char **argv)
*** 1289,1306 ****
}
/* The AIX linker will discard static constructors in object files if
! nothing else in the file is referenced, so look at them first. */
! {
! const char **export_object_lst = (const char **)object_lst;
!
! while (export_object_lst < object)
! scan_prog_file (*export_object_lst++, PASS_OBJ, SCAN_ALL);
! }
{
struct id *list = libs.first;
for (; list; list = list->next)
! scan_prog_file (list->name, PASS_FIRST, SCAN_ALL);
}
if (exports.first)
--- 1311,1339 ----
}
/* The AIX linker will discard static constructors in object files if
! nothing else in the file is referenced, so look at them first. Unless
! we are building a shared object, ignore the eh frame tables, as we
! would otherwise reference them all, hence drag all the corresponding
! objects even if nothing else is referenced. */
{
+ const char **export_object_lst = (const char **)object_lst;
struct id *list = libs.first;
+ /* Compute the filter to use from the current one, do scan, then adjust
+ the "current" filter to remove what we just included here. This will
+ control whether we need a first pass link later on or not, and what
+ will remain to be scanned there. */
+
+ scanfilter this_filter
+ = shared_obj ? ld1_filter : (ld1_filter & ~SCAN_DWEH);
+
+ while (export_object_lst < object)
+ scan_prog_file (*export_object_lst++, PASS_OBJ, this_filter);
+
for (; list; list = list->next)
! scan_prog_file (list->name, PASS_FIRST, this_filter);
!
! ld1_filter =~ this_filter;
}
if (exports.first)
*************** main (int argc, char **argv)
*** 1371,1412 ****
}
/* Load the program, searching all libraries and attempting to provide
! undefined symbols from repository information. */
! /* On AIX we do this later. */
! #ifndef COLLECT_EXPORT_LIST
! do_tlink (ld1_argv, object_lst);
! #endif
! /* If -r or they will be run via some other method, do not build the
! constructor or destructor list, just return now. */
! if (rflag
! #ifndef COLLECT_EXPORT_LIST
! || ! do_collecting
! #endif
! )
! {
! #ifdef COLLECT_EXPORT_LIST
! /* Do the link we avoided above if we are exiting. */
do_tlink (ld1_argv, object_lst);
! /* But make sure we delete the export file we may have created. */
! if (export_file != 0 && export_file[0])
! maybe_unlink (export_file);
! #endif
! maybe_unlink (c_file);
! maybe_unlink (o_file);
! return 0;
! }
! /* Examine the namelist with nm and search it for static constructors
! and destructors to call.
! Write the constructor and destructor tables to a .s file and reload. */
!
! /* On AIX we already scanned for global constructors/destructors. */
! #ifndef COLLECT_EXPORT_LIST
! scan_prog_file (output_file, PASS_FIRST, SCAN_ALL);
! #endif
#ifdef SCAN_LIBRARIES
scan_libraries (output_file);
--- 1404,1448 ----
}
/* Load the program, searching all libraries and attempting to provide
! undefined symbols from repository information.
!
! If -r or they will be run via some other method, do not build the
! constructor or destructor list, just return now. */
! {
! bool early_exit
! = rflag || (! DO_COLLECT_EXPORT_LIST && ! do_collecting);
! /* Perform the first pass link now, if we're about to exit or if we need
! to scan for things we haven't collected yet before pursuing further.
! On AIX, the latter typically includes nothing for shared objects or
! frame tables for an executable, out of what the required early scan on
! objects and libraries has performed above. In the !shared_obj case, we
! expect the relevant tables to be dragged together with their associated
! functions from precise cross reference insertions by the compiler. */
!
! if (early_exit || ld1_filter != SCAN_NOTHING)
do_tlink (ld1_argv, object_lst);
+
+ if (early_exit)
+ {
+ #ifdef COLLECT_EXPORT_LIST
+ /* Make sure we delete the export file we may have created. */
+ if (export_file != 0 && export_file[0])
+ maybe_unlink (export_file);
+ #endif
+ maybe_unlink (c_file);
+ maybe_unlink (o_file);
+ return 0;
+ }
+ }
! /* Unless we have done it all already, examine the namelist and search for
! static constructors and destructors to call. Write the constructor and
! destructor tables to a .s file and reload. */
! if (ld1_filter != SCAN_NOTHING)
! scan_prog_file (output_file, PASS_FIRST, ld1_filter);
#ifdef SCAN_LIBRARIES
scan_libraries (output_file);
*************** main (int argc, char **argv)
*** 1419,1424 ****
--- 1455,1463 ----
notice ("%d frame table(s) found\n", frame_tables.number);
}
+ /* If the scan exposed nothing of special interest, there's no need to
+ generate the glue code and relink so return now. */
+
if (constructors.number == 0 && destructors.number == 0
&& frame_tables.number == 0
#if defined (SCAN_LIBRARIES) || defined (COLLECT_EXPORT_LIST)
*************** main (int argc, char **argv)
*** 1429,1438 ****
#endif
)
{
! #ifdef COLLECT_EXPORT_LIST
! /* Do tlink without additional code generation. */
! do_tlink (ld1_argv, object_lst);
! #endif
/* Strip now if it was requested on the command line. */
if (strip_flag)
{
--- 1468,1478 ----
#endif
)
{
! /* Do tlink without additional code generation now if we didn't
! do it earlier for scanning purposes. */
! if (ld1_filter == SCAN_NOTHING)
! do_tlink (ld1_argv, object_lst);
!
/* Strip now if it was requested on the command line. */
if (strip_flag)
{
*** doc/tm.texi.ori Fri Jul 25 18:34:18 2008
--- doc/tm.texi Fri Jul 25 15:37:40 2008
*************** A C statement to issue assembly directiv
*** 8891,8896 ****
--- 8891,8902 ----
reference to the given @var{label}, using an integer of the given @var{size}.
@end defmac
+ @defmac ASM_OUTPUT_DWARF_TABLE_REF (@var{label})
+ A C statement to issue assembly directives that create a reference to
+ the given DWARF table identifier @var{label} from the current function
+ section.
+ @end defmac
+
@deftypefn {Target Hook} void TARGET_ASM_OUTPUT_DWARF_DTPREL (FILE *@var{FILE}, int @var{size}, rtx @var{x})
If defined, this target hook is a function which outputs a DTP-relative
reference to the given TLS symbol of the specified size.
*** dwarf2out.c.ori Fri Jul 25 18:32:14 2008
--- dwarf2out.c Fri Jul 25 15:37:40 2008
*************** dw_cfi_oprnd2_desc (enum dwarf_call_fram
*** 2051,2062 ****
#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
! /* Switch to eh_frame_section. If we don't have an eh_frame_section,
! switch to the data section instead, and write out a synthetic label
! for collect2. */
static void
! switch_to_eh_frame_section (void)
{
tree label;
--- 2051,2062 ----
#if defined (DWARF2_DEBUGGING_INFO) || defined (DWARF2_UNWIND_INFO)
! /* Switch [BACK] to eh_frame_section. If we don't have an eh_frame_section,
! switch to the data section instead, and write out a synthetic start label
! for collect2 the first time around. */
static void
! switch_to_eh_frame_section (bool back)
{
tree label;
*************** switch_to_eh_frame_section (void)
*** 2099,2109 ****
/* We have no special eh_frame section. Put the information in
the data section and emit special labels to guide collect2. */
switch_to_section (data_section);
! label = get_file_function_name ("F");
! ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE));
! targetm.asm_out.globalize_label (asm_out_file,
! IDENTIFIER_POINTER (label));
! ASM_OUTPUT_LABEL (asm_out_file, IDENTIFIER_POINTER (label));
}
}
--- 2099,2113 ----
/* We have no special eh_frame section. Put the information in
the data section and emit special labels to guide collect2. */
switch_to_section (data_section);
!
! if (!back)
! {
! label = get_file_function_name ("F");
! ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (PTR_SIZE));
! targetm.asm_out.globalize_label (asm_out_file,
! IDENTIFIER_POINTER (label));
! ASM_OUTPUT_LABEL (asm_out_file, IDENTIFIER_POINTER (label));
! }
}
}
*************** output_cfi (dw_cfi_ref cfi, dw_fde_ref f
*** 2230,2235 ****
--- 2234,2255 ----
}
}
+ /* Switch [BACK] to the eh or debug frame table section, depending on
+ FOR_EH. */
+ static void
+ switch_to_frame_table_section (int for_eh, bool back)
+ {
+ if (for_eh)
+ switch_to_eh_frame_section (back);
+ else
+ {
+ if (!debug_frame_section)
+ debug_frame_section = get_section (DEBUG_FRAME_SECTION,
+ SECTION_DEBUG, NULL);
+ switch_to_section (debug_frame_section);
+ }
+ }
+
/* Output the call frame information used to record information
that relates to calculating the frame pointer, and records the
location of saved registers. */
*************** output_call_frame_info (int for_eh)
*** 2295,2309 ****
if (flag_debug_asm)
app_enable ();
! if (for_eh)
! switch_to_eh_frame_section ();
! else
! {
! if (!debug_frame_section)
! debug_frame_section = get_section (DEBUG_FRAME_SECTION,
! SECTION_DEBUG, NULL);
! switch_to_section (debug_frame_section);
! }
ASM_GENERATE_INTERNAL_LABEL (section_start_label, FRAME_BEGIN_LABEL, for_eh);
ASM_OUTPUT_LABEL (asm_out_file, section_start_label);
--- 2315,2322 ----
if (flag_debug_asm)
app_enable ();
! /* Switch to the proper frame section, first time. */
! switch_to_frame_table_section (for_eh, false);
ASM_GENERATE_INTERNAL_LABEL (section_start_label, FRAME_BEGIN_LABEL, for_eh);
ASM_OUTPUT_LABEL (asm_out_file, section_start_label);
*************** output_call_frame_info (int for_eh)
*** 2572,2577 ****
--- 2585,2604 ----
for (cfi = fde->dw_fde_cfi; cfi != NULL; cfi = cfi->dw_cfi_next)
output_cfi (cfi, fde, for_eh);
+ /* If we are to emit a ref/link from function bodies to their frame
+ tables, do it now. This is typically performed to make sure that
+ tables associated with functions are dragged with them and not
+ discarded in garbage collecting links, which we need do on a per
+ function basis to cope with -ffunction-sections. */
+
+ #ifdef ASM_OUTPUT_DWARF_TABLE_REF
+ /* Switch to the function section, emit the ref to the tables, and
+ switch *back* into the table section. */
+ switch_to_section (function_section (fde->decl));
+ ASM_OUTPUT_DWARF_TABLE_REF (section_start_label);
+ switch_to_frame_table_section (for_eh, true);
+ #endif
+
/* Pad the FDE out to an address sized boundary. */
ASM_OUTPUT_ALIGN (asm_out_file,
floor_log2 ((for_eh ? PTR_SIZE : DWARF2_ADDR_SIZE)));
*** config/rs6000/rs6000-protos.h.ori Fri Jul 25 18:32:39 2008
--- config/rs6000/rs6000-protos.h Fri Jul 25 15:37:40 2008
*************** extern bool rs6000_tls_referenced_p (rtx
*** 170,175 ****
--- 170,177 ----
extern int rs6000_hard_regno_nregs (int, enum machine_mode);
extern void rs6000_conditional_register_usage (void);
+ extern void rs6000_aix_asm_output_dwarf_table_ref (char *);
+
/* Declare functions in rs6000-c.c */
extern void rs6000_pragma_longcall (struct cpp_reader *);
*** config/rs6000/rs6000.c.ori Fri Jul 25 18:33:31 2008
--- config/rs6000/rs6000.c Fri Jul 25 15:37:40 2008
*************** create_TOC_reference (rtx symbol)
*** 15134,15139 ****
--- 15134,15148 ----
gen_rtx_SYMBOL_REF (Pmode, toc_label_name))));
}
+ /* Issue assembly directives that create a reference to the given DWARF
+ FRAME_TABLE_LABEL from the current function section. */
+ void
+ rs6000_aix_asm_output_dwarf_table_ref (char * frame_table_label)
+ {
+ fprintf (asm_out_file, "\t.ref %s\n",
+ TARGET_STRIP_NAME_ENCODING (frame_table_label));
+ }
+
/* If _Unwind_* has been called from within the same module,
toc register is not guaranteed to be saved to 40(1) on function
entry. Save it there in that case. */
*** config/rs6000/aix.h.ori Fri Jul 25 18:33:45 2008
--- config/rs6000/aix.h Fri Jul 25 15:37:40 2008
***************
*** 43,48 ****
--- 43,54 ----
collect has a chance to see them, so scan the object files directly. */
#define COLLECT_EXPORT_LIST
+ /* Issue assembly directives that create a reference to the given DWARF table
+ identifier label from the current function section. This is defined to
+ ensure we drag frame frame tables associated with needed function bodies in
+ a link with garbage collection activated. */
+ #define ASM_OUTPUT_DWARF_TABLE_REF rs6000_aix_asm_output_dwarf_table_ref
+
/* Handle #pragma weak and #pragma pack. */
#define HANDLE_SYSV_PRAGMA 1
More information about the Gcc-patches
mailing list