[v3] libctf: ctf_member_next needs to return (ssize_t)-1 on error

Message ID 20230913095727.1420654-1-torbjorn.svensson@foss.st.com
State New
Headers
Series [v3] libctf: ctf_member_next needs to return (ssize_t)-1 on error |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Testing passed

Commit Message

Torbjorn SVENSSON Sept. 13, 2023, 9:57 a.m. UTC
  v1 -> v2:
Changed all functions with signed interger return type to return -1 based on
comment from Alan.

v2 -> v3:
Added ctf_set_errno_signed function to return a signed -1 value based on
comment from Nick.


Ok for trunk?

---

The function ctf_member_next should return (ssize_t)-1 on
error. As the function ctf_set_errno returns (ctf_id_t)-1L and that is
then cast to "unsigned long" as it's the return type of the function,
it's not compatible and causes the value 0xffffffff to be returned on
64-bit Windows builds. As a result, the check for a negative value in
ctf_dedup_rhash_type will never be true and a resulting infinit loop is
created.

This was found testing an arm-none-eabi toolchain built with
x86_64-w64-mingw32. If the same source tree is built with
i686-w64-mingw32, everything appears to be working correctly.

libctf/
        * ctf-impl.h (ctf_set_errno_signed): New function.
        * ctf-util.c (ctf_set_errno_signed): Likewise.
        * ctf-create.c (ctf_add_enumerator): Call ctf_set_errno_signed.
        (ctf_add_funcobjt_sym): Likewise.
        (ctf_add_member_encoded): Likewise.
        (ctf_add_member_offset): Likewise.
        (ctf_add_variable): Likewise.
        (ctf_grow_ptrtab): Likewise.
        (ctf_grow_vlen): Likewise.
        (ctf_rollback): Likewise.
        (ctf_set_array): Likewise.
        (ctf_update): Likewise.
        * ctf-dedup.c (ctf_dedup_atoms_init): Likewise.
        (ctf_dedup_conflictify_unshared): Likewise.
        (ctf_dedup_detect_name_ambiguity): Likewise.
        (ctf_dedup_emit_struct_members): Likewise.
        (ctf_dedup_emit_type): Likewise.
        (ctf_dedup_hash_kind): Likewise.
        (ctf_dedup_init): Likewise.
        (ctf_dedup_mark_conflicting_hash): Likewise.
        (ctf_dedup_multiple_input_dicts): Likewise.
        (ctf_dedup_populate_mappings): Likewise.
        (ctf_dedup_record_origin): Likewise.
        (ctf_dedup_rwalk_output_mapping): Likewise.
        (ctf_dedup_walk_output_mapping): Likewise.
        * ctf-dump.c (ctf_dump_append): Likewise.
        (ctf_dump_header): Likewise.
        (ctf_dump_header_sectfield): Likewise.
        (ctf_dump_header_strfield): Likewise.
        (ctf_dump_label): Likewise.
        (ctf_dump_member): Likewise.
        (ctf_dump_str): Likewise.
        (ctf_dump_type): Likewise.
        (ctf_dump_var): Likewise.
        * ctf-labels.c (ctf_label_info): Likewise.
        (ctf_label_iter): Likewise.
        * ctf-link.c (ctf_link_add_ctf_internal): Likewise.
        (ctf_link_add_cu_mapping): Likewise.
        (ctf_link_add): Likewise.
        (ctf_link_deduplicating_one_symtypetab): Likewise.
        (ctf_link_deduplicating_per_cu): Likewise.
        (ctf_link_deduplicating_variables): Likewise.
        (ctf_link): Likewise.
        (ctf_link_one_variable): Likewise.
        * ctf-lookup.c (ctf_func_args): Likewise.
        (ctf_func_info): Likewise.
        (grow_pptrtab): Likewise.
        * ctf-open.c (ctf_cuname_set): Likewise.
        (ctf_import): Likewise.
        (ctf_parent_name_set): Likewise.
        (ctf_setmodel): Likewise.
        * ctf-serialize.c (ctf_gzwrite): Likewise.
        (ctf_serialize): Likewise.
        (symtypetab_density): Likewise.
        * ctf-string.c (ctf_str_move_pending): Likewise.
        * ctf-types.c (ctf_array_info): Likewise.
        (ctf_func_type_info): Likewise.
        (ctf_member_count): Likewise.
        (ctf_member_info): Likewise.
        (ctf_member_next): Likewise.
        (ctf_type_align): Likewise.
        (ctf_type_encoding): Likewise.
        (ctf_type_rvisit): Likewise.
        (ctf_type_size): Likewise.

Signed-off-by: Torbjörn SVENSSON <torbjorn.svensson@foss.st.com>
Co-Authored-By: Yvan ROUX <yvan.roux@foss.st.com>
---
 libctf/ctf-create.c    | 66 +++++++++++++++++------------------
 libctf/ctf-dedup.c     | 78 +++++++++++++++++++++---------------------
 libctf/ctf-dump.c      | 22 ++++++------
 libctf/ctf-impl.h      |  1 +
 libctf/ctf-labels.c    |  6 ++--
 libctf/ctf-link.c      | 74 +++++++++++++++++++--------------------
 libctf/ctf-lookup.c    |  6 ++--
 libctf/ctf-open.c      | 16 ++++-----
 libctf/ctf-serialize.c | 28 +++++++--------
 libctf/ctf-string.c    |  6 ++--
 libctf/ctf-types.c     | 44 ++++++++++++------------
 libctf/ctf-util.c      | 12 +++++++
 12 files changed, 186 insertions(+), 173 deletions(-)
  

Comments

Nick Alcock Sept. 13, 2023, 6:37 p.m. UTC | #1
On 13 Sep 2023, Torbjörn SVENSSON verbalised:

> v1 -> v2:
> Changed all functions with signed interger return type to return -1 based on
> comment from Alan.
>
> v2 -> v3:
> Added ctf_set_errno_signed function to return a signed -1 value based on
> comment from Nick.
>
> Ok for trunk?

If this touches exactly those functions that return int, and fixes the
reported bug, it's good as far as I'm concerned, except for a couple of
possible comment improvements:

> +/* Store the specified error code into the CTF dict, and then return -1
> +   (CTF_ERR) for the benefit of the caller. */

It's not CTF_ERR in this case, it's just -1. Perhaps:

/* Store the specified error code into the CTF dict, and then return -1
   for the benefit of the caller, which is expected to return int,
   as opposed to ctf_id_t. */

> +int
> +ctf_set_errno_signed (ctf_dict_t *fp, int err)
> +{
> +  fp->ctf_errno = err;
> +  /* Don't rely on CTF_ERR here as it will not properly sign extend on 64-bit
> +     Windows ABI.  */
> +  return -1;
> +}

... that Windows is not really the problem here. It's more

/* Don't rely on CTF_ERR here; it is a ctf_id_t (unsigned long), and
   it will be truncated to a non--1 value on platforms on which int
   and unsigned long are different sizes.  */

perhaps? (At least, I think that's what's going on.)

This probably needs testing on a wide variety of platforms with
different type sizes. I'll add throwing this through my entire test
matrix to my todo list, and fix any bugs observed: but the basic idea
looks sound to me.
  
Torbjorn SVENSSON Sept. 13, 2023, 8:20 p.m. UTC | #2
On 2023-09-13 20:37, Nick Alcock wrote:
> On 13 Sep 2023, Torbjörn SVENSSON verbalised:
> 
>> v1 -> v2:
>> Changed all functions with signed interger return type to return -1 based on
>> comment from Alan.
>>
>> v2 -> v3:
>> Added ctf_set_errno_signed function to return a signed -1 value based on
>> comment from Nick.
>>
>> Ok for trunk?
> 
> If this touches exactly those functions that return int, and fixes the
> reported bug, it's good as far as I'm concerned, except for a couple of
> possible comment improvements:

I've verified the calls by building binutils (with the configure flags 
mentioned in my last mail) with CFLAGS="-Wsign-conversion -Wconversion" 
and looking for any warnings related to ctf_set_errno. After applying 
this patch, there were no warnings left.

>> +/* Store the specified error code into the CTF dict, and then return -1
>> +   (CTF_ERR) for the benefit of the caller. */
> 
> It's not CTF_ERR in this case, it's just -1. Perhaps:

True, but why is then ctf_set_errno returning CTF_ERR?
I somehow want to make it obvious that it's not wrong and that it should 
*never* be CTF_ERR in the signed function or the problem would reappear.

The other possibility is to do the inverse, meaning that the 
ctf_set_errno function is returning an integer (-1) and that there is a 
function ctf_set_errno_unsigned that is calling the ctf_set_errno 
function but casting the returned value to unsigned long (or ctf_id_t). 
I personally think this solution is a bit more clean as -1 is the error 
value from all functions, just a matter if it's signed or unsigned.

I.e:

int
ctf_set_errno (ctf_dict_t *fp, int err)
{
   fp->ctf_errno = err;
   return -1;
}

unsigned long
ctf_set_errno_unsigned (ctf_dict_t *fp, int err)
{
   return (unsigned long)ctf_set_errno (fp, err);
}

I suppose the ctf_set_errno_unsigned could even be a macro in the 
ctf-impl.h header file.



> /* Store the specified error code into the CTF dict, and then return -1
>     for the benefit of the caller, which is expected to return int,
>     as opposed to ctf_id_t. */
> 

Ok!

>> +int
>> +ctf_set_errno_signed (ctf_dict_t *fp, int err)
>> +{
>> +  fp->ctf_errno = err;
>> +  /* Don't rely on CTF_ERR here as it will not properly sign extend on 64-bit
>> +     Windows ABI.  */
>> +  return -1;
>> +}
> 
> ... that Windows is not really the problem here. It's more
> 
> /* Don't rely on CTF_ERR here; it is a ctf_id_t (unsigned long), and
>     it will be truncated to a non--1 value on platforms on which int
>     and unsigned long are different sizes.  */
> 
> perhaps? (At least, I think that's what's going on.)

The problem happens when the signed integral type is wider than unsigned 
long.

  /* Don't rely on CTF_ERR here; it is a ctf_id_t (unsigned long), and
      it will be extended to a non--1 value on platforms on which int
      is larger than unsigned long are different sizes.  */

> 
> This probably needs testing on a wide variety of platforms with
> different type sizes. I'll add throwing this through my entire test
> matrix to my todo list, and fix any bugs observed: but the basic idea
> looks sound to me.

Do you want to run this full matrix before or after submitting the patch?
If it's before; when do you think you will have time to do that?


Let me know how you want to proceed.
  
Torbjorn SVENSSON Sept. 20, 2023, 5:44 p.m. UTC | #3
Ping?

On 2023-09-13 22:20, Torbjorn SVENSSON wrote:
> 
> 
> On 2023-09-13 20:37, Nick Alcock wrote:
>> On 13 Sep 2023, Torbjörn SVENSSON verbalised:
>>
>>> v1 -> v2:
>>> Changed all functions with signed interger return type to return -1 
>>> based on
>>> comment from Alan.
>>>
>>> v2 -> v3:
>>> Added ctf_set_errno_signed function to return a signed -1 value based on
>>> comment from Nick.
>>>
>>> Ok for trunk?
>>
>> If this touches exactly those functions that return int, and fixes the
>> reported bug, it's good as far as I'm concerned, except for a couple of
>> possible comment improvements:
> 
> I've verified the calls by building binutils (with the configure flags 
> mentioned in my last mail) with CFLAGS="-Wsign-conversion -Wconversion" 
> and looking for any warnings related to ctf_set_errno. After applying 
> this patch, there were no warnings left.
> 
>>> +/* Store the specified error code into the CTF dict, and then return -1
>>> +   (CTF_ERR) for the benefit of the caller. */
>>
>> It's not CTF_ERR in this case, it's just -1. Perhaps:
> 
> True, but why is then ctf_set_errno returning CTF_ERR?
> I somehow want to make it obvious that it's not wrong and that it should 
> *never* be CTF_ERR in the signed function or the problem would reappear.
> 
> The other possibility is to do the inverse, meaning that the 
> ctf_set_errno function is returning an integer (-1) and that there is a 
> function ctf_set_errno_unsigned that is calling the ctf_set_errno 
> function but casting the returned value to unsigned long (or ctf_id_t). 
> I personally think this solution is a bit more clean as -1 is the error 
> value from all functions, just a matter if it's signed or unsigned.
> 
> I.e:
> 
> int
> ctf_set_errno (ctf_dict_t *fp, int err)
> {
>    fp->ctf_errno = err;
>    return -1;
> }
> 
> unsigned long
> ctf_set_errno_unsigned (ctf_dict_t *fp, int err)
> {
>    return (unsigned long)ctf_set_errno (fp, err);
> }
> 
> I suppose the ctf_set_errno_unsigned could even be a macro in the 
> ctf-impl.h header file.
> 
> 
> 
>> /* Store the specified error code into the CTF dict, and then return -1
>>     for the benefit of the caller, which is expected to return int,
>>     as opposed to ctf_id_t. */
>>
> 
> Ok!
> 
>>> +int
>>> +ctf_set_errno_signed (ctf_dict_t *fp, int err)
>>> +{
>>> +  fp->ctf_errno = err;
>>> +  /* Don't rely on CTF_ERR here as it will not properly sign extend 
>>> on 64-bit
>>> +     Windows ABI.  */
>>> +  return -1;
>>> +}
>>
>> ... that Windows is not really the problem here. It's more
>>
>> /* Don't rely on CTF_ERR here; it is a ctf_id_t (unsigned long), and
>>     it will be truncated to a non--1 value on platforms on which int
>>     and unsigned long are different sizes.  */
>>
>> perhaps? (At least, I think that's what's going on.)
> 
> The problem happens when the signed integral type is wider than unsigned 
> long.
> 
>   /* Don't rely on CTF_ERR here; it is a ctf_id_t (unsigned long), and
>       it will be extended to a non--1 value on platforms on which int
>       is larger than unsigned long are different sizes.  */
> 
>>
>> This probably needs testing on a wide variety of platforms with
>> different type sizes. I'll add throwing this through my entire test
>> matrix to my todo list, and fix any bugs observed: but the basic idea
>> looks sound to me.
> 
> Do you want to run this full matrix before or after submitting the patch?
> If it's before; when do you think you will have time to do that?
> 
> 
> Let me know how you want to proceed.
  
Nick Alcock Sept. 26, 2023, 2:51 p.m. UTC | #4
On 13 Sep 2023, Torbjorn SVENSSON outgrape:

> On 2023-09-13 20:37, Nick Alcock wrote:
>> On 13 Sep 2023, Torbjörn SVENSSON verbalised:
>> 
>>> v1 -> v2:
>>> Changed all functions with signed interger return type to return -1 based on
>>> comment from Alan.
>>>
>>> v2 -> v3:
>>> Added ctf_set_errno_signed function to return a signed -1 value based on
>>> comment from Nick.
>>>
>>> Ok for trunk?
>> If this touches exactly those functions that return int, and fixes the
>> reported bug, it's good as far as I'm concerned, except for a couple of
>> possible comment improvements:
>
> I've verified the calls by building binutils (with the configure flags mentioned in my last mail) with CFLAGS="-Wsign-conversion
> -Wconversion" and looking for any warnings related to ctf_set_errno. After applying this patch, there were no warnings left.

Oh right, that should work (given a platform on which this goes wrong in
the first place).

