输出初始值后,回到 assemble_variable ,接下来处理对应的变量。
assemble_variable (continue)
1468 resolve_unique_section (decl, reloc, flag_data_sections );
上面的 flag_data_sections 由选项 –fdata-sections 设置,它连同选项 –ffunction-sections (设置 flag_function_sections )被用于下面的目的 [ 【 6 】 :
如果目标机器支持任意节,在输出文件中,把每个函数或数据项放在它自己的节中。该函数或数据项的名字确定了在输出文件中对应节的名字。 在那些链接器可以执行优化以提高指令空间中引用的局部性的系统上使用这些选项。大多数系统使用 ELF 目标格式,并且运行 Solaris 2 的 SPARC 处理器具有有这样优化的链接器。在未来, AIX 可能会具有这些优化。 仅当这样做会带来大的利益时,才使用这些选项。当你指定这些选项时,汇编器及链接器将构建更大的目标及执行文件,同时汇编器及链接器将运行得更慢。如果你指定了这个选项,你将不能在所有的系统上使用 gprof ,并且如果你同时指定这个选项及‘ -g ’,你可能会有调试的问题。 |
442 void
443 resolve_unique_section (tree decl, int reloc ATTRIBUTE_UNUSED, in varasm.c
444 int flag_function_or_data_sections)
445 {
446 if (DECL_SECTION_NAME (decl) == NULL_TREE
447 && targetm .have_named_sections
448 && (flag_function_or_data_sections
449 || DECL_ONE_ONLY (decl)))
450 (*targetm .asm_out.unique_section) (decl, reloc);
451 }
这里假定没使用 –fdata-sections ,默认地, flag_data_sections 是 0 。而在 449 行 DECL_ONE_ONLY 是非 0 值,如果在多个编译单元中 decl 的拷贝需要被合并。对于 x86 芯片及 Linux OS ,在这里,这个断言返回 false 。
assemble_variable (continue)
1470 /* Handle uninitialized definitions. */
1471
1472 /* If the decl has been given an explicit section name, then it
1473 isn't common, and shouldn't be handled as such. */
1474 if (DECL_SECTION_NAME (decl) || dont_output_data)
1475 ;
1476 /* We don't implement common thread-local data at present. */
1477 else if (DECL_THREAD_LOCAL (decl))
1478 {
1479 if (DECL_COMMON (decl))
1480 sorry ("thread-local COMMON data not implemented");
1481 }
1482 else if (DECL_INITIAL (decl) == 0
1483 || DECL_INITIAL (decl) == error_mark_node
1484 || (flag_zero_initialized_in_bss
1485 /* Leave constant zeroes in .rodata so they can be shared. */
1486 && !TREE_READONLY (decl)
1487 && initializer_zerop (DECL_INITIAL (decl))))
1488 {
1489 unsigned HOST_WIDE_INT size = tree_low_cst (DECL_SIZE_UNIT (decl), 1);
1490 unsigned HOST_WIDE_INT rounded = size;
1491
1492 /* Don't allocate zero bytes of common,
1493 since that means "undefined external" in the linker. */
1494 if (size == 0)
1495 rounded = 1;
1496
1497 /* Round size up to multiple of BIGGEST_ALIGNMENT bits
1498 so that each uninitialized object starts on such a boundary. */
1499 rounded += (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1;
1500 rounded = (rounded / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)
1501 * (BIGGEST_ALIGNMENT / BITS_PER_UNIT));
1502
1503 #if !defined (ASM_OUTPUT_ALIGNED_COMMON) && !defined (ASM_OUTPUT_ALIGNED_DECL_COMMON) && !defined (ASM_OUTPUT_ALIGNED_BSS)
1504 if ((unsigned HOST_WIDE_INT) DECL_ALIGN (decl) / BITS_PER_UNIT > rounded)
1505 warning ("%Jrequested alignment for '%D' is greater than "
1506 "implemented alignment of %d", decl, decl, rounded);
1507 #endif
1508
1509 /* If the target cannot output uninitialized but not common global data
1510 i n .bss, then we have to use .data, so fall through. */
1511 if (asm_emit_uninitialised (decl, name, size, rounded))
1512 return ;
1513 }
因为 resolve_unique_section 在此处不做任何处理(如果需要,它将为 decl 选定一个独有的节),在 1474 行的 DECL_SECTION_NAME 返回 NULL 。
5.13.5.2.2.2.1. 发布汇编 – 未指定初始值
注意 don’t_output_data 是 0 。在 1484 行, flag_zero_initialized_in_bss 默认的是 1 ,它表示把 0 初始化的数据放入 bss 节( GCC 默认地把初始化为 0 的变量放入 BSS ,除非目标机器不支持 BSS )。
在 1267 行, DECL_COMMON 如果成立,表示该声明尽可能放入“ .comm ”节,这样多个未初始化的变量实例可以被合并。但是如果变量的 DECL_INITIAL 不是 error_mark_node ,该变量则不能放入“ .comm ”节。
1250 static bool
1251 asm_emit_uninitialised (tree decl, const char *name, in varasm.c
1252 unsigned HOST_WIDE_INT size ATTRIBUTE_UNUSED,
1253 unsigned HOST_WIDE_INT rounded ATTRIBUTE_UNUSED)
1254 {
1255 enum
1256 {
1257 asm_dest_common,
1258 asm_dest_bss,
1259 asm_dest_local
1260 }
1261 destination = asm_dest_local;
1262
1263 /* ??? We should handle .bss via select_section mechanisms rather than
1264 via special target hooks. That would eliminate this special case. */
1265 if (TREE_PUBLIC (decl))
1266 {
1267 if (!DECL_COMMON (decl))
1268 #ifdef ASM_EMIT_BSS
1269 destination = asm_dest_bss;
1270 #else
1271 return false;
1272 #endif
1273 else
1274 destination = asm_dest_common;
1275 }
1276
1277 if (destination == asm_dest_bss)
1278 globalize_decl (decl);
1279 resolve_unique_section (decl, 0, flag_data_sections );
通常没有初始值的全局变量(包括静态成员声明)的 DECL_COMMON 成立(注意静态变量的 TREE_PUBLIC 是不成立的,它将被输出到“ .local ”节),而具有初始值的声明则不成立。对于具有初始值的对象,在上面 1278 行,通过下面的函数输出声明的属性。
4355 static void
4356 globalize_decl (tree decl) in varasm.c
4357 {
4358 const char *name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
4359
4360 #if defined (ASM_WEAKEN_LABEL ) || defined (ASM_WEAKEN_DECL)
4361 if (DECL_WEAK (decl))
4362 {
4363 tree *p, t;
4364
4365 #ifdef ASM_WEAKEN_DECL
4366 ASM_WEAKEN_DECL (asm_out_file , decl, name, 0);
4367 #else
4368 ASM_WEAKEN_LABEL (asm_out_file , name);
4369 #endif
4370
4371 /* Remove this function from the pending weak list so that
4372 we do not emit multiple .weak directives for it. */
4373 for (p = &weak_decls ; (t = *p) ; )
4374 {
4375 if (DECL_ASSEMBLER_NAME (decl) == DECL_ASSEMBLER_NAME (TREE_VALUE (t)))
4376 *p = TREE_CHAIN (t);
4377 else
4378 p = &TREE_CHAIN (t);
4379 }
4380 return ;
4381 }
4382 #endif
4383
4384 (*targetm .asm_out.globalize_label ) (asm_out_file , name);
4385 }
上面对于我们的目标机器,宏 ASM_WEAKEN_DECL 没有定义,而宏 ASM_WEAKEN_LABEL 则定义如下,输出所谓的弱声明的属性。
240 #define ASM_WEAKEN_LABEL (FILE, NAME) / in elfos.h
241 do /
242 { /
243 fputs ("/t.weak/t", (FILE)); /
244 assemble_name ((FILE), (NAME)); /
245 fputc ('/n', (FILE)); /
246 } /
247 while (0)
对于非弱全局声明,则是通过 4384 行的钩子 globalize_label 输出。在我们的目标机器上,这个钩子函数是 default_globalize_label 。
5252 #ifdef GLOBAL_ASM_OP
5253 void
5254 default_globalize_label (FILE * stream, const char *name) in varasm.c
5255 {
5256 fputs (GLOBAL_ASM_OP, stream);
5257 assemble_name (stream, name);
5258 putc ('/n', stream);
5259 }
5260 #endif /* GLOBAL_ASM_OP */
GLOBAL_ASM_OP 在这里被定义为“ .globl ”(另一个兼容的形式是“ .global ”),它使得该符号对 ld ( GNU 链接器)可见。因此 default_globalize_label 的输出形如“ .globl b ”,其中“ b ”是相应的变量名。
输出了对象的属性及其名字之后,接着由下面的代码确定并输出其所在节(如果需要改变当前的节)。
asm_emit_unintialised (continue)
1281 if (flag_shared_data )
1282 {
1283 switch (destination)
1284 {
1285 #ifdef ASM_OUTPUT_SHARED_BSS
1286 case asm_dest_bss:
1287 ASM_OUTPUT_SHARED_BSS (asm_out_file , decl, name, size, rounded);
1288 return ;
1289 #endif
1290 #ifdef ASM_OUTPUT_SHARED_COMMON
1291 case asm_dest_common:
1292 ASM_OUTPUT_SHARED_COMMON (asm_out_file , name, size, rounded);
1293 return ;
1294 #endif
1295 #ifdef ASM_OUTPUT_SHARED_LOCAL
1296 case asm_dest_local:
1297 ASM_OUTPUT_SHARED_LOCAL (asm_out_file , name, size, rounded);
1298 return ;
1299 #endif
1300 default :
1301 break ;
1302 }
1303 }
1304
1305 switch (destination)
1306 {
1307 #ifdef ASM_EMIT_BSS
1308 case asm_dest_bss:
1309 ASM_EMIT_BSS (decl, name, size, rounded);
1310 break ;
1311 #endif
1312 case asm_dest_common:
1313 ASM_EMIT_COMMON (decl, name, size, rounded);
1314 break ;
1315 case asm_dest_local:
1316 ASM_EMIT_LOCAL (decl, name, size, rounded);
1317 break ;
1318 default :
1319 abort ();
1320 }
1321
1322 return true;
1323 }
对于 x86/Linux ,上面带有“ SHARED ”字段的宏都没有定义,这表明 flag_shared_data 在这里其实不起作用。
宏 ASM_EMIT_BSS ,对于 x86/Linux 目标机器,被定义为 asm_output_aligned_bss 。
501 static void
502 asm_output_aligned_bss (FILE *file, tree decl ATTRIBUTE_UNUSED, in varasm.c
503 const char *name, unsigned HOST_WIDE_INT size,
504 int align)
505 {
506 bss_section ();
507 ASM_OUTPUT_ALIGN (file, floor_log2 (align / BITS_PER_UNIT));
508 #ifdef ASM_DECLARE_OBJECT_NAME
509 last_assemble_variable_decl = decl;
510 ASM_DECLARE_OBJECT_NAME (file, name, decl);
511 #else
512 /* Standard thing is just output label for the object. */
513 ASM_OUTPUT_LABEL (file, name);
514 #endif /* ASM_DECLARE_OBJECT_NAME */
515 ASM_OUTPUT_SKIP (file, size ? size : 1);
516 }
首先通过下面的函数检查我们是否已经在“ .bss ”节,不是的话需要切换到“ .bss ”节。 BSS_SECTION_ASM_OP 被定义为“ /t.bss ”。
457 void
458 bss_section (void) in varasm.c
459 {
460 if (in_section != in_bss)
461 {
462 fprintf (asm_out_file , "%s/n", BSS_SECTION_ASM_OP);
463 i n_section = in_bss;
464 }
465 }
前面我们已经看过宏 ASM_OUTPUT_LABEL ,在那里它被用来输出标签。在这里我们要输出的是变量,在某些机器上,它们在本质上是不同的。这里, x86/Linux 定义了下面的宏来专门输出变量。
287 #define ASM_DECLARE_OBJECT_NAME (FILE, NAME, DECL) / in elfos.h
288 do /
289 { /
290 HOST_WIDE_INT size; /
291 /
292 ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "object"); /
293 /
294 size_directive_output = 0; /
295 if (!flag_inhibit_size_directive /
296 && (DECL) && DECL_SIZE (DECL)) /
297 { /
298 size_directive_output = 1; /
299 size = int_size_in_bytes (TREE_TYPE (DECL)); /
300 ASM_OUTPUT_SIZE_DIRECTIVE (FILE, NAME, size); /
301 } /
302 /
303 ASM_OUTPUT_LABEL (FILE, NAME); /
304 } /
305 while (0)
这里变量与标签的不同之处在于,变量有附加的说明,它由下面的宏输出。
186 #ifndef ASM_OUTPUT_TYPE_DIRECTIVE in defaults.h
187 #if defined TYPE_ASM_OP && defined TYPE_OPERAND_FMT
188 #define ASM_OUTPUT_TYPE_DIRECTIVE (STREAM, NAME, TYPE) /
189 do /
190 { /
191 fputs (TYPE_ASM_OP, STREAM); /
192 assemble_name (STREAM, NAME); /
193 fputs (", ", STREAM); /
194 fprintf (STREAM, TYPE_OPERAND_FMT, TYPE); /
195 putc ('/n', STREAM); /
196 } /
197 while (0)
198 #endif
199 #endif
上面的 TYPE_OPERAND_FMT 在 elfos.h 中定义为“ @%s ”,而 TYPE_ASM_OP 则是“ /t.type/t ”。那么宏 ASM_OUTPUT_TYPE_DIRECTIVE 将输出形如:“ .type b, @object ”的内容,其中“ b ”是相应的变量名。
202 #ifndef ASM_OUTPUT_SIZE_DIRECTIVE
203 #ifdef SIZE_ASM_OP
204 #define ASM_OUTPUT_SIZE_DIRECTIVE (STREAM, NAME, SIZE) /
205 do /
206 { /
207 HOST_WIDE_INT size_ = (SIZE); /
208 fputs (SIZE_ASM_OP, STREAM); /
209 assemble_name (STREAM, NAME); /
210 fprintf (STREAM, ", " HOST_WIDE_INT_PRINT_DEC "/n", size_); /
211 } /
212 while (0)
如果 flag_inhibit_size_directive 不是 0 ,表示禁止在 elf 中使用“ .size ”,它由编译选项 -finhibit-size-directive 设置,默认为 0 。只要不禁止,就使用 ASM_OUTPUT_SIZE_DIRECTIVE 输出变量的大小。这里 SIZE_ASM_OP 的定义是“ /t.size/t ”。输出的内容形如:“ .size b, 4 ”,其中“ b ”是相应的变量名。
接着在 ASM_DECLARE_OBJECT_NAME 的 303 行调用 ASM_OUTPUT_LABLE ,把变量名输出为标签。然后在 asm_output_aligned_bss 的 515 行,由 ASM_OUTPUT_SKIP 输出形如“ .zero 4 ”的缺省初始值,其中“ 4 ”是变量的大小。
若要输出到“ .comm ”节,在这里,其输出宏 ASM_EMIT_COMMON 被定义为宏 ASM_OUTPUT_ALIGNED_COMMON 。
164 #undef ASM_OUTPUT_ALIGNED_COMMON
165 #define ASM_OUTPUT_ALIGNED_COMMON (FILE, NAME, SIZE, ALIGN)/ in elfos.h
166 do /
167 { /
168 fprintf ((FILE), "%s", COMMON_ASM_OP); /
169 assemble_name ((FILE), (NAME)); /
170 fprintf ((FILE), ","HOST_WIDE_INT_PRINT_UNSIGNED",%u/n", /
171 (SIZE), (ALIGN) / BITS_PER_UNIT); /
172 } /
173 while (0)
上面的 COMMON_ASM_OP 被定义为“ /t.comm/t ”,最终的输出形如“ .comm b,4,4 ”,其中“ b ”是相应的变量名,第一个“ 4 ”是变量大小,后面的“ 4 ”是变量的对齐量。注意这里不会输出变量名的标签,及默认的初始值。
同样,对于局部变量(注意,包括静态变量), ASM_EMIT_LOCAL 在这里被定义为宏 ASM_OUTPUT_ALIGNED_LOCAL 。
182 #undef ASM_OUTPUT_ALIGNED_LOCAL
183 #define ASM_OUTPUT_ALIGNED_LOCAL (FILE, NAME, SIZE, ALIGN) / in elfos.h
184 do /
185 { /
186 fprintf ((FILE), "%s", LOCAL_ASM_OP); /
187 assemble_name ((FILE), (NAME)); /
188 fprintf ((FILE), "/n"); /
189 ASM_OUTPUT_ALIGNED_COMMON (FILE, NAME, SIZE, ALIGN); /
190 } /
191 while (0)
在这里, LOCAL_ASM_OP 的定义是“ /t.local/t ”,因此这里的输出形如:
.local a
.comm a,4,4
5.13.5.2.2.2.2. 发布汇编 – 指定初始值
如果全局 / 静态变量具有初始值,则来到这里。首先由下面 1524 行的 variable_section 选出目标节(参见 确定输出节 )。
assemble_variable (continue)
1515 /* Handle initialized definitions.
1516 Also handle uninitialized global definitions if -fno-common and the
1517 target doesn't support ASM_OUTPUT_BSS. */
1518
1519 /* First make the assembler name(s) global if appropriate. */
1520 if (TREE_PUBLIC (decl) && DECL_NAME (decl))
1521 globalize_decl (decl);
1522
1523 /* Switch to the appropriate section. */
1524 variable_section (decl, reloc);
1525
1526 /* dbxout.c needs to know this. */
1527 if (in_text_section ())
1528 DECL_IN_TEXT_SECTION (decl) = 1;
1529
1530 /* Output the alignment of this data. */
1531 if (align > BITS_PER_UNIT)
1532 {
1533 ASM_OUTPUT_ALIGN (asm_out_file ,
1534 floor_log2 (DECL_ALIGN (decl) / BITS_PER_UNIT));
1535 }
1536
1537 /* Do any machine/system dependent processing of the object. */
1538 #ifdef ASM_DECLARE_OBJECT_NAME
1539 last_assemble_variable_decl = decl;
1540 ASM_DECLARE_OBJECT_NAME (asm_out_file , name, decl);
1541 #else
1542 /* Standard thing is just output label for the object. */
1543 ASM_OUTPUT_LABEL (asm_out_file , name);
1544 #endif /* ASM_DECLARE_OBJECT_NAME */
1545
1546 if (!dont_output_data)
1547 {
1548 if (DECL_INITIAL (decl) && DECL_INITIAL (decl) != error_mark_node)
1549 /* Output the actual data. */
1550 output_constant (DECL_INITIAL (decl),
1551 tree_low_cst (DECL_SIZE_UNIT (decl), 1),
1552 align);
1553 else
1554 /* Leave space for it. */
1555 assemble_zeros (tree_low_cst (DECL_SIZE_UNIT (decl), 1));
1556 }
1557 }
余下的过程与我们上面所见非常相似。注意缺省初始化常量,它们在 1555 行处理。