[PATCH] gas: add .cv_ucomp and .cv_scomp pseudo-directives

Mark Harmstone mark@harmstone.com
Sat Nov 2 22:32:21 GMT 2024


Add .cv_ucomp and .cv_scomp pseudo-directives for object files for
Windows targets, which encode compressed CodeView integers according to
the algorithm in CVCompressData in
https://github.com/Microsoft/microsoft-pdb/blob/master/include/cvinfo.h.
This is essentially Microsoft's answer to the LEB128, though used in far
fewer places.

CodeView uses these to encode the "binary annotations" in the
S_INLINESITE symbol, which express the relationship between code offsets
and line numbers in inlined functions. This has to be done in the
assembler as GCC doesn't know how many bytes each instruction takes up.
There's no equivalent for this for MSVC or LLVM, as in both cases the
assembler and compiler are integrated.

.cv_ucomp represents an unsigned big-endian integer between 0 and 0x1fffffff,
taking up 1, 2, or 4 bytes:

Value between 0 and 0x7f:

	0aaaaaaa -> 0aaaaaaa (identity-mapped)

Value between 0x80 and 0x3fff:

	00aaaaaa bbbbbbbb -> 10aaaaaa bbbbbbbb

Value between 0x4000 and 0x1fffffff:
	000aaaaa bbbbbbbb ccccccccc dddddddd ->
	110aaaaa bbbbbbbb ccccccccc dddddddd

.cv_scomp represents a signed big-endian integer between -0xfffffff and
0xfffffff, encoded according to EncodeSignedInt32 in cvinfo.h. The
absolute value of the integer is shifted left one bit, the LSB set
for a negative value, and the result expressed as if it were a
.cv_ucomp: cv_scomp(x) = cv_ucomp((abs(x) << 1) | (x < 0 ? 1 : 0))
---
 gas/read.c                     | 161 +++++++++++++++++++++++++++++++++
 gas/testsuite/gas/pe/cv_comp.d |  13 +++
 gas/testsuite/gas/pe/cv_comp.s |  63 +++++++++++++
 gas/testsuite/gas/pe/pe.exp    |   2 +
 4 files changed, 239 insertions(+)
 create mode 100644 gas/testsuite/gas/pe/cv_comp.d
 create mode 100644 gas/testsuite/gas/pe/cv_comp.s

diff --git a/gas/read.c b/gas/read.c
index aefbd7aefe8..5236338317e 100644
--- a/gas/read.c
+++ b/gas/read.c
@@ -265,6 +265,9 @@ static void poend (void);
 static size_t get_macro_line_sb (sb *);
 static void generate_file_debug (void);
 static char *_find_end_of_line (char *, int, int, int);
+#ifdef TE_PE
+static void s_cv_comp (int sign);
+#endif
 
 void
 read_begin (void)
@@ -369,6 +372,10 @@ static const pseudo_typeS potable[] = {
   {"comm", s_comm, 0},
   {"common", s_mri_common, 0},
   {"common.s", s_mri_common, 1},
+#ifdef TE_PE
+  {"cv_scomp", s_cv_comp, 1},
+  {"cv_ucomp", s_cv_comp, 0},
+#endif
   {"data", s_data, 0},
   {"dc", cons, 2},
   {"dc.a", cons, 0},
@@ -5457,6 +5464,160 @@ s_leb128 (int sign)
   demand_empty_rest_of_line ();
 }
 
