What's different between compiling in 32bit mode and 64bit mode on 64bit os about execution of ioctl function? - linux

I have a 64 bit Enterprice SuSE 11
I have an application which open a HIDRAW device and operate an ioctl function on it to get raw info from this device like below:
struct hidraw_devinfo devinfo;
int fd = open("/dev/hidraw0", 0);
int ret = ioctl(fd, HIDIOCGRAWINFO, &devinfo);
...
If I compile this program in 64 bit mode there is no error and no problem and when I execute the application the ioctl function works properly.
g++ main.cpp
If I complie this program in 32 bit mode there is also no error and no problem. but when I execute the application the ioctl function return EINVAL error(errno = 22 , Invalid Argument)
g++ -m32 main.cpp
what's the problem?
Note:
struct hidraw_devinfo
{
__u32 bustype;
__s16 vendor;
__s16 product;
}

Linux ioctl definitions and compatibility layers are a fascinating topic I've just bashed my head against.
Typically ioctl definitions use a family of macros _IOW/_IOR et al that take your argument type-name as a reference, along with a magic number and ordinal value that are munged to give you your ioctl argument value (eg HIDIOCGRAWINFO). The type-name is used to encode sizeof(arg_type) into the definition. This means that the type used in user space determines the value generated by the ioctl macro - ie HIDIOCGRAWINFO may vary based on include conditions.
Here is the first point where 32- and 64-bit differ, the sizeof may differ, depending on packing, use of vague data-sizes (eg long), but especially (and unavoidably) if you use a pointer argument. So in this case a 64-bit kernel module what wants to support 32-bit clients needs do define a compatibility argument-type to match the layout of the 32-bit equivalent of the argument type and thus a 32-bit compatible ioctl. These 32-bit equivalent definitions make use of a kernel facility/layer called compat.
In your case the sizeof() is the same so that is not the path you are taking - but its important to understand the whole of what could be happening.
In addition a kernel configuration may define CONFIG_COMPAT which changes the sys-call wrappers (especially the code surrounding the user/kernel interface wrt ioctl) to ease the burden of supporting 32- and 64-bit. Part of this includes a compatibility ioctl callback called ioctl_compat.
What I've seen is with CONFIG_COMPAT defined that 32-bit programs will generate code that delivers ioctls to the ioctl_compat callback, even if it could generate the same ioctl value as 64-bit does (eg in your case). So the driver writer needs to make sure that ioctl_compat handles both the special (different) 32-bit compatible ioctl TYPEs and the normal "64-bit - or unchanged 32-bit" types.
So a kernel-module designed and tested on 32-bit only and 64-bit only systems (without CONFIG_COMPAT) may work for 32- and 64-bit programs, but not for one which supports both.
So looking in HID I see this was added in 2.6.38:
http://lxr.linux.no/#linux+v2.6.38/drivers/hid/hidraw.c#L347

The problem is probably a mismatch between the devinfo structure your program passes to the ioctl function.
I guess your work on a 64 bit system. Thus, your kernel runs in 64 bits, and the kernel module you are talking to (with ioctl) is also 64 bits.
When you compile your user program in 64 bits, the devinfo definition in the kernel module and in the user program is the same.
When you compile your user program in 32 bits, the devinfo definition in the kernel module differs from its definition in your user program. Indeed, in 32 bits, the size of some types changes: mainly long and pointers. Thus, your program create a structure of a certain size, and the kernel module interprets the data it receives differently. The kernel module probably don't understand the value you give to it because it does not look for it at the position you placed it.
The solution is to pay attention to the definition of the devinfo structure so that it has the same binary representation when compiling for 32 bits and for 64 bits.

Related

Why isn't ullong a valid kernel module parameter type in Red Hat 7?

I've been dancing around with Intel Non-Transparent Bridge hardware and discovered that the ntb_hw_intel driver is not included in Red Hat 7.
From what I can tell, the driver does not build the way it is written - there are module parameters defined as ullong, which is unambiguous in both 32- and 64-bit kernels as a 64-bit object. The driver can be made to build (and load and run) with RHEL 7.4 by changing references to "ullong" to "ulong" (which is the same type in a 64-bit kernel), except that the build whinges about invalid pointer types; the code attempts to stick the ulong * into a u64 *, which in a 64-bit kernel is defined as unsigned long long. It builds and loads and runs, but it builds ugly.
From what I can tell, the root problem is the moduleparam.h header does not define a "ullong" parameter type. Why is this?

Parameters of files syscall_32.tbl, syscall_64.tbl in build linux kernel

