I have a simple FreeRTOS application that just toggles an LED in the main loop.
When I compile it with -Os, the resulting binary has size 0. Without -Os everything works as expected. What is happening here?
My CFLAGS are:
CPUFLAG = -mthumb -mcpu=cortex-m4
FPUFLAG = -mfpu=fpv4-sp-d16 -mfloat-abi=hard
WFLAG = -Wall -Wextra -Werror -Wstrict-prototypes
CFLAGS += -std=gnu99 $(WFLAG) $(CPUFLAG) $(FPUFLAG) -mlittle-endian -mthumb -nostartfiles
CFLAGS += -ffunction-sections -fdata-sections -fno-builtin
LDFLAGS += -nostdlib --gc-sections -static
main is basically TI's blinky demo:
int main(void)
{
volatile uint32_t ui32Loop;
//
// Enable the GPIO port that is used for the on-board LED.
//
SYSCTL_RCGC2_R = SYSCTL_RCGC2_GPIOF;
//
// Do a dummy read to insert a few cycles after enabling the peripheral.
//
ui32Loop = SYSCTL_RCGC2_R;
//
// Enable the GPIO pin for the LED (PF3). Set the direction as output, and
// enable the GPIO pin for digital function.
//
GPIO_PORTF_DIR_R = 0x08;
GPIO_PORTF_DEN_R = 0x08;
//
// Loop forever.
//
while(1)
{
//
// Turn on the LED.
//
GPIO_PORTF_DATA_R |= 0x08;
//
// Delay for a bit.
//
for(ui32Loop = 0; ui32Loop < 200000; ui32Loop++)
{
}
//
// Turn off the LED.
//
GPIO_PORTF_DATA_R &= ~(0x08);
//
// Delay for a bit.
//
for(ui32Loop = 0; ui32Loop < 200000; ui32Loop++)
{
}
}
return 0;
}
With -Os this generates
text data bss dec hex filename
0 0 0 0 0 image.elf
otherwise
text data bss dec hex filename
2012 4 728 2744 ab8 image.elf
edit: differences in .map files
Since you specified -nostartfiles, the standard startup and its entrypoint is not used, so there is no reference to your main function, and --gc-sections discards the whole section as unused.
To fix, try to either name your function start or add -e _main to the ld flags.
Related
I am trying to build a simply first blinkyon a nrf51822 china clone (YJ-14015), as part of building a redox wireless and debugging why the BLE communication does not work.
As SDK I use nrf5_SDK_11 as the keyboards custom firmware is based on it.
Now I tried a very minimal example blinky with main.c
#include <stdbool.h>
#include <stdint.h>
#include "nrf_delay.h"
#include "nrf_gpio.h"
#define LED_PIN_1 1 // LED connected to pin 1
/* --> from nrf5_SDK_11/components/drivers_nrf/hal/nrf_gpio.h
__STATIC_INLINE void nrf_gpio_pin_set(uint32_t pin_number)
{
NRF_GPIO->OUTSET = (1UL << pin_number);
}
__STATIC_INLINE void nrf_gpio_pin_clear(uint32_t pin_number)
{
NRF_GPIO->OUTCLR = (1UL << pin_number);
}
*/
int main(void)
{
// Make LED pin an output pin
nrf_gpio_cfg_output(LED_PIN_1);
nrf_gpio_pin_set(LED_PIN_1);
// Toggle LEDs.
while (true)
{
// Down
NRF_GPIO->OUTCLR = (NRF_GPIO->OUT & (1UL << LED_PIN_1));
// nrf_gpio_pin_clear(LED_PIN_1);
nrf_delay_ms(1000);
// Up
NRF_GPIO->OUTSET= (NRF_GPIO->OUT | (1UL << LED_PIN_1));
// nrf_gpio_pin_set(LED_PIN_1);
nrf_delay_ms(1000);
}
}
My expectation would have been that I can see the voltage flip every second from high to low to high etc on PIN 01... Unfortunately I only get a 1.55 V vs ground if I attach it to my multi meter, but the voltage just stays constant and nothing changes. Anything I did wrong with this loop?
For flashing I use a ST-LinkV2 clone + the docker containers for openocd and the toolchain of the redox wireless project, which basically uses telnet over openocd. After adjusting for the right paths, flashing seems successful and as mentioned above, PIN 01 can be set to 1.55V, so I assume there is no problem with the flashing itself.
In case someone else stumbles across the same difficulties:
After quite a while, I figured out a way to fix the blinky example for the yj-14015. The key was to adjust the Makefile which I took from the nordic SDK according to the Makefile in the redox firmware.
The relevant lines being as follows:
#flags common to all targets
CFLAGS = -DNRF51
CFLAGS += -DGAZELL_PRESENT
CFLAGS += -DBOARD_CUSTOM
CFLAGS += -DBSP_DEFINES_ONLY
CFLAGS += -mcpu=cortex-m0
CFLAGS += -mthumb -mabi=aapcs --std=gnu99
CFLAGS += -Wall -Werror -O3 -g3
CFLAGS += -Wno-unused-function
CFLAGS += -Wno-unused-variable
CFLAGS += -mfloat-abi=soft
# keep every function in separate section. This will allow linker to dump unused functions
CFLAGS += -ffunction-sections -fdata-sections -fno-strict-aliasing
CFLAGS += -fno-builtin --short-enums
# Assembler flags
ASMFLAGS += -x assembler-with-cpp
ASMFLAGS += -DNRF51
ASMFLAGS += -DGAZELL_PRESENT
ASMFLAGS += -DBOARD_CUSTOM
ASMFLAGS += -DBSP_DEFINES_ONLY
Here would be the full Makefile.
I'm trying to write software for an ATtiny814 (tiny avr 1-series) microcontroller, but I encounter a strange problem: whatever the first function in main.c is, gets executed and the remaining code gets ignored - including the main() function.
I'm using the avr-toolchain from the Arduino IDE on macOS, but I'm not using the IDE, I just added the bin/ directory of the avr-toolchain to the PATH variable. The code just compiles fine without errors or warnings. Using pyupdi I can successfully flash the program to the chip, and again it works fine - except that only code from the first function in the main.c is executed.
Makefile:
TARGET = project
CLOCK = 2000000 # configured in main
SOURCES = main.c
OBJECTS = $(SOURCES:.c=.o)
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -D__AVR_DEVICE_NAME__=attiny814 -D__AVR_DEV_LIB_NAME__=tn814
# compiling and linking, target is the finished hex file
all: $(TARGET).hex
# compile source files to object files
.c.o:
$(COMPILE) -c $< -o $#
# link the object files together
$(TARGET).elf: $(OBJECTS)
$(COMPILE) $(OBJECTS) -o $(TARGET).elf
# convert elf file to hex file
$(TARGET).hex: $(TARGET).elf
avr-objcopy -O ihex -j .data -j .text $(TARGET).elf $(TARGET).hex
clean:
rm -f $(TARGET).hex $(TARGET).elf $(OBJECTS)
Example 1
main.c:
#include <avr/io.h>
void test1() {
// turn on PB1
PORTB.DIR |= PIN1_bm;
PORTB.OUT |= PIN1_bm;
}
int main() {
// disable protection to configure clock frequency
CCP = CCP_IOREG_gc;
// configure CPU frequency
CLKCTRL.MCLKCTRLA = CLKCTRL_CLKSEL_OSC20M_gc; // use 20 MHz internal clock as source
CLKCTRL.MCLKCTRLB = CLKCTRL_PDIV_10X_gc | CLKCTRL_PEN_bm; // divide by 10 and enable divider
// turn on PB0
PORTB.DIR |= PIN0_bm;
PORTB.OUT |= PIN0_bm;
// main program loop
while(1) {};
return 0;
}
Here, only test1() is executed, as only PB1 turns on.
Example 2
main.c:
#include <avr/io.h>
// prototype
void test1();
int main() {
// disable protection to configure clock frequency
CCP = CCP_IOREG_gc;
// configure CPU frequency
CLKCTRL.MCLKCTRLA = CLKCTRL_CLKSEL_OSC20M_gc; // use 20 MHz internal clock as source
CLKCTRL.MCLKCTRLB = CLKCTRL_PDIV_10X_gc | CLKCTRL_PEN_bm; // divide by 10 and enable divider
// turn on PB0
PORTB.DIR |= PIN0_bm;
PORTB.OUT |= PIN0_bm;
// main program loop
while(1) {};
return 0;
}
void test1() {
// turn on PB1
PORTB.DIR |= PIN1_bm;
PORTB.OUT |= PIN1_bm;
}
Here, main() gets skipped and again test1() is executed, turning PB1 on.
Example 3
main.c:
#include <avr/io.h>
void test0() {
// turn on PB0
PORTB.DIR |= PIN0_bm;
PORTB.OUT |= PIN0_bm;
}
void test1() {
// turn on PB1
PORTB.DIR |= PIN1_bm;
PORTB.OUT |= PIN1_bm;
}
No main function at all. No compiler errors. Only test0() gets executed and PB0 turns on.
I have no clue whats going on here. Btw, using the same avr-toolchain setup, I can write software for an ATtiny841 (it's a different architecture/series) as expected.
Okay, found a solution: When I compile it as described here https://github.com/vladbelous/tinyAVR_gcc_setup#example-of-compiler-usage, it works.
So in my case:
avr-gcc -c -Os -DF_CPU=2000000 -mmcu=attiny814 main.c
avr-gcc -mmcu=attiny814 -o main.elf main.o
avr-objcopy -j .text -j .data -j .rodata -O ihex main.elf main.hex
Fixed Makefile:
TARGET = project
MCU = attiny814
DEVICE = tiny814
# clock settings applied in main.c
CLOCK = 2000000
PROGRAMMER = /dev/tty.usbserial-A50285BI
SOURCES = main.c
OBJECTS = $(SOURCES:.c=.o)
COMPILE = avr-gcc -Wall -Os -DF_CPU=$(CLOCK) -mmcu=$(MCU)
# compiling and linking, target is the finished hex file
all: $(TARGET).hex
# compile source files to object files
.c.o:
$(COMPILE) -c $< -o $#
# link the object files together
$(TARGET).elf: $(OBJECTS)
$(COMPILE) $(OBJECTS) -o $(TARGET).elf
# convert elf file to hex file
$(TARGET).hex: $(TARGET).elf
avr-objcopy -j .text -j .data -j .rodata -O ihex $(TARGET).elf $(TARGET).hex
# flash (call make flash), requires pyupdi installed
flash: $(TARGET).hex
python3 -m updi.pyupdi -d $(DEVICE) -c $(PROGRAMMER) -f "$(shell pwd)/$(TARGET).hex"
clean:
rm -f $(TARGET).hex $(TARGET).elf $(OBJECTS)
I am getting a "undefined reference to 'i2c_master_init' " i think this is to do some linking but not to sure how to correct ?
UPDATE :
iv been trying to find the answer for this for a while now. and have noticed a few bit but just not sure what i am doing wrong ! sorry it must be getting hard to read now !
This is from i2c_master.h and give me no issues
static inline void i2c_master_enable(const struct i2c_master_module *const module){
/* Sanity check of arguments */
Assert(module);
Assert(module->hw);
SercomI2cm *const i2c_module = &(module->hw->I2CM);
/* Timeout counter used to force bus state */
uint32_t timeout_counter = 0;
/* Wait for module to sync */
_i2c_master_wait_for_sync(module);
/* Enable module */
i2c_module->CTRLA.reg |= SERCOM_I2CM_CTRLA_ENABLE;
#if I2C_MASTER_CALLBACK_MODE == true
/* Enable module interrupts */
system_interrupt_enable(_sercom_get_interrupt_vector(module->hw));
#endif
/* Start timeout if bus state is unknown */
while (!(i2c_module->STATUS.reg & SERCOM_I2CM_STATUS_BUSSTATE(1))) {
timeout_counter++;
if(timeout_counter >= (module->unknown_bus_state_timeout)) {
/* Timeout, force bus state to idle */
i2c_module->STATUS.reg = SERCOM_I2CM_STATUS_BUSSTATE(1);
/* Workaround #1 */
return;
}
This is in the same file and is giving me issues
enum status_code i2c_master_init(struct i2c_master_module *const module,
Sercom *const hw, const struct i2c_master_config *const config);
this is my code.....
#include <asf.h>
void initialize(void)
{
system_init();
delay_init();
ioport_init();
}
//I2c slave address
#define AM2315_ADDRESS 0xB8 //temp and humity sensor
#define DATA_LENGTH 10
#define TIMEOUT 1000 //number of time to try and send the packet before it
fails
static uint8_t write_buffer[DATA_LENGTH] = {0x00, 0x01, 0x02, 0x03, 0x04,
0x05, 0x06, 0x07, 0x08, 0x09};
static uint8_t read_buffer[DATA_LENGTH];
struct i2c_master_module i2c_master_instance;
void configure_i2c_master(void)
{
/* Initialize config structure and software module. */
struct i2c_master_config config_i2c_master;
i2c_master_get_config_defaults(&config_i2c_master);
/* Change buffer timeout to something longer. */
config_i2c_master.buffer_timeout = 10000;
/* Initialize and enable device with config. */
i2c_master_init(&i2c_master_instance, SERCOM1, &config_i2c_master);
i2c_master_enable(&i2c_master_instance);
}
int main (void)
{
initialize();
configure_i2c_master();
uint8_t timeout = 0 ;
struct i2c_master_packet packet ={
.address = AM2315_ADDRESS,
.data_length = DATA_LENGTH,
.data = write_buffer,
.ten_bit_address = false,
.hs_master_code = 0x0,
};
ioport_set_pin_dir(PIN_PA27, IOPORT_DIR_OUTPUT);
while(1)
{
delay_ms(1000);
ioport_set_pin_level(PIN_PA27, true);
delay_ms(1000);
ioport_set_pin_level(PIN_PA27, false);
//write buffer to slave untill success
while(i2c_master_read_packet_wait(&i2c_master_instance, &packet) !=
STATUS_OK){
if (timeout++ ==TIMEOUT){
break;
}
}
//Read from slave until success.
packet.data = read_buffer;
while (i2c_master_read_packet_wait(&i2c_master_instance, &packet) !=
STATUS_OK){
if (timeout++ ==TIMEOUT){
break;
}
}
}
}
this is my output...
------ Build started: Project: GccBoardProject1, Configuration: Debug ARM --
Build started.
Project "GccBoardProject1.cproj" (default targets):
Target "PreBuildEvent" skipped, due to false condition;
('$(PreBuildEvent)'!='') was evaluated as (''!='').
Target "CoreBuild" in file "C:\Program Files
(x86)\Atmel\Studio\7.0\Vs\Compiler.targets" from project
"C:\Users\benvo\Documents\Atmel
Studio\7.0\GccBoardProject1\GccBoardProject1\GccBoardProject1.cproj"
(target "Build" depends on it):
Task "RunCompilerTask"
Shell Utils Path C:\Program Files (x86)\Atmel\Studio\7.0\shellUtils
C:\Program Files (x86)\Atmel\Studio\7.0\shellUtils\make.exe all --jobs 4 --
output-sync
C:\Users\benvo\Documents\Atmel
Studio\7.0\GccBoardProject1\GccBoardProject1\src\main.c(35,6): warning: no
previous prototype for 'initialize' [-Wmissing-prototypes]
void initialize(void)
^~~~~~~~~~
C:\Users\benvo\Documents\Atmel
Studio\7.0\GccBoardProject1\GccBoardProject1\src\main.c(58,6): warning: no
previous prototype for 'configure_i2c_master' [-Wmissing-prototypes]
void configure_i2c_master(void)
^~~~~~~~~~~~~~~~~~~~
Building file: ../src/main.c
Invoking: ARM/GNU C Compiler : 6.3.1
"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-
toolchain\bin\arm-none-eabi-gcc.exe" -x c -mthumb -D__SAMD21G18A__ -DDEBUG
-DBOARD=USER_BOARD -DARM_MATH_CM0PLUS=true -DSYSTICK_MODE -
DI2C_MASTER_CALLBACK_MODE=true -I"../src/ASF/common/boards" -
I"../src/ASF/sam0/utils" -I"../src/ASF/sam0/utils/header_files" -
I"../src/ASF/sam0/utils/preprocessor" -
I"../src/ASF/thirdparty/CMSIS/Include" -
I"../src/ASF/thirdparty/CMSIS/Lib/GCC" -I"../src/ASF/common/utils" -
I"../src/ASF/sam0/utils/cmsis/samd21/include" -
I"../src/ASF/sam0/utils/cmsis/samd21/source" -
I"../src/ASF/sam0/drivers/system"
-I"../src/ASF/sam0/drivers/system/clock/clock_samd21_r21_da_ha1" -
I"../src/ASF/sam0/drivers/system/clock" -
I"../src/ASF/sam0/drivers/system/interrupt" -
I"../src/ASF/sam0/drivers/system/interrupt/system_interrupt_samd21" -
I"../src/ASF/sam0/drivers/system/pinmux" -
I"../src/ASF/sam0/drivers/system/power" -
I"../src/ASF/sam0/drivers/system/power/power_sam_d_r_h" -
I"../src/ASF/sam0/drivers/system/reset" -
I"../src/ASF/sam0/drivers/system/reset/reset_sam_d_r_h" -
I"../src/ASF/common2/boards/user_board" -I"../src" -I"../src/config" -
I"../src/ASF/common2/services/delay" -
I"../src/ASF/common2/services/delay/sam0" -
I"../src/ASF/common/services/ioport" -I"../src/ASF/sam0/drivers/sercom" -
I"../src/ASF/sam0/drivers/sercom/i2c" -O1 -fdata-sections -ffunction-
sections -mlong-calls -g3 -Wall -mcpu=cortex-m0plus -c -pipe -fno-strict-
aliasing -Wall -Wstrict-prototypes -Wmissing-prototypes -Werror-implicit-
function-declaration -Wpointer-arith -std=gnu99 -ffunction-sections -fdata-
sections -Wchar-subscripts -Wcomment -Wformat=2 -Wimplicit-int -Wmain -
Wparentheses -Wsequence-point -Wreturn-type -Wswitch -Wtrigraphs -Wunused -
Wuninitialized -Wunknown-pragmas -Wfloat-equal -Wundef -Wshadow -Wbad-
function-cast -Wwrite-strings -Wsign-compare -Waggregate-return -Wmissing-
declarations -Wformat -Wmissing-format-attribute -Wno-deprecated-
declarations -Wpacked -Wredundant-decls -Wnested-externs -Wlong-long -
Wunreachable-code -Wcast-align --param max-inline-insns-single=500 -MD -MP -
MF "src/main.d" -MT"src/main.d" -MT"src/main.o" -o "src/main.o"
"../src/main.c"
Finished building: ../src/main.c
src/main.o: In function `configure_i2c_master':
C:\Users\benvo\Documents\Atmel
Studio\7.0\GccBoardProject1\GccBoardProject1\src\main.c(69,1): error:
undefined reference to `i2c_master_init'
src/main.o: In function `main':
C:\Users\benvo\Documents\Atmel
Studio\7.0\GccBoardProject1\GccBoardProject1\src\main.c(103,1): error:
undefined reference to `i2c_master_read_packet_wait'
collect2.exe(0,0): error: ld returned 1 exit status
make: *** [GccBoardProject1.elf] Error 1
Building target: GccBoardProject1.elf
Invoking: ARM/GNU Linker : 6.3.1
"C:\Program Files (x86)\Atmel\Studio\7.0\toolchain\arm\arm-gnu-
toolchain\bin\arm-none-eabi-gcc.exe" -o GccBoardProject1.elf
src/ASF/sam0/drivers/sercom/sercom.o
src/ASF/sam0/drivers/sercom/sercom_interrupt.o
src/ASF/common2/services/delay/sam0/systick_counter.o
src/ASF/common2/boards/user_board/init.o
src/ASF/common/utils/interrupt/interrupt_sam_nvic.o
src/ASF/sam0/drivers/system/clock/clock_samd21_r21_da_ha1/clock.o
src/ASF/sam0/drivers/system/clock/clock_samd21_r21_da_ha1/gclk.o
src/ASF/sam0/drivers/system/interrupt/system_interrupt.o
src/ASF/sam0/drivers/system/pinmux/pinmux.o
src/ASF/sam0/drivers/system/system.o
src/ASF/sam0/utils/cmsis/samd21/source/gcc/startup_samd21.o
src/ASF/sam0/utils/cmsis/samd21/source/system_samd21.o
src/ASF/sam0/utils/syscalls/gcc/syscalls.o src/main.o -mthumb -Wl,-
Map="GccBoardProject1.map" --specs=nano.specs -Wl,--start-group -
larm_cortexM0l_math -lm -Wl,--end-group -
L"../src/ASF/thirdparty/CMSIS/Lib/GCC" -Wl,--gc-sections -mcpu=cortex-
m0plus -Wl,--entry=Reset_Handler -Wl,--cref -mthumb -
T../src/ASF/sam0/utils/linker_scripts/samd21/gcc/samd21g18a_flash.ld
C:\Users\benvo\Documents\Atmel
Studio\7.0\GccBoardProject1\GccBoardProject1\Debug\Makefile(333,1): error:
recipe for target 'GccBoardProject1.elf' failed
The command exited with code 2.
Done executing task "RunCompilerTask" -- FAILED.
Done building target "CoreBuild" in project "GccBoardProject1.cproj" --
FAILED.
Done building project "GccBoardProject1.cproj" -- FAILED.
Build FAILED.
========== Build: 0 succeeded or up-to-date, 1 failed, 0 skipped ==========
TAGS:
Tools, Atmel Software Framework (ASF) and Atmel Start (ASFv4), all
architectures
I have the following test setup below. When I compile the code, I get the following error: The initUSART is not recognized, but I have included the file in the appropriate places. Did I miss out on anything?
AVR Development Stack
Get input main.c file...
Compile code and return elf file...
main.o: In function `main':
/Users/sebastianscharf/Documents/Hardware/AVR/main.c:14: undefined reference to `initUSART'
collect2: error: ld returned 1 exit status
avr-objcopy: 'main.elf': No such file
avrdude: AVR device initialized and ready to accept instructions
Reading | ################################################## | 100% 0.00s
avrdude: Device signature = 0x1e950f (probably m328p)
avrdude: Expected signature for ATmega328 is 1E 95 14
Double check chip, or use -F to override this check.
avrdude done. Thank you.
main file
#include "config.h"
#include <avr/io.h>
#include <util/delay.h>
#include "USART.h"
int main(void) {
char serialCharacter;
// --- INITS --- //
DDRB = 0xff;
// Set up LED for output
initUSART();
return 0;
}
USART.h
/* These are defined for convenience */
#define USART_HAS_DATA bit_is_set(UCSR0A, RXC0)
#define USART_READY bit_is_set(UCSR0A, UDRE0)
uint8_t receiveByte(void);
/* Takes the defined BAUD and F_CPU, calculates the bit-clock multiplier, and configures the hardware USART*/
void initUSART(void);
USART.c
#include "config.h"
#include <avr/io.h>
#include <util/setbaud.h>
#include "USART.h"
void initUSART(void) {
// Requires BAUD
UBRR0H = UBRRH_VALUE;
UBRR0L = UBRRL_VALUE;
#if USE_2X
UCSR0A |= (1 << U2X0);
#else
UCSR0A &= ~(1 << U2X0);
#endif
// Enable USART
UCSR0B = (1 << TXEN0) | (1 << RXEN0);
UCSR0C = (1 << UCSZ01) | (1 << UCSZ00); // 8 data bits, 1 stop bit
}
bash file
#!/bin/bash
source bashColors.sh
echo -e "${IGreen}AVR Development Stack${Reset}"
echo -e "${BIBlue}Get input main.c file...${Reset}"
avr-gcc -g -Os -mmcu=atmega328p -c main.c USART.c &&
echo -e "${BIBlue}Compile code and return elf file...${Reset}"
avr-gcc -g -mmcu=atmega328p -o main.elf main.o &&
echo -e "${BIBlue}Convert elf file to hex...${Reset}"
# avr-objcopy converts into hex file. -j indicates that we want the information from the .text and .data segment extracted.
avr-objcopy -j .text -j .data -O ihex main.elf out.hex &&
echo -e "${BIBlue}Uploading data to microcontroller...${Reset}"
avrdude -c usbtiny -p m328
You compiled both source files properly to .o with:
avr-gcc -g -Os -mmcu=atmega328p -c main.c USART.c
Now the main.o file contains an external reference to initUSART (partly thanks to the #include "USART.h" directive which provides the proper prototype)
but this link line:
avr-gcc -g -mmcu=atmega328p -o main.elf main.o &&
only references main.o. You need to add USART.o so the symbol is resolved by the linker (the linker doesn't care about .h include files), like this:
avr-gcc -g -mmcu=atmega328p -o main.elf main.o USART.o &&
I'm trying to compile a C program that uses libvncserver but no matter what I do I keep getting undefined reference errors, the library I'm having troubles with is rfb/rfb.h.
vnc.c code (copied from here):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <rfb/rfb.h>
#define WIDTH 640
#define HEIGHT 480
#define BPP 4
/* 15 frames per second (if we can) */
#define PICTURE_TIMEOUT (1.0/15.0)
/*
* throttle camera updates
*/
int TimeToTakePicture() {
static struct timeval now={0,0}, then={0,0};
double elapsed, dnow, dthen;
gettimeofday(&now,NULL);
dnow = now.tv_sec + (now.tv_usec /1000000.0);
dthen = then.tv_sec + (then.tv_usec/1000000.0);
elapsed = dnow - dthen;
if (elapsed > PICTURE_TIMEOUT)
memcpy((char *)&then, (char *)&now, sizeof(struct timeval));
return elapsed > PICTURE_TIMEOUT;
}
/*
* simulate grabbing a picture from some device
*/
int TakePicture(unsigned char *buffer)
{
static int last_line=0, fps=0, fcount=0;
int line=0;
int i,j;
struct timeval now;
/*
* simulate grabbing data from a device by updating the entire framebuffer
*/
for(j=0;j<HEIGHT;++j) {
for(i=0;i<WIDTH;++i) {
buffer[(j*WIDTH+i)*BPP+0]=(i+j)*128/(WIDTH+HEIGHT); /* red */
buffer[(j*WIDTH+i)*BPP+1]=i*128/WIDTH; /* green */
buffer[(j*WIDTH+i)*BPP+2]=j*256/HEIGHT; /* blue */
}
buffer[j*WIDTH*BPP+0]=0xff;
buffer[j*WIDTH*BPP+1]=0xff;
buffer[j*WIDTH*BPP+2]=0xff;
}
/*
* simulate the passage of time
*
* draw a simple black line that moves down the screen. The faster the
* client, the more updates it will get, the smoother it will look!
*/
gettimeofday(&now,NULL);
line = now.tv_usec / (1000000/HEIGHT);
if (line>HEIGHT) line=HEIGHT-1;
memset(&buffer[(WIDTH * BPP) * line], 0, (WIDTH * BPP));
/* frames per second (informational only) */
fcount++;
if (last_line > line) {
fps = fcount;
fcount = 0;
}
last_line = line;
fprintf(stderr,"%03d/%03d Picture (%03d fps)\r", line, HEIGHT, fps);
/* success! We have a new picture! */
return (1==1);
}
/*
* Single-threaded application that interleaves client servicing with taking
* pictures from the camera. This way, we do not update the framebuffer
* while an encoding is working on it too (banding, and image artifacts).
*/
int main(int argc,char** argv)
{
long usec;
rfbScreenInfoPtr server=rfbGetScreen(&argc,argv,WIDTH,HEIGHT,8,3,BPP);
if(!server)
return 0;
server->desktopName = "Live Video Feed Example";
server->frameBuffer=(char*)malloc(WIDTH*HEIGHT*BPP);
server->alwaysShared=(1==1);
/* Initialize the server */
rfbInitServer(server);
/* Loop, processing clients and taking pictures */
while (rfbIsActive(server)) {
if (TimeToTakePicture())
if (TakePicture((unsigned char *)server->frameBuffer))
rfbMarkRectAsModified(server,0,0,WIDTH,HEIGHT);
usec = server->deferUpdateTime*1000;
rfbProcessEvents(server,usec);
}
return(0);
}
Compiler output:
sudo gcc -g -Wall -Wextra -O2 vnc.c
/tmp/cc7dpMCs.o: In function `main':
/home/arcm/Projects/c/vnc.c:77: undefined reference to `rfbGetScreen'
/home/arcm/Projects/c/vnc.c:84: undefined reference to `rfbInitServerWithPthreadsAndZRLE'
/home/arcm/Projects/c/vnc.c:91: undefined reference to `rfbProcessEvents'
/home/arcm/Projects/c/vnc.c:86: undefined reference to `rfbIsActive'
/home/arcm/Projects/c/vnc.c:89: undefined reference to `rfbMarkRectAsModified'
collect2: error: ld returned 1 exit status
I've got libvncserver0 and libvncserver-dev installed and i'm using ubuntu 14.04.
I tried:
sudo gcc -g -Wall -Wextra -O2 vnc.c -lm
sudo gcc -g -Wall -Wextra -O2 vnc.c -ldl
sudo gcc -g -Wall -Wextra -O2 -ldl vnc.c
sudo gcc -g -Wall -Wextra -O2 -I/usr/include/rfb -L/usr/include/rbf/rfb.h vnc.c
sudo gcc -g -Wall -Wextra -O2 -I/usr/include/rfb vnc.c
sudo gcc -g -Wall -Wextra -O2 -L/usr/include/rbf/rfb.h vnc.c
sudo gcc -g -Wall -Wextra -O2 /usr/include/rbf/rfb.h vnc.c
sudo gcc -g -Wall -Wextra -O2 -L/usr/include/rbf/rfb.h -ldl vnc.c
But I get the same errors everytime. What am I doing wrong and how can I fix it?
You don't "link" a library header, you include it so the compiler sees the library's declarations at compile time and knows that rfbGetScreen() is a function that takes so-and-so many arguments of this-and-that type and returns a rfbScreenInfoPtr. How it does this (the definition of the function) is not important to the compiler. It just adds a reference to that function, which is left for the linker to resolve. (Note the vocabulary here.)
You link your compiled code to the library binary. This is done by the linker, in a different (and later) step that just happens to be supported by the same frontend as compiling your source (gcc). In this step, any of the library functions that your code actually uses (references) are resolved by linking them in from the specified libraries.
This here...
sudo gcc -g -Wall -Wextra -O2 vnc.c
...links only the standard library and runtime, as there are no specific linking instructions in there.
This here...
-L/usr/include/rbf/rfb.h
...is nonsense, as -L is for giving directories where library binaries should be looked for (and is not necessary if the library in question is installed in the standard location).
The actual link instruction is -l. If you state -lfoo, then the library libfoo is searched for any undefined references.
This here...
-ldl
...is linking libdl, and from that you should be able to deduce that...
-lvncserver
...is what you're looking for (assuming that <rfb/rfb.h> actually does refer to libvncserver, which I don't know).
Note that the linker is processing libraries in the sequence they are given on the command line, so you need to state -lvncserver after vnc.c, because only then does the linker know which undefined references it ought to be looking for in libvncserver.
And never, ever run a compiler as sudo. Why in {.....}'s name do you think this would be necessary?