[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #16 from Mukund Sivaraman --- (In reply to Andrew Pinski from comment #15) > > At the very least, if it is possible to detect that the pointer is NULL by > > static analysis and it is being passed to a function that has the notnull > > attribute, please warn mentioning inferences being made. > The warning did not make it into gcc 4.9 due to the patches to do the > warning were not ready. Gcc 4.10 should warn about it. If it does not then > that is a bug. Thank you for this. :) It should detect at least some cases in that case. If qsort() can cause this sort of disruption to a caller if NULL is passed, I guess a change in glibc to add an assert(base != NULL) or similar abort is also in order given that the caller code becomes buggy otherwise. Do you agree? This would catch remaining cases before any inferred decisions are executed after the qsort().
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #15 from Andrew Pinski --- (In reply to Mukund Sivaraman from comment #14) > (In reply to Jakub Jelinek from comment #10) > > But the compiler doesn't know there that x is NULL. The compiler sees a > > See comment #3. It generates 2 codepaths, one where (nalloc == 0) and > another where (nalloc != 0). For the former, it deletes the if statement and > isc_mem_put() call at the free_rdatas label completely: > > free_rdatas: > if (x != NULL) > isc_mem_put(mctx, x, nalloc * sizeof(struct xrdata)); > return (result); > } > > and instead reduces free_rdata's definition to: > > free_rdatas: > return (result); > } > > How does the compiler do that if it has not inferred that x is NULL there? > > OTOH, you're the compiler developers, so if you say it doesn't know that x > is NULL, then that is that. :) Maybe the part of compiler code that does > this doesn't know it. > > Note that despite all this discussion of correctness, this optimization is > counter intuitive and will bite developers. There should at least be > warnings where they could be generated. > > The point about correctness with C standards is taken and agreed. > > See what is happening from a programmer's point of view: an explicit NULL > check is deleted. There are no warnings about qsort() used with NULL > arguments where it seems the compiler could warn (see above). Also consider > the use of notnull as an API annotation change by 3rd party libraries, which > can make caller code buggy without any way to notice it. > > At the very least, if it is possible to detect that the pointer is NULL by > static analysis and it is being passed to a function that has the notnull > attribute, please warn mentioning inferences being made. The warning did not make it into gcc 4.9 due to the patches to do the warning were not ready. Gcc 4.10 should warn about it. If it does not then that is a bug.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #14 from Mukund Sivaraman --- (In reply to Jakub Jelinek from comment #10) > But the compiler doesn't know there that x is NULL. The compiler sees a See comment #3. It generates 2 codepaths, one where (nalloc == 0) and another where (nalloc != 0). For the former, it deletes the if statement and isc_mem_put() call at the free_rdatas label completely: free_rdatas: if (x != NULL) isc_mem_put(mctx, x, nalloc * sizeof(struct xrdata)); return (result); } and instead reduces free_rdata's definition to: free_rdatas: return (result); } How does the compiler do that if it has not inferred that x is NULL there? OTOH, you're the compiler developers, so if you say it doesn't know that x is NULL, then that is that. :) Maybe the part of compiler code that does this doesn't know it. Note that despite all this discussion of correctness, this optimization is counter intuitive and will bite developers. There should at least be warnings where they could be generated. The point about correctness with C standards is taken and agreed. See what is happening from a programmer's point of view: an explicit NULL check is deleted. There are no warnings about qsort() used with NULL arguments where it seems the compiler could warn (see above). Also consider the use of notnull as an API annotation change by 3rd party libraries, which can make caller code buggy without any way to notice it. At the very least, if it is possible to detect that the pointer is NULL by static analysis and it is being passed to a function that has the notnull attribute, please warn mentioning inferences being made.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #13 from Jakub Jelinek --- (In reply to Andrew Pinski from comment #11) > It is not incorrect as the C standard says this about qsort: > nmemb can have the value zero on a call to that function; the comparison > function is not called, a search finds no matching element, and sorting > performs no rearrangement. Pointer arguments on such a call shall still have > valid values, as described in 7.1.4. > > POSIX 2008 defers to the C standard now so this is neither a glibc or a GCC > bug in the end. I've missed the "Pointer arguments on such a call shall still have valid values, as described in 7.1.4." sentence in C99 7.20.5 (was looking for that in 7.20.5.2), with that it is exactly the same thing in this regard as memcpy etc.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #12 from Andrew Pinski --- (In reply to Andrew Pinski from comment #11) > It is not incorrect as the C standard says this about qsort: > nmemb can have the value zero on a call to that function; the comparison > function is not called, a search finds no matching element, and sorting > performs no rearrangement. Pointer arguments on such a call shall still have > valid values, as described in 7.1.4. 7.1.4 says this: Each of the following statements applies unless explicitly stated otherwise in the detailed descriptions that follow: If an argument to a function has an invalid value (such as a value outside the domain of the function, or a pointer outside the address space of the program, or a null pointer, or a pointer to non-modifiable storage when the corresponding parameter is not const-qualified) or a type (after promotion) not expected by a function with variable number of arguments, the behavior is undefined. So there is not need to say it was detected to be non-null as the null pointer case is mentioned in 7.1.4.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #11 from Andrew Pinski --- (In reply to Jakub Jelinek from comment #10) > If you believe the nonnull attribute on qsort is incorrect, then you should > report that as glibc bug, not gcc bug, the prototype is provided by glibc. > The more aggressive GCC optimization is documented e.g. in > https://gcc.gnu.org/gcc-4.9/porting_to.html > plus we hope to add -fsanitize=undefined instrumentation for this in the > upcoming GCC version, so you find it out more easily. It is not incorrect as the C standard says this about qsort: nmemb can have the value zero on a call to that function; the comparison function is not called, a search finds no matching element, and sorting performs no rearrangement. Pointer arguments on such a call shall still have valid values, as described in 7.1.4. POSIX 2008 defers to the C standard now so this is neither a glibc or a GCC bug in the end.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 Jakub Jelinek changed: What|Removed |Added Status|WAITING |RESOLVED Resolution|--- |INVALID --- Comment #10 from Jakub Jelinek --- If you believe the nonnull attribute on qsort is incorrect, then you should report that as glibc bug, not gcc bug, the prototype is provided by glibc. The more aggressive GCC optimization is documented e.g. in https://gcc.gnu.org/gcc-4.9/porting_to.html plus we hope to add -fsanitize=undefined instrumentation for this in the upcoming GCC version, so you find it out more easily. > When the compiler knows at that point that base (=x) is NULL as an > argument to qsort(), why isn't it warning when the attribute expects it > to be non-NULL, esp. as it is using this inferred decision to optimize > code down below? But the compiler doesn't know there that x is NULL. The compiler sees a call to a function which must not be called with NULL, and from that derives the value range of x to be anything but NULL. Instead of qsort consider here some less controversial function, e.g. memcpy, where the standard is very clear that memcpy (NULL, "", 0); or memcpy ("", NULL, 0); is invalid despite the length 0.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #9 from Mukund Sivaraman --- Hi Jakub, Markus We discussed this during our daily standup call today, and there are two points we'd like to make: 1. The qsort() defintion in C99 doesn't explicitly state that base must not be NULL, though it seems you are deducing that from "the initial element of which is pointed to by base." The POSIX definition of qsort() adds this: "If the nel argument has the value zero, the comparison function pointed to by compar shall not be called and no rearrangement shall take place." 2. From our perpective as users of GCC, this kind of agressive optimization seems counter-intuitive. We'd like code to compile to correct object code first before performance. When the compiler knows at that point that base (=x) is NULL as an argument to qsort(), why isn't it warning when the attribute expects it to be non-NULL, esp. as it is using this inferred decision to optimize code down below? The compiler knows x is NULL at this point in this codepath regardless of what qsort()'s attributes say. Why is it using the attribute then? qsort() also does not assert (at runtime) that base is non-NULL. There is no way to detect this for code which used to run correctly before, but doesn't anymore (without it _hopefully_ crashing somewhere). Other similar functions such as memcpy(), etc. also have this annotation in glibc, whereas there is no definition of n=0 case in C99. This example of qsort() is in libc, but imagine a case where a program uses a 3rd party system installed utility shared library. If the library, in a new version, adds a nonnull annotation for a function, but the library function itself continues to work for NULL input, see what happens to the program: The library is not affected, but the pointer in the calling program is affected if the compiler infers that the pointer is non-NULL due to the attribute. The calling program is now buggy due to a change in the library. How do we discover it? It makes sense to just avoid the qsort() in our case and we will update our code to do so, but please consider the arguments above.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 Jakub Jelinek changed: What|Removed |Added CC||jakub at gcc dot gnu.org --- Comment #8 from Jakub Jelinek --- The glibc prototype for qsort is: extern void qsort (void *__base, size_t __nmemb, size_t __size, __compar_fn_t __compar) __nonnull ((1, 4)); therefore when you pass x to it, gcc derives from that that x must not be NULL. As ISO C99 says that qsort sorts an array of nmemb objects, I'd say the glibc prototype is correct and therefore BIND is buggy, because NULL is not an address of any object.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #7 from Markus Trippelsdorf --- -fno-delete-null-pointer-checks fixes the issue and is an easy workaround for now.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #6 from Markus Trippelsdorf --- (In reply to Markus Trippelsdorf from comment #4) > Could you please try to reproduce the issue with a more recent snapshot: No need. I can reproduce the issue and will look deeper tomorrow.
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #5 from Markus Trippelsdorf --- (In reply to Markus Trippelsdorf from comment #4) > Could you please try to reproduce the issue with a more recent snapshot: > ftp://gcc.gnu.org/pub/gcc/snapshots/4.10-20140518/ Sorry wrong address. This one is correct: ftp://gcc.gnu.org/pub/gcc/snapshots/4.9-20140514/
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 Markus Trippelsdorf changed: What|Removed |Added Status|UNCONFIRMED |WAITING Last reconfirmed||2014-05-19 CC||trippels at gcc dot gnu.org Ever confirmed|0 |1 --- Comment #4 from Markus Trippelsdorf --- Could you please try to reproduce the issue with a more recent snapshot: ftp://gcc.gnu.org/pub/gcc/snapshots/4.10-20140518/ There were several bug fixes for tree-ssa-threadedge.c that went in after "gcc version 4.9.1 20140507".
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #3 from Mukund Sivaraman --- The following is _incorrect_ generated x86_64 code for dns_rdataslab_fromrdataset() as compiled with: gcc version 4.9.1 20140507 (prerelease) (GCC). (the current version of GCC on Arch Linux). NOTE that there are two copies of generated code for this function. Either the first or second copy of code is called based on whether (nalloc == 0) implying (nitems == 0) too. In the (nitems == 0) case, the entire free_rdatas block is deleted by GCC as x is inferred to be NULL. In the (nitems != 0) case, the "if (x != NULL)" check is deleted as x is inferred to NOT be NULL, and isc_mem_put() is always called. The failed assertion obviously happens when (x == NULL), i.e., (nitems == 0). And the fact that the "if (x != NULL)" check was deleted is an obvious sign that code flow is through that path to cause the assertion failure. Because the code is like noodles, I've added STEP# comments in the code to take you through the (nitems == 0) case. Go from STEP #0 to STEP #13. The bug happens because the (nitems == 0) case branches into code for (nitems != 0) where checks have been removed. ; STEP #0 0051fc10 : 51fc10: 41 57 push %r15 51fc12: 41 56 push %r14 51fc14: 49 89 f7mov%rsi,%r15 51fc17: 41 55 push %r13 51fc19: 41 54 push %r12 51fc1b: 49 89 fcmov%rdi,%r12 51fc1e: 55 push %rbp 51fc1f: 53 push %rbx 51fc20: 48 83 ec 38 sub$0x38,%rsp 51fc24: 48 89 54 24 08 mov%rdx,0x8(%rsp) 51fc29: 89 4c 24 20 mov%ecx,0x20(%rsp) 51fc2d: e8 9e e7 ff ff callq 51e3d0 51fc32: 41 89 c5mov%eax,%r13d ;; STEP #1 ;; Is (nitems == 0)? If not, go to 51fc60. It is 0 in our case. 51fc35: 45 85 edtest %r13d,%r13d 51fc38: 75 26 jne51fc60 ;; STEP #2 ;; if (nitems == 0 && rdataset->type != 0) ;;return (ISC_R_FAILURE); ;; ;; Here, let's take it that (rdataset->type != 0) so that we can ;; continue execution. Jump to 51fd20 (STEP #3). 51fc3a: 66 41 83 7c 24 22 00cmpw $0x0,0x22(%r12) 51fc41: ba 19 00 00 00 mov$0x19,%edx 51fc46: 0f 84 d4 00 00 00 je 51fd20 ; out: ; ; control comes back here as part of a "return;" statement's code from ; below. 51fc4c: 48 83 c4 38 add$0x38,%rsp 51fc50: 89 d0 mov%edx,%eax 51fc52: 5b pop%rbx 51fc53: 5d pop%rbp 51fc54: 41 5c pop%r12 51fc56: 41 5d pop%r13 51fc58: 41 5e pop%r14 51fc5a: 41 5f pop%r15 51fc5c: c3 retq 51fc5d: 0f 1f 00nopl (%rax) ; (nitems != 0) case. 51fc60: 41 81 fd ff ff 00 00cmp$0x,%r13d 51fc67: ba 13 00 00 00 mov$0x13,%edx 51fc6c: 77 de ja 51fc4c 51fc6e: 44 89 e8mov%r13d,%eax 51fc71: b9 a1 00 00 00 mov$0xa1,%ecx 51fc76: ba 6b e1 63 00 mov$0x63e16b,%edx 51fc7b: 48 8d 1c 40 lea(%rax,%rax,2),%rbx 51fc7f: 4c 89 ffmov%r15,%rdi 51fc82: 48 c1 e3 04 shl$0x4,%rbx 51fc86: 48 89 demov%rbx,%rsi 51fc89: e8 d2 a9 0a 00 callq 5ca660 51fc8e: 48 85 c0test %rax,%rax 51fc91: 49 89 c6mov%rax,%r14 51fc94: 0f 84 7d 01 00 00 je 51fe17 51fc9a: 4c 89 e7mov%r12,%rdi ; /* FIRST COPY OF GENERATED CODE (nalloc > 0) */ ; ; result = dns_rdataset_first(rdataset); 51fc9d: e8 0e e8 ff ff callq 51e4b0 ; if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) ; goto free_rdatas; ; 0x1d (ISC_R_NOMORE) 51fca2: 83 f8 1dcmp$0x1d,%eax 51fca5: 74 08 je 51fcaf ; 0x0 (ISC_R_SUCCESS). if (result != ISC_R_SUCCESS && result != ; ISC_R_NOMORE), jmp to 51fd53 (free_rdatas) below. 51fca7: 85 c0 test %eax,%eax 51fca9: 0f 85 a4 00 00 00 jne51fd53 ; go below to free_rdatas. ; ignore: for loop 51fcaf: 31 ed xor%ebp,%ebp 51fcb1: 85 c0 test %eax,%eax 51fcb3: 74 1f je 51fcd4 51fcb5: e9 82 00 00 00 jmpq 51fd3c 51fcba: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1)
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #2 from Mukund Sivaraman --- This is the C function (so you can compare notes from the next comment): isc_result_t dns_rdataslab_fromrdataset(dns_rdataset_t *rdataset, isc_mem_t *mctx, isc_region_t *region, unsigned int reservelen) { /* * Use &removed as a sentinal pointer for duplicate * rdata as rdata.data == NULL is valid. */ static unsigned char removed; struct xrdata *x; unsigned char *rawbuf; #if DNS_RDATASET_FIXED unsigned char *offsetbase; #endif unsigned intbuflen; isc_result_tresult; unsigned intnitems; unsigned intnalloc; unsigned inti; #if DNS_RDATASET_FIXED unsigned int *offsettable; #endif unsigned intlength; buflen = reservelen + 2; nalloc = dns_rdataset_count(rdataset); nitems = nalloc; if (nitems == 0 && rdataset->type != 0) return (ISC_R_FAILURE); if (nalloc > 0x) return (ISC_R_NOSPACE); if (nalloc != 0) { x = isc_mem_get(mctx, nalloc * sizeof(struct xrdata)); if (x == NULL) return (ISC_R_NOMEMORY); } else x = NULL; /* * Save all of the rdata members into an array. */ result = dns_rdataset_first(rdataset); if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) goto free_rdatas; for (i = 0; i < nalloc && result == ISC_R_SUCCESS; i++) { INSIST(result == ISC_R_SUCCESS); dns_rdata_init(&x[i].rdata); dns_rdataset_current(rdataset, &x[i].rdata); INSIST(x[i].rdata.data != &removed); #if DNS_RDATASET_FIXED x[i].order = i; #endif result = dns_rdataset_next(rdataset); } if (result != ISC_R_NOMORE) goto free_rdatas; if (i != nalloc) { /* * Somehow we iterated over fewer rdatas than * dns_rdataset_count() said there were! */ result = ISC_R_FAILURE; goto free_rdatas; } /* * Put into DNSSEC order. */ qsort(x, nalloc, sizeof(struct xrdata), compare_rdata); /* * Remove duplicates and compute the total storage required. * * If an rdata is not a duplicate, accumulate the storage size * required for the rdata. We do not store the class, type, etc, * just the rdata, so our overhead is 2 bytes for the number of * records, and 8 for each rdata, (length(2), offset(4) and order(2)) * and then the rdata itself. */ for (i = 1; i < nalloc; i++) { if (compare_rdata(&x[i-1].rdata, &x[i].rdata) == 0) { x[i-1].rdata.data = &removed; #if DNS_RDATASET_FIXED /* * Preserve the least order so A, B, A -> A, B * after duplicate removal. */ if (x[i-1].order < x[i].order) x[i].order = x[i-1].order; #endif nitems--; } else { #if DNS_RDATASET_FIXED buflen += (8 + x[i-1].rdata.length); #else buflen += (2 + x[i-1].rdata.length); #endif /* * Provide space to store the per RR meta data. */ if (rdataset->type == dns_rdatatype_rrsig) buflen++; } } /* * Don't forget the last item! */ if (nalloc != 0) { #if DNS_RDATASET_FIXED buflen += (8 + x[i-1].rdata.length); #else buflen += (2 + x[i-1].rdata.length); #endif } /* * Provide space to store the per RR meta data. */ if (rdataset->type == dns_rdatatype_rrsig) buflen++; /* * Ensure that singleton types are actually singletons. */ if (nitems > 1 && dns_rdatatype_issingleton(rdataset->type)) { /* * We have a singleton type, but there's more than one * RR in the rdataset. */ result = DNS_R_SINGLETON; goto free_rdatas; } /* * Allocate the memory, set up a buffer, start copying in * data. */ rawbuf = isc_mem_get(mctx, buflen); if (rawbuf == NULL) { result = ISC_R_NOMEMORY; goto free_rdatas; } #if DNS_RDATASET_FIXED /* Allocate temporary offset table. */ offsettable = isc_mem_get(mctx, nalloc * sizeof(unsigned int)); if (offsettable == NULL) { isc_mem_put(mctx, rawbuf, buflen); result = ISC_R_NOMEMORY; goto free_rdatas; } memset(offsettable, 0, nalloc * sizeof(unsigned int)); #endif region->base = rawbuf; region->length = buflen; rawbuf += reservelen; #if DNS_RDATASET_FIXED offsetbase = rawbuf; #endif *rawbuf++ = (nitems & 0xff00) >> 8; *rawbuf++ = (nitems & 0x00ff); #if DNS_RDATASET_FIXED /* Skip load order table. Filled in later. */ rawbuf += nitems * 4; #endif for (i = 0; i < nalloc; i++) { if (x[i].rdata.data == &removed) continue; #if DNS_RDATASET_FIXED offsettable[x[i].order] = rawbuf - offsetbase;
[Bug c/61236] GCC 4.9 generates incorrect object code
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61236 --- Comment #1 from Mukund Sivaraman --- The following is _correct_ generated x8t_64 code for dns_rdataslab_fromrdataset() as compiled with: gcc version 4.8.2 20131212 (Red Hat 4.8.2-7) (GCC) Under free_rdatas label, you can see that there is a 0 check for x (%r14) as in the C code. It was not deleted by the compiler. 00521450 : ; find dns_rdata_set_first() call below: 521450: 41 57 push %r15 521452: 49 89 f7mov%rsi,%r15 521455: 41 56 push %r14 521457: 41 55 push %r13 521459: 41 54 push %r12 52145b: 49 89 fcmov%rdi,%r12 52145e: 55 push %rbp 52145f: 53 push %rbx 521460: 48 83 ec 38 sub$0x38,%rsp 521464: 48 89 54 24 10 mov%rdx,0x10(%rsp) 521469: 89 4c 24 0c mov%ecx,0xc(%rsp) 52146d: e8 ce e7 ff ff callq 51fc40 521472: 41 89 c5mov%eax,%r13d 521475: 45 85 edtest %r13d,%r13d 521478: 75 26 jne5214a0 52147a: 45 31 f6xor%r14d,%r14d 52147d: 66 41 83 7c 24 22 00cmpw $0x0,0x22(%r12) 521484: b8 19 00 00 00 mov$0x19,%eax 521489: 74 4c je 5214d7 52148b: 48 83 c4 38 add$0x38,%rsp 52148f: 5b pop%rbx 521490: 5d pop%rbp 521491: 41 5c pop%r12 521493: 41 5d pop%r13 521495: 41 5e pop%r14 521497: 41 5f pop%r15 521499: c3 retq 52149a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) 5214a0: 41 81 fd ff ff 00 00cmp$0x,%r13d 5214a7: b8 13 00 00 00 mov$0x13,%eax 5214ac: 77 dd ja 52148b 5214ae: 44 89 e8mov%r13d,%eax 5214b1: b9 a1 00 00 00 mov$0xa1,%ecx 5214b6: ba eb 20 64 00 mov$0x6420eb,%edx 5214bb: 48 8d 34 40 lea(%rax,%rax,2),%rsi 5214bf: 4c 89 ffmov%r15,%rdi 5214c2: 48 c1 e6 04 shl$0x4,%rsi 5214c6: e8 45 c2 0a 00 callq 5cd710 5214cb: 48 85 c0test %rax,%rax 5214ce: 49 89 c6mov%rax,%r14 5214d1: 0f 84 a7 00 00 00 je 52157e 5214d7: 4c 89 e7mov%r12,%rdi ; result = dns_rdataset_first(rdataset); 5214da: e8 41 e8 ff ff callq 51fd20 ; if (result != ISC_R_SUCCESS && result != ISC_R_NOMORE) ; goto free_rdatas; ; 0x1d (ISC_R_NOMORE) 5214df: 83 f8 1dcmp$0x1d,%eax 5214e2: 74 04 je 5214e8 ; 0x0 (ISC_R_SUCCESS). if (result != ISC_R_SUCCESS && result != ; ISC_R_NOMORE), jmp to 52153f (free_rdatas) below. ;; test instruction does logical AND of operands and sets zero flag if ;; %eax is 0. jne (same as jnz) tests ZF and performs conditional jump ;; if ZF=0. 5214e4: 85 c0 test %eax,%eax 5214e6: 75 57 jne52153f ; go below to free_rdatas. ; ignore: for loop 5214e8: 31 db xor%ebx,%ebx 5214ea: 85 c0 test %eax,%eax 5214ec: 75 4c jne52153a 5214ee: 45 85 edtest %r13d,%r13d 5214f1: 75 0a jne5214fd 5214f3: eb 45 jmp52153a 5214f5: 0f 1f 00nopl (%rax) ; ignore: for loop iterate (continue;) 5214f8: 41 39 ddcmp%ebx,%r13d 5214fb: 76 3d jbe52153a 5214fd: 89 d8 mov%ebx,%eax 5214ff: 48 8d 2c 40 lea(%rax,%rax,2),%rbp 521503: 48 c1 e5 04 shl$0x4,%rbp 521507: 4c 01 f5add%r14,%rbp 52150a: 48 89 efmov%rbp,%rdi 52150d: e8 4e 9e fd ff callq 4fb360 521512: 48 89 eemov%rbp,%rsi 521515: 4c 89 e7mov%r12,%rdi 521518: e8 a3 e8 ff ff callq 51fdc0 ; Second INSIST() inside for loop. First INSIST() is redundant. 52151d: 48 81 7d 00 a8 c6 8bcmpq $0x8bc6a8,0x0(%rbp) 521524: 00 521525: 0f 84 00 02 00 00 je 52172b 52152b: 4c 89 e7mov%r12,%rdi 52152e: 83 c3 01add$0x1,%ebx 521531: e8 3a e8 ff ff