I should add -Wconversion to my test flags...

>>> +/* Store the specified error code into the CTF dict, and then return -1
>>> +   (CTF_ERR) for the benefit of the caller. */
>> It's not CTF_ERR in this case, it's just -1. Perhaps:
>
> True, but why is then ctf_set_errno returning CTF_ERR?

Simply because I foolishly assumed that CTF_ERR would always end up
== -1 even when passed through a function returning int. This is, uh,
not true.

I do wish that C was defined such that we had one consistent type we
could compare with for errors and not have to worry, but we don't:
functions returning a ctf_id_t return CTF_ERR on error, functions
returning an int return -1 on error and that's just the way it is :(
(This was always true, even in the Solaris era, but when ctf_id_t was an
int this was less visible than it is now.)

> I somehow want to make it obvious that it's not wrong and that it
> should *never* be CTF_ERR in the signed function or the problem would
> reappear.

Yeah.

> The other possibility is to do the inverse, meaning that the
> ctf_set_errno function is returning an integer (-1) and that there is
> a function ctf_set_errno_unsigned that is calling the ctf_set_errno
> function but casting the returned value to unsigned long (or

... and then all the ctf_id_t-returning functions call that?

> ctf_id_t). I personally think this solution is a bit more clean as -1
> is the error value from all functions, just a matter if it's signed or
> unsigned.

Honestly I suspect all we need is a better name:

ctf_set_int_errno(...);
ctf_set_type_errno(...)

and then use one or the other, consistently. (Neither needs to call the
other: they're only two lines long!)

> I suppose the ctf_set_errno_unsigned could even be a macro in the ctf-impl.h header file.

I'd make both of them inline functions personally (I bet it would reduce
code size!)

>>> +int
>>> +ctf_set_errno_signed (ctf_dict_t *fp, int err)
>>> +{
>>> +  fp->ctf_errno = err;
>>> +  /* Don't rely on CTF_ERR here as it will not properly sign extend on 64-bit
>>> +     Windows ABI.  */
>>> +  return -1;
>>> +}
>> ... that Windows is not really the problem here. It's more
>> /* Don't rely on CTF_ERR here; it is a ctf_id_t (unsigned long), and
>>     it will be truncated to a non--1 value on platforms on which int
>>     and unsigned long are different sizes.  */
>> perhaps? (At least, I think that's what's going on.)
>
> The problem happens when the signed integral type is wider than unsigned long.

... sizeof(signed int) > sizeof(unsigned long int)?! Is that even
possible? I would have assumed from the C type hierarchy and the integer
conversion rank rules would have required that unsigned long int was at
least as big as any non-long integral type, but I don't see anywhere
it's required in the standard, dammit...

>> This probably needs testing on a wide variety of platforms with
>> different type sizes. I'll add throwing this through my entire test
>> matrix to my todo list, and fix any bugs observed: but the basic idea
>> looks sound to me.
>
> Do you want to run this full matrix before or after submitting the patch?
> If it's before; when do you think you will have time to do that?
>
> Let me know how you want to proceed.

OK, I'm back from various conferences so I can throw tests past this at
any time, it's largely automated. So once I stop faffing about and
changing my mind and we converge on something I'll throw it past every
test I've got. (It takes a day or so.)
  
Torbjorn SVENSSON Sept. 26, 2023, 5:49 p.m. UTC | #5
On 2023-09-26 16:51, Nick Alcock wrote:
> On 13 Sep 2023, Torbjorn SVENSSON outgrape:
>> On 2023-09-13 20:37, Nick Alcock wrote:
>>> On 13 Sep 2023, Torbjörn SVENSSON verbalised:
> Honestly I suspect all we need is a better name:
> 
> ctf_set_int_errno(...);
> ctf_set_type_errno(...)
> 
> and then use one or the other, consistently. (Neither needs to call the
> other: they're only two lines long!)

Ok. I've updated the patch (V4) to be like you suggested above.
> 
>> I suppose the ctf_set_errno_unsigned could even be a macro in the ctf-impl.h header file.
> 
> I'd make both of them inline functions personally (I bet it would reduce
> code size!)

I do not see any major difference in code size for the ld.exe binary 
after the change.

> 
>>>> +int
>>>> +ctf_set_errno_signed (ctf_dict_t *fp, int err)
>>>> +{
>>>> +  fp->ctf_errno = err;
>>>> +  /* Don't rely on CTF_ERR here as it will not properly sign extend on 64-bit
>>>> +     Windows ABI.  */
>>>> +  return -1;
>>>> +}
>>> ... that Windows is not really the problem here. It's more
>>> /* Don't rely on CTF_ERR here; it is a ctf_id_t (unsigned long), and
>>>      it will be truncated to a non--1 value on platforms on which int
>>>      and unsigned long are different sizes.  */
>>> perhaps? (At least, I think that's what's going on.)
>>
>> The problem happens when the signed integral type is wider than unsigned long.
> 
> ... sizeof(signed int) > sizeof(unsigned long int)?! Is that even
> possible? I would have assumed from the C type hierarchy and the integer
> conversion rank rules would have required that unsigned long int was at
> least as big as any non-long integral type, but I don't see anywhere
> it's required in the standard, dammit...

I don't know about the 'sizeof(signed int) > sizeof(unsigned long int)' 
part, but what I said was _integral type_, not _int_. In the case where 
I saw the problem, it was ssize_t but I'm not sure what that maps to, 
but it's wider than unsigned long int apparently in this case.

>>> This probably needs testing on a wide variety of platforms with
>>> different type sizes. I'll add throwing this through my entire test
>>> matrix to my todo list, and fix any bugs observed: but the basic idea
>>> looks sound to me.
>>
>> Do you want to run this full matrix before or after submitting the patch?
>> If it's before; when do you think you will have time to do that?
>>
>> Let me know how you want to proceed.
> 
> OK, I'm back from various conferences so I can throw tests past this at
> any time, it's largely automated. So once I stop faffing about and
> changing my mind and we converge on something I'll throw it past every
> test I've got. (It takes a day or so.)

If you do not see any problem with the V4 patch, then please go ahead 
and run the tests that you have to get a verdict.

Kind regards,
Torbjörn
  
Nick Alcock Sept. 28, 2023, 4:41 p.m. UTC | #6
On 26 Sep 2023, Torbjorn SVENSSON said:

> On 2023-09-26 16:51, Nick Alcock wrote:
>> On 13 Sep 2023, Torbjorn SVENSSON outgrape:
>>> On 2023-09-13 20:37, Nick Alcock wrote:
>>>> On 13 Sep 2023, Torbjörn SVENSSON verbalised:
>> Honestly I suspect all we need is a better name:
>> ctf_set_int_errno(...);
>> ctf_set_type_errno(...)
>> and then use one or the other, consistently. (Neither needs to call the
>> other: they're only two lines long!)
>
> Ok. I've updated the patch (V4) to be like you suggested above.

Thanks!

>>> I suppose the ctf_set_errno_unsigned could even be a macro in the ctf-impl.h header file.
>> I'd make both of them inline functions personally (I bet it would reduce
>> code size!)
>
> I do not see any major difference in code size for the ld.exe binary after the change.

Oh well, it was just a pious hope really.

>>>>> +int
>>>>> +ctf_set_errno_signed (ctf_dict_t *fp, int err)
>>>>> +{
>>>>> +  fp->ctf_errno = err;
>>>>> +  /* Don't rely on CTF_ERR here as it will not properly sign extend on 64-bit
>>>>> +     Windows ABI.  */
>>>>> +  return -1;
>>>>> +}
>>>> ... that Windows is not really the problem here. It's more
>>>> /* Don't rely on CTF_ERR here; it is a ctf_id_t (unsigned long), and
>>>>      it will be truncated to a non--1 value on platforms on which int
>>>>      and unsigned long are different sizes.  */
>>>> perhaps? (At least, I think that's what's going on.)
>>>
>>> The problem happens when the signed integral type is wider than unsigned long.
>> ... sizeof(signed int) > sizeof(unsigned long int)?! Is that even
>> possible? I would have assumed from the C type hierarchy and the integer
>> conversion rank rules would have required that unsigned long int was at
>> least as big as any non-long integral type, but I don't see anywhere
>> it's required in the standard, dammit...
>
> I don't know about the 'sizeof(signed int) > sizeof(unsigned long
> int)' part, but what I said was _integral type_, not _int_. In the

Ah true. My apologies.

> case where I saw the problem, it was ssize_t but I'm not sure what
> that maps to, but it's wider than unsigned long int apparently in this
> case.

Aha! So this is *not* a problem with functions returning int -- it is
specifically a problem with functions returning *size_t types*.

My apologies, I misunderstood the entire problem.  We probably *do*
still want ctf_set_errno_signed for functions returning int (for clarity
if nothing else), but for ssize_t in particular this won't do: we
probably want a ctf_set_errno_ssize_t or something. The name is awful
but I wasted a day failing to think of a better one :(

There are very few functions returning (s)size_t in libctf:

extern size_t ctf_archive_count (const ctf_archive_t *);
extern ssize_t ctf_type_lname (ctf_dict_t *, ctf_id_t, char *, size_t);
extern ssize_t ctf_type_size (ctf_dict_t *, ctf_id_t);
extern ssize_t ctf_type_align (ctf_dict_t *, ctf_id_t);
extern ssize_t ctf_member_next (ctf_dict_t *, ctf_id_t, ctf_next_t **,
				const char **name, ctf_id_t *membtype,
				int flags);

Of these, ctf_archive_count () cannot fail, so the problem reduces to
ssize_t alone.  These functions should probably

    return (ssize_t) ctf_set_errno_signed (...))

(it's rare enough that a utility functions to do this is probably
unnecessary).

We also have (in ctf_type_lname):

  if (str == NULL)
    return CTF_ERR;			/* errno is set for us.  */

This should probably become a straight -1 (no cast necessary).
ctf_type_size () already gets this right (but needs _ssize_t adjustments
to its ctf_set_errno () calls, as does get_vbytes_common in ctf-open.c).
The same is true of ctf_type_align (), and, of course, ctf_member_next
().

>>>> This probably needs testing on a wide variety of platforms with
>>>> different type sizes. I'll add throwing this through my entire test
>>>> matrix to my todo list, and fix any bugs observed: but the basic idea
>>>> looks sound to me.
>>>
>>> Do you want to run this full matrix before or after submitting the patch?
>>> If it's before; when do you think you will have time to do that?
>>>
>>> Let me know how you want to proceed.
>> OK, I'm back from various conferences so I can throw tests past this at
>> any time, it's largely automated. So once I stop faffing about and
>> changing my mind and we converge on something I'll throw it past every
>> test I've got. (It takes a day or so.)
>
> If you do not see any problem with the V4 patch, then please go ahead
> and run the tests that you have to get a verdict.

... sorry, I'm still flailing at it. Maybe the above is helpful? (It's
only a very small change atop what you've already done, I think.)
  
Torbjorn SVENSSON Sept. 29, 2023, 12:11 p.m. UTC | #7
On 2023-09-28 18:41, Nick Alcock wrote:
> On 26 Sep 2023, Torbjorn SVENSSON said:
> 
>> On 2023-09-26 16:51, Nick Alcock wrote:
>>> On 13 Sep 2023, Torbjorn SVENSSON outgrape:
>>>> On 2023-09-13 20:37, Nick Alcock wrote:
>>>>> On 13 Sep 2023, Torbjörn SVENSSON verbalised:
>>> Honestly I suspect all we need is a better name:
>>> ctf_set_int_errno(...);
>>> ctf_set_type_errno(...)
>>> and then use one or the other, consistently. (Neither needs to call the
>>> other: they're only two lines long!)
>>
>> Ok. I've updated the patch (V4) to be like you suggested above.
> 
> Thanks!
> 
>>>> I suppose the ctf_set_errno_unsigned could even be a macro in the ctf-impl.h header file.
>>> I'd make both of them inline functions personally (I bet it would reduce
>>> code size!)
>>
>> I do not see any major difference in code size for the ld.exe binary after the change.
> 
> Oh well, it was just a pious hope really.
> 
>>>>>> +int
>>>>>> +ctf_set_errno_signed (ctf_dict_t *fp, int err)
>>>>>> +{
>>>>>> +  fp->ctf_errno = err;
>>>>>> +  /* Don't rely on CTF_ERR here as it will not properly sign extend on 64-bit
>>>>>> +     Windows ABI.  */
>>>>>> +  return -1;
>>>>>> +}
>>>>> ... that Windows is not really the problem here. It's more
>>>>> /* Don't rely on CTF_ERR here; it is a ctf_id_t (unsigned long), and
>>>>>       it will be truncated to a non--1 value on platforms on which int
>>>>>       and unsigned long are different sizes.  */
>>>>> perhaps? (At least, I think that's what's going on.)
>>>>
>>>> The problem happens when the signed integral type is wider than unsigned long.
>>> ... sizeof(signed int) > sizeof(unsigned long int)?! Is that even
>>> possible? I would have assumed from the C type hierarchy and the integer
>>> conversion rank rules would have required that unsigned long int was at
>>> least as big as any non-long integral type, but I don't see anywhere
>>> it's required in the standard, dammit...
>>
>> I don't know about the 'sizeof(signed int) > sizeof(unsigned long
>> int)' part, but what I said was _integral type_, not _int_. In the
> 
> Ah true. My apologies.
> 
>> case where I saw the problem, it was ssize_t but I'm not sure what
>> that maps to, but it's wider than unsigned long int apparently in this
>> case.
> 
> Aha! So this is *not* a problem with functions returning int -- it is
> specifically a problem with functions returning *size_t types*.
> 

So, I'm not sure if were are talking past each other, but I don't think 
it's a *size_t type* issue either.

As I see it, there are 3 different scenarios.

1. The function returns a signed integral type, then -1 would be fine. 
Even a simple (int)-1 would work as it would be sign-extended.

2. The function returns an unsigned integral type that is as wide, or 
wider, than unsigned long. In this case, CTF_ERR will be zero extended 
and the value will be UINT_something_MAX (the number of binary ones 
depends on the sizeof(unsigned long)).

3. The function return an unsigned integral type that is narrower than 
unsigned long. In this case, the cast to unsigned long is wider than the 
return type and may overflow(?). Is it safe to return something bigger 
than can be represented in the return type?

I don't know if the third option above is applicable as I have not 
checked the width of the types that is calling the ctf_set_errno() 
function before my patch, but the other 2 are there.

In my mind, I see it as we need 2 different implementations. One that 
returns a simple -1 and another one that casts it for all unsigned 
calls, but maybe I'm oversimplifying this.


To make things easier, I would actually consider just having the assign 
statement and the return statement inlined (without a macro or inline 
function) directly where they are supposed to be.
Would you be open to removing the ctf_set_errno() function completely 
and just expand it to where it's called (almost like my v2 patch, but 
everywhere instead)?
Alternatively, we could start the discussion on adopting the C11 
standard instead, but I fear that this will be a much longer discussion 
than figuring out what the best approach would be for libctf.


