The error you’re getting comes out from a very simple fact : the push
instruction in 32-bit mode accepts 16-bit and 32-bit immediates as arguments. However, the push
instruction used in 64-bit mode accepts only 16-bit and 64-bit immediates. Since you’re clearly compiling your code as 64-bit, the assembler throws an error, since it cannot possibly encode such an instruction. Also, do keep in mind that you force the operand size yourself by adding the l
suffix to the push
instruction. Everything I just wrote here is exactly the same for pop
, except that it accepts registers/memory, not immediates.
However, you also need to keep in mind the differences in the ABI between 32-bit and 64-bit Linux systems. The ABI specifies, among other things, how parameters are passed to functions, and how to call the kernel from user-mode applications. Your code is clearly written for 32-bit mode, seeing how it uses the stack for passing arguments and the (very) obsolete int $0x80
way of invoking syscalls. In order to learn about the 64-bit ABI, see this document.
Alternatively, you have the option of compiling 32-bit code on a 64-bit system. Such an executable will work if you have the necessary 32-bit runtime libraries installed on your 64-bit system. Most distributions allow you to do that in different ways. Your compiler, as
, has the --32
switch for emitting 32-bit code.
I have been having issues with compiling a program. I am unsure what these mean and I would be grateful if anyone could help.
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S: Assembler
messages:
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S:41: Error:
invalid instruction suffix for `push'
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S:46: Error:
invalid instruction suffix for `push'
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S:449: Error: invalid instruction suffix for `pop'
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S:451: Error: invalid instruction suffix for `pop'
_depslibmpg123CMakeFileslibmpg123_static.dirbuild.make:378: recipe for targe
t '_deps/libmpg123/CMakeFiles/libmpg123_static.dir/dct64_sse.S.obj' failed
mingw32-make[2]: [_deps/libmpg123/CMakeFiles/libmpg123_static.dir/dct64_sse.
S.obj] Error 1
CMakeFilesMakefile2:225: recipe for target '_deps/libmpg123/CMakeFiles/libmpg12
3_static.dir/all' failed
mingw32-make[1]: [_deps/libmpg123/CMakeFiles/libmpg123_static.dir/all] Error
2
Makefile:74: recipe for target 'all' failed
mingw32-make: [all] Error 2
asked Oct 13, 2013 at 15:05
2
It appears that you are trying to build 32-bit assembly code with 64-bit assembler.
You have 2 options:
- Use 32-bit assembler, for example by utilizing
--32
option; - Change code by substituting 64-bit (extended) registers such as
%rax
, for example, instead of 32-bit registers such as%eax
used withpush
/pop
instructions.
Since the build system appears to be CMake, I’d refer you to this manual on how to configure the build for various Assembly dialects in CMake.
You could try:
set(CMAKE_ASM_FLAGS "--32")
but I haven’t tested it.
answered Oct 13, 2013 at 15:13
4
@bmmajeed on GNU/Linux, tools are typically installed to some predefined locations (e.g. /usr/local/bin
, /usr/local/lib
…). Unlike Windows, there is no one specific subidr for each app/tool. All the binaries of all tools are put together, all the libs are put together, etc. As a result, it is very painful to manually install/uninstall all the files related to some tool. Unless you do it from a tarball and keep that around, to use diogratia’s trick.
At the same time, GNU/Linux distributions are known for having so-called «package managers» together with «repositories». A repository is a server containing thousands of pre-built tools. Unlike Windows, on Linux you don’t need to search each tool in the browser and then download and install it manually. On Linux, you use a package manager:
apt update
: fetch the repository and update the local list of available packages.apt search ghdl
: search if a package named ghdl exists.apt install ghdl
: install ghdl for me.
Next time you do apt update
and apt upgrade
, it will automatically install a new version of GHDL (if available).
Moreover, the packages you get through the manager are guaranteed to work for your OS, because those were built specifically for the libraries and versions you have.
When you download a tarball for Linux, you have no guarantee about it working. In this case, we do generate pre-built tarballs for Ubuntu 20.04. However, if you used any othe Linux distribution, you might have issues. This is different from Windows, because there is one single officially supported Windows at a time, and there is very good backwards compatibility.
apt
(or apt-get
) is the default package manager on Debian/Ubuntu. On other distributions, managers are pacman
, dnf
, yum
, etc. All of the are equivalent and serve the same purpose.
Therefore, installing GHDL from tarballs or building it from sources is only suggested in case the version available through the package manager is not enough. That might be because it is outdated or because it was built with some options that don’t fit your needs. In this specific case, it might be problematic if the GHDL installed through apt
was built with openieee. However, that should be fixed as soon as they update to 1.0.0
, which was released a month ago.
I’m new to the Linux so I want to get through the basic reasons of the stuff.
Please, do see some Youtube video or some introduction to the GNU/Linux file structure and how package managers are used. I’m sure you will find examples specific about Ubuntu which will be easy to follow. I did a very basic introduction above, but explaining it in deep is out of the scope of this project.
Last night I was trying to setup my i686-elf-gcc cross compiler. After spending like an hour trying to figure out what was wrong with my current installation (I had the binaries from the debian repository, which I had assumed would work but seemed to be broken for me) and eventually decided to compile it myself. When I was finally done, I tried compiling something and it worked just fine. However, after closing out of that shell and opening a new one it no longer worked properly and kept giving a variety of different errors. I figured out that I forgot to export some of my environment variables in my zshrc, but after fixing that (I think) it still seems broken. Here are the relevant files/errors:
Makefile:
Code:
CC = /home/nv/opt/cross/bin/i686-elf-gcc
AS = nasm
AFLAGS = -f elf32
CFLAGS = -c -std=gnu99 -ffreestanding -O -Wall -w -g -I ../../include
LD = /home/nv/opt/cross/bin/i686-elf-gcc
LDFLAGS = -T linker.ld -ffreestanding -O2 -nostdlib -gcc
TARGET = bin/infinity.bin
OBJECTS = $(patsubst %.c, %.o, $(wildcard *.c) $(shell find ./src -name «*.c»)) $(patsubst %.asm, %.o, $(wildcard *.asm) $(shell find ./src -name «*.asm»))
all: $(TARGET)
$(TARGET): $(OBJECTS)
$(LD) -r -o $@ $^
%.o: %.c
$(CC) $(CFLAGS) $^ -o $@
%.o: %.asm
$(AS) $(AFLAGS) $^ -o $@
clean:
rm $(TARGET) $(OBJECTS)
src/hello.c:
Code:
#include <infinity/kernel.h>
int mod_init()
{
printk(KERN_INFO «Hello, infinity kernel!n»);
return 0;
}
int mod_uninit()
{
printk(KERN_INFO «Goodbye infinity kernel :(n»);
return 0;
}
error:
Code:
/tmp/ccdUfcT0.s: Assembler messages:
/tmp/ccdUfcT0.s:18: Error: invalid instruction suffix for `push’
/tmp/ccdUfcT0.s:44: Error: invalid instruction suffix for `push’
makefile:19: recipe for target ‘src/hello.o’ failed
make: *** [src/hello.o] Error 1
This is using the code from here, by the way (with some modifications to makefiles and other parts of the code unrelated to this), and I was able to compile the kernel as of last night… I’m now unable to compile both modules and the kernel but for simplicity I am just posting the test module:
https://github.com/GruntTheDivine/infin … ules/hello
Thanks very much, nv
Topic: Error: invalid instruction suffix for `push’ (Read 11421 times)
I tried to use one of the nightly builds, but now some of the programs I had written before no longer compile getting errors such as the thread subject.
Build log tags them as assembler messages.
Any advice on this would be appreciated.
Logged
Logged
(most of the time I ignore long posts)
[strangers don’t send me private messages, I’ll ignore them; post a topic in the forum, but first read the rules!]
Well, I checked the FAQ page already and there is nothing in it covering this problem. There were also no other threads, I checked before I posted a new one. — Turning on Full compiler logging just spits out the exact same messages.
C:UserscompAppDataLocalTemp/ccl1nhzH.s: Assembler messages:
C:UserscompAppDataLocalTemp/ccl1nhzH.s:485: Error: invalid instruction suffix for `push’
C:UserscompAppDataLocalTemp/ccl1nhzH.s:747: Error: invalid instruction suffix for `push’
C:UserscompAppDataLocalTemp/ccl1nhzH.s:749: Error: invalid instruction suffix for `push’
C:UserscompAppDataLocalTemp/ccl1nhzH.s:750: Error: invalid instruction suffix for `push’
C:UserscompAppDataLocalTemp/ccl1nhzH.s:751: Error: invalid instruction suffix for `push’
C:UserscompAppDataLocalTemp/ccl1nhzH.s:942: Error: invalid instruction suffix for `pop’
If no one has a clue, I will just download the original again, but I was hoping to not have to set it all back up again. I assume I did something wrong in adding the nightly build. I am reading now that the nightly was to be in a new folder and not meant to replace existing files?
« Last Edit: September 19, 2012, 06:40:51 am by Incantrix »
Logged
Turning on Full compiler logging just spits out the exact same messages.
(The messages would be the same regardless of the level of logging.) What you posted is just the messages (the full log is under the «Build log» tab; not the «Build messages»).
Can you post your full log?
Logged
mingw32-g++.exe -Wall -g -I»C:Program Files (x86)CodeBlocksSDL-1.2.14include» -c C:UserscompDesktopSourceLazyFooTwoCFile.cpp -o objDebugCFile.o
C:UserscompDesktopSourceLazyFooTwoCFile.cpp: In member function `void CFile::Render()’:
C:UserscompDesktopSourceLazyFooTwoCFile.cpp:248: warning: comparison between signed and unsigned integer expressions
C:UserscompAppDataLocalTemp/cc5Dk5yf.s: Assembler messages:
C:UserscompAppDataLocalTemp/cc5Dk5yf.s:485: Error: invalid instruction suffix for `push’
C:UserscompAppDataLocalTemp/cc5Dk5yf.s:747: Error: invalid instruction suffix for `push’
…
There are numerous repeats of the same error, with either ‘push’ or ‘pop’.
Logged
Unrelated to Code::Block, therefore locking the topic.
Please respect the forum rules you agreed to. Use the right forum (which is not the Code::Blocks forum) for these type of questions. We don’t provide compiler support here. Thank you.
Logged
I get the above error when trying to compile the following fortran module. module timing_module implicit none integer , dimension(8) , private :: dt real , private :: h,m,s,ms,tt real , private :: last_tt contains subroutine start_timing() implicit none call date_and_time(values=dt) print 100 , dt(1:3),dt(5:8) 100 format( 1x,i4,'/',i2,'/',i2,1x,i2,':',i2,':',i2,1x,i3) h = real(dt(5)) m = real(dt(6)) s = real(dt(7)) ms = real(dt(8)) last_tt = 60*(60*h+m) + s + ms/1000.0 end subroutine start_timing subroutine print_date_and_time implicit none call date_and_time(values=dt) print 100 , dt(1:3),dt(5:8) 100 format( 1x,i4,'/',i2,'/',i2,1x,i2,':',i2,':',i2,1x,i3) end subroutine print_date_and_time subroutine print_hms implicit none call date_and_time(values=dt) print 100 , dt(5:8) 100 format( 1x,i2,':',i2,':',i2,1x,i3) end subroutine print_hms subroutine print_ms implicit none call date_and_time(values=dt) h = real(dt(5)) m = real(dt(6)) s = real(dt(7)) ms = real(dt(8)) tt = 60*(60*h+m) + s + ms/1000.0 print 100 , tt 100 format( 1x,f14.3) end subroutine print_ms subroutine print_time_difference implicit none call date_and_time(values=dt) h = real(dt(5)) m = real(dt(6)) s = real(dt(7)) ms = real(dt(8)) tt = 60*(60*h+m) + s + ms/1000.0 print 100 , (tt-last_tt) 100 format( 1x,f14.3) last_tt=tt end subroutine print_time_difference real function time_difference() implicit none tt = 0.0 call date_and_time(values=dt) h = real(dt(5)) m = real(dt(6)) s = real(dt(7)) ms = real(dt(8)) tt = 60*(60*h+m) + s + ms/1000.0 time_difference=tt-last_tt end function time_difference end module timing_module Here is the output from gfortran -v d:documentfortrannewbookexamplesch28gfortran>gfortran -v Using built-in specs. COLLECT_GCC=gfortran COLLECT_LTO_WRAPPER=c:/program files (x86)/gfortran/bin/../libexec/gcc/i586-pc-m ingw32/4.6.0/lto-wrapper.exe Target: i586-pc-mingw32 Configured with: ../gcc-trunk/configure --prefix=/mingw --enable-languages=c,for tran --with-gmp=/home/brad/gfortran/dependencies --disable-werror --enable-threa ds --disable-nls --build=i586-pc-mingw32 --enable-libgomp --enable-shared --disa ble-win32-registry --with-dwarf2 --disable-sjlj-exceptions --enable-lto Thread model: win32 gcc version 4.6.0 20110214 (experimental) [trunk revision 170140] (GCC) d:documentfortrannewbookexamplesch28gfortran> This system is running Windows Vista Home Premium, 64 bit, sp2.
I have just downloaded and installed the latest development version on this system and still get the same message. d:documentfortrannewbookexamplesch28gfortran>gfortran -v Using built-in specs. COLLECT_GCC=gfortran COLLECT_LTO_WRAPPER=c:/program files (x86)/gfortran/bin/../libexec/gcc/i586-pc-m ingw32/4.7.0/lto-wrapper.exe Target: i586-pc-mingw32 Configured with: ../gcc-trunk/configure --prefix=/mingw --enable-languages=c,for tran --with-gmp=/home/brad/gfortran/dependencies --disable-werror --enable-threa ds --disable-nls --build=i586-pc-mingw32 --enable-libgomp --enable-shared --disa ble-win32-registry --with-dwarf2 --disable-sjlj-exceptions --enable-lto Thread model: win32 gcc version 4.7.0 20110419 (experimental) [trunk revision 170140] (GCC) hope this helps.
(In reply to comment #0) > Target: i586-pc-mingw32 > This system is running Windows Vista Home Premium, 64 bit, sp2. I think you may have the wrong version of gfortran. You are running a 64-bit OS but trying to use a 32-bit version of the software. I don't do Windows, but I suspect you want the MinGW for Win64 from the gfortran wiki
It was not clear to me which one I should have downloaded and installed. I'm trying to finish a book for springer and need most of the development stuff, including oop openmp mpi coarray so assumed that 4.7.x was the one to download. i've got a 32 bit windows system so i'll try 4.7.x on that and let you know. we try developing the examples for the book on several systems and keeping track of them all and the various compilers is a real headache. hopefully this is just a 32/64 bit windows problem from your point of view. cheers ian
Yes, this issue happens if you are mixing 32-bit binutils and 64-bit gcc. You can test, if you have actually downloaded an native 64-bit Windows gcc compiler, by specifying on command for compile only the option '-m32'. I assume it will be successful then. I've tested your module compilation by i686-w64-mingw32 fortran-compiler and it translates without flaws.
Not A GCC bug. |
У меня были проблемы с компиляцией программы. Я не уверен, что это значит, и буду благодарен, если кто-нибудь сможет помочь.
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S: Assembler
messages:
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S:41: Error:
invalid instruction suffix for `push'
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S:46: Error:
invalid instruction suffix for `push'
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S:449: Error: invalid instruction suffix for `pop'
C:UsersJoshuaDocumentsGitHubZeus-TSO_depslibmpg123dct64_sse.S:451: Error: invalid instruction suffix for `pop'
_depslibmpg123CMakeFileslibmpg123_static.dirbuild.make:378: recipe for targe
t '_deps/libmpg123/CMakeFiles/libmpg123_static.dir/dct64_sse.S.obj' failed
mingw32-make[2]: [_deps/libmpg123/CMakeFiles/libmpg123_static.dir/dct64_sse.
S.obj] Error 1
CMakeFilesMakefile2:225: recipe for target '_deps/libmpg123/CMakeFiles/libmpg12
3_static.dir/all' failed
mingw32-make[1]: [_deps/libmpg123/CMakeFiles/libmpg123_static.dir/all] Error
2
Makefile:74: recipe for target 'all' failed
mingw32-make: [all] Error 2
1
Решение
Похоже, что вы пытаетесь собрать 32-битный ассемблерный код с 64-битным ассемблером.
У вас есть 2 варианта:
- Используйте 32-битный ассемблер, например, используя
--32
вариант; - Изменить код путем замены 64-битных (расширенных) регистров, таких как
%rax
Например, вместо 32-битных регистров, таких как%eax
используется сpush
/pop
инструкции.
Поскольку система сборки выглядит как CMake, я отсылаю вас к этому руководство о том, как настроить сборку для различных сборочных диалектов в CMake.
Вы можете попробовать:
set(CMAKE_ASM_FLAGS "--32")
но я не проверял это.
3
Другие решения
Других решений пока нет …
Неверный суффикс инструкции толкания при сборке с газом
При сборке файла на ассемблере GNU я получаю следующую ошибку:
hello.s: 6: Ошибка: неверный суффикс инструкции для `push ‘
Вот файл, который я пытаюсь собрать:
.text
LC0:
.ascii "Hello, world!12"
.globl _main
_main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
movl %eax, -4(%ebp)
movl -4(%ebp), %eax
call __alloca
call ___main
movl $LC0, (%esp)
call _printf
movl $0, %eax
leave
ret
Что здесь не так и как это исправить?
Проблема в некоторой степени связана с этот вопрос хотя ошибки и инструкции в вопросах разные.
64-битные инструкции
По умолчанию большинство операций остаются 32-битными, а 64-битные аналоги вызываются четвертым битом в префиксе REX. Это означает, что каждая 32-битная инструкция имеет естественное 64-битное расширение, а расширенные регистры бесплатны в 64-битных инструкциях.
movl $1, %eax # 32-bit instruction
movq $1, %rax # 64-bit instruction
pushl %eax # Illegal instruction
pushq %rax # 1 byte instruction encoded as pushl %eax in 32 bits
pushq %r10 # 2 byte instruction encoded as pushl preceeded by REX
Создан 23 июн.
Подготовить .code32
в качестве первой строки.
--32
опция изменит целевую платформу на 32-битную.
Создан 30 ноя.
Вы собираете на 64-битном ассемблере? Ваш код выглядит 32-битным. Я получаю эту ошибку с вашим кодом при использовании 64-битного ассемблера:
example.s:6:suffix or operands invalid for `push'
Но он отлично работает с 32-битным ассемблером.
Создан 07 июн.
Вы должны использовать «64-битный синтаксис», или вы можете использовать опцию «—32»: таким образом ассемблер переключает свою цель на платформу i386.
Создан 20 июля ’11, 12:07
Не тот ответ, который вы ищете? Просмотрите другие вопросы с метками
linux
compiler-errors
assembly
gnu-assembler
or задайте свой вопрос.
I am working my way through Professional Assembly Language by Richard Blum. Why? I like lower level programming and I find assembly language interesting.
My setup is a Mint Linux x64 box. Professional Assembly Language, and many of the other books on assembly language, tend to use i386 32-bit assembly. In fact there are more books on 32-bit assembly language on the market than there are on 64-bit assembly language. It is nice to learn by going through book examples, but I don’t want to have to change them too much. I don’t mind changing command line switches, but I don’t want to have to convert all code to 64-bit assembly while learning.
Trying to compile and link 32-bit assembly on an 64-bit machine, you can run into some issues. This post goes over how to get setup so you can assemble and link both 32-bit and 64-bit assembly on an x64 Linux machine.
You can compile assembly on Linux using just gcc. If you want to see how to do that, skip to the bottom of this post. Professional Assembly Language used the gnu assembler and linker, as and ld. My guess is for learning. The ability to see each step in the process is nice. We will use as and ld. To start, I am assembling and linking this program from the Chapter 04 of the book.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# cpuid.s View the CPUID Vendor ID string using c library calls .section .data output: .asciz «The processor Vendor ID is ‘%s’n» .section .bss .lcomm buffer, 12 .section .text .globl _start _start: movl $0, %eax cpuid movl $buffer, %edi movl %ebx, (%edi) movl %edx, 4(%edi) movl %ecx, 8(%edi) pushl $buffer pushl $output call printf addl $8, %esp pushl $0 call exit |
Using the as command to assemble.
as -o cpuid.o cpuid.s cpuid.s: Assembler messages: cpuid.s:16: Error: invalid instruction suffix for 'push' cpuid.s:17: Error: invalid instruction suffix for 'push' cpuid.s:20: Error: invalid instruction suffix for 'push'
The first error I ran into says I have invalid instructions. That’s valid, I am trying to use 32-bit assembly instructions on a 64-bit machine. To fix that I had to tell the as
command it to use 32-bit assembly. This is done by passing the --32
parameter.
as --32 -o cpuid.o cpuid.s
That worked and I got a cpuid.o 32-bit object code file in the directory. Next I try to link it using the ld command.
ld -o cpuid2 cpuid.o
ld: i386 architecture of input file 'cpuid.o' is incompatible with i386:x86-64 output
cpuid.o: In function _start':
(.text+0x1f): undefined reference to 'printf'
cpuid.o: In function
_start':
(.text+0x29): undefined reference to 'exit'
There are actually two errors here. The first is line one. The second is all the lines after. The first error is the `cpuid.o' is incompatible with i386
error. It is saying I assembled in 32-bit format but I am trying to link in 64-bit format. To fix that we tell the ld
command to use a 32-bit architecture. This is done by passing the -m elf_i386
parameter. The -m
parameter is the emulation linker. The man pages defines linker emulation as the “personality of the linker, which gives the linker default values for the other aspects of the target system” and specify that “the emulation can affect various aspects of linker behavior, particularly the default linker script”. I don’t know exactly what that means, but I think it means “act like a different architecture”.
ld -m elf_i386 -o cpuid2 cpuid.o
I run that and the first error has been fixed. We still get the second error.
cpuid.o: In function '_start': (.text+0x1f): undefined reference to 'printf' cpuid.o: In function '_start': (.text+0x29): undefined reference to 'exit'
This is saying it can’t find printf or exit functions. Those functions are in libc. We need to tell the linker how to find them. The book uses dynamic linking with the --dynamic-linker
command. In the book it looks like a single dash, but the man page for ld says it is a double dash --
. Your linker location may be different. Mine was at /lib/ld-linux.so.2
. It is possible to link statically instead of dynamically but we won’t cover that.
We add in the -lc
to link libc into our program.
ld --dynamic-linker /lib/ld-linux.so.2 -m elf_i386 -o cpuid -lc cpuid.o
When I run that I get a different error. Here is where things start to get interesting.
ld: cannot find -lc
It can’t find libc.
Looking into the Linker
We can see where the linker is looking for libraries using the --verbose
parameter. We keep all of our dynamic linker and 32-bit parameters.
ld --dynamic-linker /lib/ld-linux.so.2 -m elf_i386 -lc --verbose
That gives a lot of output. At the end of the output there is a section where it attempts to open various libc.so files.
attempt to open //usr/local/lib/i386-linux-gnu/libc.so failed attempt to open //usr/local/lib/i386-linux-gnu/libc.a failed attempt to open //lib/i386-linux-gnu/libc.so failed attempt to open //lib/i386-linux-gnu/libc.a failed attempt to open //usr/lib/i386-linux-gnu/libc.so failed attempt to open //usr/lib/i386-linux-gnu/libc.a failed attempt to open //usr/local/lib32/libc.so failed attempt to open //usr/local/lib32/libc.a failed attempt to open //lib32/libc.so failed attempt to open //lib32/libc.a failed attempt to open //usr/lib32/libc.so failed attempt to open //usr/lib32/libc.a failed attempt to open //usr/local/lib/libc.so failed attempt to open //usr/local/lib/libc.a failed attempt to open //lib/libc.so failed attempt to open //lib/libc.a failed attempt to open //usr/lib/libc.so failed attempt to open //usr/lib/libc.a failed attempt to open //usr/i386-linux-gnu/lib32/libc.so failed attempt to open //usr/i386-linux-gnu/lib32/libc.a failed attempt to open //usr/x86_64-linux-gnu/lib32/libc.so failed attempt to open //usr/x86_64-linux-gnu/lib32/libc.a failed attempt to open //usr/i386-linux-gnu/lib/libc.so failed attempt to open //usr/i386-linux-gnu/lib/libc.a failed ld: cannot find -lc
All of its attempts failed.
Packages
At this point I am thinking “Maybe I don’t have the 32-bit libc on the box. I read some tutorials. There are various suggestions on adding in multiple architectures. I try some different commands. None help. It looks like I have all the packages and libraries installed already.
sudo dpkg --add-architecture i386 sudo apt-get update sudo apt-get dist-upgrade sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 sudo apt-get install multiarch-support
Then this stack overflow post recommended installing g++ multiarch. Looks like I didn’t have that installed after all.
sudo apt-get install gcc-multilib g++-multilib
That installs a bunch of packages. The important one which installs a 32-bit libc is libc6-dev-i386. A lot of the packages look related to i386 32-bit development.
The following additional packages will be installed: g++-5-multilib gcc-5-multilib lib32asan2 lib32atomic1 lib32cilkrts5 lib32gcc-5-dev lib32gomp1 lib32itm1 lib32mpx0 lib32quadmath0 lib32stdc++-5-dev lib32ubsan0 libc6-dev-i386 libc6-dev-x32 libc6-x32 libx32asan2 libx32atomic1 libx32cilkrts5 libx32gcc-5-dev libx32gcc1 libx32gomp1 libx32itm1 libx32quadmath0 libx32stdc++-5-dev libx32stdc++6 libx32ubsan0
When the install is done.
attempt to open //usr/local/lib/i386-linux-gnu/libc.so failed attempt to open //usr/local/lib/i386-linux-gnu/libc.a failed attempt to open //lib/i386-linux-gnu/libc.so failed attempt to open //lib/i386-linux-gnu/libc.a failed attempt to open //usr/lib/i386-linux-gnu/libc.so failed attempt to open //usr/lib/i386-linux-gnu/libc.a failed attempt to open //usr/local/lib32/libc.so failed attempt to open //usr/local/lib32/libc.a failed attempt to open //lib32/libc.so failed attempt to open //lib32/libc.a failed attempt to open //usr/lib32/libc.so succeeded opened script file //usr/lib32/libc.so opened script file //usr/lib32/libc.so attempt to open /lib32/libc.so.6 succeeded /lib32/libc.so.6 attempt to open /usr/lib32/libc_nonshared.a succeeded attempt to open /lib32/ld-linux.so.2 succeeded /lib32/ld-linux.so.2 /lib32/ld-linux.so.2 ld-linux.so.2 needed by /lib32/libc.so.6 found ld-linux.so.2 at /lib32/ld-linux.so.2
The linker is now able to find libc under /lib32/libc.so.6
Another piece that is interesting is the output format and architecture at the top of the ld output.
OUTPUT_FORMAT("elf32-i386", "elf32-i386", "elf32-i386") OUTPUT_ARCH(i386)
This shows that we are linking a 32-bit elf executable.
Linking
With the linker being able to find our 32-bit libc we can now go back to our ld command.
ld --dynamic-linker /lib/ld-linux.so.2 -m elf_i386 -o cpuid -lc cpuid.o ./cpuid
The linker works. It finds libc and creates an executable from our 32-bit assembly code. Running that we get the output below.
The processor Vendor ID is 'GenuineIntel'
Summary
I took the round about way to show a train of thought and maybe give some insight into how all parts are working when trying assemble and link 32-bit assembly on an x64 machine. In short, if you want to assemble and link 32-bit assembly on an x64 machine make sure your computer is configured correctly for multi-architecture. Install all of the following packages if they aren’t already installed.
sudo dpkg --add-architecture i386 sudo apt-get update sudo apt-get dist-upgrade sudo apt-get install libc6:i386 libncurses5:i386 libstdc++6:i386 sudo apt-get install multiarch-support sudo apt-get install gcc-multilib g++-multilib
When assembling using the --32
parameter. When linking use the -m elf_i386
parameter. The entire flow looks like this.
as --32 -o cpuid.o cpuid.s ld --dynamic-linker /lib/ld-linux.so.2 -m elf_i386 -o cpuid -lc cpuid.o ./cpuid
If you use the file command on the cpuid executable you will see we have assembled, linked, and executed a 32-bit assembly on a 64-bit machine.
file cpuid cpuid: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, not stripped
x64 Assembly Language
Can we still assemble x64 on the same machine? Yes. Easily. I took a hello world x64 example I found, saved it as hello.s assembled it, linked it, and executed it.
as -o hello.o hello.s ld -o hello hello.o ./hello
Using the file command again, we see we have assembled, linked, and executed a 64-bit elf executable.
file cpuid hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), statically linked, not stripped
Using GCC instead
Instead of using as
and ld
, we can use gcc to handle both stages. We still need to have our environment setup correctly as with as
and ld
. We also need to change our assembly source code globl label from _start
to main
.
We use the -m32
parameter with gcc to assemble and link in 32-bit format in one step.
gcc -m32 cpuid.s -o cpuid ./cpuid The processor Vendor ID is 'GenuineIntel' file cpuid cpuid: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.32, ...
And that’s it. One machine, both 32-bit and 64-bit assembly.