+#ifdef TE_PE
+
+/* Output a compressed CodeView integer.  The return value is the number of
+   bytes used.  */
+
+static unsigned int
+output_cv_comp (char *p, offsetT value, int sign)
+{
+  char *orig = p;
+
+  if (sign)
+    {
+      if (value >= 0)
+	value <<= 1;
+      else
+	value = (-value << 1) | 1;
+    }
+
+  if (value <= 0x7f)
+    {
+      *p++ = value;
+    }
+  else if (value <= 0x3fff)
+    {
+      *p++ = 0x80 | (value >> 8);
+      *p++ = value & 0xff;
+    }
+  else if (value <= 0x1fffffff)
+    {
+      *p++ = 0xc0 | (value >> 24);
+      *p++ = (value >> 16) & 0xff;
+      *p++ = (value >> 8) & 0xff;
+      *p++ = value & 0xff;
+    }
+  else
+    {
+      if (sign)
+	as_warn (_("value cannot be expressed as a .cv_scomp"));
+      else
+	as_warn (_("value cannot be expressed as a .cv_ucomp"));
+    }
+
+  return p - orig;
+}
+
+/* Return the size needed to output a compressed CodeView integer.  */
+
+static unsigned int
+sizeof_cv_comp (offsetT value, int sign)
+{
+  if (sign)
+    {
+      if (value >= 0)
+	value <<= 1;
+      else
+	value = (-value << 1) | 1;
+    }
+
+  if (value <= 0x7f)
+    return 1;
+  else if (value <= 0x3fff)
+    return 2;
+  else if (value <= 0x1fffffff)
+    return 4;
+  else
+    return 0;
+}
+
+/* Generate the appropriate fragments for a given expression to emit a
+   cv_comp value.  SIGN is 1 for cv_scomp, 0 for cv_ucomp.  */
+
+static void
+emit_cv_comp_expr (expressionS *exp, int sign)
+{
+  operatorT op = exp->X_op;
+
+  if (op == O_absent || op == O_illegal)
+    {
+      as_warn (_("zero assumed for missing expression"));
+      exp->X_add_number = 0;
+      op = O_constant;
+    }
+  else if (op == O_big)
+    {
+      as_bad (_("number invalid"));
+      exp->X_add_number = 0;
+      op = O_constant;
+    }
+  else if (op == O_register)
+    {
+      as_warn (_("register value used as expression"));
+      op = O_constant;
+    }
+  if (now_seg == absolute_section)
+    {
+      if (op != O_constant || exp->X_add_number != 0)
+	as_bad (_("attempt to store value in absolute section"));
+      abs_section_offset++;
+      return;
+    }
+
+  if ((op != O_constant || exp->X_add_number != 0) && in_bss ())
+    as_bad (_("attempt to store non-zero value in section `%s'"),
+	    segment_name (now_seg));
+
+  /* Let the backend know that subsequent data may be byte aligned.  */
+#ifdef md_cons_align
+  md_cons_align (1);
+#endif
+
+  if (op == O_constant)
+    {
+      offsetT value = exp->X_add_number;
+      unsigned int size;
+      char *p;
+
+      if (value < 0 && !sign)
+	as_bad (_("cannot pass a negative value to .cv_ucomp"));
+
+      size = sizeof_cv_comp (value, sign);
+      p = frag_more (size);
+      if (output_cv_comp (p, value, sign) > size)
+	abort ();
+    }
+  else
+    {
+      as_bad (_("cannot pass a non-constant value to .cv_ucomp or .cv_scomp"));
+    }
+}
+
+/* Parse the .cv_ucomp and .cv_scomp pseudos.  */
+
+static void
+s_cv_comp (int sign)
+{
+  expressionS exp;
+
+#ifdef md_flush_pending_output
+  md_flush_pending_output ();
+#endif
+
+  do
+    {
+      expression (&exp);
+      emit_cv_comp_expr (&exp, sign);
+    }
+  while (*input_line_pointer++ == ',');
+
+  input_line_pointer--;
+  demand_empty_rest_of_line ();
+}
+
+#endif /* TE_PE */
+
 /* Code for handling base64 encoded strings.
    Based upon code in sharutils' lib/base64.c source file, written by
    Simon Josefsson.  Which was partially adapted from GNU MailUtils
diff --git a/gas/testsuite/gas/pe/cv_comp.d b/gas/testsuite/gas/pe/cv_comp.d
new file mode 100644
index 00000000000..b15b9f60a56
--- /dev/null
+++ b/gas/testsuite/gas/pe/cv_comp.d
@@ -0,0 +1,13 @@
+#objdump: -s -j .text
+#name: CodeView compressed integer test
+
+.*: .*
+
+Contents of section .text:
+ 0000 21002101 212a217f 21808021 853921bf  .*
+ 0010 ff21c000 400021c0 0f424021 dfffffff  .*
+ 0020 21002102 21542180 fe218100 218a7221  .*
+ 0030 c0007ffe 21c00080 0021c01e 848021df  .*
+ 0040 fffffe21 03215521 80ff2181 01218a73  .*
+ 0050 21c0007f ff21c000 800121c0 1e848121  .*
+ 0060 dfffffff 21.*
diff --git a/gas/testsuite/gas/pe/cv_comp.s b/gas/testsuite/gas/pe/cv_comp.s
new file mode 100644
index 00000000000..2800a4f6561
--- /dev/null
+++ b/gas/testsuite/gas/pe/cv_comp.s
@@ -0,0 +1,63 @@
+	.text
+
+	.ascii		"!"
+	.cv_ucomp	0
+	.ascii		"!"
+	.cv_ucomp	1
+	.ascii		"!"
+	.cv_ucomp	42
+	.ascii		"!"
+	.cv_ucomp	127
+	.ascii		"!"
+	.cv_ucomp	128
+	.ascii		"!"
+	.cv_ucomp	1337
+	.ascii		"!"
+	.cv_ucomp	16383
+	.ascii		"!"
+	.cv_ucomp	16384
+	.ascii		"!"
+	.cv_ucomp	1000000
+	.ascii		"!"
+	.cv_ucomp	536870911
+
+	.ascii		"!"
+	.cv_scomp	0
+	.ascii		"!"
+	.cv_scomp	1
+	.ascii		"!"
+	.cv_scomp	42
+	.ascii		"!"
+	.cv_scomp	127
+	.ascii		"!"
+	.cv_scomp	128
+	.ascii		"!"
+	.cv_scomp	1337
+	.ascii		"!"
+	.cv_scomp	16383
+	.ascii		"!"
+	.cv_scomp	16384
+	.ascii		"!"
+	.cv_scomp	1000000
+	.ascii		"!"
+	.cv_scomp	268435455
+
+	.ascii		"!"
+	.cv_scomp	-1
+	.ascii		"!"
+	.cv_scomp	-42
+	.ascii		"!"
+	.cv_scomp	-127
+	.ascii		"!"
+	.cv_scomp	-128
+	.ascii		"!"
+	.cv_scomp	-1337
+	.ascii		"!"
+	.cv_scomp	-16383
+	.ascii		"!"
+	.cv_scomp	-16384
+	.ascii		"!"
+	.cv_scomp	-1000000
+	.ascii		"!"
+	.cv_scomp	-268435455
+	.ascii		"!"
diff --git a/gas/testsuite/gas/pe/pe.exp b/gas/testsuite/gas/pe/pe.exp
index 5f8396e5924..41659693972 100644
--- a/gas/testsuite/gas/pe/pe.exp
+++ b/gas/testsuite/gas/pe/pe.exp
@@ -38,6 +38,8 @@ run_dump_test "section-exclude"
 
 run_dump_test "set"
 
+run_dump_test "cv_comp"
+
 # SEH related tests
 
 # These tests are only for x86_64 targets
-- 
2.45.2



More information about the Binutils mailing list