> My apologies, I misunderstood the entire problem.  We probably *do*
> still want ctf_set_errno_signed for functions returning int (for clarity
> if nothing else), but for ssize_t in particular this won't do: we
> probably want a ctf_set_errno_ssize_t or something. The name is awful
> but I wasted a day failing to think of a better one :(
> 
> There are very few functions returning (s)size_t in libctf:
> 
> extern size_t ctf_archive_count (const ctf_archive_t *);
> extern ssize_t ctf_type_lname (ctf_dict_t *, ctf_id_t, char *, size_t);
> extern ssize_t ctf_type_size (ctf_dict_t *, ctf_id_t);
> extern ssize_t ctf_type_align (ctf_dict_t *, ctf_id_t);
> extern ssize_t ctf_member_next (ctf_dict_t *, ctf_id_t, ctf_next_t **,
> 				const char **name, ctf_id_t *membtype,
> 				int flags);
> 
> Of these, ctf_archive_count () cannot fail, so the problem reduces to
> ssize_t alone.  These functions should probably
> 
>      return (ssize_t) ctf_set_errno_signed (...))
> 
> (it's rare enough that a utility functions to do this is probably
> unnecessary).
> 
> We also have (in ctf_type_lname):
> 
>    if (str == NULL)
>      return CTF_ERR;			/* errno is set for us.  */
> 
> This should probably become a straight -1 (no cast necessary).

Yes, this is another place where the problem appears.
Looks like the -Wsign-conversion does not warn when it's the return 
statement for the function. Doing a simple grep in the tree reveals 56 
places that needs to be verified.

> ctf_type_size () already gets this right (but needs _ssize_t adjustments
> to its ctf_set_errno () calls, as does get_vbytes_common in ctf-open.c).
> The same is true of ctf_type_align (), and, of course, ctf_member_next
> ().
> 
>>>>> This probably needs testing on a wide variety of platforms with
>>>>> different type sizes. I'll add throwing this through my entire test
>>>>> matrix to my todo list, and fix any bugs observed: but the basic idea
>>>>> looks sound to me.
>>>>
>>>> Do you want to run this full matrix before or after submitting the patch?
>>>> If it's before; when do you think you will have time to do that?
>>>>
>>>> Let me know how you want to proceed.
>>> OK, I'm back from various conferences so I can throw tests past this at
>>> any time, it's largely automated. So once I stop faffing about and
>>> changing my mind and we converge on something I'll throw it past every
>>> test I've got. (It takes a day or so.)
>>
>> If you do not see any problem with the V4 patch, then please go ahead
>> and run the tests that you have to get a verdict.
> 
> ... sorry, I'm still flailing at it. Maybe the above is helpful? (It's
> only a very small change atop what you've already done, I think.)
> 

Let me know how you want to handle this. :)

Kind regards,
Torbjörn
  
Nick Alcock Oct. 2, 2023, 10:57 a.m. UTC | #8
On 29 Sep 2023, Torbjorn SVENSSON verbalised:

> On 2023-09-28 18:41, Nick Alcock wrote:
>> Aha! So this is *not* a problem with functions returning int -- it is
>> specifically a problem with functions returning *size_t types*.
>
> So, I'm not sure if were are talking past each other, but I don't think it's a *size_t type* issue either.
>
> As I see it, there are 3 different scenarios.
>
> 1. The function returns a signed integral type, then -1 would be fine.
> Even a simple (int)-1 would work as it would be sign-extended.

Ack.

> 2. The function returns an unsigned integral type that is as wide, or
> wider, than unsigned long. In this case, CTF_ERR will be zero extended
> and the value will be UINT_something_MAX (the number of binary ones
> depends on the sizeof(unsigned long)).

Ack. This is the problem area, because the interface contract for libctf
specifies (or would if it were written down anywhere) that you can test
all signed returns for error via < 0 and all unsigned ones (like
ctf_id_t) via == CTF_ERR, and CTF_ERR... has a cast in it.

I suppose if we changed CTF_ERR to (uintmax_t)-1 this might work for all
cases, but a) uintmax_t is an annoying sod that doesn't even extend to
the largest possible unsigned integral type on all platforms and b) this
just gives us the same problem from the other side if the unsigned type
is shorter than uintmax_t (which it almost always would be). What would
happen to the (unsigned long)-1 the libctf function returned when it was
promoted? Would it turn into (uintmax_t)-1 consistently, given that the
type has no sign so sign-extension presumably does not apply? I've tried
to figure it out from the standard text and I guess fevers and bacterial
infections are not compatible with reading standardese because I haven't
worked it out yet :P

(or maybe you need to be on more serious drugs than I'm on when reading
it. It might well be that way round!)

> 3. The function return an unsigned integral type that is narrower than
> unsigned long. In this case, the cast to unsigned long is wider than
> the return type and may overflow(?). Is it safe to return something
> bigger than can be represented in the return type?

Are there any of those at all? I don't think so. It's possible in theory
but I don't think we use unsigned int anywhere. I suppose size_t might
be unsigned int on some platforms, and then this problem might arise.

> In my mind, I see it as we need 2 different implementations. One that
> returns a simple -1 and another one that casts it for all unsigned
> calls, but maybe I'm oversimplifying this.

I suppose if it was always done *at the return site* so the compiler
could always see the return type properly, it would always get the cast
right: my worry is the CTF_ERR at the test site, in the user code we
cannot affect (except by changing CTF_ERR).

> To make things easier, I would actually consider just having the
> assign statement and the return statement inlined (without a macro or
> inline function) directly where they are supposed to be.
> 
> Would you be open to removing the ctf_set_errno() function completely
> and just expand it to where it's called (almost like my v2 patch, but
> everywhere instead)?

Well, you could use a macro to make it less gross. All the code
duplication makes me wince a bit.

But more problematic from my perspective is the implications for the
callers. We're trying to get either -1 or (some type)-1 to the callers
so they can error-check it in a consistent and not-too-horrifying
fashion, after all. (We could add a ctf_is_error() function or macro
that does whatever magic is needed if it's too baroque, but that would
be my least favourite option because it looks nothing like a
conditional.)

> Alternatively, we could start the discussion on adopting the C11 standard instead, but I fear that this will be a much longer
> discussion than figuring out what the best approach would be for libctf.

Agreed, or I'd have proposed it already. Toolchain projects like
binutils must be conservative, and there are no doubt lots of archaic
weird but valid targets out there where the bootstrap chain requires
binutils building before GCC and the system compiler is not C11-capable.

>> ... sorry, I'm still flailing at it. Maybe the above is helpful? (It's
>> only a very small change atop what you've already done, I think.)
>
> Let me know how you want to handle this. :)

I think we're still narrowing down the problem, really. -1 is such a
common error return, it's amazing how hard it is to make it work right :)
  
Torbjorn SVENSSON Oct. 3, 2023, 12:59 p.m. UTC | #9
Hello Nick,


On 2023-10-02 12:57, Nick Alcock wrote:
> On 29 Sep 2023, Torbjorn SVENSSON verbalised:
> 
>> On 2023-09-28 18:41, Nick Alcock wrote:
>>> Aha! So this is *not* a problem with functions returning int -- it is
>>> specifically a problem with functions returning *size_t types*.
>>
>> So, I'm not sure if were are talking past each other, but I don't think it's a *size_t type* issue either.
>>
>> As I see it, there are 3 different scenarios.
>>
>> 1. The function returns a signed integral type, then -1 would be fine.
>> Even a simple (int)-1 would work as it would be sign-extended.
> 
> Ack.
> 
>> 2. The function returns an unsigned integral type that is as wide, or
>> wider, than unsigned long. In this case, CTF_ERR will be zero extended
>> and the value will be UINT_something_MAX (the number of binary ones
>> depends on the sizeof(unsigned long)).
> 
> Ack. This is the problem area, because the interface contract for libctf
> specifies (or would if it were written down anywhere) that you can test
> all signed returns for error via < 0 and all unsigned ones (like
> ctf_id_t) via == CTF_ERR, and CTF_ERR... has a cast in it.

I don't think this is true with the current logic in place.
On a signed integral function, it could be either -1 (negative one) or, 
if the data type of the called function is wide enough, CTF_ERR.

> I suppose if we changed CTF_ERR to (uintmax_t)-1 this might work for all
> cases, but a) uintmax_t is an annoying sod that doesn't even extend to
> the largest possible unsigned integral type on all platforms and b) this
> just gives us the same problem from the other side if the unsigned type
> is shorter than uintmax_t (which it almost always would be). What would
> happen to the (unsigned long)-1 the libctf function returned when it was
> promoted? Would it turn into (uintmax_t)-1 consistently, given that the
> type has no sign so sign-extension presumably does not apply? I've tried
> to figure it out from the standard text and I guess fevers and bacterial
> infections are not compatible with reading standardese because I haven't
> worked it out yet :P
> 
> (or maybe you need to be on more serious drugs than I'm on when reading
> it. It might well be that way round!)
> 
>> 3. The function return an unsigned integral type that is narrower than
>> unsigned long. In this case, the cast to unsigned long is wider than
>> the return type and may overflow(?). Is it safe to return something
>> bigger than can be represented in the return type?
> 
> Are there any of those at all? I don't think so. It's possible in theory
> but I don't think we use unsigned int anywhere. I suppose size_t might
> be unsigned int on some platforms, and then this problem might arise.
> 
>> In my mind, I see it as we need 2 different implementations. One that
>> returns a simple -1 and another one that casts it for all unsigned
>> calls, but maybe I'm oversimplifying this.
> 
> I suppose if it was always done *at the return site* so the compiler
> could always see the return type properly, it would always get the cast
> right: my worry is the CTF_ERR at the test site, in the user code we
> cannot affect (except by changing CTF_ERR).

I suppose so, but does it really matter when we cannot do this part? Or 
are you thinking about just having the ctf_set_errno function always 
return -1 (negative one) and let the caller do the automagic conversion? 
If so, I think the idiom with return_value == CTF_ERR is going to fail.

>> To make things easier, I would actually consider just having the
>> assign statement and the return statement inlined (without a macro or
>> inline function) directly where they are supposed to be.
>>
>> Would you be open to removing the ctf_set_errno() function completely
>> and just expand it to where it's called (almost like my v2 patch, but
>> everywhere instead)?
> 
> Well, you could use a macro to make it less gross. All the code
> duplication makes me wince a bit.

How would a macro work for this?

Consider that a function returns a char*, in this case, neither CTF_ERR, 
nor -1 should be returned, but instead NULL. Using a macro would make 
this problematic to differentiate.

A possibility could be to have a macro like:

#define CTF_SET_ERRNO(fp,err) do { fp->ctf_errno = err; } while(0)

but what's the point?
Is it better to write:

if (some_failure_condition)
   {
     CTF_SET_ERRNO(fp, ENOMEM);
     return -1; // or CTF_ERR, NULL, ..., depending on the function
   }

instead of:

if (some_failure_condition)
   {
     fp->ctf_errno = ENOMEM;
     return -1; // or CTF_ERR, NULL, ..., depending on the function
   }


> But more problematic from my perspective is the implications for the
> callers. We're trying to get either -1 or (some type)-1 to the callers
> so they can error-check it in a consistent and not-too-horrifying
> fashion, after all. (We could add a ctf_is_error() function or macro
> that does whatever magic is needed if it's too baroque, but that would
> be my least favourite option because it looks nothing like a
> conditional.)

Adding a ctf_is_error()-function would be one way to go, but it would be 
an API change while all the others I've been talking about is internal 
into the library without actually touching the interface.

>> Alternatively, we could start the discussion on adopting the C11 standard instead, but I fear that this will be a much longer
>> discussion than figuring out what the best approach would be for libctf.
> 
> Agreed, or I'd have proposed it already. Toolchain projects like
> binutils must be conservative, and there are no doubt lots of archaic
> weird but valid targets out there where the bootstrap chain requires
> binutils building before GCC and the system compiler is not C11-capable.
> 
>>> ... sorry, I'm still flailing at it. Maybe the above is helpful? (It's
>>> only a very small change atop what you've already done, I think.)
>>
>> Let me know how you want to handle this. :)
> 
> I think we're still narrowing down the problem, really. -1 is such a
> common error return, it's amazing how hard it is to make it work right :)
> 

So, based on all we have talk about so for, I'm leaning towards just 
having the 2 statements in all places where an error should be returned 
and have the change contained inside the library rather than making 
external changes required.
Then the question is, would the CTF_SET_ERRNO macro have anything to 
add? I.e. should we have it even if it's just setting the field or 
should we set the field directly? Regardless, the return statement needs 
to be kept outside that block.

Please let me know what you think so that we can progress on this topic.
I'm currently blocked by this point for the tool chain that I'm supposed 
to deliver.

Kind regards,
Torbjörn
  
Nick Alcock Oct. 3, 2023, 8:53 p.m. UTC | #10
This is a really fiddly horror show, isn't it...

On 3 Oct 2023, Torbjorn SVENSSON outgrape:

> On 2023-10-02 12:57, Nick Alcock wrote:
>> On 29 Sep 2023, Torbjorn SVENSSON verbalised:
>> 
>>> On 2023-09-28 18:41, Nick Alcock wrote:
>> Ack. This is the problem area, because the interface contract for libctf
>> specifies (or would if it were written down anywhere) that you can test
>> all signed returns for error via < 0 and all unsigned ones (like
>> ctf_id_t) via == CTF_ERR, and CTF_ERR... has a cast in it.
>
> I don't think this is true with the current logic in place.
> On a signed integral function, it could be either -1 (negative one) or, if the data type of the called function is wide enough,
> CTF_ERR.

Oh. That needs fixing then -- there's no other way for the caller to
tell what to test, and honestly even '-1 for signed, CTF_ERR for
unsigned and ctf_id_t, == NULL for pointers' is complex enough that it's
a source of errors all on its own :(

>>> In my mind, I see it as we need 2 different implementations. One that
>>> returns a simple -1 and another one that casts it for all unsigned
>>> calls, but maybe I'm oversimplifying this.
>> I suppose if it was always done *at the return site* so the compiler
>> could always see the return type properly, it would always get the cast
>> right: my worry is the CTF_ERR at the test site, in the user code we
>> cannot affect (except by changing CTF_ERR).
>
> I suppose so, but does it really matter when we cannot do this part?

I was assuming that a *macro*

#define ctf_set_errno(fp, err) do {
    (fp)->ctf_errno = (err);
    return -1;
} while (0);

would return the right thing automatically -- but oh ugh that's a
behaviour change, ctf_set_errno does not do a return currently. That
won't work. Fixing that to get a ctf_set_errno macro to return a
suitable value requires the statement-expression extension which of
course we cannot rely on being present in binutils :( so a
ctf_set_errno macro is untenable. Dammit.

> Or are you thinking about just having the ctf_set_errno function
> always return -1 (negative one) and let the caller do the automagic
> conversion?

I was assuming that the callee would do it.

>             If so, I think the idiom with return_value == CTF_ERR is
> going to fail.

Oh wait, I see -- if some return types are wider than unsigned long,
CTF_ERR is not going to equal -1 for comparisons against that type. Ew.
And of course what that type is is platform-dependent.

Maybe we *can* get away with using uintmax_t in the definition of
CTF_ERR?

>>> To make things easier, I would actually consider just having the
>>> assign statement and the return statement inlined (without a macro or
>>> inline function) directly where they are supposed to be.
>>>
>>> Would you be open to removing the ctf_set_errno() function completely
>>> and just expand it to where it's called (almost like my v2 patch, but
>>> everywhere instead)?
>> Well, you could use a macro to make it less gross. All the code
>> duplication makes me wince a bit.
>
> How would a macro work for this?

