One of the modules was this one:
./i586/gr/ub/grub2-debuginfo-1.98-0.5.20080827svn.fc11.i586/usr/lib/debug/usr/lib/grub2/i386-pc/sleep.mod.debug.bz2
And the relevant message from my locally hacked version:
warning: .rel.debug_info: offset 0x50b: associated section #13 isn't
SHF_ALLOC.
That's:
<compile_unit offset=[0x4f9] producer="GNU C 4.4.0 20090219 (Red Hat
4.4.0-0.21)" language=C89 name="mod-sleep.c"
comp_dir="/usr/src/debug/grub2" low_pc=0x150 high_pc=0x7 stmt_list=0xaf>
...nothing but <base_type>s...
</compile_unit>
The relocs for the low_pc and high_pc values are:
0x00000507 386_32 0000000000 .text
0x0000050b 386_32 0000000000 .moddeps
I looked into the grub2 build. This boils down to a source file equivalent to:
typedef int blah;
__asm__ (".section .modname\n.string \"" "foo"
"\"\n");
__asm__ (".section .moddeps\n.string \"" "bar"
"\"\n");
So, this CU and all it produced being associated with a non-SHF_ALLOC
section is kosher enough. Without some decl like the typedef, it doesn't
produce the CU at all. I guess it decided it needed a CU because there
were decls at all, but then pruned all the unused types. (Personally, I
like producing a CU anyway even if it has no children at all--even for
compiling /dev/null. You compiled the source file, so that's CU
information even if nothing but name, comp_dir, language, producer.)
Since this CU contains only data, it really is fine that it's all in some
specially-named non-allocated sections. That sort of thing is to be
expected with anything like this using its own module loading relocation
magic. But indeed no DW_FORM_addr or equivalent really ought to refer to
any non-allocated section. It seems far less likely that a code section
would be marked non-alloc.
Aside from any SHF_ALLOC check, this pair should be flagged because they
are in two different sections. (I think I mentioned this before.) For not
only low_pc+high_pc, but any begin+end pair in other header formats and
whatnot that can have relocs, if either begin or end is relocated, it's
bogus if its mate is not relocated against the same section.
The trouble comes from this part of the output:
gcc .text
gcc .Ltext0:
gcc #APP
asm1 .section .modname
asm1 .string "foo"
asm1
asm2 .section .moddeps
asm2 .string "bar"
asm2
gcc .Letext0:
Then it uses .Ltext0 and .Letext0 in emitting .debug_info contents later.
This is arguably the fault of the asm not using .pushsection/.popsection or
whatnot. But my feeling is that it would be best to emit .Ltext0 only
along with the first .text entry label, and .Letext0 only along with the
last .text post-epilogue (.size et al). (That is how it does the labels
for an alternate section when a function has __attribute__((section("foo"))).)
But there are further problems. Consider:
static const char name[] __attribute__ ((section (".modname"))) =
"foo";
static const char deps[] __attribute__ ((section (".moddeps"))) =
"bar";
This produces:
.text
.Ltext0:
.section .modname,"a",@progbits
.type name, @object
.size name, 4
name:
.string "foo"
.section .moddeps,"a",@progbits
.type deps, @object
.size deps, 4
deps:
.string "bar"
.text
.Letext0:
as you would expect. And of course, the CU is useful since it describes
"name" and "deps" and their types. But it gets a low_pc and high_pc
of:
0x0000000000000015 X86_64_64 000000000000000000 +0 .text
0x000000000000001d X86_64_64 000000000000000000 +0 .text
That is harmless enough, but I think it would be better just to omit the
low_pc and high_pc attributes when there are no PC addresses to talk about.
Now, worse:
static const char name[] __attribute__ ((section (".modname"))) =
"foo";
static const char deps[] __attribute__ ((section (".moddeps"))) =
"bar";
void __attribute__ ((section (".foo"))) foo (void) {}
This produces:
Relocation section [19] '.rela.debug_ranges' for section [18]
'.debug_ranges' at offset 0xf08 contains 4 entries:
Offset Type Value Addend Name
000000000000000000 X86_64_64 000000000000000000 +0 .text
0x0000000000000008 X86_64_64 000000000000000000 +0 .text
0x0000000000000010 X86_64_64 000000000000000000 +0 .foo
0x0000000000000018 X86_64_64 000000000000000000 +6 .foo
i.e., a spurious empty range in the discontiguous ranges.
(But not in .debug_aranges!) Clearly here it should just omit the .Ltext0,
.Letext0 entry. And in this case there is only one section actually used
by PCs, so it can then drop back to just using low_pc/high_pc instead of
the ranges attribute.
Finally (not a DWARF issue), take:
typedef int blah;
void __attribute__ ((section (".foo"))) foo (void) {}
__asm__ (".section .modname\n.string \"" "foo"
"\"\n");
__asm__ (".section .moddeps\n.string \"" "bar"
"\"\n");
void __attribute__ ((section (".foo"))) bar (void) {}
This produces:
.text
.Ltext0:
.section .foo,"ax",@progbits
.globl foo
.type foo, @function
foo:
.LFB0:
...
.LFE0:
.size foo, .-foo
#APP
.section .modname
.string "foo"
.section .moddeps
.string "bar"
#NO_APP
.globl bar
.type bar, @function
bar:
.LFB1:
...
.LFE1:
.size bar, .-bar
.text
.Letext0:
So that actually puts bar into the wrong section. Again, I don't really
mind at all calling this the fault of the asm-writer. But it does speak to
the robustness of close-binding things, i.e. emit fresh .section before
each entry (only bother if there was any #APP, I guess), and emit all
labels close-by like .LF[BE][01] here rather than like .L{,e}text0.
Also note that here it emits separate .debug_ranges and .debug_aranges
entries for the foo range and the bar range, even though (modulo the
wrong-section snafu) it knows they are together in a contiguous range,
possibly modulo harmless alignment (though I see no .align here on x86).
We'll make the compressor coalesce those adjacent ranges I guess, but it
seems like it should be simple enough for the compiler to notice in the
first place.
Thanks,
Roland