On Mon, May 02, 2016 at 12:01:18AM -0400, James McCoy wrote: > Thanks for the analysis and working on a patch. I'll note that > _Alignas, if it had worked, would likely have been too new a compiler > feature to use. > > It looks like Debian's ia64 porterbox is still running, so I can try > reproducing the problem there (using prctl to force SIGBUS) and then > fiddling around with the code.
Here goes further analysis. Running utf-test under gdb I set a break point at line 807 of utf-test.c and after a number of iterations of the enclosing loop I get: Breakpoint 1, test_utf_conversions (pool=0x20001018028) at /extra/mjc/debian/subversion/subversion-1.9.4/subversion/tests/libsvn_subr/utf-test.c:807 807 SVN_ERR_ASSERT(0 == strcmp(result->data, tc->result)); (gdb) print tc->source $20 = 0x120003117 "\330\064\335\036" (gdb) print result->data $21 = 0x20001018218 "\360\220\204\236" (gdb) print tc->result $22 = 0x12000373c "\360\235\204\236" (gdb) cont Continuing. svn_tests: E235000: In file '/extra/mjc/debian/subversion/subversion-1.9.4/subversion/tests/libsvn_subr/utf-test.c' line 807: assertion failed (0 == strcmp(result->data, tc->result)) FAIL: lt-utf-test 9: test svn_utf__utf{16,32}_to_utf8 [Inferior 1 (process 24827) exited with code 01] On this iteration I believe it has just read line 769 of utf-test.c, i.e., this line: { UTF_16_BE, "\xD8\x34" "\xDD\x1E" "\0\0", "\xf0\x9d\x84\x9e" }, /* U+01D11E */ into variable tc, and it passes tc->source (which is at address 0x120003117, i.e., misaligned for apr_uint16_t type) to svn_utf__utf16_to_utf8() and the result is incorrect and results in the failed assert. On previous iterations of the test loop it happened to pass the test even with misaligned tc->source strings, and it is instructive to disassemble svn_utf__utf16_to_utf8() to see why this can happen: 1040 svn_error_t * 1041 svn_utf__utf16_to_utf8(const svn_string_t **result, 1042 const apr_uint16_t *utf16str, 1043 apr_size_t utf16len, 1044 svn_boolean_t big_endian, 1045 apr_pool_t *result_pool, 1046 apr_pool_t *scratch_pool) 1047 { 0x00000200000ed990 <+0>: ldah gp,12(t12) 0x00000200000ed994 <+4>: lda gp,10976(gp) 0x00000200000ed998 <+8>: lda sp,-160(sp) 0x00000200000ed9a0 <+16>: stq s0,8(sp) 0x00000200000ed9a4 <+20>: mov a1,s0 0x00000200000ed9a8 <+24>: stq s1,16(sp) 0x00000200000ed9ac <+28>: mov a2,s1 0x00000200000ed9b0 <+32>: stq ra,0(sp) 0x00000200000ed9b4 <+36>: stq s2,24(sp) 0x00000200000ed9b8 <+40>: stq s3,32(sp) 0x00000200000ed9bc <+44>: stq s4,40(sp) 0x00000200000ed9c0 <+48>: stq s5,48(sp) 0x00000200000ed9c4 <+52>: stq fp,56(sp) 0x00000200000ed9c8 <+56>: stq a0,152(sp) 0x00000200000ed9cc <+60>: stq a3,128(sp) 0x00000200000ed9d0 <+64>: stq a4,144(sp) 1048 static const apr_uint16_t endiancheck = 0xa55a; 1049 const svn_boolean_t arch_big_endian = 1050 (((const char*)&endiancheck)[sizeof(endiancheck) - 1] == '\x5a'); 1051 const svn_boolean_t swap_order = (!big_endian != !arch_big_endian); 1052 1053 apr_uint16_t lead_surrogate; 1054 apr_size_t length; 1055 apr_size_t offset; 1056 svn_membuf_t ucs4buf; 1057 svn_membuf_t resultbuf; 1058 svn_string_t *res; 1059 1060 if (utf16len == SVN_UTF__UNKNOWN_LENGTH) 0x00000200000ed99c <+12>: lda t0,1(a2) 0x00000200000ed9d4 <+68>: beq t0,0x200000edbd0 <svn_utf__utf16_to_utf8+576> 1061 { 1062 const apr_uint16_t *endp = utf16str; 1063 while (*endp++) 0x00000200000edbd0 <+576>: mov a1,s1 0x00000200000edbd4 <+580>: unop 0x00000200000edbe0 <+592>: lda s1,2(s1) 0x00000200000edbe4 <+596>: lda t1,-2(s1) 0x00000200000edbe8 <+600>: ldq_u t0,-2(s1) 0x00000200000edbec <+604>: extwl t0,t1,t0 0x00000200000edbf0 <+608>: bne t0,0x200000edbe0 <svn_utf__utf16_to_utf8+592> 1064 ; In the loop immediately above (lines 1061--1064 of the source code) we see the code is walking through the utf16 string (register a1 holds argument 1, i.e., utf16str, which is first copied to register s1 and used as the pointer incremented in the loop). The load instructions to get the utf16 character are the two instructions: 0x00000200000edbe8 <+600>: ldq_u t0,-2(s1) 0x00000200000edbec <+604>: extwl t0,t1,t0 The ldq_u zeroes the lowest three bits of the address and loads the quadword (64-bit word) from the resultant address, and the extwl extracts the 16-bit word identified by the lowest three bits of the address (which are in register t1) from the quadword in register t0, however this is only correct if the lowest three bits are not 0x7. If the source string address has the lowest three bits all set then, and only then, the generated code loads incorrect utf16 chars. This is a reasonable optimisation of the code made by gcc as the argument utf16str is declared to be a pointer to apr_uint16_t type, i.e., data that are 16-bit aligned, thus gcc can reason that an address with the lowest three bits set is not possible. Cheers Michael.