Not sure. I'm casting about and not even writing enough test cases I'm
afraid. :(

I disproved the macro in this reply anyway (see above). Sorry, I'm
thinking very slowly right now.

> Consider that a function returns a char*, in this case, neither CTF_ERR, nor -1 should be returned, but instead NULL. Using a macro
> would make this problematic to differentiate.

Ah yes, pointers are the third case. Obviously NULL == error for those.

> A possibility could be to have a macro like:
>
> #define CTF_SET_ERRNO(fp,err) do { fp->ctf_errno = err; } while(0)
>
> but what's the point?
> Is it better to write:
>
> if (some_failure_condition)
>   {
>     CTF_SET_ERRNO(fp, ENOMEM);
>     return -1; // or CTF_ERR, NULL, ..., depending on the function
>   }
>
> instead of:
>
> if (some_failure_condition)
>   {
>     fp->ctf_errno = ENOMEM;
>     return -1; // or CTF_ERR, NULL, ..., depending on the function
>   }

I'd say stick with the fp->ctf_errno assignment, it's far clearer than a
SHOUTY MACRO that just does an assignment. The whole point of
ctf_set_errno() in the first place was to avoid the need for multiple
statements and thus a new block: if we need multiple statements anyway
because of return-type pain like this, we might as well dump the macro.

(assuming this works, because that's a lot of changes to stick you
with.)

>> But more problematic from my perspective is the implications for the
>> callers. We're trying to get either -1 or (some type)-1 to the callers
>> so they can error-check it in a consistent and not-too-horrifying
>> fashion, after all. (We could add a ctf_is_error() function or macro
>> that does whatever magic is needed if it's too baroque, but that would
>> be my least favourite option because it looks nothing like a
>> conditional.)
>
> Adding a ctf_is_error()-function would be one way to go, but it would be an API change while all the others I've been talking about
> is internal into the library without actually touching the interface.

That's exactly my objection too (also, it's clunky as hell). I really do
want to avoid that. My big fear here is that you've found some
fundamental problem with a -1 signed, CTF_ERR unsigned return (we can't
even change the definition of CTF_ERR, because it's baked into callers,
while libctf can change under them.)

>> I think we're still narrowing down the problem, really. -1 is such a
>> common error return, it's amazing how hard it is to make it work right :)
>
> So, based on all we have talk about so for, I'm leaning towards just
> having the 2 statements in all places where an error should be
> returned and have the change contained inside the library rather than
> making external changes required.

I think you're right.

> Then the question is, would the CTF_SET_ERRNO macro have anything to
> add? I.e. should we have it even if it's just setting the field or
> should we set the field directly? Regardless, the return statement
> needs to be kept outside that block.

I'd go for dumping CTF_SET_ERRNO: just switch to a scheme where we set
fp->ctf_errno and do a return. It adds a few more {} blocks but that's
unavoidable in the absence of statement expressions. (And half the
places we care about don't need new blocks anyway.)

> Please let me know what you think so that we can progress on this topic.
> I'm currently blocked by this point for the tool chain that I'm supposed to deliver.

Oh I'm sorry!
  

Patch

diff --git a/libctf/ctf-create.c b/libctf/ctf-create.c
index 6b342dc64a2..c6e64244aa9 100644
--- a/libctf/ctf-create.c
+++ b/libctf/ctf-create.c
@@ -60,7 +60,7 @@  ctf_grow_ptrtab (ctf_dict_t *fp)
 
       if ((new_ptrtab = realloc (fp->ctf_ptrtab,
 				 new_ptrtab_len * sizeof (uint32_t))) == NULL)
-	return (ctf_set_errno (fp, ENOMEM));
+	return (ctf_set_errno_signed (fp, ENOMEM));
 
       fp->ctf_ptrtab = new_ptrtab;
       memset (fp->ctf_ptrtab + fp->ctf_ptrtab_len, 0,
@@ -87,7 +87,7 @@  ctf_grow_vlen (ctf_dict_t *fp, ctf_dtdef_t *dtd, size_t vlen)
 				dtd->dtd_vlen_alloc * 2)) == NULL)
     {
       dtd->dtd_vlen = old;
-      return (ctf_set_errno (fp, ENOMEM));
+      return (ctf_set_errno_signed (fp, ENOMEM));
     }
   memset (dtd->dtd_vlen + dtd->dtd_vlen_alloc, 0, dtd->dtd_vlen_alloc);
   dtd->dtd_vlen_alloc *= 2;
@@ -197,7 +197,7 @@  int
 ctf_update (ctf_dict_t *fp)
 {
   if (!(fp->ctf_flags & LCTF_RDWR))
-    return (ctf_set_errno (fp, ECTF_RDONLY));
+    return (ctf_set_errno_signed (fp, ECTF_RDONLY));
 
   fp->ctf_dtoldid = fp->ctf_typemax;
   return 0;
@@ -226,7 +226,7 @@  ctf_dtd_insert (ctf_dict_t *fp, ctf_dtdef_t *dtd, int flag, int kind)
   if (ctf_dynhash_insert (fp->ctf_dthash, (void *) (uintptr_t) dtd->dtd_type,
 			  dtd) < 0)
     {
-      ctf_set_errno (fp, ENOMEM);
+      ctf_set_errno_signed (fp, ENOMEM);
       return -1;
     }
 
@@ -239,7 +239,7 @@  ctf_dtd_insert (ctf_dict_t *fp, ctf_dtdef_t *dtd, int flag, int kind)
 	{
 	  ctf_dynhash_remove (fp->ctf_dthash, (void *) (uintptr_t)
 			      dtd->dtd_type);
-	  ctf_set_errno (fp, ENOMEM);
+	  ctf_set_errno_signed (fp, ENOMEM);
 	  return -1;
 	}
     }
@@ -330,7 +330,7 @@  ctf_dvd_insert (ctf_dict_t *fp, ctf_dvdef_t *dvd)
 {
   if (ctf_dynhash_insert (fp->ctf_dvhash, dvd->dvd_name, dvd) < 0)
     {
-      ctf_set_errno (fp, ENOMEM);
+      ctf_set_errno_signed (fp, ENOMEM);
       return -1;
     }
   ctf_list_append (&fp->ctf_dvdefs, dvd);
@@ -391,10 +391,10 @@  ctf_rollback (ctf_dict_t *fp, ctf_snapshot_id_t id)
   ctf_dvdef_t *dvd, *nvd;
 
   if (!(fp->ctf_flags & LCTF_RDWR))
-    return (ctf_set_errno (fp, ECTF_RDONLY));
+    return (ctf_set_errno_signed (fp, ECTF_RDONLY));
 
   if (fp->ctf_snapshot_lu >= id.snapshot_id)
-    return (ctf_set_errno (fp, ECTF_OVERROLLBACK));
+    return (ctf_set_errno_signed (fp, ECTF_OVERROLLBACK));
 
   for (dtd = ctf_list_next (&fp->ctf_dtdefs); dtd != NULL; dtd = ntd)
     {
@@ -723,11 +723,11 @@  ctf_set_array (ctf_dict_t *fp, ctf_id_t type, const ctf_arinfo_t *arp)
   ctf_array_t *vlen;
 
   if (!(fp->ctf_flags & LCTF_RDWR))
-    return (ctf_set_errno (fp, ECTF_RDONLY));
+    return (ctf_set_errno_signed (fp, ECTF_RDONLY));
 
   if (dtd == NULL
       || LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info) != CTF_K_ARRAY)
-    return (ctf_set_errno (fp, ECTF_BADID));
+    return (ctf_set_errno_signed (fp, ECTF_BADID));
 
   vlen = (ctf_array_t *) dtd->dtd_vlen;
   fp->ctf_flags |= LCTF_DIRTY;
@@ -1055,23 +1055,23 @@  ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
   uint32_t kind, vlen, root;
 
   if (name == NULL)
-    return (ctf_set_errno (fp, EINVAL));
+    return (ctf_set_errno_signed (fp, EINVAL));
 
   if (!(fp->ctf_flags & LCTF_RDWR))
-    return (ctf_set_errno (fp, ECTF_RDONLY));
+    return (ctf_set_errno_signed (fp, ECTF_RDONLY));
 
   if (dtd == NULL)
-    return (ctf_set_errno (fp, ECTF_BADID));
+    return (ctf_set_errno_signed (fp, ECTF_BADID));
 
   kind = LCTF_INFO_KIND (fp, dtd->dtd_data.ctt_info);
   root = LCTF_INFO_ISROOT (fp, dtd->dtd_data.ctt_info);
   vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
 
   if (kind != CTF_K_ENUM)
-    return (ctf_set_errno (fp, ECTF_NOTENUM));
+    return (ctf_set_errno_signed (fp, ECTF_NOTENUM));
 
   if (vlen == CTF_MAX_VLEN)
-    return (ctf_set_errno (fp, ECTF_DTFULL));
+    return (ctf_set_errno_signed (fp, ECTF_DTFULL));
 
   old_vlen = dtd->dtd_vlen;
   if (ctf_grow_vlen (fp, dtd, sizeof (ctf_enum_t) * (vlen + 1)) < 0)
@@ -1090,7 +1090,7 @@  ctf_add_enumerator (ctf_dict_t *fp, ctf_id_t enid, const char *name,
 
   for (i = 0; i < vlen; i++)
     if (strcmp (ctf_strptr (fp, en[i].cte_name), name) == 0)
-      return (ctf_set_errno (fp, ECTF_DUPLICATE));
+      return (ctf_set_errno_signed (fp, ECTF_DUPLICATE));
 
   en[i].cte_name = ctf_str_add_pending (fp, name, &en[i].cte_name);
   en[i].cte_value = value;
@@ -1119,10 +1119,10 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
   ctf_lmember_t *memb;
 
   if (!(fp->ctf_flags & LCTF_RDWR))
-    return (ctf_set_errno (fp, ECTF_RDONLY));
+    return (ctf_set_errno_signed (fp, ECTF_RDONLY));
 
   if (dtd == NULL)
-    return (ctf_set_errno (fp, ECTF_BADID));
+    return (ctf_set_errno_signed (fp, ECTF_BADID));
 
   if (name != NULL && name[0] == '\0')
     name = NULL;
@@ -1132,10 +1132,10 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
   vlen = LCTF_INFO_VLEN (fp, dtd->dtd_data.ctt_info);
 
   if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
-    return (ctf_set_errno (fp, ECTF_NOTSOU));
+    return (ctf_set_errno_signed (fp, ECTF_NOTSOU));
 
   if (vlen == CTF_MAX_VLEN)
-    return (ctf_set_errno (fp, ECTF_DTFULL));
+    return (ctf_set_errno_signed (fp, ECTF_DTFULL));
 
   old_vlen = dtd->dtd_vlen;
   if (ctf_grow_vlen (fp, dtd, sizeof (ctf_lmember_t) * (vlen + 1)) < 0)
@@ -1156,7 +1156,7 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
     {
       for (i = 0; i < vlen; i++)
 	if (strcmp (ctf_strptr (fp, memb[i].ctlm_name), name) == 0)
-	  return (ctf_set_errno (fp, ECTF_DUPLICATE));
+	  return (ctf_set_errno_signed (fp, ECTF_DUPLICATE));
     }
 
   if ((msize = ctf_type_size (fp, type)) < 0 ||
@@ -1174,7 +1174,7 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
       msize = 0;
       malign = 0;
       if (ctf_errno (fp) == ECTF_NONREPRESENTABLE)
-	ctf_set_errno (fp, 0);
+	ctf_set_errno_signed (fp, 0);
       else if (ctf_errno (fp) == ECTF_INCOMPLETE)
 	is_incomplete = 1;
       else
@@ -1212,7 +1212,7 @@  ctf_add_member_offset (ctf_dict_t *fp, ctf_id_t souid, const char *name,
 			      "incomplete type %lx to struct %lx without "
 			      "specifying explicit offset\n"),
 			    name ? name : _("(unnamed member)"), type, souid);
-	      return (ctf_set_errno (fp, ECTF_INCOMPLETE));
+	      return (ctf_set_errno_signed (fp, ECTF_INCOMPLETE));
 	    }
 
 	  if (ctf_type_encoding (fp, ltype, &linfo) == 0)
@@ -1285,7 +1285,7 @@  ctf_add_member_encoded (ctf_dict_t *fp, ctf_id_t souid, const char *name,
   int otype = type;
 
   if ((kind != CTF_K_INTEGER) && (kind != CTF_K_FLOAT) && (kind != CTF_K_ENUM))
-    return (ctf_set_errno (fp, ECTF_NOTINTFP));
+    return (ctf_set_errno_signed (fp, ECTF_NOTINTFP));
 
   if ((type = ctf_add_slice (fp, CTF_ADD_NONROOT, otype, &encoding)) == CTF_ERR)
     return -1;			/* errno is set for us.  */
@@ -1307,10 +1307,10 @@  ctf_add_variable (ctf_dict_t *fp, const char *name, ctf_id_t ref)
   ctf_dict_t *tmp = fp;
 
   if (!(fp->ctf_flags & LCTF_RDWR))
-    return (ctf_set_errno (fp, ECTF_RDONLY));
+    return (ctf_set_errno_signed (fp, ECTF_RDONLY));
 
   if (ctf_dvd_lookup (fp, name) != NULL)
-    return (ctf_set_errno (fp, ECTF_DUPLICATE));
+    return (ctf_set_errno_signed (fp, ECTF_DUPLICATE));
 
   if (ctf_lookup_by_id (&tmp, ref) == NULL)
     return -1;			/* errno is set for us.  */
@@ -1321,12 +1321,12 @@  ctf_add_variable (ctf_dict_t *fp, const char *name, ctf_id_t ref)
     return -1;
 
   if ((dvd = malloc (sizeof (ctf_dvdef_t))) == NULL)
-    return (ctf_set_errno (fp, EAGAIN));
+    return (ctf_set_errno_signed (fp, EAGAIN));
 
   if (name != NULL && (dvd->dvd_name = strdup (name)) == NULL)
     {
       free (dvd);
-      return (ctf_set_errno (fp, EAGAIN));
+      return (ctf_set_errno_signed (fp, EAGAIN));
     }
   dvd->dvd_type = ref;
   dvd->dvd_snapshots = fp->ctf_snapshots;
@@ -1350,25 +1350,25 @@  ctf_add_funcobjt_sym (ctf_dict_t *fp, int is_function, const char *name, ctf_id_
   ctf_dynhash_t *h = is_function ? fp->ctf_funchash : fp->ctf_objthash;
 
   if (!(fp->ctf_flags & LCTF_RDWR))
-    return (ctf_set_errno (fp, ECTF_RDONLY));
+    return (ctf_set_errno_signed (fp, ECTF_RDONLY));
 
   if (ctf_dynhash_lookup (fp->ctf_objthash, name) != NULL ||
       ctf_dynhash_lookup (fp->ctf_funchash, name) != NULL)
-    return (ctf_set_errno (fp, ECTF_DUPLICATE));
+    return (ctf_set_errno_signed (fp, ECTF_DUPLICATE));
 
   if (ctf_lookup_by_id (&tmp, id) == NULL)
     return -1;                                  /* errno is set for us.  */
 
   if (is_function && ctf_type_kind (fp, id) != CTF_K_FUNCTION)
-    return (ctf_set_errno (fp, ECTF_NOTFUNC));
+    return (ctf_set_errno_signed (fp, ECTF_NOTFUNC));
 
   if ((dupname = strdup (name)) == NULL)
-    return (ctf_set_errno (fp, ENOMEM));
+    return (ctf_set_errno_signed (fp, ENOMEM));
 
   if (ctf_dynhash_insert (h, dupname, (void *) (uintptr_t) id) < 0)
     {
       free (dupname);
-      return (ctf_set_errno (fp, ENOMEM));
+      return (ctf_set_errno_signed (fp, ENOMEM));
     }
   return 0;
 }
diff --git a/libctf/ctf-dedup.c b/libctf/ctf-dedup.c
index 5fdddfd0b54..5ad15ffd67f 100644
--- a/libctf/ctf-dedup.c
+++ b/libctf/ctf-dedup.c
@@ -378,7 +378,7 @@  ctf_dedup_atoms_init (ctf_dict_t *fp)
       if ((fp->ctf_dedup_atoms_alloc
 	   = ctf_dynset_create (htab_hash_string, htab_eq_string,
 				free)) == NULL)
-	return ctf_set_errno (fp, ENOMEM);
+	return ctf_set_errno_signed (fp, ENOMEM);
     }
   fp->ctf_dedup_atoms = fp->ctf_dedup_atoms_alloc;
   return 0;
@@ -543,7 +543,7 @@  ctf_dedup_record_origin (ctf_dict_t *fp, int input_num, const char *decorated,
 
   if (populate_origin)
     if (ctf_dynhash_cinsert (d->cd_struct_origin, decorated, origin) < 0)
-      return ctf_set_errno (fp, errno);
+      return ctf_set_errno_signed (fp, errno);
   return 0;
 }
 
@@ -1185,7 +1185,7 @@  ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_,
     }
   else
     if (ctf_dynhash_cinsert (d->cd_output_mapping_guard, id, hval) < 0)