I'm praticing to build a new linux kernel on virtual machine. I have some question about 2 files syscall_32.tbl and syscall_64.tbl in step import parameters of a module in them.
I'm know that file syscall_32.tbl have 5 parameters [number] [abi] [name], [entry point], [compat entry point], and file syscall_64.tbl have 4 without [compat entry point].
I have some questions that I can't find answer for them.
[number]: what is range value of this column. I find out that the numbers are union and increasing sequence. If now I import new with large number (such as 10^6), is it OK?
[abi]: I know that in file syscall_64.tbl, the value of column maybe common, 64, x32. What is meaning of each value? Why is different between them? And why machine 64-bit have value x32 in this column?
[name]: I know that [entry point] and [compat entry point] is used for function to run the syscall. And when user call system call, we don't need call the name, we only use the [number] and kernel space use [entry point] to run. What is a reason for this column ([name])?
Thanks for your view and answer. Sorry for my bad english.
For different binaries to interact, they need to agree on a set of interfaces, e.g. the size of types and layout (padding) of structs. On amd64, GNU/Linux supports three ABIs natively:
i386: For compatibility with x86 32-bit binaries. System calls are defined in syscall_32.tbl
x86_64: Native 64-bit binaries. System calls are defined syscall_64.tbl with abi=64
x32: ILP32 (32-bit int, long and pointers), but with amd64 goodies: e.g. registers are 64-bit and there is more of them than in i386. System calls are defined syscall_64.tbl with abi=x32
A binary's ABI is configured at compilation time (-m32, -m64 and -mx32 respectively for GCC), but the kernel runs in long mode in all three cases and sometimes conversions are necessary to account for ABI differences.
Regarding your questions:
[number]: Size depends on the system call convention. e.g. with int 80h, the system call number is passed through the 32-bit wide eax.
[abi]: "common" system calls can be used for both amd64 ABIs, but some, like those with pointers to structs, need special handling to account for ABI differences.
[name]: Linux provides headers with system call number definitions, e.g.
#define __NR_exit 1. The macro name is generated from the [name] column. See this answer for more information.

Is compiling ELF files with MSB flag possible in Linux

Is it possible to compile binary files with MSB endianness in GCC? If so, would they work correctly when executed?
Transferring comments into something resembling an answer.
In part, it depends on the CPU architecture. On SPARC or Power, you'd probably find MSB is the default. On Intel, which is definitely LSB by default, you probably can't.
On Intel. If I somehow altered every ELF header entry to MSB in a little-endian binary, would that execute properly?
No; you'd have to make lots of other changes to the code to give it the slightest chance of working. Basically, every number would have to be reworked from little-endian to big-endian.
Would that include instruction addresses, immediate parameters etc.? I would assume that regardless of the ELF flag, these should remain little-endian.
With just a modicum of further thought, I think there's a very chance high chance that the kernel for Intel will simply decline to execute the MSB ELF executable. It was compiled to expect LSB and knows it doesn't know how to deal with the alternative. To fix that, you'd have to rebuild the kernel and the dynamic loader, ld.so.1. And that's probably just the start of your problems.
On the whole, I would regard this as an exercise in futility. In the absence of information to the contrary, I don't think you need to worry about handling the headers of MSB ELF headers for Intel binaries; they won't exist in practice.
It do not think it is explicitely stated in the System V ABI but AFAIK, the ELF file is expected to be in native endianness (and e_ident[EI_DATA] describes the endianess used):
Byte e_ident[EI_DATA] specifies the data encoding of the
processor-specific data in the object file. The following encodings
are currently defined.
You might expect the processor-specific data to be in the processor endianness. For example, the content of the .got is processor-specific data and you definitely want it to be in native endianness.
On Intel computers, you have to use the ELFDATA2LSB.
From the System V ABI ~ Intel386 Supplement 4th edition:
For file identification in e_ident, the Intel386 architecture
requires the following values.
e_ident[EI_CLASS] = ELFCLASS32
e_ident[EI_DATA] = ELFDATA2LSB
From the System V ABI ~ AMD64 supplement Draft 0.99.6:
For file identification in e_ident, the AMD64 architecture
requires the following values.
e_ident[EI_CLASS] = ELFCLASS64
e_ident[EI_DATA] = ELFDATA2LSB

Ada + Fixed Stringss + Stm32 Arm

