# Only the last step can leave the program half-written, so it is the only one
# whose failure is fatal; not being able to produce the table just leaves the
# program without one, as happens when the tools do not know the target.
+# If the linker was not being cooperative, and did not create our own special
+# note in its own segment, then revert to try to link the binary a second time,
+# once we know the size of .gnu_debugdata, to include it.
# The nm options are spelled with their portable short forms (-P, -D) as some
# implementations lack the long ones, and undefined symbols are told by their
# type in awk instead of --defined-only, which these implementations lack too.
$(call qinfo, MINIDBG)if ! ( \
$(NM) -D -P $@ | awk '$$2 != "U" && $$2 != "w" { print $$1 }' | sort > $@.dsyms && \
$(NM) -P $@ | awk '$$2 == "T" || $$2 == "t" { print $$1 }' | sort > $@.fsyms && \
+ $(NM) -P $@ | awk '$$2 == "T" || $$2 == "t" { print $$1, $$3 }' | sort > $@.symaddrs && \
comm -13 $@.dsyms $@.fsyms > $@.ksyms && \
$(OBJCOPY) --only-keep-debug $@ $@.dbg && \
$(OBJCOPY) -S --remove-section .gdb_index --remove-section .comment --keep-symbols=$@.ksyms $@.dbg $@.mini && \
echo "$@: cannot build the symbol table, see the error above; the program is" >&2; \
echo "$@: left without one. Cross-builds need NM, OBJCOPY and READELF pointed at" >&2; \
echo "$@: the target's tools, and USE_MINIDEBUG=0 skips this step altogether." >&2; \
- elif ! READELF='$(READELF)' $(MINIDEBUG_TOOL) $@ $@.mini.xz; then \
- rm -f $@ $@.dsyms $@.fsyms $@.ksyms $@.dbg $@.mini $@.mini.xz; \
+ elif READELF='$(READELF)' $(MINIDEBUG_TOOL) $@ $@.mini.xz; then \
+ :; \
+ elif [ $$? -ne 2 ]; then \
+ rm -f $@ $@.dsyms $@.fsyms $@.symaddrs $@.ksyms $@.dbg $@.mini $@.mini.xz; \
exit 1; \
+ elif ! { echo " LD $@ (relinked with the symbol table)" >&2 && \
+ printf '.section .gnu_debugdata,"a"\n\t.hidden __symtab_xz\n\t.global __symtab_xz\n__symtab_xz:\n\t.incbin "%s"\n\t.hidden __symtab_xz_end\n\t.global __symtab_xz_end\n__symtab_xz_end:\n\t.section .note.GNU-stack,"",@progbits\n' "$@.mini.xz" > $@.mini.S && \
+ $(CC) $(ARCH_FLAGS) -c -o $@.mini.raw.o $@.mini.S && \
+ $(OBJCOPY) --remove-section .note.gnu.property $@.mini.raw.o $@.mini.o && \
+ $(LD) $(ARCH_FLAGS) $(LDFLAGS) -o $@.minidbg $@.mini.o $^ $(LDOPTS) && \
+ $(NM) -P $@.minidbg 2>/dev/null | awk '$$2 == "T" || $$2 == "t" { print $$1, $$3 }' | sort | cmp -s - $@.symaddrs; }; then \
+ echo "$@: the second linkage could not embed the symbol table either; the" >&2; \
+ echo "$@: program is left without one. USE_MINIDEBUG=0 skips this step" >&2; \
+ echo "$@: altogether." >&2; \
+ else \
+ mv -f $@.minidbg $@; \
fi; \
- rm -f $@.dsyms $@.fsyms $@.ksyms $@.dbg $@.mini $@.mini.xz
+ rm -f $@.dsyms $@.fsyms $@.symaddrs $@.ksyms $@.dbg $@.mini $@.mini.xz $@.mini.S $@.mini.raw.o $@.mini.o $@.minidbg
endef
haproxy: $(OPTIONS_OBJS) $(OBJS)
PAGE=65536
die() { echo "minidbg: $1" >&2; exit 1; }
+# die2() is only used strictly before anything is written, and tells the
+# caller with its status of 2 that the program may be kept as is, just
+# without the table; this is how a linker that leaves no segment of its
+# own to the note (e.g. gold merges the notes) is told apart from a failure
+# that may have half-written the program.
+die2() { echo "minidbg: $1" >&2; exit 2; }
-[ $# = 2 ] || die "usage: $0 <elf> <table>"
+[ $# = 2 ] || die2 "usage: $0 <elf> <table>"
-hdr=$(LC_ALL=C "$READELF" -hW "$elf") || die "cannot read $elf"
+hdr=$(LC_ALL=C "$READELF" -hW "$elf") || die2 "cannot read $elf"
case "$hdr" in
*ELF64*) w=8 ;;
*ELF32*) w=4 ;;
- *) die "unknown ELF class" ;;
+ *) die2 "unknown ELF class" ;;
esac
case "$hdr" in
*"big endian"*) be=1 ;;
# everything read from the listings is checked before being used
num() {
case "$2" in
- '' | *[!0-9]*) die "cannot read the $1 of $elf from $READELF" ;;
+ '' | *[!0-9]*) die2 "cannot read the $1 of $elf from $READELF" ;;
esac
}
# the two placeholders: the section gives the name, the note gives the segment.
# Their index in the listing is not their index in the table, so the section is
# located by the number readelf prints, and the note by where it starts.
-secs=$(LC_ALL=C "$READELF" -SW "$elf") || die "cannot read the sections of $elf"
+secs=$(LC_ALL=C "$READELF" -SW "$elf") || die2 "cannot read the sections of $elf"
sec=$(echo "$secs" | awk '{ for (i = 1; i < NF; i++) if ($i == ".gnu_debugdata") {
n = (i == 2) ? $1 : $(i - 1); gsub(/[][]/, "", n); print n; exit } }')
-[ -n "$sec" ] || die "no .gnu_debugdata section in $elf, was it reserved at link time?"
+[ -n "$sec" ] || die2 "no .gnu_debugdata section in $elf, was it reserved at link time?"
num "section index" "$sec"
note=$(echo "$secs" | awk '{ for (i = 1; i < NF; i++) if ($i == ".note.hapdbg") {
o = $(i + 3); z = $(i + 4); sub(/^0*/, "", o); sub(/^0*/, "", z)
print o " " z; exit } }')
-[ -n "$note" ] || die "no .note.hapdbg section in $elf, was it reserved at link time?"
+[ -n "$note" ] || die2 "no .note.hapdbg section in $elf, was it reserved at link time?"
# the note's own segment is the one that starts where the note does and holds
# nothing else, so that turning it into a loadable one loses no other note
-segs=$(LC_ALL=C "$READELF" -lW "$elf") || die "cannot read the segments of $elf"
+segs=$(LC_ALL=C "$READELF" -lW "$elf") || die2 "cannot read the segments of $elf"
seg=$(echo "$segs" | awk -v want="$note" '
$2 ~ /^0x/ && $3 ~ /^0x/ && $4 ~ /^0x/ {
if ($1 == "NOTE") {
}
idx++
}')
-[ -n "$seg" ] || die "the note of $elf has no segment of its own"
+[ -n "$seg" ] || die2 "the note of $elf has no segment of its own"
num "segment index" "$seg"
# the table is mapped past everything the file already maps
e=$(( $e ))
[ $e -gt $maxva ] && maxva=$e
done
-[ $maxva -gt 0 ] || die "$elf has no loadable segment"
+[ $maxva -gt 0 ] || die2 "$elf has no loadable segment"
-paylen=$(wc -c < "$pay") || die "cannot read $pay"
-[ "$paylen" -gt 0 ] || die "$pay is empty"
+paylen=$(wc -c < "$pay") || die2 "cannot read $pay"
+[ "$paylen" -gt 0 ] || die2 "$pay is empty"
elflen=$(wc -c < "$elf")
off=$(( (elflen + PAGE - 1) / PAGE * PAGE ))
vaddr=$(( (maxva + PAGE - 1) / PAGE * PAGE ))
} sym_debugdata_note = { 7, 4, 1, "HAPDBG", 0 };
#endif
+/* The table may also be linked in by a second linkage of the program, used
+ * as a fallback by the build when the linker gives the note no segment of
+ * its own and the table cannot be appended instead (e.g. gold merges the
+ * notes). It is then located through these symbols, wherever the linker
+ * placed it; they are weak so that a build without it simply has no table.
+ */
+extern const unsigned char __symtab_xz[] __attribute__((weak));
+extern const unsigned char __symtab_xz_end[] __attribute__((weak));
+
/* native ELF class / endianness / symbol-type accessor, derived from the
* toolchain since glibc doesn't provide *_NATIVE convenience macros.
*/
collect_dynsym_mem(b, info, obj);
+ /* the executable's own table, embedded by the second linkage, is
+ * located through its symbols, wherever the linker placed it.
+ */
+ if (is_exe && __symtab_xz) {
+ b->debugdata = __symtab_xz;
+ b->debugdata_size = __symtab_xz_end - __symtab_xz;
+ }
+
/* a table stored by the build is covered by a segment of its own, whose
* contents identify it.
*/