-      return ctf_set_errno (fp, errno);
+      return ctf_set_errno_signed (fp, errno);
 #endif
 
   /* Record the type in the output mapping: if this is the first time this type
@@ -1197,17 +1197,17 @@  ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_,
 				      hval)) == NULL)
     {
       if (ctf_dynhash_cinsert (d->cd_output_first_gid, hval, id) < 0)
-	return ctf_set_errno (fp, errno);
+	return ctf_set_errno_signed (fp, errno);
 
       if ((type_ids = ctf_dynset_create (htab_hash_pointer,
 					 htab_eq_pointer,
 					 NULL)) == NULL)
-	return ctf_set_errno (fp, errno);
+	return ctf_set_errno_signed (fp, errno);
       if (ctf_dynhash_insert (d->cd_output_mapping, (void *) hval,
 			      type_ids) < 0)
 	{
 	  ctf_dynset_destroy (type_ids);
-	  return ctf_set_errno (fp, errno);
+	  return ctf_set_errno_signed (fp, errno);
 	}
     }
 #ifdef ENABLE_LIBCTF_HASH_DEBUGGING
@@ -1248,7 +1248,7 @@  ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_,
 	    }
 	}
       if (err != ECTF_NEXT_END)
-	return ctf_set_errno (fp, err);
+	return ctf_set_errno_signed (fp, err);
     }
 #endif
 
@@ -1256,7 +1256,7 @@  ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_,
      don't waste time reinserting the same keys in that case.  */
   if (!ctf_dynset_exists (type_ids, id, NULL)
       && ctf_dynset_insert (type_ids, id) < 0)
-    return ctf_set_errno (fp, errno);
+    return ctf_set_errno_signed (fp, errno);
 
   /* The rest only needs to happen for types with names.  */
   if (!decorated_name)
@@ -1273,12 +1273,12 @@  ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_,
       if ((name_counts = ctf_dynhash_create (ctf_hash_string,
 					     ctf_hash_eq_string,
 					     NULL, NULL)) == NULL)
-	  return ctf_set_errno (fp, errno);
+	  return ctf_set_errno_signed (fp, errno);
       if (ctf_dynhash_cinsert (d->cd_name_counts, decorated_name,
 			       name_counts) < 0)
 	{
 	  ctf_dynhash_destroy (name_counts);
-	  return ctf_set_errno (fp, errno);
+	  return ctf_set_errno_signed (fp, errno);
 	}
     }
 
@@ -1287,7 +1287,7 @@  ctf_dedup_populate_mappings (ctf_dict_t *fp, ctf_dict_t *input _libctf_unused_,
 
   if (ctf_dynhash_cinsert (name_counts, hval,
 			   (const void *) (uintptr_t) (count + 1)) < 0)
-    return ctf_set_errno (fp, errno);
+    return ctf_set_errno_signed (fp, errno);
 
   return 0;
 }
@@ -1318,7 +1318,7 @@  ctf_dedup_mark_conflicting_hash (ctf_dict_t *fp, const char *hval)
   if (ctf_dynset_cinsert (d->cd_conflicting_types, hval) < 0)
     {
       ctf_dprintf ("Out of memory marking %s as conflicted\n", hval);
-      ctf_set_errno (fp, errno);
+      ctf_set_errno_signed (fp, errno);
       return -1;
     }
 
@@ -1340,7 +1340,7 @@  ctf_dedup_mark_conflicting_hash (ctf_dict_t *fp, const char *hval)
 	}
     }
   if (err != ECTF_NEXT_END)
-    return ctf_set_errno (fp, err);
+    return ctf_set_errno_signed (fp, err);
 
   return 0;
 }
@@ -1366,7 +1366,7 @@  ctf_dedup_hash_kind (ctf_dict_t *fp, ctf_dict_t **inputs, const char *hash)
   if (!type_ids)
     {
       ctf_dprintf ("Looked up type kind by nonexistent hash %s.\n", hash);
-      return ctf_set_errno (fp, ECTF_INTERNAL);
+      return ctf_set_errno_signed (fp, ECTF_INTERNAL);
     }
   id = ctf_dynset_lookup_any (type_ids);
   if (!ctf_assert (fp, id))
@@ -1585,7 +1585,7 @@  ctf_dedup_detect_name_ambiguity (ctf_dict_t *fp, ctf_dict_t **inputs)
 
  iterr:
   ctf_err_warn (fp, 0, err, _("iteration failed: %s"), gettext (whaterr));
-  return ctf_set_errno (fp, err);
+  return ctf_set_errno_signed (fp, err);
 
  assert_err:
   ctf_next_destroy (i);
@@ -1683,7 +1683,7 @@  ctf_dedup_init (ctf_dict_t *fp)
  oom:
   ctf_err_warn (fp, 0, ENOMEM, _("ctf_dedup_init: cannot initialize: "
 				 "out of memory"));
-  return ctf_set_errno (fp, ENOMEM);
+  return ctf_set_errno_signed (fp, ENOMEM);
 }
 
 /* No ctf_dedup calls are allowed after this call other than starting a new
@@ -1784,7 +1784,7 @@  ctf_dedup_multiple_input_dicts (ctf_dict_t *output, ctf_dict_t **inputs,
     {
       ctf_err_warn (output, 0, err, _("iteration error "
 				      "propagating conflictedness"));
-      return ctf_set_errno (output, err);
+      return ctf_set_errno_signed (output, err);
     }
 
   if (multiple)
@@ -1872,14 +1872,14 @@  ctf_dedup_conflictify_unshared (ctf_dict_t *output, ctf_dict_t **inputs)
   return 0;
 
  err_no:
-  ctf_set_errno (output, errno);
+  ctf_set_errno_signed (output, errno);
  err:
   err = ctf_errno (output);
   ctf_next_destroy (i);
  iterr:
   ctf_dynset_destroy (to_mark);
   ctf_err_warn (output, 0, err, _("conflictifying unshared types"));
-  return ctf_set_errno (output, err);
+  return ctf_set_errno_signed (output, err);
 }
 
 /* The core deduplicator.  Populate cd_output_mapping in the output ctf_dedup
@@ -1913,7 +1913,7 @@  ctf_dedup (ctf_dict_t *output, ctf_dict_t **inputs, uint32_t ninputs,
       if (ctf_dynhash_insert (d->cd_input_nums, inputs[i],
 			      (void *) (uintptr_t) i) < 0)
 	{
-	  ctf_set_errno (output, errno);
+	  ctf_set_errno_signed (output, errno);
 	  ctf_err_warn (output, 0, errno, _("ctf_dedup: cannot initialize: %s\n"),
 			ctf_errmsg (errno));
 	  goto err;
@@ -1950,7 +1950,7 @@  ctf_dedup (ctf_dict_t *output, ctf_dict_t **inputs, uint32_t ninputs,
 	}
       if (ctf_errno (inputs[i]) != ECTF_NEXT_END)
 	{
-	  ctf_set_errno (output, ctf_errno (inputs[i]));
+	  ctf_set_errno_signed (output, ctf_errno (inputs[i]));
 	  ctf_err_warn (output, 0, 0, _("iteration failure "
 					"computing type hashes"));
 	  goto err;
@@ -2176,7 +2176,7 @@  ctf_dedup_rwalk_one_output_mapping (ctf_dict_t *output,
 		    type_id, depth, arg);
 
  err_msg:
-  ctf_set_errno (output, ctf_errno (fp));
+  ctf_set_errno_signed (output, ctf_errno (fp));
   ctf_err_warn (output, 0, 0, _("%s in input file %s at type ID %lx"),
 		gettext (whaterr), ctf_link_input_name (fp), type);
  err:
@@ -2219,7 +2219,7 @@  ctf_dedup_rwalk_output_mapping (ctf_dict_t *output, ctf_dict_t **inputs,
     {
       ctf_err_warn (output, 0, ECTF_INTERNAL,
 		    _("looked up type kind by nonexistent hash %s"), hval);
-      return ctf_set_errno (output, ECTF_INTERNAL);
+      return ctf_set_errno_signed (output, ECTF_INTERNAL);
     }
 
   /* Have we seen this type before?  */
@@ -2237,7 +2237,7 @@  ctf_dedup_rwalk_output_mapping (ctf_dict_t *output, ctf_dict_t **inputs,
 	{
 	  ctf_err_warn (output, 0, ENOMEM,
 			_("out of memory tracking already-visited types"));
-	  return ctf_set_errno (output, ENOMEM);
+	  return ctf_set_errno_signed (output, ENOMEM);
 	}
     }
 
@@ -2274,7 +2274,7 @@  ctf_dedup_rwalk_output_mapping (ctf_dict_t *output, ctf_dict_t **inputs,
   if (err != ECTF_NEXT_END)
     {
       ctf_err_warn (output, 0, err, _("cannot walk conflicted type"));
-      return ctf_set_errno (output, err);
+      return ctf_set_errno_signed (output, err);
     }
 
   return 0;
@@ -2376,7 +2376,7 @@  ctf_dedup_walk_output_mapping (ctf_dict_t *output, ctf_dict_t **inputs,
   if ((already_visited = ctf_dynset_create (htab_hash_string,
 					    htab_eq_string,
 					    NULL)) == NULL)
-    return ctf_set_errno (output, ENOMEM);
+    return ctf_set_errno_signed (output, ENOMEM);
 
   sort_arg.inputs = inputs;
   sort_arg.ninputs = ninputs;
@@ -2400,7 +2400,7 @@  ctf_dedup_walk_output_mapping (ctf_dict_t *output, ctf_dict_t **inputs,
   if (err != ECTF_NEXT_END)
     {
       ctf_err_warn (output, 0, err, _("cannot recurse over output mapping"));
-      ctf_set_errno (output, err);
+      ctf_set_errno_signed (output, err);
       goto err;
     }
   ctf_dynset_destroy (already_visited);
@@ -2664,7 +2664,7 @@  ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
 	      ctf_err_warn (output, 0, err,
 			    _("cannot create per-CU CTF archive for CU %s"),
 			    ctf_link_input_name (input));
-	      return ctf_set_errno (output, err);
+	      return ctf_set_errno_signed (output, err);
 	    }
 
 	  ctf_import_unref (target, output);
@@ -2687,7 +2687,7 @@  ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
       ctf_err_warn (output, 0, ctf_errno (input),
 		    _("%s: lookup failure for type %lx"),
 		    ctf_link_input_name (real_input), type);
-      return ctf_set_errno (output, ctf_errno (input));
+      return ctf_set_errno_signed (output, ctf_errno (input));
     }
 
   name = ctf_strraw (real_input, tp->ctt_name);
@@ -2765,7 +2765,7 @@  ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
 			      ctf_link_input_name (input), input_num, name,
 			      type);
 		ctf_next_destroy (i);
-		return ctf_set_errno (output, ctf_errno (target));
+		return ctf_set_errno_signed (output, ctf_errno (target));
 	      }
 	  }
 	if (ctf_errno (input) != ECTF_NEXT_END)
@@ -2860,7 +2860,7 @@  ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
 
 	if ((args = calloc (fi.ctc_argc, sizeof (ctf_id_t))) == NULL)
 	  {
-	    ctf_set_errno (input, ENOMEM);
+	    ctf_set_errno_signed (input, ENOMEM);
 	    goto err_input;
 	  }
 
@@ -2912,7 +2912,7 @@  ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
 	/* Record the need to emit the members of this structure later.  */
 	if (ctf_dynhash_insert (d->cd_emission_struct_members, id, out_id) < 0)
 	  {
-	    ctf_set_errno (target, errno);
+	    ctf_set_errno_signed (target, errno);
 	    goto err_target;
 	  }
 	break;
@@ -2921,7 +2921,7 @@  ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
       ctf_err_warn (output, 0, ECTF_CORRUPT, _("%s: unknown type kind for "
 					       "input type %lx"),
 		    ctf_link_input_name (input), type);