How to create a simple function returns a string on a arm platform?
procedure Main is
function tst_func return String is
begin
return "string";
end tst_func;
str : String := tst_func; -- <-- Doesnt work, runtime error.
-- Adacore gpl compiller, crossdev, arm elf hosted of win os.
-- Hardware is smt32f407 discovery board.
begin
...
The problem is a bug in the runtime system: if your program doesn’t involve any tasking, the environment task’s secondary stack isn’t set up properly, so when your function tries to return a string it thinks the secondary stack has been exhausted and raises Storage_Error.
I have reported this to AdaCore: their recommendation was to include
delay until Ada.Real_Time.Clock;
in your main program.
The bug will likely be resolved in the next GNAT GPL release.
The issue here seems to be that using Ada on small embedded CPUs like the STm32 (ARM Cortex) or the Actel AVR or TI MSP430 often involves compromises, because the platform may not be capable of running a full Ada RTS (Runtime System) including things like tasking.
Instead, a minimal RTS may be supplied, with restrictions specified by pragmas, that doesn't support tasking, or in this case, features requiring the secondary stack. Funnily enough, the RTS for the AVR does include the files s-secsta.ads,.adb which implement package System.Secondary_Stack so the much more powerful STm32 ought to be capable of it. You could look at the RTS sources supplied with the Adacore GPL package to see if these files are present or not.
So - options.
1) Work around, either using fixed length strings, or a table of string constants, or returning an access String (i.e. pointer) to a string allocated on the heap (don't forget to free it!) though heap use is not normally recommended for embedded programming.
2) Find a better RTS. You can compile and link against a different RTS by supplying -RTS=... arguments to the compiler. Here is a thread discussing alternative RTS strategies for this CPU.

how come an x64 OS can run a code compiled for x86 machine

Basically, what I wonder is how come an x86-64 OS can run a code compiled for x86 machine. I know when first x64 Systems has been introduced, this wasn't a feature of any of them. After that, they somehow managed to do this.
Note that I know that x86 assembly language is a subset of x86-64 assembly language and ISA's is designed in such a way that they can support backward compatibility. But what confuses me here is stack calling conventions. These conventions differ a lot depending on the architecture. For example, in x86, in order to backup frame pointer, proceses pushes where it points to stack(RAM) and pops after it is done. On the other hand, in x86-64, processes doesn't need to update frame pointer at all since all the references is given via stack pointer. And secondly, While in x86 architecture arguments to functions is passed by stack in x86-64, registers are used for that purpose.
Maybe this differences between stack calling conventions of x86-64 and x64 architecture may not affect the way program stack grows as long as different conventions are not used at the same time and this is mostly the case because x32 functions are called by other x32's and same for x64. But, at one point, a function (probably a system function) will call a function whose code is compiled for a x86-64 machine with some arguments, at this point, I am curious about how OS(or some other control unit) handle to get this function work.
Thanks in advance.
Part of the way that the i386/x86-64 architecture is designed is that the CS and other segment registers refer to entries in the GDT. The GDT entries have a few special bits besides the base and limit that describe the operating mode and privilege level of the current running task.
If the CS register refers to a 32-bit code segment, the processor will run in what is essentially i386 compatibility mode. Likewise 64-bit code requires a 64-bit code segment.
So, putting this all together.
When the OS wants to run a 32-bit task, during the task switch into it, it loads a value into CS which refers to a 32-bit code segment. Interrupt handlers also have segment registers associated with them, so when a system call occurs or an interrupt occurs, the handler will switch back to the OS's 64-bit code segment, (allowing the 64-bit OS code to run correctly) and the OS then can do its work and continue scheduling new tasks.
As a follow up with regards to calling convention. Neither i386 or x86-64 require the use of frame pointers. The code is free to do as it pleases. In fact, many compilers (gcc, clang, VS) offer the ability to compile 32-bit code without frame pointers. What is important is that the calling convention is implemented consistently. If all the code expects arguments to be passed on the stack, that's fine, but the called code better agree with that. Likewise, passing via registers is fine too, just everyone has to agree (at least at the library interface level, internal functions can generally do as they please).
Beyond that, just keep in mind that the difference between the two isn't really an issue because every process gets its own private view of memory. A side consequence though is that 32-bit apps can't load 64-bit dlls, and 64-bit apps can't load 32-bit dlls, because a process either has a 32-bit code segment or a 64-bit code segment. It can't be both.
The processor in put into legacy mode, but that requires everything executing at that time to be 32bit code. This switching is handled by the OS.
Windows : It uses WoW64. WoW64 is responsible for changing the processor mode, it also provides the compatible dll and registry functions.
Linux : Until recently Linux used to (like windows) shift to running the processor in legacy mode when ever it started executing 32bit code, you needed all the 32bit glibc libraries installed, and it would break if it tried to work together with 64bit code. Now there are implementing the X32 ABI which should make everything run like smoother and allow 32bit applications to access x64 feature like increased no. of registers. See this article on the x32 abi
PS : I am not very certain on the details of things, but it should give you a start.
Also, this answer combined with Evan Teran's answer probably give a rough picture of everything that is happening.

Resources