Using errno with Ada

I there a good way to read ‘errno’ in Ada?

I am trying to import it using this:

Error_Number : Integer
with Import => True,
     Convention => C,
     External_Name => "errno";

However, I get a cryptic linker error when I try to use it:

x86_64-linux-gnu-gnatlink-12 test_ioctl.ali            
/usr/bin/ld: errno: TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches non-TLS reference in ./test_ioctl.o      
/usr/bin/ld: /lib/x86_64-linux-gnu/libc.so.6: error adding symbols: bad value
collect2: error: ld returned 1 exit status
x86_64-linux-gnu-gnatlink-12: error when calling /usr/bin/x86_64-linux-gnu-gcc-12
gnatmake: *** link failed.

Any ideas?

Thanks,
N

This is a good question and I saw that quite a few people had had this exact same issue (related to errno) when working with C codebases: https://www.linuxquestions.org/questions/programming-9/errno-tls-definition-in-lib64-libc-so-6-section-tbss-mismatches-non-tls-reference-528681/ and https://www.linuxquestions.org/questions/linux-general-1/ld-errno-tbss-mismatches-non-tls-reference-588894/ and Fixing /usr/bin/ld: errno: TLS definition in /lib/libc.so.6 section .tbss mismatches

It seems that errno is not a normal symbol and even in C applications the C header needs to be imported so that this issue does not happen, it is not enough to declare it as extern

I cannot help you much further though :confused:

Best regards,
Fer

Oh, the magic of C preprocessing!

Under the hood, errno may be a function call to collect a thread-local value; so errno looks global, but in fact may not be.

I tried this, in errno.c:

#include <sys/errno.h>
#include <stdio.h>

int main()
{
  int current_errno = errno;
  printf("errno: %d\n", current_errno);
  return 0;
}

and cpp errno.c tells me at the end

# 4 "errno.c"
int main()
{
  int current_errno = 
# 6 "errno.c" 3 4
                     (*__error())
# 6 "errno.c"
                          ;
  printf("errno: %d\n", current_errno);
  return 0;
}

so, on a Mac, errno is in fact a function call to whatever __error is pointing to (or maybe, my C isn’t that great, __error() returns a pointer to the appropriate actual errno? otherwise, how could you say e.g. errno = EINVAL; ?).

For portability, you’d have to write a C function to pick up errno and return that, and then import that function.

First of all it is int.
Second, a along story, but its short version is that you cannot link to an object. Linux provides a function that returns the address of errno. Use it:

   function errno return int is
      function errno_location return access int;
      pragma Import (C, errno_location, "__errno_location");
   begin
      return errno_location.all;
   end errno;
1 Like

Use GNAT.OS_Lib.Errno and GNAT.OSLib.Errno_Message:

   function Errno return Integer;

   function Errno_Message
     (Err     : Integer := Errno;
      Default : String  := "") return String;
   --  Return a message describing the given Errno value. If none is provided
   --  by the system, return Default if not empty, else return a generic
   --  message indicating the numeric errno value.
3 Likes

Thanks for the replies guys!

In this case Fabien’s is the simplest, but for the general C case what I do in these cases is to have a simple wrapper function in C that calls whatever complex magic/macro/C++ element is there, so I can safely bind to my C wrapper.

2 Likes

It would be nice to have a tool that detects those macros and generates those simple wrapper functions. That could complement gcc -fdump-ada-spec for easily generating bindings to C libraries. In fact, I don’t know why gcc couldn’t do that by itself.

1 Like

Per cppreference, errno is:

macro which expands to POSIX-compatible thread-local error number variable

The errno in libc.so.6 here is in thread local storage (TLS):

and your import attempts to find a non-thread local storage version:

2 Likes

You may also consider POSIX Ada with Florist:
function Get_Error_Code return Error_Code;
(in generated posix.ads)