-      return ctf_set_errno (output, ECTF_CORRUPT);
+      return ctf_set_errno_signed (output, ECTF_CORRUPT);
     }
 
   if (!emission_hashed
@@ -2931,7 +2931,7 @@  ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
     {
       ctf_err_warn (output, 0, ENOMEM, _("out of memory tracking deduplicated "
 					 "global type IDs"));
-	return ctf_set_errno (output, ENOMEM);
+	return ctf_set_errno_signed (output, ENOMEM);
     }
 
   if (!emission_hashed && new_type != 0)
@@ -2944,21 +2944,21 @@  ctf_dedup_emit_type (const char *hval, ctf_dict_t *output, ctf_dict_t **inputs,
  oom_hash:
   ctf_err_warn (output, 0, ENOMEM, _("out of memory creating emission-tracking "
 				     "hashes"));
-  return ctf_set_errno (output, ENOMEM);
+  return ctf_set_errno_signed (output, ENOMEM);
 
  err_input:
   ctf_err_warn (output, 0, ctf_errno (input),
 		_("%s (%i): while emitting deduplicated %s, error getting "
 		  "input type %lx"), ctf_link_input_name (input),
 		input_num, errtype, type);
-  return ctf_set_errno (output, ctf_errno (input));
+  return ctf_set_errno_signed (output, ctf_errno (input));
  err_target:
   ctf_err_warn (output, 0, ctf_errno (target),
 		_("%s (%i): while emitting deduplicated %s, error emitting "
 		  "target type from input type %lx"),
 		ctf_link_input_name (input), input_num,
 		errtype, type);
-  return ctf_set_errno (output, ctf_errno (target));
+  return ctf_set_errno_signed (output, ctf_errno (target));
 }
 
 /* Traverse the cd_emission_struct_members and emit the members of all
@@ -3051,11 +3051,11 @@  ctf_dedup_emit_struct_members (ctf_dict_t *output, ctf_dict_t **inputs,
   ctf_err_warn (output, 0, ctf_errno (err_fp),
 		_("%s (%i): error emitting members for structure type %lx"),
 		ctf_link_input_name (input_fp), input_num, err_type);
-  return ctf_set_errno (output, ctf_errno (err_fp));
+  return ctf_set_errno_signed (output, ctf_errno (err_fp));
  iterr:
   ctf_err_warn (output, 0, err, _("iteration failure emitting "
 				  "structure members"));
-  return ctf_set_errno (output, err);
+  return ctf_set_errno_signed (output, err);
 }
 
 /* Emit deduplicated types into the outputs.  The shared type repository is
diff --git a/libctf/ctf-dump.c b/libctf/ctf-dump.c
index 686951a9869..28b38c1d765 100644
--- a/libctf/ctf-dump.c
+++ b/libctf/ctf-dump.c
@@ -56,7 +56,7 @@  ctf_dump_append (ctf_dump_state_t *state, char *str)
   ctf_dump_item_t *cdi;
 
   if ((cdi = malloc (sizeof (struct ctf_dump_item))) == NULL)
-    return (ctf_set_errno (state->cds_fp, ENOMEM));
+    return (ctf_set_errno_signed (state->cds_fp, ENOMEM));
 
   cdi->cdi_item = str;
   ctf_list_append (&state->cds_items, cdi);
@@ -261,7 +261,7 @@  ctf_dump_header_strfield (ctf_dict_t *fp, ctf_dump_state_t *state,
   return 0;
 
  err:
-  return (ctf_set_errno (fp, errno));
+  return (ctf_set_errno_signed (fp, errno));
 }
 
 /* Dump one section-offset field from the file header into the cds_items.  */
@@ -281,7 +281,7 @@  ctf_dump_header_sectfield (ctf_dict_t *fp, ctf_dump_state_t *state,
   return 0;
 
  err:
-  return (ctf_set_errno (fp, errno));
+  return (ctf_set_errno_signed (fp, errno));
 }
 
 /* Dump the file header into the cds_items.  */
@@ -398,7 +398,7 @@  ctf_dump_header (ctf_dict_t *fp, ctf_dump_state_t *state)
   return 0;
  err:
   free (flagstr);
-  return (ctf_set_errno (fp, errno));
+  return (ctf_set_errno_signed (fp, errno));
 }
 
 /* Dump a single label into the cds_items.  */
@@ -412,7 +412,7 @@  ctf_dump_label (const char *name, const ctf_lblinfo_t *info,
   ctf_dump_state_t *state = arg;
 
   if (asprintf (&str, "%s -> ", name) < 0)
-    return (ctf_set_errno (state->cds_fp, errno));
+    return (ctf_set_errno_signed (state->cds_fp, errno));
 
   if ((typestr = ctf_dump_format_type (state->cds_fp, info->ctb_type,
 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
@@ -471,7 +471,7 @@  ctf_dump_objts (ctf_dict_t *fp, ctf_dump_state_t *state, int functions)
       continue;
 
     oom:
-      ctf_set_errno (fp, ENOMEM);
+      ctf_set_errno_signed (fp, ENOMEM);
       ctf_next_destroy (i);
       return -1;
     }
@@ -487,7 +487,7 @@  ctf_dump_var (const char *name, ctf_id_t type, void *arg)
   ctf_dump_state_t *state = arg;
 
   if (asprintf (&str, "%s -> ", name) < 0)
-    return (ctf_set_errno (state->cds_fp, errno));
+    return (ctf_set_errno_signed (state->cds_fp, errno));
 
   if ((typestr = ctf_dump_format_type (state->cds_fp, type,
 				       CTF_ADD_ROOT | CTF_FT_REFS)) == NULL)
@@ -540,7 +540,7 @@  ctf_dump_member (const char *name, ctf_id_t id, unsigned long offset,
  oom:
   free (typestr);
   free (bit);
-  return (ctf_set_errno (state->cdm_fp, errno));
+  return (ctf_set_errno_signed (state->cdm_fp, errno));
 }
 
 /* Report the number of digits in the hexadecimal representation of a type
@@ -569,7 +569,7 @@  ctf_dump_type (ctf_id_t id, int flag, void *arg)
 
   /* Indent neatly.  */
   if (asprintf (&indent, "    %*s", type_hex_digits (id), "") < 0)
-    return (ctf_set_errno (state->cds_fp, ENOMEM));
+    return (ctf_set_errno_signed (state->cds_fp, ENOMEM));
 
   /* Dump the type itself.  */
   if ((str = ctf_dump_format_type (state->cds_fp, id,
@@ -654,7 +654,7 @@  ctf_dump_type (ctf_id_t id, int flag, void *arg)
  oom:
   free (indent);
   free (str);
-  return ctf_set_errno (state->cds_fp, ENOMEM);
+  return ctf_set_errno_signed (state->cds_fp, ENOMEM);
 }
 
 /* Dump the string table into the cds_items.  */
@@ -671,7 +671,7 @@  ctf_dump_str (ctf_dict_t *fp, ctf_dump_state_t *state)
       if (asprintf (&str, "0x%lx: %s",
 		    (unsigned long) (s - fp->ctf_str[CTF_STRTAB_0].cts_strs),
 		    s) < 0)
-	return (ctf_set_errno (fp, errno));
+	return (ctf_set_errno_signed (fp, errno));
       ctf_dump_append (state, str);
       s += strlen (s) + 1;
     }
diff --git a/libctf/ctf-impl.h b/libctf/ctf-impl.h
index da687762c89..ff3d2bf1f01 100644
--- a/libctf/ctf-impl.h
+++ b/libctf/ctf-impl.h
@@ -742,6 +742,7 @@  extern void ctf_arc_close_internal (struct ctf_archive *);
 extern const ctf_preamble_t *ctf_arc_bufpreamble (const ctf_sect_t *);
 extern void *ctf_set_open_errno (int *, int);
 extern unsigned long ctf_set_errno (ctf_dict_t *, int);
+extern int ctf_set_errno_signed (ctf_dict_t *, int);
 extern void ctf_flip_header (ctf_header_t *);
 extern int ctf_flip (ctf_dict_t *, ctf_header_t *, unsigned char *, int);
 
diff --git a/libctf/ctf-labels.c b/libctf/ctf-labels.c
index 16b111b14df..20d007d36d0 100644
--- a/libctf/ctf-labels.c
+++ b/libctf/ctf-labels.c
@@ -74,7 +74,7 @@  ctf_label_iter (ctf_dict_t *fp, ctf_label_f *func, void *arg)
     return -1;			/* errno is set for us.  */
 
   if (num_labels == 0)
-    return (ctf_set_errno (fp, ECTF_NOLABELDATA));
+    return (ctf_set_errno_signed (fp, ECTF_NOLABELDATA));
 
   for (i = 0; i < num_labels; i++, ctlp++)
     {
@@ -84,7 +84,7 @@  ctf_label_iter (ctf_dict_t *fp, ctf_label_f *func, void *arg)
 	  ctf_err_warn (fp, 0, ECTF_CORRUPT,
 			"failed to decode label %u with type %u",
 			ctlp->ctl_label, ctlp->ctl_type);
-	  return (ctf_set_errno (fp, ECTF_CORRUPT));
+	  return (ctf_set_errno_signed (fp, ECTF_CORRUPT));
 	}
 
       linfo.ctb_type = ctlp->ctl_type;
@@ -134,7 +134,7 @@  ctf_label_info (ctf_dict_t *fp, const char *lname, ctf_lblinfo_t *linfo)
     return rc;
 
   if (rc != 1)
-    return (ctf_set_errno (fp, ECTF_NOLABEL));
+    return (ctf_set_errno_signed (fp, ECTF_NOLABEL));
 
   return 0;
 }
diff --git a/libctf/ctf-link.c b/libctf/ctf-link.c
index 9babec2aa37..33c8cc0cdcc 100644
--- a/libctf/ctf-link.c
+++ b/libctf/ctf-link.c
@@ -142,7 +142,7 @@  ctf_link_add_ctf_internal (ctf_dict_t *fp, ctf_archive_t *ctf,
  oom1:
   free (filename);
  oom:
-  return ctf_set_errno (fp, ENOMEM);
+  return ctf_set_errno_signed (fp, ENOMEM);
 }
 
 /* Add a file, memory buffer, or unopened file (by name) to a link.
@@ -173,12 +173,12 @@  ctf_link_add (ctf_dict_t *fp, ctf_archive_t *ctf, const char *name,
 	      void *buf _libctf_unused_, size_t n _libctf_unused_)
 {
   if (buf)
-    return (ctf_set_errno (fp, ECTF_NOTYET));
+    return (ctf_set_errno_signed (fp, ECTF_NOTYET));
 
   if (!((ctf && name && !buf)
 	|| (name && !buf && !ctf)
 	|| (buf && name && !ctf)))
-    return (ctf_set_errno (fp, EINVAL));
+    return (ctf_set_errno_signed (fp, EINVAL));
 
   /* We can only lazily open files if libctf.so is in use rather than
      libctf-nobfd.so.  This is a little tricky: in shared libraries, we can use
@@ -187,21 +187,21 @@  ctf_link_add (ctf_dict_t *fp, ctf_archive_t *ctf, const char *name,
 
 #if defined (PIC)
   if (!buf && !ctf && name && !ctf_open)
-    return (ctf_set_errno (fp, ECTF_NEEDSBFD));
+    return (ctf_set_errno_signed (fp, ECTF_NEEDSBFD));
 #elif NOBFD
   if (!buf && !ctf && name)
-    return (ctf_set_errno (fp, ECTF_NEEDSBFD));
+    return (ctf_set_errno_signed (fp, ECTF_NEEDSBFD));
 #endif
 
   if (fp->ctf_link_outputs)
-    return (ctf_set_errno (fp, ECTF_LINKADDEDLATE));
+    return (ctf_set_errno_signed (fp, ECTF_LINKADDEDLATE));
   if (fp->ctf_link_inputs == NULL)
     fp->ctf_link_inputs = ctf_dynhash_create (ctf_hash_string,
 					      ctf_hash_eq_string, free,
 					      ctf_link_input_close);
 
   if (fp->ctf_link_inputs == NULL)
-    return (ctf_set_errno (fp, ENOMEM));
+    return (ctf_set_errno_signed (fp, ENOMEM));
 
   return ctf_link_add_ctf_internal (fp, ctf, NULL, name);
 }
@@ -243,7 +243,7 @@  ctf_link_lazy_open (ctf_dict_t *fp, ctf_link_input_t *input)
 #else
   ctf_err_warn (fp, 0, ECTF_NEEDSBFD, _("cannot open %s lazily"),
 		input->clin_filename);
-  ctf_set_errno (fp, ECTF_NEEDSBFD);
+  ctf_set_errno_signed (fp, ECTF_NEEDSBFD);
   return -1;
 #endif
 
@@ -257,7 +257,7 @@  ctf_link_lazy_open (ctf_dict_t *fp, ctf_link_input_t *input)
 
       ctf_err_warn (fp, 0, err, _("opening CTF %s failed"),
 		    input->clin_filename);
-      ctf_set_errno (fp, err);
+      ctf_set_errno_signed (fp, err);
       return -1;
     }
 
@@ -378,7 +378,7 @@  ctf_link_add_cu_mapping (ctf_dict_t *fp, const char *from, const char *to)
 
   /* Mappings cannot be set up if per-CU output dicts already exist.  */
   if (fp->ctf_link_outputs && ctf_dynhash_elements (fp->ctf_link_outputs) != 0)
-      return (ctf_set_errno (fp, ECTF_LINKADDEDLATE));
+      return (ctf_set_errno_signed (fp, ECTF_LINKADDEDLATE));
 
   if (fp->ctf_link_in_cu_mapping == NULL)
     fp->ctf_link_in_cu_mapping = ctf_dynhash_create (ctf_hash_string,
@@ -407,7 +407,7 @@  ctf_link_add_cu_mapping (ctf_dict_t *fp, const char *from, const char *to)
 
   if ((err = ctf_dynhash_insert (fp->ctf_link_in_cu_mapping, f, t)) < 0)
     {
-      ctf_set_errno (fp, err);
+      ctf_set_errno_signed (fp, err);
       goto oom_noerrno;
     }
 
@@ -426,7 +426,7 @@  ctf_link_add_cu_mapping (ctf_dict_t *fp, const char *from, const char *to)
 				     t, one_out)) < 0)
 	{
 	  ctf_dynhash_destroy (one_out);
-	  ctf_set_errno (fp, err);
+	  ctf_set_errno_signed (fp, err);
 	  goto oom_noerrno;
 	}
     }
@@ -438,14 +438,14 @@  ctf_link_add_cu_mapping (ctf_dict_t *fp, const char *from, const char *to)
 
   if (ctf_dynhash_insert (one_out, f, NULL) < 0)
     {
-      ctf_set_errno (fp, err);
+      ctf_set_errno_signed (fp, err);
       goto oom_noerrno;
     }
 
   return 0;
 
  oom:
-  ctf_set_errno (fp, errno);
+  ctf_set_errno_signed (fp, errno);
  oom_noerrno:
   free (f);
   free (t);
@@ -582,7 +582,7 @@  ctf_link_one_variable (ctf_dict_t *fp, ctf_dict_t *in_fp, const char *name,
 
   if (check_variable (name, per_cu_out_fp, dst_type, &dvd))
     if (ctf_add_variable (per_cu_out_fp, name, dst_type) < 0)
-      return (ctf_set_errno (fp, ctf_errno (per_cu_out_fp)));
+      return (ctf_set_errno_signed (fp, ctf_errno (per_cu_out_fp)));
   return 0;
 }
 
@@ -680,7 +680,7 @@  ctf_link_deduplicating_count_inputs (ctf_dict_t *fp, ctf_dynhash_t *cu_names,
     {
       ctf_err_warn (fp, 0, err, _("iteration error counting deduplicating "
 				  "CTF link inputs"));
-      ctf_set_errno (fp, err);
+      ctf_set_errno_signed (fp, err);
       return -1;
     }
 
@@ -877,7 +877,7 @@  ctf_link_deduplicating_close_inputs (ctf_dict_t *fp, ctf_dynhash_t *cu_names,
 	{
 	  ctf_err_warn (fp, 0, err, _("iteration error in deduplicating link "
 				      "input freeing"));
-	  ctf_set_errno (fp, err);
+	  ctf_set_errno_signed (fp, err);
 	}
     }
   else
@@ -915,7 +915,7 @@  ctf_link_deduplicating_variables (ctf_dict_t *fp, ctf_dict_t **inputs,
 	    }
 	}
       if (ctf_errno (inputs[i]) != ECTF_NEXT_END)
-	return ctf_set_errno (fp, ctf_errno (inputs[i]));
+	return ctf_set_errno_signed (fp, ctf_errno (inputs[i]));
 
       /* Next the symbols.  We integrate data symbols even though the compiler
 	 is currently doing the same, to allow the compiler to stop in
@@ -930,7 +930,7 @@  ctf_link_deduplicating_variables (ctf_dict_t *fp, ctf_dict_t **inputs,
 	    }
 	}
       if (ctf_errno (inputs[i]) != ECTF_NEXT_END)
-	return ctf_set_errno (fp, ctf_errno (inputs[i]));
+	return ctf_set_errno_signed (fp, ctf_errno (inputs[i]));
 
       /* Finally the function symbols.  */
 
