arm-none-eabi-gcc's default linker script defines several sections with VMA of 0. Most of these contain debug information:
/* Stabs debugging sections. */
.stab 0 : { *(.stab) }
.stabstr 0 : { *(.stabstr) }
.stab.excl 0 : { *(.stab.excl) }
.stab.exclstr 0 : { *(.stab.exclstr) }
.stab.index 0 : { *(.stab.index) }
.stab.indexstr 0 : { *(.stab.indexstr) }
.comment 0 : { *(.comment) }
.gnu.build.attributes : { *(.gnu.build.attributes .gnu.build.attributes.*) }
/* DWARF debug sections.
Symbols in the DWARF debugging sections are relative to the beginning
of the section so we begin them at 0. */
/* DWARF 1. */
.debug 0 : { *(.debug) }
.line 0 : { *(.line) }
/* GNU DWARF 1 extensions. */
.debug_srcinfo 0 : { *(.debug_srcinfo) }
.debug_sfnames 0 : { *(.debug_sfnames) }
/* DWARF 1.1 and DWARF 2. */
.debug_aranges 0 : { *(.debug_aranges) }
.debug_pubnames 0 : { *(.debug_pubnames) }
/* DWARF 2. */
.debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
.debug_abbrev 0 : { *(.debug_abbrev) }
.debug_line 0 : { *(.debug_line .debug_line.* .debug_line_end) }
.debug_frame 0 : { *(.debug_frame) }
.debug_str 0 : { *(.debug_str) }
.debug_loc 0 : { *(.debug_loc) }
.debug_macinfo 0 : { *(.debug_macinfo) }
//snip several more of these, until...
.ARM.attributes 0 : { KEEP (*(.ARM.attributes)) KEEP (*(.gnu.attributes)) }
.note.gnu.arm.ident 0 : { KEEP (*(.note.gnu.arm.ident)) }
I don't understand what this does, and the explanatory comment on the DWARF sections doesn't help - the sections can't really all have the same start address unless they're all size 0! Plus, the script is assigning address 0 to the sections, not the symbols. And that comment didn't apply to the "Stabs" section...
Using readelf -S shows that they all theoretically have an address of 0, but also all have different offsets - presumably these offsets are their actual addresses if loaded:
[13] .stab PROGBITS 00000000 009a6c 00009c 0c 14 0 4
[14] .stabstr STRTAB 00000000 009b08 00014d 00 0 0 1
...
[16] .debug_aranges PROGBITS 00000000 009cb0 0005f0 00 0 0 8
[17] .debug_info PROGBITS 00000000 00a2a0 0110c9 00 0 0 1
[18] .debug_abbrev PROGBITS 00000000 01b369 00401d 00 0 0 1
[19] .debug_line PROGBITS 00000000 01f386 0063ed 00 0 0 1
[20] .debug_frame PROGBITS 00000000 025774 00097c 00 0 0 4
[21] .debug_str PROGBITS 00000000 0260f0 001f29 01 MS 0 0 1
[22] .debug_line_str PROGBITS 00000000 028019 0000b3 01 MS 0 0 1
[23] .debug_loclists PROGBITS 00000000 0280cc 00221c 00 0 0 1
[24] .debug_rnglists PROGBITS 00000000 02a2e8 000495 00 0 0 1
[25] .ARM.attributes ARM_ATTRIBUTES 00000000 02a77d 00002e 00 0 0 1
The only idea I have is that virtual address 0 might be considered a special address, which is not taken literally, but instead means that sections with this address will not be loaded into memory (and so will not have a load address) unless the code is being run under a debugger. I have not been able to find any evidence to support this, however.
Can anyone explain to me what is actually happening with these duplicate addresses?
This is explained in the elf(5) man page:
sh_flags
...
SHF_ALLOC
This section occupies memory during process
execution. Some control sections do not reside in
the memory image of an object file. This attribute
is off for those sections.
sh_addr
If this section appears in the memory image of a process,
this member holds the address at which the section's first
byte should reside. Otherwise, the member contains zero.
You should see that the SHF_ALLOC flag is unset for those sections (no A in the Flags field of the readelf -S output) and so they do not reside in the memory image. There is thus no need to specify a start address, so that field is set to zero.
These sections actually do not need to be loaded into memory by exec* under any circumstances, debugger or not. They are certainly not needed for the programmer to run, and when debugging, the debugger will open and read the binary file to parse out that data, as a separate operation from executing the program.
Related
I am trying to compile an application using arm-none-eabi-gcc and would like specific section names to be contained in my binary. The binary is usually compiled by my IDE.
From the official GNU documentation:
SECTIONS { ...
secname : {
contents
}
... }
secname must meet the constraints of your output format. In formats which only support a limited number of sections, such as a.out, the name must be one of the names supported by the format (a.out, for example, allows only .text, .data or .bss).
The last sentence implies that depending on the output format you can have other or more sectionnames. The binary generated by my IDE contains amongst other things the following sections:
$ readelf -S TARGETBINARY.elf
There are 27 section headers, starting at offset 0x5335d8:
Section Headers:
[Nr] Name Type Addr Off Size ES Flg Lk Inf Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .sram PROGBITS 34000000 010000 053384 00 WAX 0 0 16
[ 2] .non_cacheable PROGBITS 34180000 070000 040000 00 WAX 0 0 256
[ 3] .ARM.exidx ARM_EXIDX 341c0000 0b0000 000008 00 AL 1 0 4
[ 4] .heap NOBITS 341c0008 0b0008 001000 00 WA 0 0 1
[ 5] .llce_boot_end PROGBITS 43840000 0b0008 000000 00 W 0 0 1
[ 6] .can_43_llce_shar PROGBITS 43800000 0b0008 000000 00 W 0 0 1
[ 7] .lin_43_llce_shar PROGBITS 4383d000 0b0008 000000 00 W 0 0 1
[ 8] .llce_meas_shared PROGBITS 4384ffe0 0b0008 000000 00 W 0 0 1
[ 9] .shareable_ram_bs PROGBITS 22c00000 0b0008 000000 00 W 0 0 1
[10] .shareable_ram_da PROGBITS 22c00000 0b0008 000000 00 W 0 0 1
[11] .debug_info PROGBITS 00000000 0b0008 12ade1 00 0 0 1
[12] .debug_abbrev PROGBITS 00000000 1dade9 01dceb 00 0 0 1
[13] .debug_aranges PROGBITS 00000000 1f8ad8 002848 00 0 0 8
[14] .debug_macro PROGBITS 00000000 1fb320 06d270 00 0 0 1
[15] .debug_line PROGBITS 00000000 268590 091d40 00 0 0 1
[16] .debug_str PROGBITS 00000000 2fa2d0 1a4bbc 01 MS 0 0 1
[17] .comment PROGBITS 00000000 49ee8c 000080 01 MS 0 0 1
[18] .ARM.attributes ARM_ATTRIBUTES 00000000 49ef0c 000037 00 0 0 1
[19] .debug_loc PROGBITS 00000000 49ef43 059247 00 0 0 1
[20] .debug_ranges PROGBITS 00000000 4f8190 0045a0 00 0 0 8
[21] .debug_frame PROGBITS 00000000 4fc730 00e310 00 0 0 4
[22] .stab PROGBITS 00000000 50aa40 00009c 0c 23 0 4
[23] .stabstr STRTAB 00000000 50aadc 00014d 00 0 0 1
[24] .symtab SYMTAB 00000000 50ac2c 017910 10 25 3938 4
[25] .strtab STRTAB 00000000 52253c 010f3e 00 0 0 1
[26] .shstrtab STRTAB 00000000 53347a 00015e 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
y (purecode), p (processor specific)
$
The generated binary info:
$file TARGETBINARY.elf
TARGETBINARY.elf: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), statically linked, with debug_info, not stripped
Nothing fancy regarding file's output I guess...
As you can see this elf format does not for instance have a section named .bss, .text, etc... Instead it has amongst other things a .sram section, which is precisely what I want to have as well in my binary. Yet when I try to have a .sram section and no .bss section the linker tells me I am missing a .bss section. Which makes me believe I must be missing a linker option somehow...
This is my simplified linker script:
MEMORY
{
foo : ORIGIN = 0x22C00000, LENGTH = 0x00004000
}
SECTIONS
{
.sram :
{
__sram_bss_start = .;
*(.bss)
*(.bss*)
__sram_bss_end = .;
*(.text)
} > foo
}
This is the linker script used by my IDE: https://pastebin.com/c1JfXbY7
What option would allow me to have such .sram sections etc and no .bss nor .text section?
The GNU linker's documentation speaks about "--oformat=output-format" but there is not much info here about what possibilities there are for this option that could allow me to achieve what I meed here.
So I read all the relevant parts of the official documentation, but did not find anything usefull so far
As you can see this elf format does not for instance have a section named .bss, .text, etc... Instead it has amongst other things a .sram section, which is precisely what I want to have as well in my binary. Yet when I try to have a .sram section and no .bss section the linker tells me I am missing a .bss section. Which makes me believe I must be missing a linker option somehow...
Why do you want an .sram section versus a .bss? The simple solution is to use .bss. It is used by _start which is code that runs before main. The main routine needs all global and static variable to be set. There are two classes. 'Zero' variables as per .bss and initialized values. By default the _start implementation will look for a .bss section to initialize.
You don't say what libc your are using nor the compiler/linker options. If you insist on having just .sram, then you need to explore options like, -freestanding, -nostartfiles, and -nostdlib. By not having a .bss section you have broken some coupling between the standard library and the compiler/linker.
I think your real question is how to make a 'ram image' that you can program to ram without needing flash to be active as this is not supported by your IDE. There are various ways to this goal. You need to start by exploring the options above; especially -nostartfiles, which will require you to replace them. The other way is to provide the symbols that the linker wants. There is no reason not to name a section '.bss' and put it in RAM. The regions you have 'foo' is better name 'RAM' and you can put multiple section by using the '>RAM' linker syntax.
The options are enumerated in this answer.
Change the build options.
Provide the library.
Provide an alternative library.
Avoid the function call/data use
It would be difficult to avoid global and static data (but possible). You can define the symbols that the linker/startup wants. You haven't actually given an error message. It might be hidden by your 'IDE' which you might want to give details of. Eclipse based tools like 'Modus', STMCubeIDE, etc hide details like this from the developer.
Here is an example project that adds a binary section to an elf object file. This works with GCC:
#include <unistd.h>
extern char _binary_metamorphosis_txt_start;
int main() {
char* x = &_binary_metamorphosis_txt_start;
int r;
r = write(1,"Starting:\n",10);
r = write(1,x,40);
r = write(1,"\n",1);
return r;
}
In the unlinked assembly, we can see:
leaq _binary_metamorphosis_txt_start(%rip), %rsi
Then, we stick a bunch of text into an object file:
> vim metamorphosis.txt
... copy Franz Kafka's classic work into a text file ...
> ld -r -b binary metamorphosis.txt -o metamorphosis.o
> objdump -x metamorphosis.o
metamorphosis.o: file format elf64-x86-64
...
SYMBOL TABLE:
0000000000000000 l d .data 0000000000000000 .data
0000000000009587 g .data 0000000000000000 _binary_metamorphosis_txt_end
0000000000000000 g .data 0000000000000000 _binary_metamorphosis_txt_start
0000000000009587 g *ABS* 0000000000000000 _binary_metamorphosis_txt_size
Then we compile and link it all together:
> gcc -O -Wall main.c metamorphosis.o
> ./a.out
Starting:
One morning, when Gregor Samsa woke from
Cool. It works. But what if, instead of linking some extra content into my binary, I want to reserve some space and then mmap something in myself. I don't want to hardcode the virtual address. I just want to reserve space near the text, data, and bss sections. And I don't want it to start out backed by anything. From the previous example, the binary I end up with has these sections (the contents of The Metamorphosis end up in .data after linking):
> readelf -W -S a.out
There are 29 section headers, starting at offset 0xaf50:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 0000000000000238 000238 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 0000000000000254 000254 000020 00 A 0 0 4
[ 3] .note.gnu.build-id NOTE 0000000000000274 000274 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000298 000298 00001c 00 A 5 0 8
[ 5] .dynsym DYNSYM 00000000000002b8 0002b8 0000a8 18 A 6 1 8
[ 6] .dynstr STRTAB 0000000000000360 000360 000083 00 A 0 0 1
[ 7] .gnu.version VERSYM 00000000000003e4 0003e4 00000e 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 00000000000003f8 0003f8 000020 00 A 6 1 8
[ 9] .rela.dyn RELA 0000000000000418 000418 0000c0 18 A 5 0 8
[10] .rela.plt RELA 00000000000004d8 0004d8 000018 18 AI 5 22 8
[11] .init PROGBITS 00000000000004f0 0004f0 000017 00 AX 0 0 4
[12] .plt PROGBITS 0000000000000510 000510 000020 10 AX 0 0 16
[13] .plt.got PROGBITS 0000000000000530 000530 000008 08 AX 0 0 8
[14] .text PROGBITS 0000000000000540 000540 0001d2 00 AX 0 0 16
[15] .fini PROGBITS 0000000000000714 000714 000009 00 AX 0 0 4
[16] .rodata PROGBITS 0000000000000720 000720 00000f 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 0000000000000730 000730 00003c 00 A 0 0 4
[18] .eh_frame PROGBITS 0000000000000770 000770 000100 00 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000200db8 000db8 000008 08 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000200dc0 000dc0 000008 08 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000200dc8 000dc8 0001f0 10 WA 6 0 8
[22] .got PROGBITS 0000000000200fb8 000fb8 000048 08 WA 0 0 8
[23] .data PROGBITS 0000000000201000 001000 009597 00 WA 0 0 8
[24] .bss NOBITS 000000000020a597 00a597 000001 00 WA 0 0 1
[25] .comment PROGBITS 0000000000000000 00a597 000029 01 MS 0 0 1
[26] .symtab SYMTAB 0000000000000000 00a5c0 000630 18 27 43 8
[27] .strtab STRTAB 0000000000000000 00abf0 000260 00 0 0 1
[28] .shstrtab STRTAB 0000000000000000 00ae50 0000fe 00 0 0 1
The very first section has type NULL. It also has no flags (not even allocate). There are also .symtab, .strtab, and .shstrtab section that are not allocated, but unlike the NULL section, these have non-zero size. However, I don't think any of these cause virtual address space to be reserved. What I'm trying to do is get GCC to emit something like a NULL section but with a non-zero size of my choosing. And it should provide a promise than nothing else will use that virual address space. That way, at runtime, I could back it with memory from a file using MAP_FIXED:
extern char _my_unmapped_section_start;
int main() {
char* x = &_my_unmapped_section_start;
int fd = open("/etc/bunch_of_data.bin", O_RDONLY);
mmap(x, 4096 * 100, PROT_READ, MAP_SHARED | MAP_FIXED, fd, 0);
char theThousandthCharacter = x[999];
...
}
I don't even want it to start out zeroed like a bss section is. If anything, I want it to be like a PROT_NONE mapping. I've tried going about this several ways:
Linker scripts with NOLOAD or DSECT
Patching object files with objcopy --add-section
I've not been able to get either of these working though. What I'm trying to figure out is whether or not it's possible to get a ELF binary that has think behavior. I'm not totally sure whether or not ELF can even represent this. If it can, then is there a straightforward way to get gcc and ld to do this?
To address the "XY problem" concern, I'll add that I'm not actually trying to use C for anything. This is just part of an idea for implementing a high-level language. I'm pondering how to share read-only (immutable) data that's part of the standard library that I don't want to duplicate into every compiled binary. It would be nice to get RIP-relative addressing for this data rather than going through a GOT like C would typically do in this situation. For function calls, ASLR on shared objects (the standard library) is pretty standard and provides some amount of attack mitigation, so I'm fine with the standard "go through of GOT" on function calls. But on immutable data (in a read-only memory map), I don't belive there is any reason to prefer the extra indirection. So I was thinking about how to make it possible to skip the GOT for data.
I have some assembler for the Microblaze that I want to load at address 0x00000000 (ie to ensure it is executed on a reset).
I have a linker script that should do this (I think):
SECTIONS
{
ENTRY(_start)
. = 0x0000;
.vectors.reset : { *(.vectors.reset) }
. = 0x0008;
.vectors.sw_exception : { *(.vectors.sw_exception) }
. = 0x0010;
.vectors.interrupt : { *(.vectors.interrupt) }
. = 0x0018;
.vectors.hw_exception : { *(.vectors.hw_exception) }
. = 0x100;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
But when the code is compiled it seems to be offset by 0x1000:
objdump -h startup.MICROBLAZE.elf
startup.MICROBLAZE.elf: file format elf32-big
Sections:
Idx Name Size VMA LMA File off Algn
0 .vectors.reset 00000008 00000000 00000000 00001000 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .vectors.sw_exception 00000008 00000008 00000008 00001008 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
2 .vectors.interrupt 00000008 00000010 00000010 00001010 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
3 .vectors.hw_exception 00000008 00000018 00000018 00001018 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
4 .text 00000020 00000100 00000100 00001100 2**4
CONTENTS, ALLOC, LOAD, READONLY, CODE
Where does that offset come from and how can I supress/control it?
Edit: It seems the 0x1000 offset is the physical offset of the code section in the compiled file/object - is that correct?
According to your objdump listing everything is suppose to be laid out as you expect. I.e. VMA and LMA of your .text section are pointing to address 0x100.
The offset 0x1000 as you correctly guessed is offset of the .text section inside ELF file. But this section will be loaded to address 0x100.
If you try to disassemble your ELF with
$ objdump -S startup.MICROBLAZE.elf
you will see the proper instructions layout. It is also helpful to produce MAP file on a linkage stage with -Wl,-Map,output.map gcc flags.
I'm sometimes getting this crash output below on my Coldfire uCLinux system. How do I work out what's causing the problem?
Apr 4 10:44:33 (none) user.debug syslog: starting NTP
sh: page allocation failure. order:8, mode:0xd0
Stack from 41da5dcc:
4005b0f2 400553b6 40207431 406131f8 00000008 000000d0 00000008 00000000
000000a2 000a2000 000a2000 0000000c 40544a14 00000000 405434fc 00000077
41da5eac 00000000 00000010 00000000 41da5008 41da5000 00000000 00000100
00000000 41da5000 00000000 000200d0 4024eecc 00000080 00000000 00000000
4005de52 000000d0 00000008 4024eec8 00000000 00000001 00004d09 00079100
00000004 00003f20 00013424 41cd7000 41da5fcc 41da5f2a 00015790 00000000
Call Trace with CONFIG_FRAME_POINTER disabled:
[4005b0f2] [400553b6] [40207431] [4005de52] [40067d64]
[40093892] [4004b15e] [400390d8] [40020e70] [400677d8]
[40020e70] [401f0c92] [40068468] [4006aa4e] [40020ea0]
[4002386c]
Mem-Info:
DMA per-cpu:
CPU 0: hi: 0, btch: 1 usd: 0
Active_anon:0 active_file:0 inactive_anon:0
inactive_file:4484 dirty:0 writeback:0 unstable:0
free:8806 slab:565 mapped:0 pagetables:0 bounce:0
DMA free:35216kB min:1016kB low:1268kB high:1524kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:17936kB present:65024kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0
DMA: 0*4kB 0*8kB 1*16kB 4*32kB 6*64kB 3*128kB 46*256kB 44*512kB 0*1024kB 0*2048kB 0*4096kB 0*8192kB 0*16384kB = 35216kB
4484 total pagecache pages
0 pages RAM
0 pages reserved
0 pages shared
0 pages non-shared
Allocation of length 663552 from process 476 (sh) failed
DMA per-cpu:
CPU 0: hi: 0, btch: 1 usd: 0
Active_anon:0 active_file:0 inactive_anon:0
inactive_file:4484 dirty:0 writeback:0 unstable:0
free:8804 slab:567 mapped:0 pagetables:0 bounce:0
DMA free:35216kB min:1016kB low:1268kB high:1524kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:17936kB present:65024kB pages_scanned:0 all_unreclaimable? no
lowmem_reserve[]: 0 0 0
DMA: 0*4kB 0*8kB 1*16kB 4*32kB 6*64kB 3*128kB 46*256kB 44*512kB 0*1024kB 0*2048kB 0*4096kB 0*8192kB 0*16384kB = 35216kB
4484 total pagecache pages
Unable to allocate RAM for process text/data, errno 12
sh: page allocation failure. order:8, mode:0xd0
Stack from 41ea6dcc:
4005b0f2 400553b6 40207431 40645848 00000008 000000d0 00000008 00000000
000000a2 000a2000 000a2000 0000000c 40544a6c 00000000 405434fc 00000077
41ea6eac 00000000 00000010 00000000 41ea6008 41ea6000 00000000 00000100
00000000 41ea6000 00000000 000200d0 4024eecc 00000080 00000000 00000000
4005de52 000000d0 00000008 4024eec8 00000000 00000001 00004d09 00079100
00000004 00003f20 00013424 410ae600 41ea6fcc 41ea6f2a 00015790 00000000
Call Trace with CONFIG_FRAME_POINTER disabled:
[4005b0f2] [400553b6] [40207431] [4005de52] [40067d64]
[40093892] [4004b15e] [400390d8] [40020e70] [400677d8]
[40020e70] [401f0c92] [40068468] [4006aa4e] [40020ea0]
[400239c2] [4002386c]
Mem-Info:
Your system has run out of 1 MB free pages. With the power of two allocator, you need a free page of size 1 MB to allocate 663552 byes. This is caused by memory fragmentation. Normally, an MMU would reorganize the free space so that it appears contiguous for new allocations.
You can only take care of the problem through prevention. If the 663552 bytes are the sh binary, you will have to prevent it from being continously re-loaded into memory. This might be done by putting it into an XIP file system.
It might be a heap allocation done by the shell. In this case, you will have to change whatever processing is causing such a large malloc.
At the system level, you will also have to see which programs are large or cause large mallocs and change their behavior so that they don't cause more fragmentation.
I am a little confused as to where uninitialized global variables go in the ELF file. I have this simple program to test in which sections the variables will be located:
const int a = 11;
int b = 10;
int c;
int main()
{
return 0;
}
I know that uninitialized global variable should be put into .bss section of ELF file, but objdump -h gives me the following output:
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 0000000a 00000000 00000000 00000034 2**2
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .data 00000004 00000000 00000000 00000040 2**2
CONTENTS, ALLOC, LOAD, DATA
2 .bss 00000000 00000000 00000000 00000044 2**2
ALLOC
3 .rodata 00000004 00000000 00000000 00000044 2**2
CONTENTS, ALLOC, LOAD, READONLY, DATA
4 .comment 00000024 00000000 00000000 00000048 2**0
CONTENTS, READONLY
5 .note.GNU-stack 00000000 00000000 00000000 0000006c 2**0
CONTENTS, READONLY
So the variable a goes to .rodata, b goes to .data, and c goes nowhere? When i change the code to:
int c = 0;
everything is as expected - .bss section has the length 4, but what happens with the variable c when it is not initialized?
It goes into a "common section". You can see it with objdump -t or by using nm.
I'm not quite sure I understand what this is about, but the reference to the ld -warn-common flag says this:
int i;
A common symbol. If there are only
(one or more) common symbols for a
variable, it goes in the uninitialized
data area of the output file. The
linker merges multiple common symbols
for the same variable into a single
symbol. If they are of different
sizes, it picks the largest size. The
linker turns a common symbol into a
declaration, if there is a definition
of the same variable.
(Found via the nm man page.) There is more information after that in the man page itself.