@@ -943,7 +943,7 @@  ctf_link_deduplicating_variables (ctf_dict_t *fp, ctf_dict_t **inputs,
 	    }
 	}
       if (ctf_errno (inputs[i]) != ECTF_NEXT_END)
-	return ctf_set_errno (fp, ctf_errno (inputs[i]));
+	return ctf_set_errno_signed (fp, ctf_errno (inputs[i]));
     }
   return 0;
 }
@@ -1070,12 +1070,12 @@  ctf_link_deduplicating_one_symtypetab (ctf_dict_t *fp, ctf_dict_t *input,
 			_("symbol %s in input file %s found conflicting "
 			  "even when trying in per-CU dict."), name,
 			ctf_unnamed_cuname (input));
-	  return (ctf_set_errno (fp, ECTF_DUPLICATE));
+	  return (ctf_set_errno_signed (fp, ECTF_DUPLICATE));
 	}
     }
   if (ctf_errno (input) != ECTF_NEXT_END)
     {
-      ctf_set_errno (fp, ctf_errno (input));
+      ctf_set_errno_signed (fp, ctf_errno (input));
       ctf_err_warn (fp, 0, ctf_errno (input),
 		    functions ? _("iterating over function symbols") :
 		    _("iterating over data symbols"));
@@ -1147,7 +1147,7 @@  ctf_link_deduplicating_per_cu (ctf_dict_t *fp)
 	{
 	  ctf_err_warn (fp, 0, EFBIG, _("too many inputs in deduplicating "
 					"link: %li"), (long int) ninputs);
-	  ctf_set_errno (fp, EFBIG);
+	  ctf_set_errno_signed (fp, EFBIG);
 	  goto err_open_inputs;
 	}
 
@@ -1172,7 +1172,7 @@  ctf_link_deduplicating_per_cu (ctf_dict_t *fp)
 	      ctf_err_warn (fp, 0, err, _("cannot open archive %s in "
 					  "CU-mapped CTF link"),
 			    only_input->clin_filename);
-	      ctf_set_errno (fp, err);
+	      ctf_set_errno_signed (fp, err);
 	      goto err_open_inputs;
 	    }
 	  ctf_next_destroy (ai);
@@ -1218,7 +1218,7 @@  ctf_link_deduplicating_per_cu (ctf_dict_t *fp)
 	  ctf_err_warn (fp, 0, err, _("cannot create per-CU CTF archive "
 				      "for %s"),
 			out_name);
-	  ctf_set_errno (fp, err);
+	  ctf_set_errno_signed (fp, err);
 	  goto err_inputs;
 	}
 
@@ -1233,7 +1233,7 @@  ctf_link_deduplicating_per_cu (ctf_dict_t *fp)
 
       if (ctf_dedup (out, inputs, ninputs, parents, 1) < 0)
 	{
-	  ctf_set_errno (fp, ctf_errno (out));
+	  ctf_set_errno_signed (fp, ctf_errno (out));
 	  ctf_err_warn (fp, 0, 0, _("CU-mapped deduplication failed for %s"),
 			out_name);
 	  goto err_inputs;
@@ -1242,7 +1242,7 @@  ctf_link_deduplicating_per_cu (ctf_dict_t *fp)
       if ((outputs = ctf_dedup_emit (out, inputs, ninputs, parents,
 				     &noutputs, 1)) == NULL)
 	{
-	  ctf_set_errno (fp, ctf_errno (out));
+	  ctf_set_errno_signed (fp, ctf_errno (out));
 	  ctf_err_warn (fp, 0, 0, _("CU-mapped deduplicating link type emission "
 				     "failed for %s"), out_name);
 	  goto err_inputs;
@@ -1258,7 +1258,7 @@  ctf_link_deduplicating_per_cu (ctf_dict_t *fp)
       if (!(fp->ctf_link_flags & CTF_LINK_OMIT_VARIABLES_SECTION)
 	  && ctf_link_deduplicating_variables (out, inputs, ninputs, 1) < 0)
 	{
-	  ctf_set_errno (fp, ctf_errno (out));
+	  ctf_set_errno_signed (fp, ctf_errno (out));
 	  ctf_err_warn (fp, 0, 0, _("CU-mapped deduplicating link variable "
 				    "emission failed for %s"), out_name);
 	  goto err_inputs_outputs;
@@ -1291,7 +1291,7 @@  ctf_link_deduplicating_per_cu (ctf_dict_t *fp)
       if ((in_arc = ctf_new_archive_internal (0, 0, NULL, outputs[0], NULL,
 					      NULL, &err)) == NULL)
 	{
-	  ctf_set_errno (fp, err);
+	  ctf_set_errno_signed (fp, err);
 	  goto err_outputs;
 	}
 
@@ -1330,7 +1330,7 @@  ctf_link_deduplicating_per_cu (ctf_dict_t *fp)
     {
       ctf_err_warn (fp, 0, err, _("iteration error in CU-mapped deduplicating "
 				  "link"));
-      return ctf_set_errno (fp, err);
+      return ctf_set_errno_signed (fp, err);
     }
 
   return 0;
@@ -1355,7 +1355,7 @@  ctf_link_empty_outputs (ctf_dict_t *fp)
     {
       fp->ctf_flags &= ~LCTF_LINKING;
       ctf_err_warn (fp, 1, err, _("iteration error removing old outputs"));
-      ctf_set_errno (fp, err);
+      ctf_set_errno_signed (fp, err);
       return -1;
     }
   return 0;
@@ -1503,7 +1503,7 @@  ctf_link (ctf_dict_t *fp, int flags)
 					       ctf_dict_close);
 
   if (fp->ctf_link_outputs == NULL)
-    return ctf_set_errno (fp, ENOMEM);
+    return ctf_set_errno_signed (fp, ENOMEM);
 
   fp->ctf_flags |= LCTF_LINKING;
   ctf_link_deduplicating (fp);
@@ -1536,7 +1536,7 @@  ctf_link (ctf_dict_t *fp, int flags)
 	{
 	  fp->ctf_flags &= ~LCTF_LINKING;
 	  ctf_err_warn (fp, 1, err, _("iteration error creating empty CUs"));
-	  ctf_set_errno (fp, err);
+	  ctf_set_errno_signed (fp, err);
 	  return -1;
 	}
     }
@@ -1593,7 +1593,7 @@  ctf_link_add_strtab (ctf_dict_t *fp, ctf_link_strtab_string_f *add_string,
     }
 
   if (err)
-    ctf_set_errno (fp, err);
+    ctf_set_errno_signed (fp, err);
 
   return -err;
 }
@@ -1635,7 +1635,7 @@  ctf_link_add_linker_symbol (ctf_dict_t *fp, ctf_link_sym_t *sym)
  oom:
   ctf_dynhash_destroy (fp->ctf_dynsyms);
   fp->ctf_dynsyms = NULL;
-  ctf_set_errno (fp, ENOMEM);
+  ctf_set_errno_signed (fp, ENOMEM);
   return -ENOMEM;
 }
 
@@ -1658,7 +1658,7 @@  ctf_link_shuffle_syms (ctf_dict_t *fp)
 					    NULL, free);
       if (!fp->ctf_dynsyms)
 	{
-	  ctf_set_errno (fp, ENOMEM);
+	  ctf_set_errno_signed (fp, ENOMEM);
 	  return -ENOMEM;
 	}
     }
@@ -1758,7 +1758,7 @@  ctf_link_shuffle_syms (ctf_dict_t *fp)
   free (fp->ctf_dynsymidx);
   fp->ctf_dynsymidx = NULL;
   fp->ctf_dynsymmax = 0;
-  ctf_set_errno (fp, err);
+  ctf_set_errno_signed (fp, err);
   return -err;
 }
 
diff --git a/libctf/ctf-lookup.c b/libctf/ctf-lookup.c
index c65849118cb..61cde7a041f 100644
--- a/libctf/ctf-lookup.c
+++ b/libctf/ctf-lookup.c
@@ -30,7 +30,7 @@  grow_pptrtab (ctf_dict_t *fp, size_t new_len)
 
   if ((new_pptrtab = realloc (fp->ctf_pptrtab, sizeof (uint32_t)
 			      * new_len)) == NULL)
-    return (ctf_set_errno (fp, ENOMEM));
+    return (ctf_set_errno_signed (fp, ENOMEM));
 
   fp->ctf_pptrtab = new_pptrtab;
 
@@ -1046,7 +1046,7 @@  ctf_func_info (ctf_dict_t *fp, unsigned long symidx, ctf_funcinfo_t *fip)
     return -1;					/* errno is set for us.  */
 
   if (ctf_type_kind (fp, type) != CTF_K_FUNCTION)
-    return (ctf_set_errno (fp, ECTF_NOTFUNC));
+    return (ctf_set_errno_signed (fp, ECTF_NOTFUNC));
 
   return ctf_func_type_info (fp, type, fip);
 }
@@ -1064,7 +1064,7 @@  ctf_func_args (ctf_dict_t *fp, unsigned long symidx, uint32_t argc,
     return -1;					/* errno is set for us.  */
 
   if (ctf_type_kind (fp, type) != CTF_K_FUNCTION)
-    return (ctf_set_errno (fp, ECTF_NOTFUNC));
+    return (ctf_set_errno_signed (fp, ECTF_NOTFUNC));
 
   return ctf_func_type_args (fp, type, argc, argv);
 }
diff --git a/libctf/ctf-open.c b/libctf/ctf-open.c
index 35f635b6559..4c60d62f648 100644
--- a/libctf/ctf-open.c
+++ b/libctf/ctf-open.c
@@ -159,7 +159,7 @@  get_vbytes_common (ctf_dict_t *fp, unsigned short kind,
     case CTF_K_RESTRICT:
       return 0;
     default:
-      ctf_set_errno (fp, ECTF_CORRUPT);
+      ctf_set_errno_signed (fp, ECTF_CORRUPT);
       ctf_err_warn (fp, 0, 0, _("detected invalid CTF kind: %x"), kind);
       return -1;
     }
@@ -1935,7 +1935,7 @@  ctf_parent_name_set (ctf_dict_t *fp, const char *name)
     free (fp->ctf_dynparname);
 
   if ((fp->ctf_dynparname = strdup (name)) == NULL)
-    return (ctf_set_errno (fp, ENOMEM));
+    return (ctf_set_errno_signed (fp, ENOMEM));
   fp->ctf_parname = fp->ctf_dynparname;
   return 0;
 }
@@ -1956,7 +1956,7 @@  ctf_cuname_set (ctf_dict_t *fp, const char *name)
     free (fp->ctf_dyncuname);
 
   if ((fp->ctf_dyncuname = strdup (name)) == NULL)
-    return (ctf_set_errno (fp, ENOMEM));
+    return (ctf_set_errno_signed (fp, ENOMEM));
   fp->ctf_cuname = fp->ctf_dyncuname;
   return 0;
 }
@@ -1969,10 +1969,10 @@  int
 ctf_import (ctf_dict_t *fp, ctf_dict_t *pfp)
 {
   if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0))
-    return (ctf_set_errno (fp, EINVAL));
+    return (ctf_set_errno_signed (fp, EINVAL));
 
   if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel)
-    return (ctf_set_errno (fp, ECTF_DMODEL));
+    return (ctf_set_errno_signed (fp, ECTF_DMODEL));
 
   if (fp->ctf_parent && !fp->ctf_parent_unreffed)
     ctf_dict_close (fp->ctf_parent);
@@ -2008,10 +2008,10 @@  int
 ctf_import_unref (ctf_dict_t *fp, ctf_dict_t *pfp)
 {
   if (fp == NULL || fp == pfp || (pfp != NULL && pfp->ctf_refcnt == 0))
-    return (ctf_set_errno (fp, EINVAL));
+    return (ctf_set_errno_signed (fp, EINVAL));
 
   if (pfp != NULL && pfp->ctf_dmodel != fp->ctf_dmodel)
-    return (ctf_set_errno (fp, ECTF_DMODEL));
+    return (ctf_set_errno_signed (fp, ECTF_DMODEL));
 
   if (fp->ctf_parent && !fp->ctf_parent_unreffed)
     ctf_dict_close (fp->ctf_parent);
@@ -2052,7 +2052,7 @@  ctf_setmodel (ctf_dict_t *fp, int model)
 	}
     }
 
-  return (ctf_set_errno (fp, EINVAL));
+  return (ctf_set_errno_signed (fp, EINVAL));
 }
 
 /* Return the data model constant for the CTF dict.  */
diff --git a/libctf/ctf-serialize.c b/libctf/ctf-serialize.c
index ba830a2b095..e861ca0fcaf 100644
--- a/libctf/ctf-serialize.c
+++ b/libctf/ctf-serialize.c
@@ -123,7 +123,7 @@  symtypetab_density (ctf_dict_t *fp, ctf_dict_t *symfp, ctf_dynhash_t *symhash,
 
       if ((linker_known = ctf_dynhash_create (ctf_hash_string, ctf_hash_eq_string,
 					      NULL, NULL)) == NULL)
-	return (ctf_set_errno (fp, ENOMEM));
+	return (ctf_set_errno_signed (fp, ENOMEM));
 
       while ((err = ctf_dynhash_cnext (symfp->ctf_dynsyms, &i,
 				       &name, &ctf_sym)) == 0)
@@ -147,7 +147,7 @@  symtypetab_density (ctf_dict_t *fp, ctf_dict_t *symfp, ctf_dynhash_t *symhash,
 	  if (ctf_dynhash_cinsert (linker_known, name, ctf_sym) < 0)
 	    {
 	      ctf_dynhash_destroy (linker_known);
-	      return (ctf_set_errno (fp, ENOMEM));
+	      return (ctf_set_errno_signed (fp, ENOMEM));
 	    }
 	}
       if (err != ECTF_NEXT_END)
@@ -155,7 +155,7 @@  symtypetab_density (ctf_dict_t *fp, ctf_dict_t *symfp, ctf_dynhash_t *symhash,
 	  ctf_err_warn (fp, 0, err, _("iterating over linker-known symbols during "
 				  "serialization"));
 	  ctf_dynhash_destroy (linker_known);
-	  return (ctf_set_errno (fp, err));
+	  return (ctf_set_errno_signed (fp, err));
 	}
     }
 
@@ -219,7 +219,7 @@  symtypetab_density (ctf_dict_t *fp, ctf_dict_t *symfp, ctf_dynhash_t *symhash,
       ctf_err_warn (fp, 0, err, _("iterating over CTF symtypetab during "
 				  "serialization"));
       ctf_dynhash_destroy (linker_known);
-      return (ctf_set_errno (fp, err));
+      return (ctf_set_errno_signed (fp, err));
     }
 
   if (!(flags & CTF_SYMTYPETAB_FORCE_INDEXED))
@@ -236,7 +236,7 @@  symtypetab_density (ctf_dict_t *fp, ctf_dict_t *symfp, ctf_dynhash_t *symhash,
 	  ctf_err_warn (fp, 0, err, _("iterating over linker-known symbols "
 				      "during CTF serialization"));
 	  ctf_dynhash_destroy (linker_known);
-	  return (ctf_set_errno (fp, err));
+	  return (ctf_set_errno_signed (fp, err));
 	}
     }
 
@@ -709,7 +709,7 @@  ctf_emit_symtypetab_sects (ctf_dict_t *fp, emit_symtypetab_state_t *s,
   return 0;
 
  oom:
-  ctf_set_errno (fp, EAGAIN);
+  ctf_set_errno_signed (fp, EAGAIN);
   goto err;
 symerr:
   ctf_err_warn (fp, 0, err, _("error serializing symtypetabs"));
@@ -970,7 +970,7 @@  ctf_serialize (ctf_dict_t *fp)
   memset (&symstate, 0, sizeof (emit_symtypetab_state_t));
 
   if (!(fp->ctf_flags & LCTF_RDWR))
-    return (ctf_set_errno (fp, ECTF_RDONLY));
+    return (ctf_set_errno_signed (fp, ECTF_RDONLY));
 
   /* Update required?  */
   if (!(fp->ctf_flags & LCTF_DIRTY))
@@ -1026,7 +1026,7 @@  ctf_serialize (ctf_dict_t *fp)
   buf_size = sizeof (ctf_header_t) + hdr.cth_stroff + hdr.cth_strlen;
 
   if ((buf = malloc (buf_size)) == NULL)
-    return (ctf_set_errno (fp, EAGAIN));
+    return (ctf_set_errno_signed (fp, EAGAIN));
 
   memcpy (buf, &hdr, sizeof (ctf_header_t));
   t = (unsigned char *) buf + sizeof (ctf_header_t) + hdr.cth_objtoff;
@@ -1106,7 +1106,7 @@  ctf_serialize (ctf_dict_t *fp)
 				       1, &err)) == NULL)
     {
       free (buf);
-      return (ctf_set_errno (fp, err));
+      return (ctf_set_errno_signed (fp, err));
     }
 
   (void) ctf_setmodel (nfp, ctf_getmodel (fp));
@@ -1221,7 +1221,7 @@  ctf_serialize (ctf_dict_t *fp)
 
 oom:
   free (buf);
-  return (ctf_set_errno (fp, EAGAIN));
+  return (ctf_set_errno_signed (fp, EAGAIN));
 err:
   free (buf);
   return -1;					/* errno is set for us.  */
@@ -1248,7 +1248,7 @@  ctf_gzwrite (ctf_dict_t *fp, gzFile fd)
   while (resid != 0)
     {
       if ((len = gzwrite (fd, buf, resid)) <= 0)
-	return (ctf_set_errno (fp, errno));
+	return (ctf_set_errno_signed (fp, errno));
       resid -= len;
       buf += len;
     }
@@ -1258,7 +1258,7 @@  ctf_gzwrite (ctf_dict_t *fp, gzFile fd)
   while (resid != 0)
     {
       if ((len = gzwrite (fd, buf, resid)) <= 0)
-	return (ctf_set_errno (fp, errno));
+	return (ctf_set_errno_signed (fp, errno));
       resid -= len;
       buf += len;
     }
@@ -1378,7 +1378,7 @@  ctf_compress_write (ctf_dict_t *fp, int fd)
     {
       if ((len = write (fd, bp, buf_len)) < 0)
 	{
-	  err = ctf_set_errno (fp, errno);
+	  err = ctf_set_errno_signed (fp, errno);
 	  ctf_err_warn (fp, 0, 0, _("ctf_compress_write: error writing"));
 	  goto ret;
 	}
@@ -1412,7 +1412,7 @@  ctf_write (ctf_dict_t *fp, int fd)
     {
       if ((len = write (fd, bp, buf_len)) < 0)
 	{
-	  err = ctf_set_errno (fp, errno);
+	  err = ctf_set_errno_signed (fp, errno);
 	  ctf_err_warn (fp, 0, 0, _("ctf_compress_write: error writing"));
 	  goto ret;
 	}
diff --git a/libctf/ctf-string.c b/libctf/ctf-string.c
index 911e94700f1..6cb994e0302 100644
--- a/libctf/ctf-string.c
+++ b/libctf/ctf-string.c
@@ -298,7 +298,7 @@  ctf_str_move_pending (ctf_dict_t *fp, uint32_t *new_ref, ptrdiff_t bytes)
     return 0;
 
   if (ctf_dynset_insert (fp->ctf_str_pending_ref, (void *) new_ref) < 0)
-    return (ctf_set_errno (fp, ENOMEM));
+    return (ctf_set_errno_signed (fp, ENOMEM));
 
   ctf_dynset_remove (fp->ctf_str_pending_ref,
 		     (void *) ((signed char *) new_ref - bytes));
@@ -327,7 +327,7 @@  ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
 						 NULL, NULL);
   if (!fp->ctf_syn_ext_strtab)
     {
-      ctf_set_errno (fp, ENOMEM);
+      ctf_set_errno_signed (fp, ENOMEM);
       return 0;
     }
 
@@ -338,7 +338,7 @@  ctf_str_add_external (ctf_dict_t *fp, const char *str, uint32_t offset)
     {
       /* No need to bother freeing the syn_ext_strtab: it will get freed at
 	 ctf_str_write_strtab time if unreferenced.  */
-      ctf_set_errno (fp, ENOMEM);
+      ctf_set_errno_signed (fp, ENOMEM);
       return 0;
     }
 
diff --git a/libctf/ctf-types.c b/libctf/ctf-types.c
index c20ff825d9a..d94188bbc37 100644
--- a/libctf/ctf-types.c
+++ b/libctf/ctf-types.c
@@ -119,7 +119,7 @@  ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
 	return -1;			/* errno is set for us.  */
 
       if ((i = ctf_next_create ()) == NULL)
-	return ctf_set_errno (ofp, ENOMEM);
+	return ctf_set_errno_signed (ofp, ENOMEM);
       i->cu.ctn_fp = ofp;
       i->ctn_tp = tp;
 
@@ -129,7 +129,7 @@  ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
       if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
 	{
 	  ctf_next_destroy (i);
-	  return (ctf_set_errno (ofp, ECTF_NOTSOU));
+	  return (ctf_set_errno_signed (ofp, ECTF_NOTSOU));
 	}
 
       if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
@@ -150,14 +150,14 @@  ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
     }
 
   if ((void (*) (void)) ctf_member_next != i->ctn_iter_fun)
-    return (ctf_set_errno (ofp, ECTF_NEXT_WRONGFUN));
+    return (ctf_set_errno_signed (ofp, ECTF_NEXT_WRONGFUN));
 
   if (ofp != i->cu.ctn_fp)
-    return (ctf_set_errno (ofp, ECTF_NEXT_WRONGFP));
+    return (ctf_set_errno_signed (ofp, ECTF_NEXT_WRONGFP));
 
   /* Resolve to the native dict of this type.  */
   if ((fp = ctf_get_dict (ofp, type)) == NULL)
-    return (ctf_set_errno (ofp, ECTF_NOPARENT));
+    return (ctf_set_errno_signed (ofp, ECTF_NOPARENT));
 
   max_vlen = LCTF_INFO_VLEN (fp, i->ctn_tp->ctt_info);
 
@@ -177,7 +177,7 @@  ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
 
       if (ctf_struct_member (fp, &memb, i->ctn_tp, i->u.ctn_vlen, i->ctn_size,
 			     i->ctn_n) < 0)
-        return (ctf_set_errno (ofp, ctf_errno (fp)));
+        return (ctf_set_errno_signed (ofp, ctf_errno (fp)));
 
       membname = ctf_strptr (fp, memb.ctlm_name);
 
@@ -216,12 +216,12 @@  ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
 	  ctf_next_destroy (i);
 	  *it = NULL;
 	  i->ctn_type = 0;
-	  ctf_set_errno (ofp, ctf_errno (fp));
+	  ctf_set_errno_signed (ofp, ctf_errno (fp));
 	  return ret;
 	}
 
       if (!ctf_assert (fp, (i->ctn_next == NULL)))
-        return (ctf_set_errno (ofp, ctf_errno (fp)));
+        return (ctf_set_errno_signed (ofp, ctf_errno (fp)));
 
       i->ctn_type = 0;
       /* This sub-struct has ended: on to the next real member.  */
@@ -233,7 +233,7 @@  ctf_member_next (ctf_dict_t *fp, ctf_id_t type, ctf_next_t **it,
  end_iter:
   ctf_next_destroy (i);
   *it = NULL;
-  return ctf_set_errno (ofp, ECTF_NEXT_END);
+  return ctf_set_errno_signed (ofp, ECTF_NEXT_END);
 }
 
 /* Iterate over the members of an ENUM.  We pass the string name and associated
@@ -861,7 +861,7 @@  ctf_type_lname (ctf_dict_t *fp, ctf_id_t type, char *buf, size_t len)
   free (str);
 
   if (slen >= len)
-    (void) ctf_set_errno (fp, ECTF_NAMELEN);
+    (void) ctf_set_errno_signed (fp, ECTF_NAMELEN);
 
   return slen;
 }
@@ -956,7 +956,7 @@  ctf_type_size (ctf_dict_t *fp, ctf_id_t type)
 
     case CTF_K_FORWARD:
       /* Forwards do not have a meaningful size.  */
-      return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
+      return (ctf_set_errno_signed (ofp, ECTF_INCOMPLETE));
 
     default: /* including slices of enums, etc */
       return (ctf_get_ctt_size (fp, tp, NULL, NULL));
@@ -1039,7 +1039,7 @@  ctf_type_align (ctf_dict_t *fp, ctf_id_t type)
 
     case CTF_K_FORWARD:
       /* Forwards do not have a meaningful alignment.  */
-      return (ctf_set_errno (ofp, ECTF_INCOMPLETE));
+      return (ctf_set_errno_signed (ofp, ECTF_INCOMPLETE));
 
     default:  /* including slices of enums, etc */
       return (ctf_get_ctt_size (fp, tp, NULL, NULL));
@@ -1235,7 +1235,7 @@  ctf_type_encoding (ctf_dict_t *fp, ctf_id_t type, ctf_encoding_t *ep)
 	break;
       }
     default:
-      return (ctf_set_errno (ofp, ECTF_NOTINTFP));
+      return (ctf_set_errno_signed (ofp, ECTF_NOTINTFP));
     }
 
   return 0;
@@ -1370,7 +1370,7 @@  ctf_member_count (ctf_dict_t *fp, ctf_id_t type)
   kind = LCTF_INFO_KIND (fp, tp->ctt_info);
 
   if (kind != CTF_K_STRUCT && kind != CTF_K_UNION && kind != CTF_K_ENUM)
-    return (ctf_set_errno (ofp, ECTF_NOTSUE));
+    return (ctf_set_errno_signed (ofp, ECTF_NOTSUE));
 
   return LCTF_INFO_VLEN (fp, tp->ctt_info);
 }
@@ -1398,7 +1398,7 @@  ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
   kind = LCTF_INFO_KIND (fp, tp->ctt_info);
 
   if (kind != CTF_K_STRUCT && kind != CTF_K_UNION)
-    return (ctf_set_errno (ofp, ECTF_NOTSOU));
+    return (ctf_set_errno_signed (ofp, ECTF_NOTSOU));
 
   n = LCTF_INFO_VLEN (fp, tp->ctt_info);
   if ((dtd = ctf_dynamic_type (fp, type)) != NULL)
@@ -1418,7 +1418,7 @@  ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
       const char *membname;
 
       if (ctf_struct_member (fp, &memb, tp, vlen, vbytes, i) < 0)
-        return (ctf_set_errno (ofp, ctf_errno (fp)));
+        return (ctf_set_errno_signed (ofp, ctf_errno (fp)));
 
       membname = ctf_strptr (fp, memb.ctlm_name);
 
@@ -1439,7 +1439,7 @@  ctf_member_info (ctf_dict_t *fp, ctf_id_t type, const char *name,
 	}
     }
 
-  return (ctf_set_errno (ofp, ECTF_NOMEMBNAM));
+  return (ctf_set_errno_signed (ofp, ECTF_NOMEMBNAM));
 }
 
 /* Return the array type, index, and size information for the specified ARRAY.  */
@@ -1457,7 +1457,7 @@  ctf_array_info (ctf_dict_t *fp, ctf_id_t type, ctf_arinfo_t *arp)
     return -1;			/* errno is set for us.  */
 
   if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ARRAY)
-    return (ctf_set_errno (ofp, ECTF_NOTARRAY));
+    return (ctf_set_errno_signed (ofp, ECTF_NOTARRAY));
 
   if ((dtd = ctf_dynamic_type (ofp, type)) != NULL)
     ap = (const ctf_array_t *) dtd->dtd_vlen;
@@ -1536,7 +1536,7 @@  ctf_enum_value (ctf_dict_t *fp, ctf_id_t type, const char *name, int *valp)
 
   if (LCTF_INFO_KIND (fp, tp->ctt_info) != CTF_K_ENUM)
     {
-      (void) ctf_set_errno (ofp, ECTF_NOTENUM);
+      (void) ctf_set_errno_signed (ofp, ECTF_NOTENUM);
       return -1;
     }
 
@@ -1557,7 +1557,7 @@  ctf_enum_value (ctf_dict_t *fp, ctf_id_t type, const char *name, int *valp)
 	}
     }
 
-  ctf_set_errno (ofp, ECTF_NOENUMNAM);
+  ctf_set_errno_signed (ofp, ECTF_NOENUMNAM);
   return -1;
 }
 
@@ -1584,7 +1584,7 @@  ctf_func_type_info (ctf_dict_t *fp, ctf_id_t type, ctf_funcinfo_t *fip)
   kind = LCTF_INFO_KIND (fp, tp->ctt_info);
 
   if (kind != CTF_K_FUNCTION)
-    return (ctf_set_errno (ofp, ECTF_NOTFUNC));
+    return (ctf_set_errno_signed (ofp, ECTF_NOTFUNC));
 
   fip->ctc_return = tp->ctt_type;
   fip->ctc_flags = 0;
@@ -1697,7 +1697,7 @@  ctf_type_rvisit (ctf_dict_t *fp, ctf_id_t type, ctf_visit_f *func,
       ctf_lmember_t memb;
 
       if (ctf_struct_member (fp, &memb, tp, vlen, vbytes, i) < 0)
-        return (ctf_set_errno (ofp, ctf_errno (fp)));
+        return (ctf_set_errno_signed (ofp, ctf_errno (fp)));
 
       if ((rc = ctf_type_rvisit (fp, memb.ctlm_type,
 				 func, arg, ctf_strptr (fp, memb.ctlm_name),
diff --git a/libctf/ctf-util.c b/libctf/ctf-util.c
index 9f83ab9ab0b..022ce785f95 100644
--- a/libctf/ctf-util.c
+++ b/libctf/ctf-util.c
@@ -265,6 +265,18 @@  ctf_set_errno (ctf_dict_t *fp, int err)
   return CTF_ERR;
 }
 
+/* Store the specified error code into the CTF dict, and then return -1
+   (CTF_ERR) for the benefit of the caller. */
+
+int
+ctf_set_errno_signed (ctf_dict_t *fp, int err)
+{
+  fp->ctf_errno = err;
+  /* Don't rely on CTF_ERR here as it will not properly sign extend on 64-bit
+     Windows ABI.  */
+  return -1;
+}
+
 /* Create a ctf_next_t.  */
 
 ctf_next_t *